├── examples ├── net0.pub ├── net0.key ├── net0-ap1.key ├── net0-ap2.key ├── net0-master.key ├── net0.bin ├── net0.peers ├── test-net0.sh └── net0.json ├── .gitignore ├── udht.h ├── chacha20.h ├── token.h ├── curve25519.h ├── auth-data.h ├── ubus.h ├── auth-data.c ├── siphash.h ├── stun.h ├── service.h ├── fprime.h ├── unetd.h ├── CMakeLists.txt ├── sha512.h ├── edsign.h ├── wg.h ├── siphash.c ├── curve25519.c ├── rtnl.c ├── mss-bpf.c ├── ed25519.h ├── enroll.h ├── pex.h ├── bpf.c ├── f25519.h ├── dht.h ├── network.h ├── wg.c ├── fprime.c ├── utils.h ├── host.h ├── pex-msg.h ├── udht-ubus.c ├── edsign.c ├── bpf_skb_utils.h ├── stun.c ├── PEX.md ├── main.c ├── service.c ├── token.c ├── scripts └── update-cmd.pl ├── f25519.c ├── chacha20.c ├── utils.c ├── linux └── wireguard.h ├── sha512.c ├── ed25519.c ├── wg-linux.c ├── pex-stun.c ├── vxlan.c ├── wg-user.c └── host.c /examples/net0.pub: -------------------------------------------------------------------------------- 1 | z+DZB9UkXV+69h8cdukSfLxTdMWDcF8wyrjqWa1THuQ= -------------------------------------------------------------------------------- /examples/net0.key: -------------------------------------------------------------------------------- 1 | EHCvNDZW7v+WjAeFT6EYXhIHm8exRdjnlz35hxefgWg= 2 | -------------------------------------------------------------------------------- /examples/net0-ap1.key: -------------------------------------------------------------------------------- 1 | 0GFjANSeGOgsKPQrXm64K8qcf8B5dZGa830SOMGZq0A= 2 | -------------------------------------------------------------------------------- /examples/net0-ap2.key: -------------------------------------------------------------------------------- 1 | +E8d8Yjqd9KFRE4yWccEUNIdVABD/2FwNB8lC43K3kg= 2 | -------------------------------------------------------------------------------- /examples/net0-master.key: -------------------------------------------------------------------------------- 1 | wC0/vC8jGcYZnu0ncTYQa/XM2NdZWmP06Gb+/eHOOnc= 2 | -------------------------------------------------------------------------------- /examples/net0.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwrt/unetd/master/examples/net0.bin -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | CMakeCache.txt 3 | CMakeFiles 4 | *.cmake 5 | *.a 6 | *.so 7 | *.dylib 8 | unetd 9 | unet-tool 10 | unet-dht 11 | -------------------------------------------------------------------------------- /examples/net0.peers: -------------------------------------------------------------------------------- 1 | { 2 | "test": { 3 | "key": "uEXsoKYc31VH/kzv55eHVmKlzvmoGOPwA7xeTjGgLXc=", 4 | "endpoint": "192.168.1.4:51830", 5 | "ipaddr": [ "192.168.99.1" ] 6 | } 7 | } 8 | 9 | -------------------------------------------------------------------------------- /udht.h: -------------------------------------------------------------------------------- 1 | #ifndef __UDHT_H 2 | #define __UDHT_H 3 | 4 | int udht_reconnect(void); 5 | void udht_network_add(const uint8_t *auth_key, int seq); 6 | void udht_network_flush(int seq); 7 | 8 | #ifdef UBUS_SUPPORT 9 | void udht_ubus_init(void); 10 | #else 11 | static inline void udht_ubus_init(void) 12 | { 13 | } 14 | #endif 15 | 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /chacha20.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2022 Felix Fietkau 4 | */ 5 | #ifndef __UNETD_CHACHA20_H 6 | #define __UNETD_CHACHA20_H 7 | 8 | #define CHACHA20_NONCE_SIZE 8 9 | #define CHACHA20_KEY_SIZE 32 10 | 11 | void chacha20_encrypt_msg(void *msg, size_t len, const void *nonce, const void *key); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /token.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2025 Felix Fietkau 4 | */ 5 | #ifndef __UNETD_TOKEN_H 6 | #define __UNETD_TOKEN_H 7 | 8 | void *token_create(struct network *net, struct network_host *target, 9 | const char *service, struct blob_attr *info, size_t *len); 10 | bool token_parse(struct blob_buf *buf, const char *token); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /examples/test-net0.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ifname="${1:-wg0}" 3 | host="${2:-ap1}" 4 | 5 | ip link add dev $ifname type wireguard > /dev/null 2>&1 6 | 7 | # [ "$ifname" != "net0" ] && ln -sf net0.bin "${ifname}.bin" 8 | 9 | ../unetd -D $PWD -d -h $PWD/hosts -N '{ 10 | "name": "'"$ifname"'", 11 | "type": "dynamic", 12 | "auth_key": "'"$(cat ./net0.pub)"'", 13 | "key": "'"$(cat ./net0-${host}.key)"'", 14 | "file": "'"$PWD/net0.json"'", 15 | "tunnels": { 16 | "vx0": "l2-tunnel" 17 | }, 18 | "peer_data": [ "'"$PWD/net0.peers"'" ], 19 | "update-cmd": "'"$PWD/../scripts/update-cmd.pl"'" 20 | }' 21 | -------------------------------------------------------------------------------- /curve25519.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #ifndef CURVE25519_H 7 | #define CURVE25519_H 8 | 9 | #include 10 | #include 11 | 12 | enum curve25519_lengths { 13 | CURVE25519_KEY_SIZE = 32 14 | }; 15 | 16 | void curve25519(uint8_t mypublic[static CURVE25519_KEY_SIZE], const uint8_t secret[static CURVE25519_KEY_SIZE], const uint8_t basepoint[static CURVE25519_KEY_SIZE]); 17 | void curve25519_generate_public(uint8_t pub[static CURVE25519_KEY_SIZE], const uint8_t secret[static CURVE25519_KEY_SIZE]); 18 | static inline void curve25519_clamp_secret(uint8_t secret[static CURVE25519_KEY_SIZE]) 19 | { 20 | secret[0] &= 248; 21 | secret[31] = (secret[31] & 127) | 64; 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /auth-data.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2022 Felix Fietkau 4 | */ 5 | #ifndef __AUTH_DATA_H 6 | #define __AUTH_DATA_H 7 | 8 | #include 9 | 10 | #include 11 | 12 | #include "edsign.h" 13 | #include "curve25519.h" 14 | 15 | #define UNET_AUTH_MAGIC 0x754e6574 16 | 17 | struct unet_auth_hdr { 18 | uint32_t magic; 19 | 20 | uint8_t version; 21 | uint8_t _pad[3]; 22 | 23 | uint8_t signature[EDSIGN_SIGNATURE_SIZE]; 24 | } __packed; 25 | 26 | struct unet_auth_data { 27 | uint64_t timestamp; 28 | uint8_t pubkey[CURVE25519_KEY_SIZE]; 29 | uint32_t flags; 30 | } __packed; 31 | 32 | int unet_auth_data_validate(const uint8_t *key, const void *buf, size_t len, 33 | uint64_t *timestamp, const char **json_data); 34 | 35 | static inline const struct unet_auth_data * 36 | net_data_auth_data_hdr(const void *net_data) 37 | { 38 | return net_data + sizeof(struct unet_auth_hdr); 39 | } 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /examples/net0.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "port": 3456, 4 | "peer-exchange-port": 3458, 5 | "keepalive": 10 6 | }, 7 | "hosts": { 8 | "master": { 9 | "key": "25sPrbtEtIiANFr00tC5MS2UMfXmHFj/AJyDi4wR8n4=", 10 | "endpoint": "192.168.1.3", 11 | "subnet": [ 12 | "192.168.3.0/24" 13 | ], 14 | "ipaddr": [ 15 | "192.168.3.1" 16 | ] 17 | }, 18 | "ap1": { 19 | "key": "mxQQxpwinlDxy0bp564b25il1oDiaf/a8jkaKQBcjw4=", 20 | "groups": [ 21 | "ap" 22 | ], 23 | "subnet": [ 24 | "192.168.4.0/24" 25 | ], 26 | "ipaddr": [ 27 | "192.168.4.1" 28 | ], 29 | "port": 3457 30 | }, 31 | "ap2": { 32 | "key": "+hiP+1FZci9Hp44gWEPigbsMHMe6De7nnMbVDJFhDjU=", 33 | "groups": [ 34 | "ap" 35 | ], 36 | "subnet": [ 37 | "192.168.5.0/24" 38 | ], 39 | "ipaddr": [ 40 | "192.168.5.1" 41 | ], 42 | "port": 3457 43 | } 44 | }, 45 | "services": { 46 | "l2-tunnel": { 47 | "type": "vxlan", 48 | "config": { 49 | }, 50 | "members": [ 51 | "master", 52 | "@ap" 53 | ] 54 | }, 55 | "usteer": { 56 | "members": [ 57 | "@ap" 58 | ] 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /ubus.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2022 Felix Fietkau 4 | */ 5 | #ifndef __UNETD_UBUS_H 6 | #define __UNETD_UBUS_H 7 | 8 | #ifdef UBUS_SUPPORT 9 | void unetd_ubus_init(void); 10 | void unetd_ubus_notify(const char *type, struct blob_attr *data); 11 | void unetd_ubus_network_notify(struct network *net); 12 | void unetd_ubus_netifd_update(struct blob_attr *data); 13 | void unetd_ubus_netifd_add_route(struct network *net, union network_endpoint *ep); 14 | struct blob_attr *unetd_ubus_get_network_addr_list(const char *name); 15 | #else 16 | static inline void unetd_ubus_init(void) 17 | { 18 | } 19 | static inline void unetd_ubus_notify(const char *type, struct blob_attr *data) 20 | { 21 | } 22 | static inline void unetd_ubus_network_notify(struct network *net) 23 | { 24 | } 25 | static inline void unetd_ubus_netifd_update(struct blob_attr *data) 26 | { 27 | } 28 | static inline void unetd_ubus_netifd_add_route(struct network *net, union network_endpoint *ep) 29 | { 30 | } 31 | static inline struct blob_attr *unetd_ubus_get_network_addr_list(const char *name) 32 | { 33 | return NULL; 34 | } 35 | #endif 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /auth-data.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2022 Felix Fietkau 4 | */ 5 | #include "edsign.h" 6 | #include "ed25519.h" 7 | #include "auth-data.h" 8 | 9 | int unet_auth_data_validate(const uint8_t *key, const void *buf, size_t len, 10 | uint64_t *timestamp, const char **json_data) 11 | { 12 | const struct unet_auth_hdr *hdr = buf; 13 | const struct unet_auth_data *data = net_data_auth_data_hdr(buf); 14 | struct edsign_verify_state vst; 15 | 16 | if (len <= sizeof(*hdr) + sizeof(*data)) 17 | return -1; 18 | 19 | len -= sizeof(*hdr); 20 | 21 | if (hdr->magic != cpu_to_be32(UNET_AUTH_MAGIC) || 22 | hdr->version != 0 || data->flags != 0 || 23 | data->timestamp == 0) 24 | return -1; 25 | 26 | if (key && memcmp(data->pubkey, key, EDSIGN_PUBLIC_KEY_SIZE) != 0) 27 | return -2; 28 | 29 | edsign_verify_init(&vst, hdr->signature, data->pubkey); 30 | edsign_verify_add(&vst, data, len); 31 | if (!edsign_verify(&vst, hdr->signature, data->pubkey)) 32 | return -3; 33 | 34 | if (((char *)data)[len - 1] != 0) 35 | return -2; 36 | 37 | if (timestamp) 38 | *timestamp = be64_to_cpu(data->timestamp); 39 | 40 | if (json_data) 41 | *json_data = (const char *)(data + 1); 42 | 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /siphash.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2016 Jason A. Donenfeld . All Rights Reserved. 2 | * 3 | * This file is provided under a dual BSD/GPLv2 license. 4 | * 5 | * SipHash: a fast short-input PRF 6 | * https://131002.net/siphash/ 7 | * 8 | * This implementation is specifically for SipHash2-4 for a secure PRF 9 | * and HalfSipHash1-3/SipHash1-3 for an insecure PRF only suitable for 10 | * hashtables. 11 | */ 12 | 13 | #ifndef _LINUX_SIPHASH_H 14 | #define _LINUX_SIPHASH_H 15 | 16 | #include 17 | #include 18 | #include 19 | #include "utils.h" 20 | 21 | #define SIPHASH_ALIGNMENT __alignof__(uint64_t) 22 | typedef struct { 23 | uint64_t key[2]; 24 | } siphash_key_t; 25 | 26 | static inline bool siphash_key_is_zero(const siphash_key_t *key) 27 | { 28 | return !(key->key[0] | key->key[1]); 29 | } 30 | 31 | uint64_t siphash(const void *data, size_t len, const siphash_key_t *key); 32 | 33 | static inline void siphash_to_le64(void *dest, const void *data, size_t len, 34 | const siphash_key_t *key) 35 | { 36 | uint64_t hash = siphash(data, len, key); 37 | 38 | *(uint64_t *)dest = cpu_to_le64(hash); 39 | } 40 | 41 | static inline void siphash_to_be64(void *dest, const void *data, size_t len, 42 | const siphash_key_t *key) 43 | { 44 | uint64_t hash = siphash(data, len, key); 45 | 46 | *(uint64_t *)dest = cpu_to_be64(hash); 47 | } 48 | 49 | #endif /* _LINUX_SIPHASH_H */ 50 | -------------------------------------------------------------------------------- /stun.h: -------------------------------------------------------------------------------- 1 | #ifndef __UNETD_STUN_H 2 | #define __UNETD_STUN_H 3 | 4 | #include 5 | #include 6 | 7 | #define STUN_MSGTYPE_BINDING_REQUEST 0x0001 8 | #define STUN_MSGTYPE_BINDING_RESPONSE 0x0101 9 | #define STUN_MSGTYPE_BINDING_ERROR 0x0111 10 | #define STUN_MSGTYPE_BINDING_INDICATION 0x0011 11 | 12 | #define STUN_MSGTYPE_SHARED_SECRET_REQUEST 0x0002 13 | #define STUN_MSGTYPE_SHARED_SECRET_RESPONSE 0x0102 14 | #define STUN_MSGTYPE_SHARED_SECRET_ERROR 0x0112 15 | 16 | #define STUN_MAGIC 0x2112a442 17 | 18 | enum tlv_type { 19 | STUN_TLV_MAPPED_ADDRESS = 0x01, 20 | STUN_TLV_RESPONSE_ADDRESS = 0x02, 21 | STUN_TLV_CHANGE_REQUEST = 0x03, 22 | STUN_TLV_SOURCE_ADDRESS = 0x04, 23 | STUN_TLV_CHANGED_ADDRESS = 0x05, 24 | STUN_TLV_XOR_MAPPED_ADDRESS = 0x20, 25 | STUN_TLV_RESPONSE_PORT = 0x27, 26 | }; 27 | 28 | struct stun_msg_hdr { 29 | uint16_t msg_type; 30 | uint16_t msg_len; 31 | uint32_t magic; 32 | uint8_t transaction[12]; 33 | }; 34 | 35 | struct stun_msg_tlv { 36 | uint16_t type; 37 | uint16_t len; 38 | }; 39 | 40 | struct stun_tlv_policy { 41 | uint16_t type; 42 | uint16_t min_len; 43 | }; 44 | 45 | struct stun_request { 46 | uint8_t transaction[12]; 47 | uint16_t port; 48 | bool pending; 49 | }; 50 | 51 | bool stun_msg_is_valid(const void *data, size_t len); 52 | const void *stun_msg_request_prepare(struct stun_request *req, size_t *len, 53 | uint16_t response_port); 54 | bool stun_msg_request_complete(struct stun_request *req, const void *data, 55 | size_t len); 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /service.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2022 Felix Fietkau 4 | */ 5 | #ifndef __UNETD_SERVICE_H 6 | #define __UNETD_SERVICE_H 7 | 8 | struct vxlan_tunnel; 9 | struct service_ops; 10 | 11 | struct network_service { 12 | struct vlist_node node; 13 | 14 | struct blob_attr *config; 15 | 16 | const char *type; 17 | 18 | const struct service_ops *ops; 19 | union { 20 | struct vxlan_tunnel *vxlan; 21 | void *priv; 22 | }; 23 | 24 | int n_members; 25 | struct network_host *members[]; 26 | }; 27 | 28 | struct service_ops { 29 | void (*init)(struct network *net, 30 | struct network_service *s_new, 31 | struct network_service *s_old); 32 | void (*peer_update)(struct network *net, struct network_service *s, 33 | struct network_peer *peer); 34 | void (*free)(struct network *net, struct network_service *s); 35 | }; 36 | 37 | extern const struct service_ops vxlan_ops; 38 | 39 | static inline const char * 40 | network_service_name(struct network_service *s) 41 | { 42 | return s->node.avl.key; 43 | } 44 | 45 | void network_services_init(struct network *net); 46 | void network_services_free(struct network *net); 47 | void network_services_add(struct network *net, struct blob_attr *data); 48 | void network_services_peer_update(struct network *net, struct network_peer *peer); 49 | 50 | static inline void network_services_update_start(struct network *net) 51 | { 52 | vlist_update(&net->services); 53 | } 54 | 55 | static inline void network_services_update_done(struct network *net) 56 | { 57 | vlist_flush(&net->services); 58 | } 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /fprime.h: -------------------------------------------------------------------------------- 1 | /* Arithmetic in prime fields 2 | * Daniel Beer , 10 Jan 2014 3 | * 4 | * This file is in the public domain. 5 | */ 6 | 7 | #ifndef FPRIME_H_ 8 | #define FPRIME_H_ 9 | 10 | #include 11 | #include 12 | 13 | /* Maximum size of a field element (or a prime). Field elements are 14 | * always manipulated and stored in normalized form, with 0 <= x < p. 15 | * You can use normalize() to convert a denormalized bitstring to normal 16 | * form. 17 | * 18 | * Operations are constant with respect to the value of field elements, 19 | * but not with respect to the modulus. 20 | * 21 | * The modulus is a number p, such that 2p-1 fits in FPRIME_SIZE bytes. 22 | */ 23 | #define FPRIME_SIZE 32 24 | 25 | /* Load a large constant */ 26 | void fprime_from_bytes(uint8_t *x, 27 | const uint8_t *in, size_t len, 28 | const uint8_t *modulus); 29 | 30 | /* Copy an element */ 31 | static inline void fprime_copy(uint8_t *x, const uint8_t *a) 32 | { 33 | memcpy(x, a, FPRIME_SIZE); 34 | } 35 | 36 | /* Compare two field points in constant time. Return one if equal, zero 37 | * otherwise. This should be performed only on normalized values. 38 | */ 39 | uint8_t fprime_eq(const uint8_t *x, const uint8_t *y); 40 | 41 | /* Conditional copy. If condition == 0, then zero is copied to dst. If 42 | * condition == 1, then one is copied to dst. Any other value results in 43 | * undefined behaviour. 44 | */ 45 | void fprime_select(uint8_t *dst, 46 | const uint8_t *zero, const uint8_t *one, 47 | uint8_t condition); 48 | 49 | /* Add one value to another. The two pointers must be distinct. */ 50 | void fprime_add(uint8_t *r, const uint8_t *a, const uint8_t *modulus); 51 | 52 | /* Multiply two values to get a third. r must be distinct from a and b */ 53 | void fprime_mul(uint8_t *r, const uint8_t *a, const uint8_t *b, 54 | const uint8_t *modulus); 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /unetd.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2022 Felix Fietkau 4 | */ 5 | #ifndef __UNETD_H 6 | #define __UNETD_H 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #ifdef UBUS_SUPPORT 14 | #include 15 | #endif 16 | #include "utils.h" 17 | #include "siphash.h" 18 | #include "wg.h" 19 | #include "pex-msg.h" 20 | #include "pex.h" 21 | #include "network.h" 22 | #include "host.h" 23 | #include "service.h" 24 | #include "ubus.h" 25 | #include "auth-data.h" 26 | #include "chacha20.h" 27 | #include "token.h" 28 | 29 | extern const char *mssfix_path; 30 | extern const char *data_dir; 31 | extern int global_pex_port; 32 | bool unetd_debug_active(void); 33 | void unetd_debug_printf(const char *format, ...); 34 | #ifdef UBUS_SUPPORT 35 | void unetd_udebug_config(struct udebug_ubus *ctx, struct blob_attr *data, 36 | bool enabled); 37 | #endif 38 | 39 | #define D(format, ...) \ 40 | do { \ 41 | unetd_debug_printf("%s(%d) " format "\n", \ 42 | __func__, __LINE__, ##__VA_ARGS__); \ 43 | } while (0) 44 | 45 | #define D_NET(net, format, ...) D("network %s " format, network_name(net), ##__VA_ARGS__) 46 | #define D_HOST(net, host, format, ...) D_NET(net, "host %s " format, network_host_name(host), ##__VA_ARGS__) 47 | #define D_PEER(net, peer, format, ...) D_NET(net, "host %s " format, network_peer_name(peer), ##__VA_ARGS__) 48 | #define D_SERVICE(net, service, format, ...) D_NET(net, "service %s " format, network_service_name(service), ##__VA_ARGS__) 49 | 50 | #define UNETD_DATA_DIR "/etc/unetd" 51 | #define UNETD_MSS_BPF_PATH "/lib/bpf/mss.o" 52 | #define UNETD_MSS_PRIO_BASE 0x130 53 | 54 | #define UNETD_DATA_UPDATE_DELAY (10 * 1000) 55 | 56 | #define UNETD_PEX_HOST_ACITVE_TIMEOUT 60 57 | 58 | void unetd_write_hosts(void); 59 | int unetd_attach_mssfix(int ifindex, int mtu); 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | PROJECT(unetd C) 4 | 5 | 6 | SET(SOURCES 7 | main.c network.c host.c service.c 8 | pex.c pex-stun.c 9 | wg.c wg-user.c 10 | ) 11 | 12 | SET(RUNSTATEDIR /var/run) 13 | 14 | ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -Wmissing-declarations -DRUNSTATEDIR="${RUNSTATEDIR}") 15 | FIND_LIBRARY(libjson NAMES json-c json) 16 | 17 | OPTION(UBUS_SUPPORT "enable ubus support" ON) 18 | OPTION(VXLAN_SUPPORT "enable VXLAN support" ON) 19 | IF(CMAKE_SYSTEM_NAME STREQUAL "Linux") 20 | FIND_LIBRARY(nl nl-tiny) 21 | SET(SOURCES ${SOURCES} wg-linux.c) 22 | ELSE() 23 | SET(nl "") 24 | SET(VXLAN_SUPPORT OFF) 25 | ENDIF() 26 | IF(VXLAN_SUPPORT) 27 | find_library(bpf NAMES bpf) 28 | find_library(elf NAMES elf) 29 | find_library(zlib NAMES z) 30 | SET(SOURCES ${SOURCES} bpf.c vxlan.c rtnl.c) 31 | ADD_DEFINITIONS(-DVXLAN_SUPPORT) 32 | ELSE() 33 | SET(bpf "") 34 | SET(elf "") 35 | SET(zlib "") 36 | ENDIF() 37 | 38 | IF(UBUS_SUPPORT) 39 | SET(SOURCES ${SOURCES} ubus.c enroll.c token.c) 40 | SET(DHT_SOURCES ${DHT_SOURCES} udht-ubus.c) 41 | ADD_DEFINITIONS(-DUBUS_SUPPORT=1) 42 | FIND_LIBRARY(ubus ubus) 43 | FIND_LIBRARY(udebug NAMES udebug) 44 | ELSE() 45 | SET(ubus "") 46 | SET(udebug "") 47 | ENDIF() 48 | 49 | ADD_LIBRARY(unet SHARED curve25519.c siphash.c sha512.c fprime.c f25519.c ed25519.c edsign.c auth-data.c chacha20.c pex-msg.c utils.c stun.c) 50 | TARGET_LINK_LIBRARIES(unet ubox) 51 | 52 | ADD_EXECUTABLE(unetd ${SOURCES}) 53 | TARGET_LINK_LIBRARIES(unetd unet ubox ${ubus} blobmsg_json ${libjson} ${nl} ${bpf} ${elf} ${zlib} ${udebug}) 54 | 55 | ADD_EXECUTABLE(unet-tool cli.c) 56 | TARGET_LINK_LIBRARIES(unet-tool unet blobmsg_json ${libjson} ubox) 57 | 58 | ADD_EXECUTABLE(unet-dht dht.c udht.c ${DHT_SOURCES}) 59 | TARGET_LINK_LIBRARIES(unet-dht unet ${ubus} ubox) 60 | 61 | INSTALL(TARGETS unetd unet unet-tool unet-dht 62 | RUNTIME DESTINATION sbin 63 | LIBRARY DESTINATION lib 64 | ) 65 | -------------------------------------------------------------------------------- /sha512.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Felix Fietkau 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | /* SHA512 18 | * Daniel Beer , 22 Apr 2014 19 | * 20 | * This file is in the public domain. 21 | */ 22 | 23 | #ifndef SHA512_H_ 24 | #define SHA512_H_ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | /* Feed a full block in */ 32 | #define SHA512_BLOCK_SIZE 128 33 | 34 | /* SHA512 state. State is updated as data is fed in, and then the final 35 | * hash can be read out in slices. 36 | * 37 | * Data is fed in as a sequence of full blocks terminated by a single 38 | * partial block. 39 | */ 40 | struct sha512_state { 41 | uint64_t h[8]; 42 | uint8_t partial[SHA512_BLOCK_SIZE]; 43 | size_t len; 44 | }; 45 | 46 | /* Set up a new context */ 47 | void sha512_init(struct sha512_state *s); 48 | 49 | void sha512_add(struct sha512_state *s, const void *data, size_t len); 50 | 51 | /* Fetch a slice of the hash result. */ 52 | #define SHA512_HASH_SIZE 64 53 | 54 | void sha512_final(struct sha512_state *s, uint8_t *hash); 55 | 56 | static inline void * 57 | sha512_final_get(struct sha512_state *s) 58 | { 59 | sha512_final(s, s->partial); 60 | return s->partial; 61 | } 62 | 63 | void hmac_sha512(void *dest, const void *key, size_t key_len, 64 | const void *data, size_t data_len); 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /edsign.h: -------------------------------------------------------------------------------- 1 | /* Edwards curve signature system 2 | * Daniel Beer , 22 Apr 2014 3 | * 4 | * This file is in the public domain. 5 | */ 6 | 7 | #ifndef EDSIGN_H_ 8 | #define EDSIGN_H_ 9 | 10 | #include 11 | #include 12 | #include "sha512.h" 13 | 14 | /* This is the Ed25519 signature system, as described in: 15 | * 16 | * Daniel J. Bernstein, Niels Duif, Tanja Lange, Peter Schwabe, Bo-Yin 17 | * Yang. High-speed high-security signatures. Journal of Cryptographic 18 | * Engineering 2 (2012), 77–89. Document ID: 19 | * a1a62a2f76d23f65d622484ddd09caf8. URL: 20 | * http://cr.yp.to/papers.html#ed25519. Date: 2011.09.26. 21 | * 22 | * The format and calculation of signatures is compatible with the 23 | * Ed25519 implementation in SUPERCOP. Note, however, that our secret 24 | * keys are half the size: we don't store a copy of the public key in 25 | * the secret key (we generate it on demand). 26 | */ 27 | 28 | /* Any string of 32 random bytes is a valid secret key. There is no 29 | * clamping of bits, because we don't use the key directly as an 30 | * exponent (the exponent is derived from part of a key expansion). 31 | */ 32 | #define EDSIGN_SECRET_KEY_SIZE 32 33 | 34 | /* Given a secret key, produce the public key (a packed Edwards-curve 35 | * point). 36 | */ 37 | #define EDSIGN_PUBLIC_KEY_SIZE 32 38 | 39 | void edsign_sec_to_pub(void *pub, const void *secret); 40 | 41 | /* Produce a signature for a message. */ 42 | #define EDSIGN_SIGNATURE_SIZE 64 43 | 44 | void edsign_sign(uint8_t *signature, const uint8_t *pub, 45 | const uint8_t *secret, 46 | const uint8_t *message, size_t len); 47 | 48 | struct edsign_verify_state { 49 | struct sha512_state sha; 50 | }; 51 | 52 | void edsign_verify_init(struct edsign_verify_state *st, const void *sig, 53 | const void *pub); 54 | 55 | static inline void 56 | edsign_verify_add(struct edsign_verify_state *st, const void *data, int len) 57 | { 58 | sha512_add(&st->sha, data, len); 59 | } 60 | 61 | /* Verify a message signature. Returns non-zero if ok. */ 62 | bool edsign_verify(struct edsign_verify_state *st, const void *sig, const void *pub); 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /wg.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2022 Felix Fietkau 4 | */ 5 | #ifndef __UNETD_WG_H 6 | #define __UNETD_WG_H 7 | 8 | #define WG_KEY_LEN 32 9 | #define WG_KEY_LEN_HEX (WG_KEY_LEN * 2 + 1) 10 | 11 | enum wg_update_cmd { 12 | WG_PEER_CREATE, 13 | WG_PEER_UPDATE, 14 | WG_PEER_DELETE, 15 | }; 16 | 17 | struct network; 18 | struct network_peer; 19 | union network_endpoint; 20 | 21 | struct wg_ops { 22 | const char *name; 23 | 24 | bool (*check)(struct network *net); 25 | 26 | int (*init)(struct network *net); 27 | void (*cleanup)(struct network *net); 28 | int (*init_local)(struct network *net, struct network_peer *peer); 29 | int (*peer_refresh)(struct network *net); 30 | int (*peer_update)(struct network *net, struct network_peer *peer, 31 | enum wg_update_cmd cmd); 32 | int (*peer_connect)(struct network *net, struct network_peer *peer, 33 | union network_endpoint *ep); 34 | }; 35 | 36 | struct wg { 37 | const struct wg_ops *ops; 38 | }; 39 | 40 | extern const struct wg_ops wg_user_ops; 41 | extern const struct wg_ops wg_linux_ops; 42 | 43 | int wg_init_network(struct network *net); 44 | void wg_cleanup_network(struct network *net); 45 | 46 | #define wg_init_local(net, ...) (net)->wg.ops->init_local(net, ##__VA_ARGS__) 47 | #define wg_peer_update(net, ...) (net)->wg.ops->peer_update(net, ##__VA_ARGS__) 48 | #define wg_peer_connect(net, ...) (net)->wg.ops->peer_connect(net, ##__VA_ARGS__) 49 | #define wg_peer_refresh(net) (net)->wg.ops->peer_refresh(net) 50 | 51 | /* internal */ 52 | struct network_peer *wg_peer_update_start(struct network *net, const uint8_t *key); 53 | void wg_peer_update_done(struct network *net, struct network_peer *peer); 54 | void wg_peer_set_last_handshake(struct network *net, struct network_peer *peer, 55 | uint64_t now, uint64_t sec); 56 | void wg_peer_set_rx_bytes(struct network *net, struct network_peer *peer, 57 | uint64_t bytes); 58 | void wg_peer_set_tx_bytes(struct network *net, struct network_peer *peer, 59 | uint64_t bytes); 60 | void wg_peer_set_endpoint(struct network *net, struct network_peer *peer, 61 | void *data, size_t len); 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /siphash.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2016 Jason A. Donenfeld . All Rights Reserved. 2 | * 3 | * This file is provided under a dual BSD/GPLv2 license. 4 | * 5 | * SipHash: a fast short-input PRF 6 | * https://131002.net/siphash/ 7 | * 8 | * This implementation is specifically for SipHash2-4 for a secure PRF 9 | */ 10 | 11 | #include 12 | #include "siphash.h" 13 | 14 | static inline uint64_t rol64(uint64_t word, unsigned int shift) 15 | { 16 | return (word << (shift & 63)) | (word >> ((-shift) & 63)); 17 | } 18 | 19 | 20 | #define SIPROUND \ 21 | do { \ 22 | v0 += v1; v1 = rol64(v1, 13); v1 ^= v0; v0 = rol64(v0, 32); \ 23 | v2 += v3; v3 = rol64(v3, 16); v3 ^= v2; \ 24 | v0 += v3; v3 = rol64(v3, 21); v3 ^= v0; \ 25 | v2 += v1; v1 = rol64(v1, 17); v1 ^= v2; v2 = rol64(v2, 32); \ 26 | } while (0) 27 | 28 | #define PREAMBLE(len) \ 29 | uint64_t v0 = 0x736f6d6570736575ULL; \ 30 | uint64_t v1 = 0x646f72616e646f6dULL; \ 31 | uint64_t v2 = 0x6c7967656e657261ULL; \ 32 | uint64_t v3 = 0x7465646279746573ULL; \ 33 | uint64_t b = ((uint64_t)(len)) << 56; \ 34 | v3 ^= key->key[1]; \ 35 | v2 ^= key->key[0]; \ 36 | v1 ^= key->key[1]; \ 37 | v0 ^= key->key[0]; 38 | 39 | #define POSTAMBLE \ 40 | v3 ^= b; \ 41 | SIPROUND; \ 42 | SIPROUND; \ 43 | v0 ^= b; \ 44 | v2 ^= 0xff; \ 45 | SIPROUND; \ 46 | SIPROUND; \ 47 | SIPROUND; \ 48 | SIPROUND; \ 49 | return (v0 ^ v1) ^ (v2 ^ v3); 50 | 51 | 52 | uint64_t siphash(const void *data, size_t len, const siphash_key_t *key) 53 | { 54 | const uint8_t *end = data + len - (len % sizeof(uint64_t)); 55 | const uint8_t left = len & (sizeof(uint64_t) - 1); 56 | uint64_t m; 57 | PREAMBLE(len) 58 | for (; data != end; data += sizeof(uint64_t)) { 59 | m = get_unaligned_le64(data); 60 | v3 ^= m; 61 | SIPROUND; 62 | SIPROUND; 63 | v0 ^= m; 64 | } 65 | switch (left) { 66 | case 7: b |= ((uint64_t)end[6]) << 48; fallthrough; 67 | case 6: b |= ((uint64_t)end[5]) << 40; fallthrough; 68 | case 5: b |= ((uint64_t)end[4]) << 32; fallthrough; 69 | case 4: b |= get_unaligned_le32(end); break; 70 | case 3: b |= ((uint64_t)end[2]) << 16; fallthrough; 71 | case 2: b |= get_unaligned_le16(end); break; 72 | case 1: b |= end[0]; 73 | } 74 | POSTAMBLE 75 | } 76 | -------------------------------------------------------------------------------- /curve25519.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2018-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include "curve25519.h" 7 | 8 | #include 9 | #include 10 | #include "utils.h" 11 | 12 | #ifndef __BYTE_ORDER__ 13 | #include 14 | #if !defined(BYTE_ORDER) || !defined(BIG_ENDIAN) || !defined(LITTLE_ENDIAN) 15 | #error "Unable to determine endianness." 16 | #endif 17 | #define __BYTE_ORDER__ BYTE_ORDER 18 | #define __ORDER_BIG_ENDIAN__ BIG_ENDIAN 19 | #define __ORDER_LITTLE_ENDIAN__ LITTLE_ENDIAN 20 | #endif 21 | 22 | #ifdef __linux__ 23 | #include 24 | typedef __u64 u64; 25 | typedef __u32 u32; 26 | typedef __u8 u8; 27 | typedef __s64 s64; 28 | #else 29 | typedef uint64_t u64, __le64; 30 | typedef uint32_t u32, __le32; 31 | typedef uint8_t u8; 32 | typedef int64_t s64; 33 | #endif 34 | #ifndef __unused 35 | #define __unused __attribute__((unused)) 36 | #endif 37 | #ifndef __always_inline 38 | #define __always_inline __inline __attribute__((__always_inline__)) 39 | #endif 40 | #ifndef noinline 41 | #define noinline __attribute__((noinline)) 42 | #endif 43 | #ifndef __aligned 44 | #define __aligned(x) __attribute__((aligned(x))) 45 | #endif 46 | #ifndef __force 47 | #define __force 48 | #endif 49 | 50 | static __always_inline __unused void put_unaligned_le64(u64 s, u8 *d) 51 | { 52 | __le64 l = cpu_to_le64(s); 53 | __builtin_memcpy(d, &l, sizeof(l)); 54 | } 55 | 56 | static noinline void memzero_explicit(void *s, size_t count) 57 | { 58 | memset(s, 0, count); 59 | asm volatile("": :"r"(s) : "memory"); 60 | } 61 | 62 | #ifdef __SIZEOF_INT128__ 63 | #include "curve25519-hacl64.h" 64 | #else 65 | #include "curve25519-fiat32.h" 66 | #endif 67 | 68 | void curve25519_generate_public(uint8_t pub[static CURVE25519_KEY_SIZE], const uint8_t secret[static CURVE25519_KEY_SIZE]) 69 | { 70 | static const uint8_t basepoint[CURVE25519_KEY_SIZE] __aligned(sizeof(uintptr_t)) = { 9 }; 71 | 72 | curve25519(pub, secret, basepoint); 73 | } 74 | 75 | void curve25519(uint8_t mypublic[static CURVE25519_KEY_SIZE], const uint8_t secret[static CURVE25519_KEY_SIZE], const uint8_t basepoint[static CURVE25519_KEY_SIZE]) 76 | { 77 | curve25519_generic(mypublic, secret, basepoint); 78 | } 79 | -------------------------------------------------------------------------------- /rtnl.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2022 Felix Fietkau 4 | */ 5 | #define _GNU_SOURCE 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "unetd.h" 13 | 14 | static struct nl_sock *rtnl; 15 | bool rtnl_ignore_errors; 16 | 17 | static int 18 | unetd_nl_error_cb(struct sockaddr_nl *nla, struct nlmsgerr *err, 19 | void *arg) 20 | { 21 | struct nlmsghdr *nlh = (struct nlmsghdr *) err - 1; 22 | struct nlattr *tb[NLMSGERR_ATTR_MAX + 1]; 23 | struct nlattr *attrs; 24 | int ack_len = sizeof(*nlh) + sizeof(int) + sizeof(*nlh); 25 | int len = nlh->nlmsg_len; 26 | const char *errstr = "(unknown)"; 27 | 28 | if (rtnl_ignore_errors) 29 | return NL_STOP; 30 | 31 | if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS)) 32 | return NL_STOP; 33 | 34 | if (!(nlh->nlmsg_flags & NLM_F_CAPPED)) 35 | ack_len += err->msg.nlmsg_len - sizeof(*nlh); 36 | 37 | attrs = (void *) ((unsigned char *) nlh + ack_len); 38 | len -= ack_len; 39 | 40 | nla_parse(tb, NLMSGERR_ATTR_MAX, attrs, len, NULL); 41 | if (tb[NLMSGERR_ATTR_MSG]) 42 | errstr = nla_data(tb[NLMSGERR_ATTR_MSG]); 43 | 44 | D("Netlink error(%d): %s\n", err->error, errstr); 45 | 46 | return NL_STOP; 47 | } 48 | 49 | int rtnl_call(struct nl_msg *msg) 50 | { 51 | int ret; 52 | 53 | ret = nl_send_auto_complete(rtnl, msg); 54 | nlmsg_free(msg); 55 | 56 | if (ret < 0) 57 | return ret; 58 | 59 | return nl_wait_for_ack(rtnl); 60 | } 61 | 62 | int rtnl_init(void) 63 | { 64 | int fd, opt; 65 | 66 | if (rtnl) 67 | return 0; 68 | 69 | rtnl = nl_socket_alloc(); 70 | if (!rtnl) 71 | return -1; 72 | 73 | if (nl_connect(rtnl, NETLINK_ROUTE)) 74 | goto free; 75 | 76 | nl_socket_disable_seq_check(rtnl); 77 | nl_socket_set_buffer_size(rtnl, 65536, 0); 78 | nl_cb_err(nl_socket_get_cb(rtnl), NL_CB_CUSTOM, unetd_nl_error_cb, NULL); 79 | 80 | fd = nl_socket_get_fd(rtnl); 81 | 82 | opt = 1; 83 | setsockopt(fd, SOL_NETLINK, NETLINK_EXT_ACK, &opt, sizeof(opt)); 84 | 85 | opt = 1; 86 | setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, &opt, sizeof(opt)); 87 | 88 | return 0; 89 | 90 | free: 91 | nl_socket_free(rtnl); 92 | rtnl = NULL; 93 | return -1; 94 | } 95 | -------------------------------------------------------------------------------- /mss-bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2021 Felix Fietkau 4 | */ 5 | #define KBUILD_MODNAME "foo" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "bpf_skb_utils.h" 21 | 22 | const volatile static uint32_t mtu = 1420; 23 | 24 | static __always_inline unsigned int 25 | optlen(const u_int8_t *opt) 26 | { 27 | if (opt[0] <= TCPOPT_NOP || opt[1] == 0) 28 | return 1; 29 | 30 | return opt[1]; 31 | } 32 | 33 | static __always_inline void 34 | fixup_tcp(struct skb_parser_info *info, __u16 mss) 35 | { 36 | struct tcphdr *tcph; 37 | __u32 end, offset = info->offset + sizeof(*tcph); 38 | __u16 oldmss; 39 | __u8 flags; 40 | __u8 *opt; 41 | int i; 42 | 43 | tcph = skb_parse_tcp(info); 44 | if (!tcph) 45 | return; 46 | 47 | flags = tcp_flag_byte(tcph); 48 | if (!(flags & TCPHDR_SYN)) 49 | return; 50 | 51 | end = info->offset; 52 | 53 | #pragma unroll 54 | for (i = 0; i < 5; i++) { 55 | if (offset + 4 > end) 56 | return; 57 | 58 | opt = skb_ptr(info->skb, offset, 4); 59 | if (!opt) 60 | return; 61 | 62 | offset += optlen(opt); 63 | if (opt[0] != TCPOPT_MSS || opt[1] != TCPOLEN_MSS) 64 | continue; 65 | 66 | goto found; 67 | } 68 | return; 69 | 70 | found: 71 | oldmss = (opt[2] << 8) | opt[3]; 72 | if (oldmss <= mss) 73 | return; 74 | 75 | opt[2] = mss >> 8; 76 | opt[3] = mss & 0xff; 77 | csum_replace2(&tcph->check, bpf_htons(oldmss), bpf_htons(mss)); 78 | } 79 | 80 | SEC("tc") 81 | int mssfix(struct __sk_buff *skb) 82 | { 83 | struct skb_parser_info info; 84 | __u16 mss; 85 | int type; 86 | 87 | skb_parse_init(&info, skb); 88 | if (!skb_parse_ethernet(&info)) 89 | return TC_ACT_UNSPEC; 90 | 91 | skb_parse_vlan(&info); 92 | skb_parse_vlan(&info); 93 | 94 | if (!skb_parse_ipv4(&info, 60) && !skb_parse_ipv6(&info, 60)) 95 | return TC_ACT_UNSPEC; 96 | 97 | if (info.proto != IPPROTO_TCP) 98 | return TC_ACT_UNSPEC; 99 | 100 | mss = mtu; 101 | mss -= info.offset + sizeof(struct tcphdr); 102 | fixup_tcp(&info, mss); 103 | 104 | return TC_ACT_UNSPEC; 105 | } 106 | -------------------------------------------------------------------------------- /ed25519.h: -------------------------------------------------------------------------------- 1 | /* Edwards curve operations 2 | * Daniel Beer , 9 Jan 2014 3 | * 4 | * This file is in the public domain. 5 | */ 6 | 7 | #ifndef ED25519_H_ 8 | #define ED25519_H_ 9 | 10 | #include "f25519.h" 11 | 12 | /* This is not the Ed25519 signature system. Rather, we're implementing 13 | * basic operations on the twisted Edwards curve over (Z mod 2^255-19): 14 | * 15 | * -x^2 + y^2 = 1 - (121665/121666)x^2y^2 16 | * 17 | * With the positive-x base point y = 4/5. 18 | * 19 | * These functions will not leak secret data through timing. 20 | * 21 | * For more information, see: 22 | * 23 | * Bernstein, D.J. & Lange, T. (2007) "Faster addition and doubling on 24 | * elliptic curves". Document ID: 95616567a6ba20f575c5f25e7cebaf83. 25 | * 26 | * Hisil, H. & Wong, K K. & Carter, G. & Dawson, E. (2008) "Twisted 27 | * Edwards curves revisited". Advances in Cryptology, ASIACRYPT 2008, 28 | * Vol. 5350, pp. 326-343. 29 | */ 30 | 31 | /* Projective coordinates */ 32 | struct ed25519_pt { 33 | uint8_t x[F25519_SIZE]; 34 | uint8_t y[F25519_SIZE]; 35 | uint8_t t[F25519_SIZE]; 36 | uint8_t z[F25519_SIZE]; 37 | }; 38 | 39 | extern const struct ed25519_pt ed25519_base; 40 | 41 | /* Convert between projective and affine coordinates (x/y in F25519) */ 42 | void ed25519_project(struct ed25519_pt *p, 43 | const uint8_t *x, const uint8_t *y); 44 | 45 | void ed25519_unproject(uint8_t *x, uint8_t *y, 46 | const struct ed25519_pt *p); 47 | 48 | /* Compress/uncompress points. try_unpack() will check that the 49 | * compressed point is on the curve, returning 1 if the unpacked point 50 | * is valid, and 0 otherwise. 51 | */ 52 | #define ED25519_PACK_SIZE F25519_SIZE 53 | 54 | void ed25519_pack(uint8_t *c, const uint8_t *x, const uint8_t *y); 55 | uint8_t ed25519_try_unpack(uint8_t *x, uint8_t *y, const uint8_t *c); 56 | 57 | /* Add, double and scalar multiply */ 58 | #define ED25519_EXPONENT_SIZE 32 59 | 60 | /* Prepare an exponent by clamping appropriate bits */ 61 | static inline void ed25519_prepare(uint8_t *e) 62 | { 63 | e[0] &= 0xf8; 64 | e[31] &= 0x7f; 65 | e[31] |= 0x40; 66 | } 67 | 68 | /* Order of the group generated by the base point */ 69 | static inline void ed25519_copy(struct ed25519_pt *dst, 70 | const struct ed25519_pt *src) 71 | { 72 | memcpy(dst, src, sizeof(*dst)); 73 | } 74 | 75 | void ed25519_add(struct ed25519_pt *r, 76 | const struct ed25519_pt *a, const struct ed25519_pt *b); 77 | void ed25519_smult(struct ed25519_pt *r, const struct ed25519_pt *a, 78 | const uint8_t *e); 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /enroll.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2024 Felix Fietkau 4 | */ 5 | #ifndef __ENROLL_H 6 | #define __ENROLL_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include "utils.h" 12 | #include "curve25519.h" 13 | #include "sha512.h" 14 | 15 | #define ENROLL_SESSION_ID_LEN 4 16 | #define ENROLL_HASH_SIZE 32 17 | 18 | #define ENROLL_MAX_PEERS 64 19 | 20 | struct network; 21 | 22 | struct enroll_peer { 23 | struct avl_node node; 24 | 25 | struct sockaddr_in6 addr; 26 | uint64_t nonce; 27 | 28 | uint8_t session_id[ENROLL_SESSION_ID_LEN]; 29 | uint8_t session_key[CURVE25519_KEY_SIZE]; 30 | uint8_t pubkey[CURVE25519_KEY_SIZE]; 31 | 32 | struct blob_attr *enroll_meta; 33 | uint8_t enroll_key[CURVE25519_KEY_SIZE]; 34 | 35 | bool has_secret; 36 | bool has_key; 37 | bool confirmed; 38 | bool accepted; 39 | 40 | struct blob_attr meta[]; 41 | }; 42 | 43 | struct enroll_state { 44 | struct network *net; 45 | 46 | struct avl_tree peers; 47 | 48 | struct uloop_timeout timeout; 49 | struct uloop_timeout connect_timer; 50 | uint8_t privkey[2 * CURVE25519_KEY_SIZE]; 51 | uint8_t pubkey[CURVE25519_KEY_SIZE]; 52 | uint64_t nonce; 53 | 54 | struct blob_attr *meta; 55 | struct blob_attr *enroll_meta; 56 | 57 | uint8_t secret_hash[SHA512_HASH_SIZE]; 58 | bool has_secret; 59 | bool auto_accept; 60 | 61 | unsigned int connect_interval; 62 | unsigned int n_connect; 63 | union network_endpoint connect[]; 64 | }; 65 | 66 | enum { 67 | ENROLL_START_ATTR_NETWORK, 68 | ENROLL_START_ATTR_TIMEOUT, 69 | ENROLL_START_ATTR_CONNECT, 70 | ENROLL_START_ATTR_INTERVAL, 71 | ENROLL_START_ATTR_ENROLL_AUTO, 72 | ENROLL_START_ATTR_ENROLL_SECRET, 73 | ENROLL_START_ATTR_ENROLL_INFO, 74 | ENROLL_START_ATTR_INFO, 75 | __ENROLL_START_ATTR_MAX, 76 | }; 77 | 78 | #ifdef UBUS_SUPPORT 79 | 80 | extern const struct blobmsg_policy enroll_start_policy[__ENROLL_START_ATTR_MAX]; 81 | 82 | void pex_enroll_recv(void *data, size_t len, struct sockaddr_in6 *addr); 83 | 84 | struct enroll_state *enroll_state(void); 85 | void enroll_net_cleanup(struct network *net); 86 | void enroll_peer_info(struct blob_buf *buf, struct enroll_peer *peer); 87 | void enroll_peer_accept(struct enroll_peer *peer, struct blob_attr *meta); 88 | int enroll_start(struct blob_attr *data); 89 | void enroll_stop(void); 90 | 91 | #else 92 | 93 | static inline void pex_enroll_recv(void *data, size_t len, struct sockaddr_in6 *addr) 94 | { 95 | } 96 | 97 | static inline void enroll_net_cleanup(struct network *net) 98 | { 99 | } 100 | 101 | #endif 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /pex.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2022 Felix Fietkau 4 | */ 5 | #ifndef __UNETD_PEX_H 6 | #define __UNETD_PEX_H 7 | 8 | #include 9 | #include 10 | #include "stun.h" 11 | 12 | #define NETWORK_PEX_HOSTS_LIMIT 128 13 | 14 | struct network; 15 | 16 | struct network_pex_host { 17 | struct list_head list; 18 | uint64_t timeout; 19 | uint64_t last_active; 20 | uint64_t last_ping; 21 | bool interface; 22 | union network_endpoint endpoint; 23 | }; 24 | 25 | struct network_pex { 26 | struct uloop_fd fd; 27 | struct list_head hosts; 28 | int num_hosts; 29 | struct uloop_timeout request_update_timer; 30 | }; 31 | 32 | enum network_stun_state { 33 | STUN_STATE_IDLE, 34 | STUN_STATE_PEX_QUERY_WAIT, 35 | STUN_STATE_STUN_QUERY_SEND, 36 | STUN_STATE_STUN_QUERY_WAIT, 37 | }; 38 | 39 | struct network_stun_server { 40 | struct list_head list; 41 | 42 | struct avl_node pending_node; 43 | struct stun_request req; 44 | 45 | const char *host; 46 | uint8_t seq; 47 | bool req_auth_port; 48 | bool pending; 49 | }; 50 | 51 | struct network_stun { 52 | struct list_head servers; 53 | struct avl_tree pending; 54 | 55 | struct uloop_timeout timer; 56 | 57 | enum network_stun_state state; 58 | bool wgport_disabled; 59 | 60 | uint16_t auth_port_ext; 61 | uint16_t port_local; 62 | uint16_t port_ext; 63 | 64 | int retry; 65 | 66 | struct uloop_fd socket; 67 | }; 68 | 69 | enum pex_event { 70 | PEX_EV_HANDSHAKE, 71 | PEX_EV_ENDPOINT_CHANGE, 72 | PEX_EV_QUERY, 73 | PEX_EV_PING, 74 | }; 75 | 76 | void network_pex_init(struct network *net); 77 | int network_pex_open(struct network *net); 78 | void network_pex_close(struct network *net); 79 | void network_pex_free(struct network *net); 80 | void network_pex_reload(); 81 | 82 | void network_pex_event(struct network *net, struct network_peer *peer, 83 | enum pex_event ev); 84 | struct network_pex_host * 85 | network_pex_create_host(struct network *net, union network_endpoint *ep, 86 | unsigned int timeout); 87 | 88 | void network_stun_init(struct network *net); 89 | void network_stun_free(struct network *net); 90 | void network_stun_server_add(struct network *net, const char *host); 91 | void network_stun_rx_packet(struct network *net, const void *data, size_t len); 92 | void network_stun_update_port(struct network *net, bool auth, uint16_t val); 93 | void network_stun_start(struct network *net); 94 | 95 | static inline bool network_pex_active(struct network_pex *pex) 96 | { 97 | return pex->fd.fd >= 0; 98 | } 99 | 100 | int global_pex_open(const char *unix_path); 101 | 102 | #endif 103 | -------------------------------------------------------------------------------- /bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2022 Felix Fietkau 4 | */ 5 | #define _GNU_SOURCE 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "unetd.h" 18 | 19 | static int unetd_bpf_pr(enum libbpf_print_level level, const char *format, 20 | va_list args) 21 | { 22 | return vfprintf(stderr, format, args); 23 | } 24 | 25 | static void unetd_init_env(void) 26 | { 27 | struct rlimit limit = { 28 | .rlim_cur = RLIM_INFINITY, 29 | .rlim_max = RLIM_INFINITY, 30 | }; 31 | 32 | setrlimit(RLIMIT_MEMLOCK, &limit); 33 | } 34 | 35 | static void 36 | unetd_set_prog_mtu(struct bpf_object *obj, uint32_t mtu) 37 | { 38 | struct bpf_map *map = NULL; 39 | 40 | while ((map = bpf_object__next_map(obj, map)) != NULL) { 41 | if (!strstr(bpf_map__name(map), ".rodata")) 42 | continue; 43 | 44 | bpf_map__set_initial_value(map, &mtu, sizeof(mtu)); 45 | } 46 | } 47 | 48 | static int 49 | unetd_attach_bpf_prog(int ifindex, int fd, bool egress) 50 | { 51 | DECLARE_LIBBPF_OPTS(bpf_tc_hook, hook, 52 | .attach_point = egress ? BPF_TC_EGRESS : BPF_TC_INGRESS, 53 | .ifindex = ifindex); 54 | DECLARE_LIBBPF_OPTS(bpf_tc_opts, attach_tc, 55 | .flags = BPF_TC_F_REPLACE, 56 | .handle = 1, 57 | .prog_fd = fd, 58 | .priority = UNETD_MSS_PRIO_BASE); 59 | 60 | bpf_tc_hook_create(&hook); 61 | 62 | return bpf_tc_attach(&hook, &attach_tc); 63 | } 64 | 65 | int unetd_attach_mssfix(int ifindex, int mtu) 66 | { 67 | struct bpf_program *prog; 68 | struct bpf_object *obj; 69 | int prog_fd; 70 | int ret = -1; 71 | 72 | if (rtnl_init()) 73 | return -1; 74 | 75 | unetd_init_env(); 76 | libbpf_set_print(unetd_bpf_pr); 77 | 78 | obj = bpf_object__open_file(mssfix_path, NULL); 79 | if (libbpf_get_error(obj)) { 80 | perror("bpf_object__open_file"); 81 | goto out; 82 | } 83 | 84 | prog = bpf_object__find_program_by_name(obj, "mssfix"); 85 | if (!prog) { 86 | perror("bpf_object__find_program_by_name"); 87 | goto out; 88 | } 89 | 90 | bpf_program__set_type(prog, BPF_PROG_TYPE_SCHED_CLS); 91 | 92 | unetd_set_prog_mtu(obj, mtu); 93 | 94 | if (bpf_object__load(obj)) { 95 | perror("bpf_object__load"); 96 | goto out; 97 | } 98 | 99 | prog_fd = bpf_program__fd(prog); 100 | unetd_attach_bpf_prog(ifindex, prog_fd, true); 101 | unetd_attach_bpf_prog(ifindex, prog_fd, false); 102 | 103 | ret = 0; 104 | 105 | out: 106 | bpf_object__close(obj); 107 | 108 | return ret; 109 | } 110 | -------------------------------------------------------------------------------- /f25519.h: -------------------------------------------------------------------------------- 1 | /* Arithmetic mod p = 2^255-19 2 | * Daniel Beer , 8 Jan 2014 3 | * 4 | * This file is in the public domain. 5 | */ 6 | 7 | #ifndef F25519_H_ 8 | #define F25519_H_ 9 | 10 | #include 11 | #include 12 | 13 | /* Field elements are represented as little-endian byte strings. All 14 | * operations have timings which are independent of input data, so they 15 | * can be safely used for cryptography. 16 | * 17 | * Computation is performed on un-normalized elements. These are byte 18 | * strings which fall into the range 0 <= x < 2p. Use f25519_normalize() 19 | * to convert to a value 0 <= x < p. 20 | * 21 | * Elements received from the outside may greater even than 2p. 22 | * f25519_normalize() will correctly deal with these numbers too. 23 | */ 24 | #define F25519_SIZE 32 25 | 26 | /* Identity constants */ 27 | extern const uint8_t f25519_one[F25519_SIZE]; 28 | 29 | /* Load a small constant */ 30 | void f25519_load(uint8_t *x, uint32_t c); 31 | 32 | /* Copy two points */ 33 | static inline void f25519_copy(uint8_t *x, const uint8_t *a) 34 | { 35 | memcpy(x, a, F25519_SIZE); 36 | } 37 | 38 | /* Normalize a field point x < 2*p by subtracting p if necessary */ 39 | void f25519_normalize(uint8_t *x); 40 | 41 | /* Compare two field points in constant time. Return one if equal, zero 42 | * otherwise. This should be performed only on normalized values. 43 | */ 44 | uint8_t f25519_eq(const uint8_t *x, const uint8_t *y); 45 | 46 | /* Conditional copy. If condition == 0, then zero is copied to dst. If 47 | * condition == 1, then one is copied to dst. Any other value results in 48 | * undefined behaviour. 49 | */ 50 | void f25519_select(uint8_t *dst, 51 | const uint8_t *zero, const uint8_t *one, 52 | uint8_t condition); 53 | 54 | /* Add/subtract two field points. The three pointers are not required to 55 | * be distinct. 56 | */ 57 | void f25519_add(uint8_t *r, const uint8_t *a, const uint8_t *b); 58 | void f25519_sub(uint8_t *r, const uint8_t *a, const uint8_t *b); 59 | 60 | /* Unary negation */ 61 | void f25519_neg(uint8_t *r, const uint8_t *a); 62 | 63 | /* Multiply two field points. The __distinct variant is used when r is 64 | * known to be in a different location to a and b. 65 | */ 66 | void f25519_mul__distinct(uint8_t *r, const uint8_t *a, const uint8_t *b); 67 | 68 | /* Take the reciprocal of a field point. The __distinct variant is used 69 | * when r is known to be in a different location to x. 70 | */ 71 | void f25519_inv__distinct(uint8_t *r, const uint8_t *x); 72 | 73 | /* Compute one of the square roots of the field element, if the element 74 | * is square. The other square is -r. 75 | * 76 | * If the input is not square, the returned value is a valid field 77 | * element, but not the correct answer. If you don't already know that 78 | * your element is square, you should square the return value and test. 79 | */ 80 | void f25519_sqrt(uint8_t *r, const uint8_t *x); 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /dht.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2009-2011 by Juliusz Chroboczek 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #ifdef __cplusplus 24 | extern "C" { 25 | #endif 26 | 27 | typedef void 28 | dht_callback_t(void *closure, int event, 29 | const unsigned char *info_hash, 30 | const void *data, size_t data_len); 31 | 32 | #define DHT_EVENT_NONE 0 33 | #define DHT_EVENT_VALUES 1 34 | #define DHT_EVENT_VALUES6 2 35 | #define DHT_EVENT_SEARCH_DONE 3 36 | #define DHT_EVENT_SEARCH_DONE6 4 37 | 38 | extern FILE *dht_debug; 39 | 40 | int dht_init(int s, int s6, const unsigned char *id, const unsigned char *v); 41 | int dht_insert_node(const unsigned char *id, struct sockaddr *sa, int salen); 42 | int dht_ping_node(const struct sockaddr *sa, int salen); 43 | int dht_periodic(const void *buf, size_t buflen, 44 | const struct sockaddr *from, int fromlen, time_t *tosleep, 45 | dht_callback_t *callback, void *closure); 46 | int dht_search(const unsigned char *id, int port, int af, 47 | dht_callback_t *callback, void *closure); 48 | int dht_nodes(int af, 49 | int *good_return, int *dubious_return, int *cached_return, 50 | int *incoming_return); 51 | void dht_dump_tables(FILE *f); 52 | int dht_get_nodes(struct sockaddr_in *sin, int *num, 53 | struct sockaddr_in6 *sin6, int *num6); 54 | int dht_uninit(void); 55 | 56 | /* This must be provided by the user. */ 57 | int dht_sendto(int sockfd, const void *buf, int len, int flags, 58 | const struct sockaddr *to, int tolen); 59 | int dht_blacklisted(const struct sockaddr *sa, int salen); 60 | void dht_hash(void *hash_return, int hash_size, 61 | const void *v1, int len1, 62 | const void *v2, int len2, 63 | const void *v3, int len3); 64 | int dht_random_bytes(void *buf, size_t size); 65 | 66 | #ifdef __cplusplus 67 | } 68 | #endif 69 | -------------------------------------------------------------------------------- /network.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2022 Felix Fietkau 4 | */ 5 | #ifndef __UNETD_NETWORK_H 6 | #define __UNETD_NETWORK_H 7 | 8 | #include 9 | #include 10 | #include "curve25519.h" 11 | 12 | enum network_type { 13 | NETWORK_TYPE_FILE, 14 | NETWORK_TYPE_INLINE, 15 | NETWORK_TYPE_DYNAMIC, 16 | }; 17 | 18 | struct wg_ops; 19 | struct network_group; 20 | struct network_host; 21 | 22 | struct network { 23 | struct avl_node node; 24 | 25 | struct wg wg; 26 | 27 | struct { 28 | struct blob_attr *data; 29 | enum network_type type; 30 | int keepalive; 31 | uint8_t key[CURVE25519_KEY_SIZE]; 32 | uint8_t pubkey[CURVE25519_KEY_SIZE]; 33 | uint8_t auth_key[CURVE25519_KEY_SIZE]; 34 | const char *file; 35 | const char *interface; 36 | const char *update_cmd; 37 | const char *domain; 38 | struct blob_attr *tunnels; 39 | struct blob_attr *net_data; 40 | struct blob_attr *local_network; 41 | struct blob_attr *auth_connect; 42 | struct blob_attr *peer_data; 43 | } config; 44 | 45 | struct { 46 | uint64_t hash; 47 | union network_addr addr; 48 | struct network_host *local_host; 49 | unsigned int keepalive; 50 | int port; 51 | int pex_port; 52 | bool local_host_changed; 53 | struct blob_attr *stun_list; 54 | } net_config; 55 | 56 | void *net_data; 57 | size_t net_data_len; 58 | uint64_t net_data_version; 59 | int num_net_queries; 60 | unsigned int update_refused; 61 | 62 | struct uloop_timeout reload_timer; 63 | 64 | int ifindex; 65 | struct network_host *prev_local_host; 66 | 67 | struct list_head dynamic_peers; 68 | struct avl_tree hosts; 69 | struct vlist_tree peers; 70 | 71 | struct avl_tree groups; 72 | struct vlist_tree services; 73 | 74 | struct uloop_timeout connect_timer; 75 | 76 | struct network_pex pex; 77 | struct network_stun stun; 78 | }; 79 | 80 | enum { 81 | NETWORK_ATTR_NAME, 82 | NETWORK_ATTR_TYPE, 83 | NETWORK_ATTR_KEY, 84 | NETWORK_ATTR_AUTH_KEY, 85 | NETWORK_ATTR_FILE, 86 | NETWORK_ATTR_DATA, 87 | NETWORK_ATTR_INTERFACE, 88 | NETWORK_ATTR_UPDATE_CMD, 89 | NETWORK_ATTR_KEEPALIVE, 90 | NETWORK_ATTR_DOMAIN, 91 | NETWORK_ATTR_TUNNELS, 92 | NETWORK_ATTR_LOCAL_NET, 93 | NETWORK_ATTR_AUTH_CONNECT, 94 | NETWORK_ATTR_PEER_DATA, 95 | __NETWORK_ATTR_MAX, 96 | }; 97 | 98 | extern struct avl_tree networks; 99 | extern const struct blobmsg_policy network_policy[__NETWORK_ATTR_MAX]; 100 | 101 | static inline const char *network_name(struct network *net) 102 | { 103 | return net->node.key; 104 | } 105 | 106 | void network_get_config(struct network *net, struct blob_buf *buf); 107 | bool network_skip_endpoint_route(struct network *net, union network_endpoint *ep); 108 | void network_fill_host_addr(union network_addr *addr, uint8_t *key); 109 | int network_save_dynamic(struct network *net); 110 | void network_soft_reload(struct network *net); 111 | void network_free_all(void); 112 | 113 | int unetd_network_add(const char *name, struct blob_attr *config); 114 | int unetd_network_remove(const char *name); 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /wg.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2022 Felix Fietkau 4 | */ 5 | #include "unetd.h" 6 | 7 | static const struct wg_ops *wg_get_ops(struct network *net) 8 | { 9 | if (wg_user_ops.check(net)) 10 | return &wg_user_ops; 11 | 12 | #ifdef linux 13 | return &wg_linux_ops; 14 | #else 15 | return NULL; 16 | #endif 17 | } 18 | 19 | int wg_init_network(struct network *net) 20 | { 21 | net->wg.ops = wg_get_ops(net); 22 | 23 | if (!net->wg.ops) 24 | return -1; 25 | 26 | return net->wg.ops->init(net); 27 | } 28 | 29 | void wg_cleanup_network(struct network *net) 30 | { 31 | if (net->wg.ops) 32 | net->wg.ops->cleanup(net); 33 | } 34 | 35 | static void 36 | wg_peer_set_connected(struct network *net, struct network_peer *peer, bool val) 37 | { 38 | if (peer->state.connected == val) 39 | return; 40 | 41 | peer->state.connected = val; 42 | network_services_peer_update(net, peer); 43 | } 44 | 45 | struct network_peer *wg_peer_update_start(struct network *net, const uint8_t *key) 46 | { 47 | struct network_peer *peer; 48 | 49 | peer = vlist_find(&net->peers, key, peer, node); 50 | if (!peer || peer->indirect) 51 | return NULL; 52 | 53 | peer->state.handshake = false; 54 | peer->state.idle++; 55 | if (peer->state.ping_wait > 0) 56 | peer->state.ping_wait--; 57 | if (peer->state.idle >= 2 * net->net_config.keepalive) 58 | wg_peer_set_connected(net, peer, false); 59 | if (peer->state.idle > net->net_config.keepalive) 60 | network_pex_event(net, peer, PEX_EV_PING); 61 | 62 | return peer; 63 | } 64 | 65 | void wg_peer_update_done(struct network *net, struct network_peer *peer) 66 | { 67 | if (peer->state.handshake) 68 | network_pex_event(net, peer, PEX_EV_HANDSHAKE); 69 | } 70 | 71 | void wg_peer_set_last_handshake(struct network *net, struct network_peer *peer, 72 | uint64_t now, uint64_t sec) 73 | { 74 | peer->state.last_handshake_diff = now - sec; 75 | if (sec == peer->state.last_handshake) 76 | return; 77 | 78 | peer->state.handshake = true; 79 | peer->state.last_handshake = sec; 80 | sec = now - sec; 81 | if (sec <= net->net_config.keepalive) { 82 | if (peer->state.idle > sec) 83 | peer->state.idle = sec; 84 | wg_peer_set_connected(net, peer, true); 85 | } 86 | } 87 | 88 | void wg_peer_set_rx_bytes(struct network *net, struct network_peer *peer, 89 | uint64_t bytes) 90 | { 91 | int64_t diff = bytes - peer->state.rx_bytes; 92 | 93 | peer->state.rx_bytes = bytes; 94 | if (diff > 0) { 95 | peer->state.idle = 0; 96 | wg_peer_set_connected(net, peer, true); 97 | } 98 | } 99 | 100 | void wg_peer_set_tx_bytes(struct network *net, struct network_peer *peer, 101 | uint64_t bytes) 102 | { 103 | peer->state.tx_bytes = bytes; 104 | } 105 | 106 | void wg_peer_set_endpoint(struct network *net, struct network_peer *peer, 107 | void *data, size_t len) 108 | { 109 | if (len > sizeof(peer->state.endpoint)) 110 | return; 111 | 112 | if (!memcmp(&peer->state.endpoint, data, len)) 113 | return; 114 | 115 | memset(&peer->state.endpoint, 0, sizeof(peer->state.endpoint)); 116 | memcpy(&peer->state.endpoint, data, len); 117 | network_pex_event(net, peer, PEX_EV_ENDPOINT_CHANGE); 118 | } 119 | -------------------------------------------------------------------------------- /fprime.c: -------------------------------------------------------------------------------- 1 | /* Arithmetic in prime fields 2 | * Daniel Beer , 10 Jan 2014 3 | * 4 | * This file is in the public domain. 5 | */ 6 | 7 | #include "fprime.h" 8 | 9 | static void raw_add(uint8_t *x, const uint8_t *p) 10 | { 11 | uint16_t c = 0; 12 | int i; 13 | 14 | for (i = 0; i < FPRIME_SIZE; i++) { 15 | c += ((uint16_t)x[i]) + ((uint16_t)p[i]); 16 | x[i] = c; 17 | c >>= 8; 18 | } 19 | } 20 | 21 | static void raw_try_sub(uint8_t *x, const uint8_t *p) 22 | { 23 | uint8_t minusp[FPRIME_SIZE]; 24 | uint16_t c = 0; 25 | int i; 26 | 27 | for (i = 0; i < FPRIME_SIZE; i++) { 28 | c = ((uint16_t)x[i]) - ((uint16_t)p[i]) - c; 29 | minusp[i] = c; 30 | c = (c >> 8) & 1; 31 | } 32 | 33 | fprime_select(x, minusp, x, c); 34 | } 35 | 36 | /* Warning: this function is variable-time */ 37 | static int prime_msb(const uint8_t *p) 38 | { 39 | int i; 40 | uint8_t x; 41 | 42 | for (i = FPRIME_SIZE - 1; i >= 0; i--) 43 | if (p[i]) 44 | break; 45 | 46 | x = p[i]; 47 | i <<= 3; 48 | 49 | while (x) { 50 | x >>= 1; 51 | i++; 52 | } 53 | 54 | return i - 1; 55 | } 56 | 57 | /* Warning: this function may be variable-time in the argument n */ 58 | static void shift_n_bits(uint8_t *x, int n) 59 | { 60 | uint16_t c = 0; 61 | int i; 62 | 63 | for (i = 0; i < FPRIME_SIZE; i++) { 64 | c |= ((uint16_t)x[i]) << n; 65 | x[i] = c; 66 | c >>= 8; 67 | } 68 | } 69 | 70 | static inline int min_int(int a, int b) 71 | { 72 | return a < b ? a : b; 73 | } 74 | 75 | void fprime_from_bytes(uint8_t *n, 76 | const uint8_t *x, size_t len, 77 | const uint8_t *modulus) 78 | { 79 | const int preload_total = min_int(prime_msb(modulus) - 1, len << 3); 80 | const int preload_bytes = preload_total >> 3; 81 | const int preload_bits = preload_total & 7; 82 | const int rbits = (len << 3) - preload_total; 83 | int i; 84 | 85 | memset(n, 0, FPRIME_SIZE); 86 | 87 | for (i = 0; i < preload_bytes; i++) 88 | n[i] = x[len - preload_bytes + i]; 89 | 90 | if (preload_bits) { 91 | shift_n_bits(n, preload_bits); 92 | n[0] |= x[len - preload_bytes - 1] >> (8 - preload_bits); 93 | } 94 | 95 | for (i = rbits - 1; i >= 0; i--) { 96 | const uint8_t bit = (x[i >> 3] >> (i & 7)) & 1; 97 | 98 | shift_n_bits(n, 1); 99 | n[0] |= bit; 100 | raw_try_sub(n, modulus); 101 | } 102 | } 103 | 104 | void fprime_select(uint8_t *dst, 105 | const uint8_t *zero, const uint8_t *one, 106 | uint8_t condition) 107 | { 108 | const uint8_t mask = -condition; 109 | int i; 110 | 111 | for (i = 0; i < FPRIME_SIZE; i++) 112 | dst[i] = zero[i] ^ (mask & (one[i] ^ zero[i])); 113 | } 114 | 115 | void fprime_add(uint8_t *r, const uint8_t *a, const uint8_t *modulus) 116 | { 117 | raw_add(r, a); 118 | raw_try_sub(r, modulus); 119 | } 120 | 121 | void fprime_mul(uint8_t *r, const uint8_t *a, const uint8_t *b, 122 | const uint8_t *modulus) 123 | { 124 | int i; 125 | 126 | memset(r, 0, FPRIME_SIZE); 127 | 128 | for (i = prime_msb(modulus); i >= 0; i--) { 129 | const uint8_t bit = (b[i >> 3] >> (i & 7)) & 1; 130 | uint8_t plusa[FPRIME_SIZE]; 131 | 132 | shift_n_bits(r, 1); 133 | raw_try_sub(r, modulus); 134 | 135 | fprime_copy(plusa, r); 136 | fprime_add(plusa, a, modulus); 137 | 138 | fprime_select(r, r, plusa, bit); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2022 Felix Fietkau 4 | */ 5 | #ifndef __UNETD_UTILS_H 6 | #define __UNETD_UTILS_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | struct nl_msg; 13 | 14 | union network_addr { 15 | struct { 16 | uint8_t network_id[8]; 17 | uint8_t host_addr[8]; 18 | }; 19 | struct in_addr in; 20 | struct in6_addr in6; 21 | }; 22 | 23 | union network_endpoint { 24 | struct sockaddr sa; 25 | struct sockaddr_in in; 26 | struct sockaddr_in6 in6; 27 | }; 28 | 29 | static inline void * 30 | network_endpoint_addr(union network_endpoint *ep, int *addr_len) 31 | { 32 | if (ep->sa.sa_family == AF_INET6) { 33 | if (addr_len) 34 | *addr_len = sizeof(ep->in6.sin6_addr); 35 | return &ep->in6.sin6_addr; 36 | } 37 | 38 | if (addr_len) 39 | *addr_len = sizeof(ep->in.sin_addr); 40 | return &ep->in.sin_addr; 41 | } 42 | 43 | static inline bool 44 | network_endpoint_addr_equal(union network_endpoint *ep1, union network_endpoint *ep2) 45 | { 46 | const void *a1, *a2; 47 | int len; 48 | 49 | if (ep1->sa.sa_family != ep2->sa.sa_family) 50 | return false; 51 | 52 | a1 = network_endpoint_addr(ep1, &len); 53 | a2 = network_endpoint_addr(ep2, &len); 54 | 55 | return !memcmp(a1, a2, len); 56 | } 57 | 58 | int network_get_endpoint(union network_endpoint *dest, int af, const char *str, 59 | int default_port, int idx); 60 | int network_get_subnet(int af, union network_addr *addr, int *mask, 61 | const char *str); 62 | int network_get_local_addr(void *local, const union network_endpoint *target); 63 | 64 | void *unet_read_file(const char *name, size_t *len); 65 | 66 | #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) 67 | 68 | #define bitmask_size(len) (4 * DIV_ROUND_UP(len, 32)) 69 | 70 | static inline bool bitmask_test(uint32_t *mask, unsigned int i) 71 | { 72 | return mask[i / 32] & (1 << (i % 32)); 73 | } 74 | 75 | static inline void bitmask_set(uint32_t *mask, unsigned int i) 76 | { 77 | mask[i / 32] |= 1 << (i % 32); 78 | } 79 | 80 | static inline void bitmask_clear(uint32_t *mask, unsigned int i) 81 | { 82 | mask[i / 32] &= ~(1 << (i % 32)); 83 | } 84 | 85 | static inline void bitmask_set_val(uint32_t *mask, unsigned int i, bool val) 86 | { 87 | if (val) 88 | bitmask_set(mask, i); 89 | else 90 | bitmask_clear(mask, i); 91 | } 92 | 93 | static inline uint16_t get_unaligned_be16(const uint8_t *p) 94 | { 95 | return p[1] | p[0] << 8; 96 | } 97 | 98 | static inline uint32_t get_unaligned_be32(const uint8_t *p) 99 | { 100 | return p[3] | p[2] << 8 | p[1] << 16 | p[0] << 24; 101 | } 102 | 103 | static inline uint64_t get_unaligned_be64(const uint8_t *p) 104 | { 105 | return (uint64_t)get_unaligned_be32(p) << 32 | 106 | get_unaligned_be32(p + 4); 107 | } 108 | 109 | static inline uint16_t get_unaligned_le16(const uint8_t *p) 110 | { 111 | return p[0] | p[1] << 8; 112 | } 113 | 114 | static inline uint32_t get_unaligned_le32(const uint8_t *p) 115 | { 116 | return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; 117 | } 118 | 119 | static inline uint64_t get_unaligned_le64(const uint8_t *p) 120 | { 121 | return (uint64_t)get_unaligned_le32(p + 4) << 32 | 122 | get_unaligned_le32(p); 123 | } 124 | 125 | int rtnl_init(void); 126 | int rtnl_call(struct nl_msg *msg); 127 | 128 | uint64_t unet_gettime(void); 129 | 130 | int sendto_rawudp(int fd, const void *addr, void *ip_hdr, size_t ip_hdrlen, 131 | const void *data, size_t len); 132 | 133 | #endif 134 | -------------------------------------------------------------------------------- /host.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2022 Felix Fietkau 4 | */ 5 | #ifndef __UNETD_HOST_H 6 | #define __UNETD_HOST_H 7 | 8 | enum peer_endpoint_type { 9 | ENDPOINT_TYPE_STATIC, 10 | ENDPOINT_TYPE_PEX, 11 | ENDPOINT_TYPE_ENDPOINT_NOTIFY, 12 | ENDPOINT_TYPE_ENDPOINT_PORT_NOTIFY, 13 | __ENDPOINT_TYPE_MAX, 14 | }; 15 | 16 | struct network_peer { 17 | struct vlist_node node; 18 | uint8_t key[CURVE25519_KEY_SIZE]; 19 | union network_addr local_addr; 20 | const char *endpoint; 21 | struct blob_attr *ipaddr; 22 | struct blob_attr *subnet; 23 | struct blob_attr *meta; 24 | int port; 25 | int pex_port; 26 | bool dynamic; 27 | bool indirect; 28 | 29 | struct { 30 | int connect_attempt; 31 | bool connected; 32 | bool handshake; 33 | bool has_local_ep_addr; 34 | union network_addr local_ep_addr; 35 | union network_endpoint endpoint; 36 | 37 | uint8_t next_endpoint_idx; 38 | union network_endpoint next_endpoint[__ENDPOINT_TYPE_MAX]; 39 | uint64_t last_ep_update; 40 | 41 | uint64_t rx_bytes, tx_bytes; 42 | uint64_t last_handshake; 43 | uint64_t last_request; 44 | uint64_t last_query_sent; 45 | 46 | int ping_wait; 47 | int last_handshake_diff; 48 | int idle; 49 | int num_net_queries; 50 | } state; 51 | }; 52 | 53 | struct network_dynamic_peer { 54 | struct list_head list; 55 | 56 | struct network_peer peer; 57 | }; 58 | 59 | struct network_host { 60 | struct avl_node node; 61 | 62 | const char *gateway; 63 | struct network_peer peer; 64 | }; 65 | 66 | struct network_group { 67 | struct avl_node node; 68 | const char *name; 69 | 70 | int n_members; 71 | struct network_host **members; 72 | }; 73 | 74 | static inline const char *network_host_name(struct network_host *host) 75 | { 76 | if (!host) 77 | return "(none)"; 78 | 79 | return host->node.key; 80 | } 81 | 82 | static inline bool network_host_is_peer(struct network_host *host) 83 | { 84 | return !!host->peer.node.avl.key; 85 | } 86 | 87 | static inline const char *network_peer_name(struct network_peer *peer) 88 | { 89 | struct network_host *host; 90 | 91 | if (!peer || peer->dynamic) 92 | return "(none)"; 93 | 94 | host = container_of(peer, struct network_host, peer); 95 | return network_host_name(host); 96 | } 97 | 98 | 99 | static inline bool 100 | network_host_uses_peer_route(struct network_host *host, struct network *net, 101 | struct network_peer *peer) 102 | { 103 | struct network_host *peer_host = container_of(peer, struct network_host, peer); 104 | 105 | if (host == peer_host || host == net->net_config.local_host) 106 | return false; 107 | 108 | if (net->net_config.local_host->gateway && 109 | !strcmp(net->net_config.local_host->gateway, network_peer_name(peer))) 110 | return true; 111 | 112 | if (peer_host->gateway && 113 | !strcmp(peer_host->gateway, network_host_name(net->net_config.local_host))) 114 | return true; 115 | 116 | if (!host->gateway) 117 | return false; 118 | 119 | return !strcmp(host->gateway, network_peer_name(peer)); 120 | } 121 | 122 | #define for_each_routed_host(cur_host, net, peer) \ 123 | avl_for_each_element(&(net)->hosts, cur_host, node) \ 124 | if (network_host_uses_peer_route(host, net, peer)) 125 | 126 | 127 | void network_hosts_update_start(struct network *net); 128 | void network_hosts_update_done(struct network *net); 129 | void network_hosts_add(struct network *net, struct blob_attr *hosts); 130 | void network_hosts_reload_dynamic_peers(struct network *net); 131 | 132 | void network_hosts_init(struct network *net); 133 | void network_hosts_free(struct network *net); 134 | 135 | #endif 136 | -------------------------------------------------------------------------------- /pex-msg.h: -------------------------------------------------------------------------------- 1 | #ifndef __PEX_MSG_H 2 | #define __PEX_MSG_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "curve25519.h" 8 | #include "siphash.h" 9 | #include "utils.h" 10 | 11 | #define UNETD_GLOBAL_PEX_PORT 51819 12 | #define PEX_BUF_SIZE 1024 13 | #define PEX_RX_BUF_SIZE 16384 14 | #define UNETD_NET_DATA_SIZE_MAX (128 * 1024) 15 | 16 | enum pex_opcode { 17 | PEX_MSG_HELLO, 18 | PEX_MSG_NOTIFY_PEERS, 19 | PEX_MSG_QUERY, 20 | PEX_MSG_PING, 21 | PEX_MSG_PONG, 22 | PEX_MSG_UPDATE_REQUEST, 23 | PEX_MSG_UPDATE_RESPONSE, 24 | PEX_MSG_UPDATE_RESPONSE_DATA, 25 | PEX_MSG_UPDATE_RESPONSE_NO_DATA, 26 | PEX_MSG_ENDPOINT_NOTIFY, 27 | PEX_MSG_ENDPOINT_PORT_NOTIFY, 28 | PEX_MSG_ENROLL, 29 | PEX_MSG_UPDATE_RESPONSE_REFUSED, 30 | }; 31 | 32 | #define PEX_ID_LEN 8 33 | 34 | struct pex_hdr { 35 | uint8_t version; 36 | uint8_t opcode; 37 | uint16_t len; 38 | uint8_t id[PEX_ID_LEN]; 39 | }; 40 | 41 | struct pex_ext_hdr { 42 | uint64_t nonce; 43 | uint8_t auth_id[PEX_ID_LEN]; 44 | }; 45 | 46 | #define PEER_EP_F_IPV6 (1 << 0) 47 | #define PEER_EP_F_LOCAL (1 << 1) 48 | 49 | struct pex_peer_endpoint { 50 | uint16_t flags; 51 | uint16_t port; 52 | uint8_t peer_id[PEX_ID_LEN]; 53 | uint8_t addr[16]; 54 | }; 55 | 56 | struct pex_hello { 57 | uint16_t flags; 58 | uint8_t local_addr[16]; 59 | }; 60 | 61 | struct pex_update_request { 62 | uint64_t req_id; /* must be first */ 63 | uint64_t cur_version; 64 | }; 65 | 66 | struct pex_update_response { 67 | uint64_t req_id; /* must be first */ 68 | uint32_t data_len; 69 | uint8_t e_key[CURVE25519_KEY_SIZE]; 70 | }; 71 | 72 | struct pex_update_response_data { 73 | uint64_t req_id; /* must be first */ 74 | uint32_t offset; 75 | }; 76 | 77 | struct pex_update_response_no_data { 78 | uint64_t req_id; /* must be first */ 79 | uint64_t cur_version; 80 | }; 81 | 82 | struct pex_endpoint_port_notify { 83 | uint16_t port; 84 | }; 85 | 86 | struct pex_msg_update_send_ctx { 87 | const uint8_t *pubkey; 88 | const uint8_t *auth_key; 89 | uint64_t req_id; 90 | bool ext; 91 | 92 | void *data; 93 | void *cur; 94 | int rem; 95 | }; 96 | 97 | struct pex_msg_local_control { 98 | int msg_type; 99 | uint8_t auth_id[PEX_ID_LEN]; 100 | union network_endpoint ep; 101 | int timeout; 102 | }; 103 | 104 | struct pex_hdr *pex_rx_accept(void *data, size_t len, bool ext); 105 | 106 | typedef void (*pex_recv_cb_t)(void *data, size_t len, struct sockaddr_in6 *addr); 107 | typedef void (*pex_recv_control_cb_t)(struct pex_msg_local_control *msg, int len); 108 | 109 | int pex_open(void *addr, size_t addr_len, pex_recv_cb_t cb, bool server); 110 | int pex_unix_open(const char *path, pex_recv_control_cb_t cb); 111 | void pex_close(void); 112 | 113 | int pex_socket(void); 114 | int pex_raw_socket(int family); 115 | uint64_t pex_network_hash(const uint8_t *auth_key, uint64_t req_id); 116 | struct pex_hdr *__pex_msg_init(const uint8_t *pubkey, uint8_t opcode); 117 | struct pex_hdr *__pex_msg_init_ext(const uint8_t *pubkey, const uint8_t *auth_key, 118 | uint8_t opcode, bool ext); 119 | int __pex_msg_send(int fd, const void *addr, void *ip_hdr, size_t ip_hdrlen); 120 | void *pex_msg_append(size_t len); 121 | void *pex_msg_tail(void); 122 | 123 | struct pex_update_request * 124 | pex_msg_update_request_init(const uint8_t *pubkey, const uint8_t *priv_key, 125 | const uint8_t *auth_key, union network_endpoint *addr, 126 | uint64_t cur_version, bool ext); 127 | void *pex_msg_update_response_recv(const void *data, int len, enum pex_opcode op, 128 | int *data_len, uint64_t *timestamp); 129 | 130 | void pex_msg_update_response_init(struct pex_msg_update_send_ctx *ctx, 131 | const uint8_t *pubkey, const uint8_t *auth_key, 132 | const uint8_t *peer_key, bool ext, 133 | struct pex_update_request *req, 134 | const void *data, int len); 135 | bool pex_msg_update_response_continue(struct pex_msg_update_send_ctx *ctx); 136 | 137 | #endif 138 | -------------------------------------------------------------------------------- /udht-ubus.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "udht.h" 3 | #include "curve25519.h" 4 | 5 | static struct ubus_auto_conn conn; 6 | static struct blob_buf b; 7 | 8 | static void 9 | udht_ubus_network_add(struct blob_attr *data, int seq) 10 | { 11 | static const struct blobmsg_policy net_policy = 12 | { "config", BLOBMSG_TYPE_TABLE }; 13 | enum { 14 | CONFIG_ATTR_AUTH_KEY, 15 | CONFIG_ATTR_DHT, 16 | __CONFIG_ATTR_MAX 17 | }; 18 | static const struct blobmsg_policy policy[__CONFIG_ATTR_MAX] = { 19 | [CONFIG_ATTR_AUTH_KEY] = { "auth_key", BLOBMSG_TYPE_STRING }, 20 | [CONFIG_ATTR_DHT] = { "dht", BLOBMSG_TYPE_BOOL }, 21 | }; 22 | struct blob_attr *tb[__CONFIG_ATTR_MAX]; 23 | struct blob_attr *config, *cur; 24 | uint8_t auth_key[CURVE25519_KEY_SIZE]; 25 | 26 | blobmsg_parse(&net_policy, 1, &config, blobmsg_data(data), blobmsg_len(data)); 27 | 28 | if (!config) 29 | return; 30 | 31 | blobmsg_parse(policy, __CONFIG_ATTR_MAX, tb, blobmsg_data(config), blobmsg_len(config)); 32 | if ((cur = tb[CONFIG_ATTR_DHT]) == NULL || !blobmsg_get_u8(cur)) 33 | return; 34 | 35 | if ((cur = tb[CONFIG_ATTR_AUTH_KEY]) == NULL || 36 | (b64_decode(blobmsg_get_string(cur), auth_key, CURVE25519_KEY_SIZE) != 37 | CURVE25519_KEY_SIZE)) 38 | return; 39 | 40 | udht_network_add(auth_key, seq); 41 | } 42 | 43 | 44 | static void 45 | udht_ubus_network_cb(struct ubus_request *req, int type, 46 | struct blob_attr *msg) 47 | { 48 | static const struct blobmsg_policy policy = 49 | { "networks", BLOBMSG_TYPE_TABLE }; 50 | struct blob_attr *networks, *cur; 51 | int *seq = req->priv; 52 | int rem; 53 | 54 | blobmsg_parse(&policy, 1, &networks, blobmsg_data(msg), blobmsg_len(msg)); 55 | 56 | if (!networks) 57 | return; 58 | 59 | blobmsg_for_each_attr(cur, networks, rem) 60 | udht_ubus_network_add(cur, *seq); 61 | } 62 | 63 | static void 64 | udht_ubus_update_networks(struct ubus_context *ctx) 65 | { 66 | static int seq; 67 | uint32_t id; 68 | 69 | seq++; 70 | 71 | if (ubus_lookup_id(ctx, "unetd", &id) == 0) 72 | ubus_invoke(ctx, id, "network_get", b.head, udht_ubus_network_cb, &seq, 5000); 73 | 74 | udht_network_flush(seq); 75 | } 76 | 77 | static int 78 | udht_ubus_unetd_cb(struct ubus_context *ctx, struct ubus_object *obj, 79 | struct ubus_request_data *req, const char *method, 80 | struct blob_attr *msg) 81 | { 82 | udht_ubus_update_networks(ctx); 83 | 84 | return 0; 85 | } 86 | 87 | static void 88 | udht_subscribe_unetd(struct ubus_context *ctx) 89 | { 90 | static struct ubus_subscriber sub = { 91 | .cb = udht_ubus_unetd_cb 92 | }; 93 | uint32_t id; 94 | 95 | if (!sub.obj.id && ubus_register_subscriber(ctx, &sub)) 96 | return; 97 | 98 | if (ubus_lookup_id(ctx, "unetd", &id)) 99 | return; 100 | 101 | ubus_subscribe(ctx, &sub, id); 102 | 103 | /* ensure that unetd's socket is ready by testing if it's reachable over ubus */ 104 | if (ubus_invoke(ctx, id, "network_get", b.head, NULL, NULL, 10000)) 105 | return; 106 | 107 | udht_reconnect(); 108 | udht_ubus_update_networks(ctx); 109 | } 110 | 111 | static void 112 | udht_ubus_event_cb(struct ubus_context *ctx, struct ubus_event_handler *ev, 113 | const char *type, struct blob_attr *msg) 114 | { 115 | static const struct blobmsg_policy policy = 116 | { "path", BLOBMSG_TYPE_STRING }; 117 | struct blob_attr *attr; 118 | 119 | blobmsg_parse(&policy, 1, &attr, blobmsg_data(msg), blobmsg_len(msg)); 120 | if (!attr) 121 | return; 122 | 123 | if (!strcmp(blobmsg_get_string(attr), "unetd")) 124 | udht_subscribe_unetd(ctx); 125 | } 126 | 127 | static void 128 | ubus_connect_handler(struct ubus_context *ctx) 129 | { 130 | static struct ubus_event_handler ev = { 131 | .cb = udht_ubus_event_cb, 132 | }; 133 | 134 | ubus_register_event_handler(ctx, &ev, "ubus.object.add"); 135 | udht_subscribe_unetd(ctx); 136 | } 137 | 138 | void udht_ubus_init(void) 139 | { 140 | blob_buf_init(&b, 0); 141 | conn.cb = ubus_connect_handler; 142 | ubus_auto_connect(&conn); 143 | } 144 | -------------------------------------------------------------------------------- /edsign.c: -------------------------------------------------------------------------------- 1 | /* Edwards curve signature system 2 | * Daniel Beer , 22 Apr 2014 3 | * 4 | * This file is in the public domain. 5 | */ 6 | 7 | #include "ed25519.h" 8 | #include "sha512.h" 9 | #include "fprime.h" 10 | #include "edsign.h" 11 | 12 | #define EXPANDED_SIZE 64 13 | 14 | static const uint8_t ed25519_order[FPRIME_SIZE] = { 15 | 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 16 | 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 17 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 18 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 19 | }; 20 | 21 | static void expand_key(uint8_t *expanded, const uint8_t *secret) 22 | { 23 | struct sha512_state s; 24 | 25 | sha512_init(&s); 26 | sha512_add(&s, secret, EDSIGN_SECRET_KEY_SIZE); 27 | sha512_final(&s, expanded); 28 | 29 | ed25519_prepare(expanded); 30 | } 31 | 32 | static uint8_t upp(struct ed25519_pt *p, const uint8_t *packed) 33 | { 34 | uint8_t x[F25519_SIZE]; 35 | uint8_t y[F25519_SIZE]; 36 | uint8_t ok = ed25519_try_unpack(x, y, packed); 37 | 38 | ed25519_project(p, x, y); 39 | return ok; 40 | } 41 | 42 | static void pp(uint8_t *packed, const struct ed25519_pt *p) 43 | { 44 | uint8_t x[F25519_SIZE]; 45 | uint8_t y[F25519_SIZE]; 46 | 47 | ed25519_unproject(x, y, p); 48 | ed25519_pack(packed, x, y); 49 | } 50 | 51 | static void sm_pack(uint8_t *r, const uint8_t *k) 52 | { 53 | struct ed25519_pt p; 54 | 55 | ed25519_smult(&p, &ed25519_base, k); 56 | pp(r, &p); 57 | } 58 | 59 | void edsign_sec_to_pub(void *pub, const void *secret) 60 | { 61 | uint8_t expanded[EXPANDED_SIZE]; 62 | 63 | expand_key(expanded, secret); 64 | sm_pack(pub, expanded); 65 | } 66 | 67 | static void save_hash(struct sha512_state *s, uint8_t *out) 68 | { 69 | void *hash; 70 | 71 | hash = sha512_final_get(s); 72 | fprime_from_bytes(out, hash, SHA512_HASH_SIZE, ed25519_order); 73 | } 74 | 75 | static void generate_k(uint8_t *k, const uint8_t *kgen_key, 76 | const uint8_t *message, size_t len) 77 | { 78 | struct sha512_state s; 79 | 80 | sha512_init(&s); 81 | sha512_add(&s, kgen_key, 32); 82 | sha512_add(&s, message, len); 83 | save_hash(&s, k); 84 | } 85 | 86 | static void hash_message(uint8_t *z, const uint8_t *r, const uint8_t *a, 87 | const uint8_t *m, size_t len) 88 | { 89 | struct sha512_state s; 90 | 91 | sha512_init(&s); 92 | sha512_add(&s, r, 32); 93 | sha512_add(&s, a, 32); 94 | sha512_add(&s, m, len); 95 | save_hash(&s, z); 96 | } 97 | 98 | void edsign_sign(uint8_t *signature, const uint8_t *pub, 99 | const uint8_t *secret, 100 | const uint8_t *message, size_t len) 101 | { 102 | uint8_t expanded[EXPANDED_SIZE]; 103 | uint8_t e[FPRIME_SIZE]; 104 | uint8_t s[FPRIME_SIZE]; 105 | uint8_t k[FPRIME_SIZE]; 106 | uint8_t z[FPRIME_SIZE]; 107 | 108 | expand_key(expanded, secret); 109 | 110 | /* Generate k and R = kB */ 111 | generate_k(k, expanded + 32, message, len); 112 | sm_pack(signature, k); 113 | 114 | /* Compute z = H(R, A, M) */ 115 | hash_message(z, signature, pub, message, len); 116 | 117 | /* Obtain e */ 118 | fprime_from_bytes(e, expanded, 32, ed25519_order); 119 | 120 | /* Compute s = ze + k */ 121 | fprime_mul(s, z, e, ed25519_order); 122 | fprime_add(s, k, ed25519_order); 123 | memcpy(signature + 32, s, 32); 124 | } 125 | 126 | void edsign_verify_init(struct edsign_verify_state *st, const void *sig, 127 | const void *pub) 128 | { 129 | sha512_init(&st->sha); 130 | sha512_add(&st->sha, sig, 32); 131 | sha512_add(&st->sha, pub, 32); 132 | } 133 | 134 | bool edsign_verify(struct edsign_verify_state *st, const void *sig, const void *pub) 135 | { 136 | struct ed25519_pt p; 137 | struct ed25519_pt q; 138 | uint8_t lhs[F25519_SIZE]; 139 | uint8_t rhs[F25519_SIZE]; 140 | uint8_t z[FPRIME_SIZE]; 141 | uint8_t ok = 1; 142 | 143 | /* Compute z = H(R, A, M) */ 144 | save_hash(&st->sha, z); 145 | 146 | /* sB = (ze + k)B = ... */ 147 | sm_pack(lhs, sig + 32); 148 | 149 | /* ... = zA + R */ 150 | ok &= upp(&p, pub); 151 | ed25519_smult(&p, &p, z); 152 | ok &= upp(&q, sig); 153 | ed25519_add(&p, &p, &q); 154 | pp(rhs, &p); 155 | 156 | /* Equal? */ 157 | return ok & f25519_eq(lhs, rhs); 158 | } 159 | -------------------------------------------------------------------------------- /bpf_skb_utils.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2022 Felix Fietkau 4 | */ 5 | #ifndef __BPF_SKB_UTILS_H 6 | #define __BPF_SKB_UTILS_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | struct skb_parser_info { 19 | struct __sk_buff *skb; 20 | __u32 offset; 21 | int proto; 22 | }; 23 | 24 | static __always_inline void *__skb_data(struct __sk_buff *skb) 25 | { 26 | return (void *)(long)READ_ONCE(skb->data); 27 | } 28 | 29 | static __always_inline void * 30 | skb_ptr(struct __sk_buff *skb, __u32 offset, __u32 len) 31 | { 32 | void *ptr = __skb_data(skb) + offset; 33 | void *end = (void *)(long)(skb->data_end); 34 | 35 | if (ptr + len >= end) 36 | return NULL; 37 | 38 | return ptr; 39 | } 40 | 41 | static __always_inline void * 42 | skb_info_ptr(struct skb_parser_info *info, __u32 len) 43 | { 44 | __u32 offset = info->offset; 45 | return skb_ptr(info->skb, offset, len); 46 | } 47 | 48 | static __always_inline void 49 | skb_parse_init(struct skb_parser_info *info, struct __sk_buff *skb) 50 | { 51 | *info = (struct skb_parser_info){ 52 | .skb = skb 53 | }; 54 | } 55 | 56 | static __always_inline struct ethhdr * 57 | skb_parse_ethernet(struct skb_parser_info *info) 58 | { 59 | struct ethhdr *eth; 60 | int len; 61 | 62 | len = sizeof(*eth) + 2 * sizeof(struct vlan_hdr) + sizeof(struct ipv6hdr); 63 | if (len > info->skb->len) 64 | len = info->skb->len; 65 | bpf_skb_pull_data(info->skb, len); 66 | 67 | eth = skb_info_ptr(info, sizeof(*eth)); 68 | if (!eth) 69 | return NULL; 70 | 71 | info->proto = eth->h_proto; 72 | info->offset += sizeof(*eth); 73 | 74 | return eth; 75 | } 76 | 77 | static __always_inline struct vlan_hdr * 78 | skb_parse_vlan(struct skb_parser_info *info) 79 | { 80 | struct vlan_hdr *vlh; 81 | 82 | if (info->proto != bpf_htons(ETH_P_8021Q) && 83 | info->proto != bpf_htons(ETH_P_8021AD)) 84 | return NULL; 85 | 86 | vlh = skb_info_ptr(info, sizeof(*vlh)); 87 | if (!vlh) 88 | return NULL; 89 | 90 | info->proto = vlh->h_vlan_encapsulated_proto; 91 | info->offset += sizeof(*vlh); 92 | 93 | return vlh; 94 | } 95 | 96 | static __always_inline struct iphdr * 97 | skb_parse_ipv4(struct skb_parser_info *info, int min_l4_bytes) 98 | { 99 | struct iphdr *iph; 100 | int proto, hdr_len; 101 | __u32 pull_len; 102 | 103 | if (info->proto != bpf_htons(ETH_P_IP)) 104 | return NULL; 105 | 106 | iph = skb_info_ptr(info, sizeof(*iph)); 107 | if (!iph) 108 | return NULL; 109 | 110 | hdr_len = iph->ihl * 4; 111 | if (hdr_len < sizeof(*iph)) 112 | return NULL; 113 | 114 | pull_len = info->offset + hdr_len + min_l4_bytes; 115 | if (pull_len > info->skb->len) 116 | pull_len = info->skb->len; 117 | 118 | if (bpf_skb_pull_data(info->skb, pull_len)) 119 | return NULL; 120 | 121 | iph = skb_info_ptr(info, sizeof(*iph)); 122 | if (!iph) 123 | return NULL; 124 | 125 | info->proto = iph->protocol; 126 | info->offset += hdr_len; 127 | 128 | return iph; 129 | } 130 | 131 | static __always_inline struct ipv6hdr * 132 | skb_parse_ipv6(struct skb_parser_info *info, int max_l4_bytes) 133 | { 134 | struct ipv6hdr *ip6h; 135 | __u32 pull_len; 136 | 137 | if (info->proto != bpf_htons(ETH_P_IPV6)) 138 | return NULL; 139 | 140 | pull_len = info->offset + sizeof(*ip6h) + max_l4_bytes; 141 | if (pull_len > info->skb->len) 142 | pull_len = info->skb->len; 143 | 144 | if (bpf_skb_pull_data(info->skb, pull_len)) 145 | return NULL; 146 | 147 | ip6h = skb_info_ptr(info, sizeof(*ip6h)); 148 | if (!ip6h) 149 | return NULL; 150 | 151 | info->proto = READ_ONCE(ip6h->nexthdr); 152 | info->offset += sizeof(*ip6h); 153 | 154 | return ip6h; 155 | } 156 | 157 | static __always_inline struct tcphdr * 158 | skb_parse_tcp(struct skb_parser_info *info) 159 | { 160 | struct tcphdr *tcph; 161 | 162 | if (info->proto != IPPROTO_TCP) 163 | return NULL; 164 | 165 | tcph = skb_info_ptr(info, sizeof(*tcph)); 166 | if (!tcph) 167 | return NULL; 168 | 169 | info->offset += tcph->doff * 4; 170 | 171 | return tcph; 172 | } 173 | 174 | #endif 175 | -------------------------------------------------------------------------------- /stun.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2022 Felix Fietkau 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "stun.h" 10 | 11 | static uint8_t tx_buf[256]; 12 | 13 | bool stun_msg_is_valid(const void *data, size_t len) 14 | { 15 | const struct stun_msg_hdr *hdr = data; 16 | 17 | if (len <= sizeof(*hdr)) 18 | return false; 19 | 20 | return hdr->magic == htonl(STUN_MAGIC); 21 | } 22 | 23 | static void *stun_msg_init(uint16_t type) 24 | { 25 | struct stun_msg_hdr *hdr = (struct stun_msg_hdr *)tx_buf; 26 | 27 | memset(hdr, 0, sizeof(*hdr)); 28 | hdr->msg_type = htons(type); 29 | hdr->magic = htonl(STUN_MAGIC); 30 | 31 | return hdr; 32 | } 33 | 34 | static void *stun_msg_add_tlv(uint16_t type, uint16_t len) 35 | { 36 | struct stun_msg_hdr *hdr = (struct stun_msg_hdr *)tx_buf; 37 | uint16_t data_len = ntohs(hdr->msg_len); 38 | struct stun_msg_tlv *tlv; 39 | void *data = hdr + 1; 40 | 41 | data += data_len; 42 | 43 | tlv = data; 44 | tlv->type = htons(type); 45 | tlv->len = htons(len); 46 | 47 | if (len & 3) 48 | len = (len + 3) & ~3; 49 | 50 | data_len += sizeof(*tlv) + len; 51 | hdr->msg_len = htons(data_len); 52 | 53 | return tlv + 1; 54 | } 55 | 56 | static void 57 | stun_msg_parse_attr(const struct stun_tlv_policy *policy, 58 | const struct stun_msg_tlv **tb, int len, 59 | const struct stun_msg_tlv *tlv) 60 | { 61 | uint16_t type; 62 | int i; 63 | 64 | type = ntohs(tlv->type); 65 | 66 | for (i = 0; i < len; i++) { 67 | if (policy[i].type != type) 68 | continue; 69 | 70 | if (ntohs(tlv->len) < policy[i].min_len) 71 | return; 72 | 73 | tb[i] = tlv; 74 | return; 75 | } 76 | } 77 | 78 | static void 79 | stun_msg_parse(const struct stun_tlv_policy *policy, 80 | const struct stun_msg_tlv **tb, int len, 81 | const void *data, size_t data_len) 82 | { 83 | const struct stun_msg_hdr *hdr = data; 84 | const struct stun_msg_tlv *tlv; 85 | const void *end = data + data_len; 86 | uint16_t cur_len; 87 | 88 | data += sizeof(*hdr); 89 | while (1) { 90 | tlv = data; 91 | data = tlv + 1; 92 | if (data > end) 93 | break; 94 | 95 | cur_len = ntohs(tlv->len); 96 | if (data + cur_len > end) 97 | break; 98 | 99 | stun_msg_parse_attr(policy, tb, len, tlv); 100 | data += (cur_len + 3) & ~3; 101 | } 102 | } 103 | 104 | const void *stun_msg_request_prepare(struct stun_request *req, size_t *len, 105 | uint16_t response_port) 106 | { 107 | struct stun_msg_hdr *hdr; 108 | FILE *f; 109 | 110 | hdr = stun_msg_init(STUN_MSGTYPE_BINDING_REQUEST); 111 | if (response_port) { 112 | uint16_t *tlv_port = stun_msg_add_tlv(STUN_TLV_RESPONSE_PORT, 2); 113 | *tlv_port = htons(response_port); 114 | } 115 | 116 | f = fopen("/dev/urandom", "r"); 117 | if (!f) 118 | return NULL; 119 | 120 | if (fread(hdr->transaction, 12, 1, f) != 1) 121 | return NULL; 122 | 123 | fclose(f); 124 | memcpy(req->transaction, hdr->transaction, sizeof(req->transaction)); 125 | req->pending = true; 126 | req->port = 0; 127 | *len = htons(hdr->msg_len) + sizeof(*hdr); 128 | 129 | return hdr; 130 | } 131 | 132 | bool stun_msg_request_complete(struct stun_request *req, const void *data, 133 | size_t len) 134 | { 135 | enum { 136 | PARSE_ATTR_MAPPED, 137 | PARSE_ATTR_XOR_MAPPED, 138 | __PARSE_ATTR_MAX 139 | }; 140 | const struct stun_msg_tlv *tb[__PARSE_ATTR_MAX]; 141 | static const struct stun_tlv_policy policy[__PARSE_ATTR_MAX] = { 142 | [PARSE_ATTR_MAPPED] = { STUN_TLV_MAPPED_ADDRESS, 8 }, 143 | [PARSE_ATTR_XOR_MAPPED] = { STUN_TLV_XOR_MAPPED_ADDRESS, 8 } 144 | }; 145 | const struct stun_msg_hdr *hdr = data; 146 | const void *tlv_data; 147 | uint16_t port; 148 | 149 | if (!req->pending) 150 | return false; 151 | 152 | if (!stun_msg_is_valid(data, len)) 153 | return false; 154 | 155 | if (hdr->msg_type != htons(STUN_MSGTYPE_BINDING_RESPONSE)) 156 | return false; 157 | 158 | if (memcmp(hdr->transaction, req->transaction, sizeof(hdr->transaction)) != 0) 159 | return false; 160 | 161 | stun_msg_parse(policy, tb, __PARSE_ATTR_MAX, data, len); 162 | 163 | if (tb[PARSE_ATTR_XOR_MAPPED]) { 164 | tlv_data = tb[PARSE_ATTR_XOR_MAPPED] + 1; 165 | tlv_data += 2; 166 | port = ntohs(*(const uint16_t *)tlv_data); 167 | port ^= STUN_MAGIC >> 16; 168 | } else if (tb[PARSE_ATTR_MAPPED]) { 169 | tlv_data = tb[PARSE_ATTR_MAPPED] + 1; 170 | tlv_data += 2; 171 | port = ntohs(*(const uint16_t *)tlv_data); 172 | } else 173 | return false; 174 | 175 | req->port = port; 176 | return true; 177 | } 178 | -------------------------------------------------------------------------------- /PEX.md: -------------------------------------------------------------------------------- 1 | 2 | # PEX - Peer Endpoint eXchange protocol 3 | 4 | ## Header: 5 | 6 | struct pex_hdr { 7 | uint8_t version; 8 | uint8_t opcode; 9 | uint16_t len; 10 | uint8_t id[8]; 11 | }; 12 | 13 | - version: always 0 for now 14 | - opcode: message type 15 | - len: payload length 16 | - id: local peer id 17 | 18 | All multi-byte integer fields are in big-endian byte order. 19 | Peer identifiers contain the first 8 bytes of the public key 20 | 21 | ## Message types 22 | 23 | ### opcode=0: PEX_MSG_HELLO 24 | 25 | Payload (single item): 26 | 27 | struct pex_hello { 28 | uint16_t flags; 29 | uint8_t local_addr[16]; 30 | }; 31 | 32 | - local_addr: Local IPv4 or IPv6 address used for connecting to the remote endpoint 33 | - flags: 34 | Bit 0: local_addr is an IPv6 address 35 | 36 | Sent after any successful handshake. 37 | 38 | ### opcode=1: PEX_MSG_NOTIFY_PEERS 39 | 40 | Used to send information about one or more peers, either proactively, or as a response to PEX_MSG_QUERY 41 | 42 | Payload (multiple): 43 | 44 | struct pex_peer_endpoint { 45 | uint16_t flags; 46 | uint16_t port; 47 | uint8_t peer_id[PEX_ID_LEN]; 48 | uint8_t addr[16]; 49 | }; 50 | 51 | - port: endpoint port 52 | - addr: IPv4 or IPv6 endpoint address 53 | - peer_id: peer ID 54 | - flags: 55 | Bit 0: addr is an IPv6 address 56 | Bit 1: addr refers to the local network address of the peer 57 | 58 | ### opcode=2: PEX_MSG_QUERY 59 | 60 | Used to ask for the endpoint address of one or more peers. Expects a PEX_MSG_NOTIFY_PEERS response, but only if there is known data about any of the queried peers. 61 | 62 | Payload (multiple): 63 | 64 | uint8_t peer_id[8]; 65 | 66 | For any peer in the payload list that has a known endpoint address, compare the IP address against the endpoint address of the sender of this message. 67 | If the IP address matches, send back the local address of the peer (from the PEX_MSG_HELLO message) instead of the discovered wireguard endpoint address. This helps with establishing a direct connection through double-NAT. 68 | 69 | ### opcode=3: PEX_MSG_PING 70 | 71 | Used to ping a peer (to keep the connection alive). 72 | No payload. 73 | 74 | ### opcode=4: PEX_MSG_PONG 75 | 76 | Response to PEX_MSG_PING. 77 | No payload. 78 | 79 | ## Unencrypted messages (outside of the tunnel) 80 | 81 | These are only supported for networks using signed network data that can be updated dynamically. 82 | The struct pex_hdr header is followed by a second header: 83 | 84 | struct pex_ext_hdr { 85 | uint64_t nonce; 86 | uint8_t auth_id[8]; 87 | }; 88 | 89 | - nonce: nonce for id hash 90 | - auth_id: first 8 bytes of the auth public key 91 | 92 | In these messages, pex_hdr::id is XORed with siphash(req_id || req_id, auth_key) 93 | 94 | ### opcode=5: PEX_MSG_UPDATE_REQUEST 95 | 96 | This message can be used outside of the wireguard tunnel in order to request signed network data 97 | It is used to ask a peer for the latest signed network data 98 | 99 | Payload: 100 | struct pex_update_request { 101 | uint64_t cur_version; 102 | uint32_t req_id; 103 | }; 104 | 105 | - cur_version: latest version of the network data that the sender already has 106 | - req_id: request id copied to response messages 107 | 108 | ### opcode=6: PEX_MSG_UPDATE_RESPONSE 109 | 110 | Used to send updated signed network data to a peer 111 | 112 | Payload: 113 | struct pex_update_response { 114 | uint64_t req_id; 115 | uint32_t data_len; 116 | uint8_t e_key[32]; 117 | }; 118 | 119 | followed by the first chunk of network data. 120 | 121 | - req_id: request id of the PEX_MSG_UPDATE_REQUEST message 122 | - data_len: total length of the network data 123 | - e_key: ephemeral curve25519 public key 124 | 125 | The network data is chacha20 encrypted with the following key: 126 | DH(e_key_priv, peer_key) 127 | And using req_id as nonce. 128 | 129 | - e_key_priv: private key belonging to e_key 130 | - peer_key: public key belonging to the receiver (from the network data) 131 | 132 | ### opcode=7: PEX_MSG_UPDATE_RESPONSE_DATA 133 | 134 | Continuation of PEX_MSG_UPDATE_RESPONSE network data 135 | 136 | Payload: 137 | struct pex_update_response_data { 138 | uint64_t req_id; 139 | uint32_t offset; 140 | }; 141 | 142 | followed by encrypted network data 143 | 144 | ### opcode=8: PEX_MSG_UPDATE_RESPONSE_NO_DATA 145 | 146 | Indicates that the network data with the timestamp given in PEX_MSG_UPDATE_REQUEST 147 | is up to date 148 | 149 | Payload: 150 | 151 | struct pex_update_response_no_data { 152 | uint64_t req_id; 153 | uint64_t cur_version; 154 | }; 155 | 156 | - req_id: request id of the PEX_MSG_UPDATE_REQUEST message 157 | - cur_version: latest version of the network data 158 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2022 Felix Fietkau 4 | */ 5 | #define _GNU_SOURCE 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "unetd.h" 11 | 12 | struct cmdline_network { 13 | struct cmdline_network *next; 14 | char *data; 15 | }; 16 | 17 | static struct cmdline_network *cmd_nets; 18 | static const char *hosts_file; 19 | const char *mssfix_path = UNETD_MSS_BPF_PATH; 20 | const char *data_dir = UNETD_DATA_DIR; 21 | int global_pex_port = UNETD_GLOBAL_PEX_PORT; 22 | 23 | static bool debug; 24 | 25 | #ifdef UBUS_SUPPORT 26 | static struct udebug ud; 27 | static struct udebug_buf udb_log; 28 | 29 | static const struct udebug_buf_meta meta_log = { 30 | .name = "log", 31 | .format = UDEBUG_FORMAT_STRING 32 | }; 33 | 34 | static struct udebug_ubus_ring rings[] = { 35 | { 36 | .buf = &udb_log, 37 | .meta = &meta_log, 38 | .default_entries = 1024, 39 | .default_size = 64 * 1024, 40 | } 41 | }; 42 | #endif 43 | 44 | bool unetd_debug_active(void) 45 | { 46 | #ifdef UBUS_SUPPORT 47 | if (udebug_buf_valid(&udb_log)) 48 | return true; 49 | #endif 50 | return debug; 51 | } 52 | 53 | static void __attribute__((format (printf, 1, 0))) 54 | unetd_udebug_vprintf(const char *format, va_list ap) 55 | { 56 | #ifdef UBUS_SUPPORT 57 | if (!udebug_buf_valid(&udb_log)) 58 | return; 59 | 60 | udebug_entry_init(&udb_log); 61 | udebug_entry_vprintf(&udb_log, format, ap); 62 | udebug_entry_add(&udb_log); 63 | #endif 64 | } 65 | 66 | void unetd_debug_printf(const char *format, ...) 67 | { 68 | va_list ap; 69 | 70 | va_start(ap, format); 71 | 72 | if (debug) { 73 | va_list ap2; 74 | 75 | va_copy(ap2, ap); 76 | vfprintf(stderr, format, ap2); 77 | va_end(ap2); 78 | } 79 | 80 | unetd_udebug_vprintf(format, ap); 81 | va_end(ap); 82 | } 83 | 84 | #ifdef UBUS_SUPPORT 85 | void unetd_udebug_config(struct udebug_ubus *ctx, struct blob_attr *data, 86 | bool enabled) 87 | { 88 | udebug_ubus_apply_config(&ud, rings, ARRAY_SIZE(rings), data, enabled); 89 | } 90 | #endif 91 | 92 | static void 93 | network_write_hosts(struct network *net, FILE *f) 94 | { 95 | struct network_host *host; 96 | char ip[INET6_ADDRSTRLEN]; 97 | 98 | if (!net->net_config.local_host) 99 | return; 100 | 101 | avl_for_each_element(&net->hosts, host, node) { 102 | inet_ntop(AF_INET6, &host->peer.local_addr, ip, sizeof(ip)); 103 | fprintf(f, "%s\t%s%s%s\n", ip, network_host_name(host), 104 | net->config.domain ? "." : "", 105 | net->config.domain ? net->config.domain : ""); 106 | } 107 | } 108 | 109 | void unetd_write_hosts(void) 110 | { 111 | struct network *net; 112 | char *tmpfile = NULL; 113 | FILE *f; 114 | int fd; 115 | 116 | if (!hosts_file) 117 | return; 118 | 119 | if (asprintf(&tmpfile, "%s.XXXXXXXX", hosts_file) < 0) 120 | return; 121 | 122 | fd = mkstemp(tmpfile); 123 | if (fd < 0) { 124 | perror("mkstemp"); 125 | goto out; 126 | } 127 | 128 | chmod(tmpfile, 0644); 129 | f = fdopen(fd, "w"); 130 | if (!f) { 131 | close(fd); 132 | goto out; 133 | } 134 | 135 | avl_for_each_element(&networks, net, node) 136 | network_write_hosts(net, f); 137 | 138 | fclose(f); 139 | 140 | if (rename(tmpfile, hosts_file)) 141 | unlink(tmpfile); 142 | 143 | out: 144 | free(tmpfile); 145 | } 146 | 147 | static void add_networks(void) 148 | { 149 | struct cmdline_network *net; 150 | static struct blob_buf b; 151 | struct blob_attr *name; 152 | 153 | for (net = cmd_nets; net; net = net->next) { 154 | blob_buf_init(&b, 0); 155 | if (!blobmsg_add_json_from_string(&b, net->data)) 156 | continue; 157 | 158 | blobmsg_parse(&network_policy[NETWORK_ATTR_NAME], 1, &name, 159 | blobmsg_data(b.head), blobmsg_len(b.head)); 160 | if (!name) 161 | continue; 162 | 163 | unetd_network_add(blobmsg_get_string(name), b.head); 164 | } 165 | 166 | blob_buf_free(&b); 167 | } 168 | 169 | int main(int argc, char **argv) 170 | { 171 | struct cmdline_network *net; 172 | const char *unix_socket = NULL; 173 | int ch; 174 | 175 | while ((ch = getopt(argc, argv, "D:dh:u:M:N:P:")) != -1) { 176 | switch (ch) { 177 | case 'D': 178 | data_dir = optarg; 179 | break; 180 | case 'd': 181 | debug = true; 182 | break; 183 | case 'h': 184 | hosts_file = optarg; 185 | break; 186 | case 'N': 187 | net = calloc(1, sizeof(*net)); 188 | net->next = cmd_nets; 189 | net->data = optarg; 190 | cmd_nets = net; 191 | break; 192 | case 'M': 193 | mssfix_path = optarg; 194 | break; 195 | case 'P': 196 | global_pex_port = atoi(optarg); 197 | break; 198 | case 'u': 199 | unix_socket = optarg; 200 | break; 201 | } 202 | } 203 | 204 | uloop_init(); 205 | #ifdef UBUS_SUPPORT 206 | udebug_init(&ud); 207 | udebug_auto_connect(&ud, NULL); 208 | for (size_t i = 0; i < ARRAY_SIZE(rings); i++) 209 | udebug_ubus_ring_init(&ud, &rings[i]); 210 | #endif 211 | 212 | unetd_ubus_init(); 213 | unetd_write_hosts(); 214 | global_pex_open(unix_socket); 215 | add_networks(); 216 | uloop_run(); 217 | pex_close(); 218 | network_free_all(); 219 | uloop_done(); 220 | 221 | return 0; 222 | } 223 | -------------------------------------------------------------------------------- /service.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2022 Felix Fietkau 4 | */ 5 | #include 6 | #include "unetd.h" 7 | 8 | enum { 9 | SERVICE_ATTR_TYPE, 10 | SERVICE_ATTR_CONFIG, 11 | SERVICE_ATTR_MEMBERS, 12 | __SERVICE_ATTR_MAX 13 | }; 14 | 15 | static const struct blobmsg_policy service_policy[__SERVICE_ATTR_MAX] = { 16 | [SERVICE_ATTR_TYPE] = { "type", BLOBMSG_TYPE_STRING }, 17 | [SERVICE_ATTR_CONFIG] = { "config", BLOBMSG_TYPE_TABLE }, 18 | [SERVICE_ATTR_MEMBERS] = { "members", BLOBMSG_TYPE_ARRAY }, 19 | }; 20 | 21 | void network_services_free(struct network *net) 22 | { 23 | vlist_flush_all(&net->services); 24 | } 25 | 26 | static int 27 | __service_add_member(struct network_host **list, int *n, struct network_host *member) 28 | { 29 | int i; 30 | 31 | for (i = 0; i < *n; i++) { 32 | if (list[i] == member) 33 | return 0; 34 | } 35 | 36 | list[(*n)++] = member; 37 | return 1; 38 | } 39 | 40 | static int 41 | __service_add_group(struct network_host **list, int *n, struct network_group *group) 42 | { 43 | int i, count = 0; 44 | 45 | for (i = 0; i < group->n_members; i++) 46 | count += __service_add_member(list, n, group->members[i]); 47 | 48 | return count; 49 | } 50 | 51 | static int 52 | __service_parse_members(struct network *net, struct network_service *s, 53 | const char *name) 54 | { 55 | struct network_group *group; 56 | struct network_host *host; 57 | unsigned int count = 0; 58 | 59 | if (name[0] != '@') { 60 | host = avl_find_element(&net->hosts, name, host, node); 61 | 62 | if (!host) 63 | return 0; 64 | 65 | if (s) 66 | __service_add_member(s->members, &s->n_members, host); 67 | 68 | return 1; 69 | } 70 | 71 | name++; 72 | group = avl_find_element(&net->groups, name, group, node); 73 | if (!group) { 74 | if (name[0] && strcmp(name, "all") != 0) 75 | return 0; 76 | 77 | avl_for_each_element(&net->hosts, host, node) { 78 | if (s) 79 | __service_add_member(s->members, &s->n_members, host); 80 | count++; 81 | } 82 | return count; 83 | } 84 | 85 | if (s) 86 | return __service_add_group(s->members, &s->n_members, group); 87 | else 88 | return group->n_members; 89 | } 90 | 91 | static int 92 | service_parse_members(struct network *net, struct network_service *s, 93 | struct blob_attr *data) 94 | { 95 | struct blob_attr *cur; 96 | int rem; 97 | int n = 0; 98 | 99 | blobmsg_for_each_attr(cur, data, rem) 100 | n += __service_parse_members(net, s, blobmsg_get_string(cur)); 101 | 102 | return n; 103 | } 104 | 105 | static void 106 | service_add(struct network *net, struct blob_attr *data) 107 | { 108 | struct network_service *s; 109 | struct blob_attr *tb[__SERVICE_ATTR_MAX]; 110 | struct blob_attr *cur, *config; 111 | const char *name = blobmsg_name(data); 112 | const char *type = NULL; 113 | char *name_buf, *type_buf; 114 | void *config_buf; 115 | int n_members; 116 | 117 | blobmsg_parse(service_policy, __SERVICE_ATTR_MAX, tb, 118 | blobmsg_data(data), blobmsg_len(data)); 119 | 120 | if ((cur = tb[SERVICE_ATTR_TYPE]) != NULL) 121 | type = blobmsg_get_string(cur); 122 | 123 | if (!tb[SERVICE_ATTR_MEMBERS] || 124 | blobmsg_check_array(tb[SERVICE_ATTR_MEMBERS], BLOBMSG_TYPE_STRING) < 0) 125 | return; 126 | 127 | config = tb[SERVICE_ATTR_CONFIG]; 128 | 129 | n_members = service_parse_members(net, NULL, tb[SERVICE_ATTR_MEMBERS]); 130 | s = calloc_a(sizeof(*s) + n_members * sizeof(s->members[0]), 131 | &name_buf, strlen(name) + 1, 132 | &type_buf, type ? strlen(type) + 1 : 0, 133 | &config_buf, config ? blob_pad_len(config) : 0); 134 | 135 | strcpy(name_buf, name); 136 | if (type) 137 | s->type = strcpy(type_buf, type); 138 | if (config) 139 | s->config = memcpy(config_buf, config, blob_pad_len(config)); 140 | #ifdef VXLAN_SUPPORT 141 | if (type && !strcmp(type, "vxlan")) 142 | s->ops = &vxlan_ops; 143 | #endif 144 | 145 | service_parse_members(net, s, tb[SERVICE_ATTR_MEMBERS]); 146 | vlist_add(&net->services, &s->node, name_buf); 147 | } 148 | 149 | void network_services_add(struct network *net, struct blob_attr *data) 150 | { 151 | struct blob_attr *cur; 152 | int rem; 153 | 154 | blobmsg_for_each_attr(cur, data, rem) 155 | service_add(net, cur); 156 | } 157 | 158 | static void 159 | service_update(struct vlist_tree *tree, struct vlist_node *node_new, 160 | struct vlist_node *node_old) 161 | { 162 | struct network *net = container_of(tree, struct network, services); 163 | struct network_service *s_old, *s_new; 164 | 165 | s_new = container_of_safe(node_new, struct network_service, node); 166 | s_old = container_of_safe(node_old, struct network_service, node); 167 | 168 | if (s_new && s_old && s_new->ops && s_new->ops == s_old->ops) { 169 | s_new->ops->init(net, s_new, s_old); 170 | goto out; 171 | } 172 | 173 | if (s_new && s_new->ops) 174 | s_new->ops->init(net, s_new, NULL); 175 | 176 | if (s_old && s_old->ops) 177 | s_old->ops->free(net, s_old); 178 | 179 | out: 180 | free(s_old); 181 | } 182 | 183 | void network_services_peer_update(struct network *net, struct network_peer *peer) 184 | { 185 | struct network_service *s; 186 | 187 | vlist_for_each_element(&net->services, s, node) { 188 | if (!s->ops || !s->ops->peer_update) 189 | continue; 190 | 191 | s->ops->peer_update(net, s, peer); 192 | } 193 | } 194 | 195 | void network_services_init(struct network *net) 196 | { 197 | vlist_init(&net->services, avl_strcmp, service_update); 198 | } 199 | -------------------------------------------------------------------------------- /token.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2025 Felix Fietkau 4 | */ 5 | #include 6 | #include "unetd.h" 7 | #include "sha512.h" 8 | 9 | static uint8_t salt[8]; 10 | static uint64_t nonce; 11 | 12 | struct token_hdr { 13 | uint8_t src[PEX_ID_LEN]; 14 | uint8_t salt[8]; 15 | uint64_t nonce; 16 | uint8_t hmac[SHA512_HASH_SIZE / 2]; 17 | }; 18 | 19 | static bool token_init(void) 20 | { 21 | static bool init_done; 22 | FILE *f; 23 | 24 | if (init_done) 25 | return true; 26 | 27 | f = fopen("/dev/urandom", "r"); 28 | if (!f) 29 | return false; 30 | 31 | init_done = fread(salt, sizeof(salt), 1, f) == 1; 32 | fclose(f); 33 | 34 | return init_done; 35 | } 36 | 37 | 38 | static bool 39 | token_verify_service(struct network *net, const char *name, 40 | struct network_host *local_host, 41 | struct network_host *target) 42 | { 43 | struct network_service *s; 44 | bool dest_found = false; 45 | bool src_found = false; 46 | 47 | s = vlist_find(&net->services, name, s, node); 48 | if (!s) 49 | return false; 50 | 51 | for (size_t i = 0; i < s->n_members; i++) { 52 | if (s->members[i] == local_host) 53 | src_found = true; 54 | if (s->members[i] == target) 55 | dest_found = true; 56 | } 57 | 58 | if (!src_found || !dest_found) 59 | return false; 60 | 61 | return true; 62 | } 63 | 64 | 65 | void *token_create(struct network *net, struct network_host *target, 66 | const char *service, struct blob_attr *info, size_t *len) 67 | { 68 | struct network_host *local_host = net->net_config.local_host; 69 | size_t data_len = blob_pad_len(info); 70 | uint8_t dh_key[CURVE25519_KEY_SIZE]; 71 | uint8_t hmac[SHA512_HASH_SIZE]; 72 | struct sha512_state s; 73 | struct token_hdr *hdr; 74 | const void *key; 75 | void *data; 76 | 77 | if (!local_host || !token_init() || target == local_host) 78 | return NULL; 79 | 80 | if (service && !token_verify_service(net, service, local_host, target)) 81 | return NULL; 82 | 83 | hdr = data = malloc(sizeof(*hdr) + data_len); 84 | data += sizeof(*hdr); 85 | 86 | memcpy(hdr->src, local_host->peer.key, sizeof(hdr->src)); 87 | memcpy(hdr->salt, salt, sizeof(hdr->salt)); 88 | hdr->nonce = nonce++; 89 | 90 | curve25519(dh_key, net->config.key, target->peer.key); 91 | sha512_init(&s); 92 | sha512_add(&s, dh_key, sizeof(dh_key)); 93 | sha512_add(&s, salt, sizeof(salt)); 94 | key = sha512_final_get(&s); 95 | 96 | memcpy(data, info, data_len); 97 | chacha20_encrypt_msg(data, data_len, &hdr->nonce, key); 98 | 99 | hmac_sha512(hmac, key, SHA512_HASH_SIZE, data, data_len); 100 | memcpy(hdr->hmac, hmac, sizeof(hdr->hmac)); 101 | 102 | *len = data_len + sizeof(*hdr); 103 | 104 | return hdr; 105 | } 106 | 107 | static bool 108 | token_decrypt(struct network *net, struct token_hdr *hdr, size_t len, 109 | struct network_host **host) 110 | { 111 | struct network_host *local_host = net->net_config.local_host; 112 | uint8_t dh_key[CURVE25519_KEY_SIZE]; 113 | uint8_t pubkey[WG_KEY_LEN] = {}; 114 | uint8_t hmac[SHA512_HASH_SIZE]; 115 | struct network_peer *peer; 116 | struct sha512_state s; 117 | const void *key; 118 | void *data; 119 | 120 | data = hdr + 1; 121 | memcpy(pubkey, hdr->src, sizeof(hdr->src)); 122 | peer = avl_find_ge_element(&net->peers.avl, pubkey, peer, node.avl); 123 | if (!peer || peer == &local_host->peer) 124 | return false; 125 | 126 | if (memcmp(peer->key, pubkey, sizeof(hdr->src)) != 0) 127 | return false; 128 | 129 | memcpy(pubkey, peer->key, sizeof(pubkey)); 130 | curve25519(dh_key, net->config.key, pubkey); 131 | sha512_init(&s); 132 | sha512_add(&s, dh_key, sizeof(dh_key)); 133 | sha512_add(&s, hdr->salt, sizeof(hdr->salt)); 134 | key = sha512_final_get(&s); 135 | 136 | hmac_sha512(hmac, key, SHA512_HASH_SIZE, data, len); 137 | if (memcmp(hdr->hmac, hmac, sizeof(hdr->hmac)) != 0) 138 | return false; 139 | 140 | chacha20_encrypt_msg(data, len, &hdr->nonce, key); 141 | *host = container_of(peer, struct network_host, peer); 142 | 143 | return true; 144 | } 145 | 146 | bool token_parse(struct blob_buf *buf, const char *token) 147 | { 148 | enum { 149 | TOKEN_ATTR_SERVICE, 150 | __TOKEN_ATTR_MAX, 151 | }; 152 | struct blobmsg_policy policy[__TOKEN_ATTR_MAX] = { 153 | [TOKEN_ATTR_SERVICE] = { "service", BLOBMSG_TYPE_STRING }, 154 | }; 155 | struct blob_attr *tb[__TOKEN_ATTR_MAX], *cur; 156 | struct network_host *host; 157 | struct token_hdr *hdr; 158 | struct network *net; 159 | bool ret = false; 160 | size_t len; 161 | void *data; 162 | 163 | len = B64_DECODE_LEN(strlen(token)); 164 | hdr = malloc(len); 165 | len = b64_decode(token, hdr, len); 166 | if (len <= sizeof(*hdr) + sizeof(struct blob_attr)) 167 | goto out; 168 | 169 | data = hdr + 1; 170 | len -= sizeof(*hdr); 171 | avl_for_each_element(&networks, net, node) { 172 | struct network_host *local_host = net->net_config.local_host; 173 | 174 | if (!local_host) 175 | continue; 176 | 177 | ret = token_decrypt(net, hdr, len, &host); 178 | if (!ret) 179 | continue; 180 | 181 | blobmsg_add_string(buf, "network", network_name(net)); 182 | blobmsg_add_string(buf, "host", network_host_name(host)); 183 | 184 | if (blob_pad_len(data) != len) { 185 | ret = false; 186 | break; 187 | } 188 | 189 | blobmsg_parse_attr(policy, __TOKEN_ATTR_MAX, tb, data); 190 | 191 | cur = tb[TOKEN_ATTR_SERVICE]; 192 | if (cur && !token_verify_service(net, blobmsg_get_string(cur), 193 | local_host, host)) 194 | ret = false; 195 | break; 196 | } 197 | if (!ret) 198 | goto out; 199 | 200 | 201 | blob_put_raw(buf, blobmsg_data(data), blobmsg_len(data)); 202 | 203 | out: 204 | free(hdr); 205 | return ret; 206 | } 207 | -------------------------------------------------------------------------------- /scripts/update-cmd.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | use FindBin qw($Bin); 3 | require "$Bin/json_pp.pm"; 4 | use Data::Dumper; 5 | use strict; 6 | 7 | sub create_state() { 8 | return { 9 | route => {}, 10 | ipaddr => {}, 11 | }; 12 | } 13 | 14 | sub cmd($) { 15 | my $cmd = shift; 16 | print STDERR "command: $cmd\n"; 17 | system($cmd); 18 | } 19 | 20 | sub fetch_active_data_linux($$) { 21 | my $ifname = shift; 22 | my $data = shift; 23 | 24 | open DATA, "(ip -4 r s dev $ifname; ip -6 r s dev $ifname) |"; 25 | while () { 26 | chomp; 27 | s/^\s+//; 28 | my @data = split /\s+/, $_; 29 | next if $data[0] =~ /^fe80:/; 30 | next if $data[0] =~ /^ff..:/; 31 | $data[0] =~ /(:|\/)/ or do { 32 | $data[0] .= '/32'; 33 | }; 34 | $data->{route}->{$data[0]} = 'delete'; 35 | } 36 | close DATA; 37 | 38 | open DATA, "ip a s dev $ifname |"; 39 | while () { 40 | chomp; 41 | s/^\s+//; 42 | my @data = split /\s+/, $_; 43 | next unless $data[0] =~ /inet/; 44 | next if $data[1] =~ /^fe80:/; 45 | $data->{ipaddr}->{$data[1]} = 'delete'; 46 | } 47 | close DATA; 48 | } 49 | 50 | sub fetch_active_data_darwin($$) { 51 | my $ifname = shift; 52 | my $data = shift; 53 | 54 | open DATA, "netstat -rn |"; 55 | while () { 56 | chomp; 57 | s/^\s+//; 58 | my @data = split /\s+/, $_; 59 | next unless $data[3] eq $ifname; 60 | next if $data[0] =~ /^fe80:/; 61 | next if $data[0] =~ /^ff..:/; 62 | $data[0] =~ /(:|\/)/ or do { 63 | my $mask = 32; 64 | my @addr = split /\./, $data[0]; 65 | while (@addr < 4) { 66 | push @addr, '0'; 67 | $mask -= 8; 68 | } 69 | $data[0] = join(".", @addr)."/$mask"; 70 | }; 71 | $data->{route}->{$data[0]} = 'delete'; 72 | } 73 | close DATA; 74 | 75 | open DATA, "ifconfig $ifname |"; 76 | while () { 77 | chomp; 78 | s/^\s+//; 79 | my @data = split /\s+/, $_; 80 | next unless $data[0] =~ /inet/; 81 | next if $data[1] =~ /^fe80:/; 82 | $data->{ipaddr}->{$data[1]} = 'delete'; 83 | } 84 | close DATA; 85 | } 86 | 87 | sub update_data($$$) { 88 | my $data = shift; 89 | my $delete = shift; 90 | my $add = shift; 91 | 92 | return unless $data->{"link-up"} eq 1; 93 | foreach my $val (@{$data->{ipaddr}}, @{$data->{ip6addr}}) { 94 | my $ip = $val->{ipaddr}; 95 | my $mask = $val->{mask}; 96 | 97 | if ($ip =~ /:/) { 98 | my $route = $ip; 99 | if (not($ip =~ /::/) and $mask eq 64) { 100 | $route =~ s/((\w+):(\w+):(\w+):(\w+)):.*/$1::/; 101 | } else { 102 | $route = "$ip/128"; 103 | } 104 | push @{$data->{routes6}}, { target => "$route", "netmask" => $mask }; 105 | } else { 106 | push @{$data->{routes}}, { target => "$ip", "netmask" => 32 }; 107 | }; 108 | if ($delete->{ipaddr}->{$ip}) { 109 | delete $delete->{ipaddr}->{$ip}; 110 | } elsif ($delete->{ipaddr}->{"$ip/$mask"}) { 111 | delete $delete->{ipaddr}->{"$ip/$mask"}; 112 | } else { 113 | $add->{ipaddr}->{"$ip/$mask"} = 'add'; 114 | } 115 | } 116 | foreach my $val (@{$data->{routes}}, @{$data->{routes6}}) { 117 | my $ip = $val->{target}.'/'.$val->{netmask}; 118 | 119 | if ($delete->{route}->{$ip}) { 120 | delete $delete->{route}->{$ip}; 121 | } else { 122 | $add->{route}->{$ip} = 'add'; 123 | } 124 | } 125 | } 126 | 127 | sub set_active_data_linux($$$) { 128 | my $ifname = shift; 129 | my $delete = shift; 130 | my $add = shift; 131 | 132 | (keys %{$add->{ipaddr}}, keys %{$add->{route}}) > 0 and cmd("ip l s dev $ifname up"); 133 | 134 | foreach my $ip (keys %{$delete->{ipaddr}}) { 135 | cmd("ip a d $ip dev $ifname"); 136 | } 137 | foreach my $route (keys %{$delete->{route}}) { 138 | cmd("ip r d $route dev $ifname"); 139 | } 140 | 141 | foreach my $ip (keys %{$add->{ipaddr}}) { 142 | cmd("ip a a $ip dev $ifname"); 143 | } 144 | foreach my $route (keys %{$add->{route}}) { 145 | cmd("ip r a $route dev $ifname"); 146 | } 147 | } 148 | 149 | sub set_active_data_darwin($$$) { 150 | my $ifname = shift; 151 | my $delete = shift; 152 | my $add = shift; 153 | 154 | foreach my $ip (keys %{$delete->{ipaddr}}) { 155 | $ip =~ s/\/.*//; 156 | if ($ip =~ /:/) { 157 | cmd("ifconfig $ifname inet6 delete $ip"); 158 | } else { 159 | cmd("ifconfig $ifname delete $ip"); 160 | } 161 | } 162 | foreach my $route (keys %{$delete->{route}}) { 163 | if ($route =~ /:/) { 164 | cmd("route delete -inet6 $route -iface $ifname"); 165 | } else { 166 | cmd("route delete -inet $route -iface $ifname"); 167 | } 168 | } 169 | foreach my $ip (keys %{$add->{ipaddr}}) { 170 | my @ip = split /\//, $ip; 171 | 172 | if ($ip[0] =~ /:/) { 173 | cmd("ifconfig $ifname inet6 add $ip[0] prefixlen $ip[1]"); 174 | } else { 175 | cmd("ifconfig $ifname add $ip[0]/$ip[1] $ip[0]"); 176 | } 177 | } 178 | foreach my $route (keys %{$add->{route}}) { 179 | if ($route =~ /:/) { 180 | cmd("route add -inet6 $route -iface $ifname"); 181 | } else { 182 | cmd("route add -inet $route -iface $ifname"); 183 | } 184 | } 185 | } 186 | 187 | my $json = $ARGV[0]; 188 | my $platform = `uname`; 189 | my $data = JSON::PP::decode_json($json) or die "Failed to decode JSON data\n"; 190 | 191 | foreach my $round (1 .. 2) { 192 | my $delete = create_state(); 193 | my $add = create_state(); 194 | 195 | if ($platform =~ /Darwin/) { 196 | fetch_active_data_darwin($data->{ifname}, $delete); 197 | } elsif ($platform =~ /Linux/) { 198 | fetch_active_data_linux($data->{ifname}, $delete); 199 | } else { 200 | die "Unsupported platform $platform\n"; 201 | } 202 | 203 | update_data($data, $delete, $add); 204 | 205 | if ($platform =~ /Darwin/) { 206 | set_active_data_darwin($data->{ifname}, $delete, $add); 207 | } elsif ($platform =~ /Linux/) { 208 | set_active_data_linux($data->{ifname}, $delete, $add); 209 | } 210 | } 211 | 212 | # print Data::Dumper->Dump([$add, $delete], ["add", "delete"])."\n"; 213 | -------------------------------------------------------------------------------- /f25519.c: -------------------------------------------------------------------------------- 1 | /* Arithmetic mod p = 2^255-19 2 | * Daniel Beer , 5 Jan 2014 3 | * 4 | * This file is in the public domain. 5 | */ 6 | 7 | #include "f25519.h" 8 | 9 | const uint8_t f25519_one[F25519_SIZE] = {1}; 10 | 11 | void f25519_load(uint8_t *x, uint32_t c) 12 | { 13 | int i; 14 | 15 | for (i = 0; i < sizeof(c); i++) { 16 | x[i] = c; 17 | c >>= 8; 18 | } 19 | 20 | for (; i < F25519_SIZE; i++) 21 | x[i] = 0; 22 | } 23 | 24 | void f25519_normalize(uint8_t *x) 25 | { 26 | uint8_t minusp[F25519_SIZE]; 27 | uint16_t c; 28 | int i; 29 | 30 | /* Reduce using 2^255 = 19 mod p */ 31 | c = (x[31] >> 7) * 19; 32 | x[31] &= 127; 33 | 34 | for (i = 0; i < F25519_SIZE; i++) { 35 | c += x[i]; 36 | x[i] = c; 37 | c >>= 8; 38 | } 39 | 40 | /* The number is now less than 2^255 + 18, and therefore less than 41 | * 2p. Try subtracting p, and conditionally load the subtracted 42 | * value if underflow did not occur. 43 | */ 44 | c = 19; 45 | 46 | for (i = 0; i + 1 < F25519_SIZE; i++) { 47 | c += x[i]; 48 | minusp[i] = c; 49 | c >>= 8; 50 | } 51 | 52 | c += ((uint16_t)x[i]) - 128; 53 | minusp[31] = c; 54 | 55 | /* Load x-p if no underflow */ 56 | f25519_select(x, minusp, x, (c >> 15) & 1); 57 | } 58 | 59 | uint8_t f25519_eq(const uint8_t *x, const uint8_t *y) 60 | { 61 | uint8_t sum = 0; 62 | int i; 63 | 64 | for (i = 0; i < F25519_SIZE; i++) 65 | sum |= x[i] ^ y[i]; 66 | 67 | sum |= (sum >> 4); 68 | sum |= (sum >> 2); 69 | sum |= (sum >> 1); 70 | 71 | return (sum ^ 1) & 1; 72 | } 73 | 74 | void f25519_select(uint8_t *dst, 75 | const uint8_t *zero, const uint8_t *one, 76 | uint8_t condition) 77 | { 78 | const uint8_t mask = -condition; 79 | int i; 80 | 81 | for (i = 0; i < F25519_SIZE; i++) 82 | dst[i] = zero[i] ^ (mask & (one[i] ^ zero[i])); 83 | } 84 | 85 | void f25519_add(uint8_t *r, const uint8_t *a, const uint8_t *b) 86 | { 87 | uint16_t c = 0; 88 | int i; 89 | 90 | /* Add */ 91 | for (i = 0; i < F25519_SIZE; i++) { 92 | c >>= 8; 93 | c += ((uint16_t)a[i]) + ((uint16_t)b[i]); 94 | r[i] = c; 95 | } 96 | 97 | /* Reduce with 2^255 = 19 mod p */ 98 | r[31] &= 127; 99 | c = (c >> 7) * 19; 100 | 101 | for (i = 0; i < F25519_SIZE; i++) { 102 | c += r[i]; 103 | r[i] = c; 104 | c >>= 8; 105 | } 106 | } 107 | 108 | void f25519_sub(uint8_t *r, const uint8_t *a, const uint8_t *b) 109 | { 110 | uint32_t c = 0; 111 | int i; 112 | 113 | /* Calculate a + 2p - b, to avoid underflow */ 114 | c = 218; 115 | for (i = 0; i + 1 < F25519_SIZE; i++) { 116 | c += 65280 + ((uint32_t)a[i]) - ((uint32_t)b[i]); 117 | r[i] = c; 118 | c >>= 8; 119 | } 120 | 121 | c += ((uint32_t)a[31]) - ((uint32_t)b[31]); 122 | r[31] = c & 127; 123 | c = (c >> 7) * 19; 124 | 125 | for (i = 0; i < F25519_SIZE; i++) { 126 | c += r[i]; 127 | r[i] = c; 128 | c >>= 8; 129 | } 130 | } 131 | 132 | void f25519_neg(uint8_t *r, const uint8_t *a) 133 | { 134 | uint32_t c = 0; 135 | int i; 136 | 137 | /* Calculate 2p - a, to avoid underflow */ 138 | c = 218; 139 | for (i = 0; i + 1 < F25519_SIZE; i++) { 140 | c += 65280 - ((uint32_t)a[i]); 141 | r[i] = c; 142 | c >>= 8; 143 | } 144 | 145 | c -= ((uint32_t)a[31]); 146 | r[31] = c & 127; 147 | c = (c >> 7) * 19; 148 | 149 | for (i = 0; i < F25519_SIZE; i++) { 150 | c += r[i]; 151 | r[i] = c; 152 | c >>= 8; 153 | } 154 | } 155 | 156 | void f25519_mul__distinct(uint8_t *r, const uint8_t *a, const uint8_t *b) 157 | { 158 | uint32_t c = 0; 159 | int i; 160 | 161 | for (i = 0; i < F25519_SIZE; i++) { 162 | int j; 163 | 164 | c >>= 8; 165 | for (j = 0; j <= i; j++) 166 | c += ((uint32_t)a[j]) * ((uint32_t)b[i - j]); 167 | 168 | for (; j < F25519_SIZE; j++) 169 | c += ((uint32_t)a[j]) * 170 | ((uint32_t)b[i + F25519_SIZE - j]) * 38; 171 | 172 | r[i] = c; 173 | } 174 | 175 | r[31] &= 127; 176 | c = (c >> 7) * 19; 177 | 178 | for (i = 0; i < F25519_SIZE; i++) { 179 | c += r[i]; 180 | r[i] = c; 181 | c >>= 8; 182 | } 183 | } 184 | 185 | static void f25519_mul_c(uint8_t *r, const uint8_t *a, uint32_t b) 186 | { 187 | uint32_t c = 0; 188 | int i; 189 | 190 | for (i = 0; i < F25519_SIZE; i++) { 191 | c >>= 8; 192 | c += b * ((uint32_t)a[i]); 193 | r[i] = c; 194 | } 195 | 196 | r[31] &= 127; 197 | c >>= 7; 198 | c *= 19; 199 | 200 | for (i = 0; i < F25519_SIZE; i++) { 201 | c += r[i]; 202 | r[i] = c; 203 | c >>= 8; 204 | } 205 | } 206 | 207 | void f25519_inv__distinct(uint8_t *r, const uint8_t *x) 208 | { 209 | uint8_t s[F25519_SIZE]; 210 | int i; 211 | 212 | /* This is a prime field, so by Fermat's little theorem: 213 | * 214 | * x^(p-1) = 1 mod p 215 | * 216 | * Therefore, raise to (p-2) = 2^255-21 to get a multiplicative 217 | * inverse. 218 | * 219 | * This is a 255-bit binary number with the digits: 220 | * 221 | * 11111111... 01011 222 | * 223 | * We compute the result by the usual binary chain, but 224 | * alternate between keeping the accumulator in r and s, so as 225 | * to avoid copying temporaries. 226 | */ 227 | 228 | /* 1 1 */ 229 | f25519_mul__distinct(s, x, x); 230 | f25519_mul__distinct(r, s, x); 231 | 232 | /* 1 x 248 */ 233 | for (i = 0; i < 248; i++) { 234 | f25519_mul__distinct(s, r, r); 235 | f25519_mul__distinct(r, s, x); 236 | } 237 | 238 | /* 0 */ 239 | f25519_mul__distinct(s, r, r); 240 | 241 | /* 1 */ 242 | f25519_mul__distinct(r, s, s); 243 | f25519_mul__distinct(s, r, x); 244 | 245 | /* 0 */ 246 | f25519_mul__distinct(r, s, s); 247 | 248 | /* 1 */ 249 | f25519_mul__distinct(s, r, r); 250 | f25519_mul__distinct(r, s, x); 251 | 252 | /* 1 */ 253 | f25519_mul__distinct(s, r, r); 254 | f25519_mul__distinct(r, s, x); 255 | } 256 | 257 | /* Raise x to the power of (p-5)/8 = 2^252-3, using s for temporary 258 | * storage. 259 | */ 260 | static void exp2523(uint8_t *r, const uint8_t *x, uint8_t *s) 261 | { 262 | int i; 263 | 264 | /* This number is a 252-bit number with the binary expansion: 265 | * 266 | * 111111... 01 267 | */ 268 | 269 | /* 1 1 */ 270 | f25519_mul__distinct(r, x, x); 271 | f25519_mul__distinct(s, r, x); 272 | 273 | /* 1 x 248 */ 274 | for (i = 0; i < 248; i++) { 275 | f25519_mul__distinct(r, s, s); 276 | f25519_mul__distinct(s, r, x); 277 | } 278 | 279 | /* 0 */ 280 | f25519_mul__distinct(r, s, s); 281 | 282 | /* 1 */ 283 | f25519_mul__distinct(s, r, r); 284 | f25519_mul__distinct(r, s, x); 285 | } 286 | 287 | void f25519_sqrt(uint8_t *r, const uint8_t *a) 288 | { 289 | uint8_t v[F25519_SIZE]; 290 | uint8_t i[F25519_SIZE]; 291 | uint8_t x[F25519_SIZE]; 292 | uint8_t y[F25519_SIZE]; 293 | 294 | /* v = (2a)^((p-5)/8) [x = 2a] */ 295 | f25519_mul_c(x, a, 2); 296 | exp2523(v, x, y); 297 | 298 | /* i = 2av^2 - 1 */ 299 | f25519_mul__distinct(y, v, v); 300 | f25519_mul__distinct(i, x, y); 301 | f25519_load(y, 1); 302 | f25519_sub(i, i, y); 303 | 304 | /* r = avi */ 305 | f25519_mul__distinct(x, v, a); 306 | f25519_mul__distinct(r, x, i); 307 | } 308 | -------------------------------------------------------------------------------- /chacha20.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | chacha-merged.c version 20080118 4 | D. J. Bernstein 5 | Public domain. 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include "utils.h" 12 | #include "chacha20.h" 13 | 14 | struct chacha_ctx { 15 | uint32_t input[16]; 16 | }; 17 | 18 | #define LOAD32_LE(SRC) get_unaligned_le32(SRC) 19 | 20 | #define STORE32_LE(DST, W) store32_le((DST), (W)) 21 | 22 | static inline void 23 | store32_le(uint8_t dst[4], uint32_t w) 24 | { 25 | dst[0] = (uint8_t) w; w >>= 8; 26 | dst[1] = (uint8_t) w; w >>= 8; 27 | dst[2] = (uint8_t) w; w >>= 8; 28 | dst[3] = (uint8_t) w; 29 | } 30 | 31 | 32 | #define ROTL32(X, B) rotl32((X), (B)) 33 | static inline uint32_t 34 | rotl32(const uint32_t x, const int b) 35 | { 36 | return (x << b) | (x >> (32 - b)); 37 | } 38 | 39 | typedef struct chacha_ctx chacha_ctx; 40 | 41 | #define U32C(v) (v##U) 42 | 43 | #define U32V(v) ((uint32_t)(v) &U32C(0xFFFFFFFF)) 44 | 45 | #define ROTATE(v, c) (ROTL32(v, c)) 46 | #define XOR(v, w) ((v) ^ (w)) 47 | #define PLUS(v, w) (U32V((v) + (w))) 48 | #define PLUSONE(v) (PLUS((v), 1)) 49 | 50 | #define QUARTERROUND(a, b, c, d) \ 51 | a = PLUS(a, b); \ 52 | d = ROTATE(XOR(d, a), 16); \ 53 | c = PLUS(c, d); \ 54 | b = ROTATE(XOR(b, c), 12); \ 55 | a = PLUS(a, b); \ 56 | d = ROTATE(XOR(d, a), 8); \ 57 | c = PLUS(c, d); \ 58 | b = ROTATE(XOR(b, c), 7); 59 | 60 | static void 61 | chacha_keysetup(chacha_ctx *ctx, const uint8_t *k) 62 | { 63 | ctx->input[0] = U32C(0x61707865); 64 | ctx->input[1] = U32C(0x3320646e); 65 | ctx->input[2] = U32C(0x79622d32); 66 | ctx->input[3] = U32C(0x6b206574); 67 | ctx->input[4] = LOAD32_LE(k + 0); 68 | ctx->input[5] = LOAD32_LE(k + 4); 69 | ctx->input[6] = LOAD32_LE(k + 8); 70 | ctx->input[7] = LOAD32_LE(k + 12); 71 | ctx->input[8] = LOAD32_LE(k + 16); 72 | ctx->input[9] = LOAD32_LE(k + 20); 73 | ctx->input[10] = LOAD32_LE(k + 24); 74 | ctx->input[11] = LOAD32_LE(k + 28); 75 | } 76 | 77 | static void 78 | chacha_ivsetup(chacha_ctx *ctx, const uint8_t *iv, const uint8_t *counter) 79 | { 80 | ctx->input[12] = counter == NULL ? 0 : LOAD32_LE(counter + 0); 81 | ctx->input[13] = counter == NULL ? 0 : LOAD32_LE(counter + 4); 82 | ctx->input[14] = LOAD32_LE(iv + 0); 83 | ctx->input[15] = LOAD32_LE(iv + 4); 84 | } 85 | 86 | static void 87 | chacha20_encrypt_bytes(chacha_ctx *ctx, const uint8_t *m, uint8_t *c, 88 | unsigned long long bytes) 89 | { 90 | uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, 91 | x15; 92 | uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, 93 | j15; 94 | uint8_t *ctarget = NULL; 95 | uint8_t tmp[64]; 96 | unsigned int i; 97 | 98 | if (!bytes) { 99 | return; /* LCOV_EXCL_LINE */ 100 | } 101 | j0 = ctx->input[0]; 102 | j1 = ctx->input[1]; 103 | j2 = ctx->input[2]; 104 | j3 = ctx->input[3]; 105 | j4 = ctx->input[4]; 106 | j5 = ctx->input[5]; 107 | j6 = ctx->input[6]; 108 | j7 = ctx->input[7]; 109 | j8 = ctx->input[8]; 110 | j9 = ctx->input[9]; 111 | j10 = ctx->input[10]; 112 | j11 = ctx->input[11]; 113 | j12 = ctx->input[12]; 114 | j13 = ctx->input[13]; 115 | j14 = ctx->input[14]; 116 | j15 = ctx->input[15]; 117 | 118 | for (;;) { 119 | if (bytes < 64) { 120 | memset(tmp, 0, 64); 121 | for (i = 0; i < bytes; ++i) { 122 | tmp[i] = m[i]; 123 | } 124 | m = tmp; 125 | ctarget = c; 126 | c = tmp; 127 | } 128 | x0 = j0; 129 | x1 = j1; 130 | x2 = j2; 131 | x3 = j3; 132 | x4 = j4; 133 | x5 = j5; 134 | x6 = j6; 135 | x7 = j7; 136 | x8 = j8; 137 | x9 = j9; 138 | x10 = j10; 139 | x11 = j11; 140 | x12 = j12; 141 | x13 = j13; 142 | x14 = j14; 143 | x15 = j15; 144 | for (i = 20; i > 0; i -= 2) { 145 | QUARTERROUND(x0, x4, x8, x12) 146 | QUARTERROUND(x1, x5, x9, x13) 147 | QUARTERROUND(x2, x6, x10, x14) 148 | QUARTERROUND(x3, x7, x11, x15) 149 | QUARTERROUND(x0, x5, x10, x15) 150 | QUARTERROUND(x1, x6, x11, x12) 151 | QUARTERROUND(x2, x7, x8, x13) 152 | QUARTERROUND(x3, x4, x9, x14) 153 | } 154 | x0 = PLUS(x0, j0); 155 | x1 = PLUS(x1, j1); 156 | x2 = PLUS(x2, j2); 157 | x3 = PLUS(x3, j3); 158 | x4 = PLUS(x4, j4); 159 | x5 = PLUS(x5, j5); 160 | x6 = PLUS(x6, j6); 161 | x7 = PLUS(x7, j7); 162 | x8 = PLUS(x8, j8); 163 | x9 = PLUS(x9, j9); 164 | x10 = PLUS(x10, j10); 165 | x11 = PLUS(x11, j11); 166 | x12 = PLUS(x12, j12); 167 | x13 = PLUS(x13, j13); 168 | x14 = PLUS(x14, j14); 169 | x15 = PLUS(x15, j15); 170 | 171 | x0 = XOR(x0, LOAD32_LE(m + 0)); 172 | x1 = XOR(x1, LOAD32_LE(m + 4)); 173 | x2 = XOR(x2, LOAD32_LE(m + 8)); 174 | x3 = XOR(x3, LOAD32_LE(m + 12)); 175 | x4 = XOR(x4, LOAD32_LE(m + 16)); 176 | x5 = XOR(x5, LOAD32_LE(m + 20)); 177 | x6 = XOR(x6, LOAD32_LE(m + 24)); 178 | x7 = XOR(x7, LOAD32_LE(m + 28)); 179 | x8 = XOR(x8, LOAD32_LE(m + 32)); 180 | x9 = XOR(x9, LOAD32_LE(m + 36)); 181 | x10 = XOR(x10, LOAD32_LE(m + 40)); 182 | x11 = XOR(x11, LOAD32_LE(m + 44)); 183 | x12 = XOR(x12, LOAD32_LE(m + 48)); 184 | x13 = XOR(x13, LOAD32_LE(m + 52)); 185 | x14 = XOR(x14, LOAD32_LE(m + 56)); 186 | x15 = XOR(x15, LOAD32_LE(m + 60)); 187 | 188 | j12 = PLUSONE(j12); 189 | /* LCOV_EXCL_START */ 190 | if (!j12) { 191 | j13 = PLUSONE(j13); 192 | } 193 | /* LCOV_EXCL_STOP */ 194 | 195 | STORE32_LE(c + 0, x0); 196 | STORE32_LE(c + 4, x1); 197 | STORE32_LE(c + 8, x2); 198 | STORE32_LE(c + 12, x3); 199 | STORE32_LE(c + 16, x4); 200 | STORE32_LE(c + 20, x5); 201 | STORE32_LE(c + 24, x6); 202 | STORE32_LE(c + 28, x7); 203 | STORE32_LE(c + 32, x8); 204 | STORE32_LE(c + 36, x9); 205 | STORE32_LE(c + 40, x10); 206 | STORE32_LE(c + 44, x11); 207 | STORE32_LE(c + 48, x12); 208 | STORE32_LE(c + 52, x13); 209 | STORE32_LE(c + 56, x14); 210 | STORE32_LE(c + 60, x15); 211 | 212 | if (bytes <= 64) { 213 | if (bytes < 64) { 214 | for (i = 0; i < (unsigned int) bytes; ++i) { 215 | ctarget[i] = c[i]; /* ctarget cannot be NULL */ 216 | } 217 | } 218 | ctx->input[12] = j12; 219 | ctx->input[13] = j13; 220 | 221 | return; 222 | } 223 | bytes -= 64; 224 | c += 64; 225 | m += 64; 226 | } 227 | } 228 | 229 | void chacha20_encrypt_msg(void *msg, size_t len, const void *nonce, const void *key) 230 | { 231 | struct chacha_ctx ctx; 232 | 233 | chacha_keysetup(&ctx, key); 234 | chacha_ivsetup(&ctx, nonce, NULL); 235 | chacha20_encrypt_bytes(&ctx, msg, msg, len); 236 | } 237 | -------------------------------------------------------------------------------- /utils.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2022 Felix Fietkau 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | #include "unetd.h" 19 | 20 | int network_get_endpoint(union network_endpoint *dest, int af, const char *str, 21 | int default_port, int idx) 22 | { 23 | struct addrinfo hints = { 24 | .ai_flags = AI_ADDRCONFIG, 25 | .ai_family = af, 26 | }; 27 | char *buf = strdup(str); 28 | char *host = buf, *port; 29 | struct addrinfo *ai, *ai_cur; 30 | int n_res; 31 | int ret = -1; 32 | 33 | memset(dest, 0, sizeof(*dest)); 34 | 35 | if (*host == '[') { 36 | if (af == AF_INET) 37 | goto out; 38 | 39 | host++; 40 | port = strchr(host, ']'); 41 | if (!port) 42 | goto out; 43 | 44 | *(port++) = 0; 45 | if (!*port) 46 | port = NULL; 47 | else if (*port == ':') 48 | port++; 49 | else 50 | goto out; 51 | hints.ai_family = AF_INET6; 52 | hints.ai_flags |= AI_NUMERICHOST; 53 | } else { 54 | host = buf; 55 | 56 | port = strchr(host, ':'); 57 | if (port) 58 | *(port++) = 0; 59 | } 60 | 61 | if (getaddrinfo(host, port, &hints, &ai) || !ai) 62 | goto out; 63 | 64 | while (1) { 65 | ai_cur = ai; 66 | for (n_res = 0; ai_cur; ai_cur = ai_cur->ai_next, n_res++) 67 | if (!idx--) 68 | goto found; 69 | 70 | idx %= n_res; 71 | } 72 | 73 | found: 74 | if (ai_cur->ai_addrlen > sizeof(*dest)) 75 | goto free_ai; 76 | 77 | memcpy(dest, ai_cur->ai_addr, ai_cur->ai_addrlen); 78 | if (!port) 79 | dest->in.sin_port = htons(default_port); 80 | ret = 0; 81 | 82 | free_ai: 83 | freeaddrinfo(ai); 84 | 85 | out: 86 | free(buf); 87 | return ret; 88 | } 89 | 90 | int network_get_subnet(int af, union network_addr *addr, int *mask, const char *str) 91 | { 92 | char *buf = strdup(str); 93 | char *sep, *end; 94 | int ret = -1; 95 | 96 | if (af == AF_INET6) 97 | *mask = 128; 98 | else 99 | *mask = 32; 100 | 101 | sep = strchr(buf, '/'); 102 | if (sep) { 103 | unsigned long val; 104 | 105 | *(sep++) = 0; 106 | 107 | val = strtoul(sep, &end, 0); 108 | if ((end && *end) || val > *mask) 109 | goto out; 110 | 111 | *mask = val; 112 | } 113 | 114 | if (inet_pton(af, buf, addr) == 1) 115 | ret = 0; 116 | 117 | out: 118 | free(buf); 119 | return ret; 120 | } 121 | 122 | int network_get_local_addr(void *local, const union network_endpoint *target) 123 | { 124 | union network_endpoint ep = {}; 125 | socklen_t len; 126 | int ret = -1; 127 | int fd; 128 | 129 | memset(local, 0, sizeof(union network_addr)); 130 | if (target->sa.sa_family == AF_INET6) 131 | len = sizeof(ep.in6); 132 | else 133 | len = sizeof(ep.in); 134 | 135 | fd = socket(target->sa.sa_family, SOCK_DGRAM, IPPROTO_UDP); 136 | if (fd < 0) 137 | return -1; 138 | 139 | if (connect(fd, (const struct sockaddr *)target, len)) 140 | goto out; 141 | 142 | len = sizeof(ep); 143 | if (getsockname(fd, &ep.sa, &len)) 144 | goto out; 145 | 146 | if (ep.sa.sa_family == AF_INET6) 147 | memcpy(local, &ep.in6.sin6_addr, sizeof(ep.in6.sin6_addr)); 148 | else 149 | memcpy(local, &ep.in.sin_addr, sizeof(ep.in.sin_addr)); 150 | ret = 0; 151 | 152 | out: 153 | close(fd); 154 | return ret; 155 | } 156 | 157 | void *unet_read_file(const char *name, size_t *len) 158 | { 159 | struct stat st; 160 | void *data; 161 | FILE *f; 162 | 163 | f = fopen(name, "r"); 164 | if (!f) 165 | goto error; 166 | 167 | if (fstat(fileno(f), &st) < 0) 168 | goto close; 169 | 170 | if (*len && st.st_size > *len) 171 | goto close; 172 | 173 | data = malloc(st.st_size); 174 | if (!data) 175 | goto close; 176 | 177 | if (fread(data, 1, st.st_size, f) != st.st_size) { 178 | free(data); 179 | goto close; 180 | } 181 | fclose(f); 182 | 183 | *len = st.st_size; 184 | return data; 185 | 186 | close: 187 | fclose(f); 188 | error: 189 | *len = 0; 190 | return NULL; 191 | } 192 | 193 | uint64_t unet_gettime(void) 194 | { 195 | struct timespec ts; 196 | 197 | clock_gettime(CLOCK_MONOTONIC, &ts); 198 | 199 | return ts.tv_sec; 200 | } 201 | 202 | static inline uint32_t 203 | csum_tcpudp_nofold(uint32_t saddr, uint32_t daddr, uint8_t proto, uint32_t len) 204 | { 205 | uint64_t sum = 0; 206 | 207 | sum += saddr; 208 | sum += daddr; 209 | #if __BYTE_ORDER == __LITTLE_ENDIAN 210 | sum += (proto + len) << 8; 211 | #else 212 | sum += proto + len; 213 | #endif 214 | 215 | sum = (sum & 0xffffffff) + (sum >> 32); 216 | sum = (sum & 0xffffffff) + (sum >> 32); 217 | 218 | return (uint32_t)sum; 219 | } 220 | 221 | static inline uint32_t csum_add(uint32_t sum, uint32_t addend) 222 | { 223 | sum += addend; 224 | return sum + (sum < addend); 225 | } 226 | 227 | static inline uint16_t csum_fold(uint32_t sum) 228 | { 229 | sum = (sum & 0xffff) + (sum >> 16); 230 | sum = (sum & 0xffff) + (sum >> 16); 231 | 232 | return (uint16_t)~sum; 233 | } 234 | 235 | static uint32_t csum_partial(const void *buf, int len) 236 | { 237 | const uint16_t *data = buf; 238 | uint32_t sum = 0; 239 | 240 | while (len > 1) { 241 | sum += *data++; 242 | len -= 2; 243 | } 244 | 245 | if (len == 1) 246 | #if __BYTE_ORDER == __LITTLE_ENDIAN 247 | sum += *(uint8_t *)data; 248 | #else 249 | sum += *(uint8_t *)data << 8; 250 | #endif 251 | 252 | sum = (sum & 0xffff) + (sum >> 16); 253 | sum = (sum & 0xffff) + (sum >> 16); 254 | 255 | return sum; 256 | } 257 | 258 | static void fixup_udpv4(void *hdr, size_t hdrlen, const void *data, size_t len) 259 | { 260 | struct ip *ip = hdr; 261 | struct udphdr *udp = hdr + ip->ip_hl * 4; 262 | uint16_t udp_len = sizeof(*udp) + len; 263 | uint32_t sum; 264 | 265 | if ((void *)&udp[1] > hdr + hdrlen) 266 | return; 267 | 268 | udp->uh_sum = 0; 269 | udp->uh_ulen = htons(udp_len); 270 | sum = csum_tcpudp_nofold(*(uint32_t *)&ip->ip_src, *(uint32_t *)&ip->ip_dst, 271 | ip->ip_p, udp_len); 272 | sum = csum_add(sum, csum_partial(udp, sizeof(*udp))); 273 | sum = csum_add(sum, csum_partial(data, len)); 274 | udp->uh_sum = csum_fold(sum); 275 | 276 | ip->ip_len = htons(hdrlen + len); 277 | ip->ip_sum = 0; 278 | ip->ip_sum = csum_fold(csum_partial(ip, sizeof(*ip))); 279 | 280 | #ifdef __APPLE__ 281 | ip->ip_len = hdrlen + len; 282 | #endif 283 | } 284 | 285 | static void fixup_udpv6(void *hdr, size_t hdrlen, const void *data, size_t len) 286 | { 287 | struct ip6_hdr *ip = hdr; 288 | struct udphdr *udp = hdr + sizeof(*ip); 289 | uint16_t udp_len = htons(sizeof(*udp) + len); 290 | 291 | if ((void *)&udp[1] > hdr + hdrlen) 292 | return; 293 | 294 | ip->ip6_plen = htons(sizeof(*udp) + len); 295 | udp->uh_sum = 0; 296 | udp->uh_ulen = udp_len; 297 | udp->uh_sum = csum_fold(csum_partial(hdr, sizeof(*ip) + sizeof(*udp))); 298 | 299 | #ifdef __APPLE__ 300 | ip->ip6_plen = sizeof(*udp) + len; 301 | #endif 302 | } 303 | 304 | static void fixup_ip_udp_header(void *hdr, size_t hdrlen, const void *data, size_t len) 305 | { 306 | if (hdrlen >= sizeof(struct ip6_hdr) + sizeof(struct udphdr)) 307 | fixup_udpv6(hdr, hdrlen, data, len); 308 | else if (hdrlen >= sizeof(struct ip) + sizeof(struct udphdr)) 309 | fixup_udpv4(hdr, hdrlen, data, len); 310 | } 311 | 312 | int sendto_rawudp(int fd, const void *addr, void *ip_hdr, size_t ip_hdrlen, 313 | const void *data, size_t len) 314 | { 315 | const struct sockaddr *sa = addr; 316 | struct iovec iov[2] = { 317 | { .iov_base = ip_hdr, .iov_len = ip_hdrlen }, 318 | { .iov_base = (void *)data, .iov_len = len } 319 | }; 320 | struct msghdr msg = { 321 | .msg_name = (void *)addr, 322 | .msg_iov = iov, 323 | .msg_iovlen = ARRAY_SIZE(iov), 324 | }; 325 | 326 | if (sa->sa_family == AF_INET6) 327 | msg.msg_namelen = sizeof(struct sockaddr_in6); 328 | else 329 | msg.msg_namelen = sizeof(struct sockaddr_in); 330 | 331 | fixup_ip_udp_header(ip_hdr, ip_hdrlen, data, len); 332 | 333 | return sendmsg(fd, &msg, 0); 334 | } 335 | -------------------------------------------------------------------------------- /linux/wireguard.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) OR MIT */ 2 | /* 3 | * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. 4 | * 5 | * Documentation 6 | * ============= 7 | * 8 | * The below enums and macros are for interfacing with WireGuard, using generic 9 | * netlink, with family WG_GENL_NAME and version WG_GENL_VERSION. It defines two 10 | * methods: get and set. Note that while they share many common attributes, 11 | * these two functions actually accept a slightly different set of inputs and 12 | * outputs. 13 | * 14 | * WG_CMD_GET_DEVICE 15 | * ----------------- 16 | * 17 | * May only be called via NLM_F_REQUEST | NLM_F_DUMP. The command should contain 18 | * one but not both of: 19 | * 20 | * WGDEVICE_A_IFINDEX: NLA_U32 21 | * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMSIZ - 1 22 | * 23 | * The kernel will then return several messages (NLM_F_MULTI) containing the 24 | * following tree of nested items: 25 | * 26 | * WGDEVICE_A_IFINDEX: NLA_U32 27 | * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMSIZ - 1 28 | * WGDEVICE_A_PRIVATE_KEY: NLA_EXACT_LEN, len WG_KEY_LEN 29 | * WGDEVICE_A_PUBLIC_KEY: NLA_EXACT_LEN, len WG_KEY_LEN 30 | * WGDEVICE_A_LISTEN_PORT: NLA_U16 31 | * WGDEVICE_A_FWMARK: NLA_U32 32 | * WGDEVICE_A_PEERS: NLA_NESTED 33 | * 0: NLA_NESTED 34 | * WGPEER_A_PUBLIC_KEY: NLA_EXACT_LEN, len WG_KEY_LEN 35 | * WGPEER_A_PRESHARED_KEY: NLA_EXACT_LEN, len WG_KEY_LEN 36 | * WGPEER_A_ENDPOINT: NLA_MIN_LEN(struct sockaddr), struct sockaddr_in or struct sockaddr_in6 37 | * WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL: NLA_U16 38 | * WGPEER_A_LAST_HANDSHAKE_TIME: NLA_EXACT_LEN, struct __kernel_timespec 39 | * WGPEER_A_RX_BYTES: NLA_U64 40 | * WGPEER_A_TX_BYTES: NLA_U64 41 | * WGPEER_A_ALLOWEDIPS: NLA_NESTED 42 | * 0: NLA_NESTED 43 | * WGALLOWEDIP_A_FAMILY: NLA_U16 44 | * WGALLOWEDIP_A_IPADDR: NLA_MIN_LEN(struct in_addr), struct in_addr or struct in6_addr 45 | * WGALLOWEDIP_A_CIDR_MASK: NLA_U8 46 | * 0: NLA_NESTED 47 | * ... 48 | * 0: NLA_NESTED 49 | * ... 50 | * ... 51 | * WGPEER_A_PROTOCOL_VERSION: NLA_U32 52 | * 0: NLA_NESTED 53 | * ... 54 | * ... 55 | * 56 | * It is possible that all of the allowed IPs of a single peer will not 57 | * fit within a single netlink message. In that case, the same peer will 58 | * be written in the following message, except it will only contain 59 | * WGPEER_A_PUBLIC_KEY and WGPEER_A_ALLOWEDIPS. This may occur several 60 | * times in a row for the same peer. It is then up to the receiver to 61 | * coalesce adjacent peers. Likewise, it is possible that all peers will 62 | * not fit within a single message. So, subsequent peers will be sent 63 | * in following messages, except those will only contain WGDEVICE_A_IFNAME 64 | * and WGDEVICE_A_PEERS. It is then up to the receiver to coalesce these 65 | * messages to form the complete list of peers. 66 | * 67 | * Since this is an NLA_F_DUMP command, the final message will always be 68 | * NLMSG_DONE, even if an error occurs. However, this NLMSG_DONE message 69 | * contains an integer error code. It is either zero or a negative error 70 | * code corresponding to the errno. 71 | * 72 | * WG_CMD_SET_DEVICE 73 | * ----------------- 74 | * 75 | * May only be called via NLM_F_REQUEST. The command should contain the 76 | * following tree of nested items, containing one but not both of 77 | * WGDEVICE_A_IFINDEX and WGDEVICE_A_IFNAME: 78 | * 79 | * WGDEVICE_A_IFINDEX: NLA_U32 80 | * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMSIZ - 1 81 | * WGDEVICE_A_FLAGS: NLA_U32, 0 or WGDEVICE_F_REPLACE_PEERS if all current 82 | * peers should be removed prior to adding the list below. 83 | * WGDEVICE_A_PRIVATE_KEY: len WG_KEY_LEN, all zeros to remove 84 | * WGDEVICE_A_LISTEN_PORT: NLA_U16, 0 to choose randomly 85 | * WGDEVICE_A_FWMARK: NLA_U32, 0 to disable 86 | * WGDEVICE_A_PEERS: NLA_NESTED 87 | * 0: NLA_NESTED 88 | * WGPEER_A_PUBLIC_KEY: len WG_KEY_LEN 89 | * WGPEER_A_FLAGS: NLA_U32, 0 and/or WGPEER_F_REMOVE_ME if the 90 | * specified peer should not exist at the end of the 91 | * operation, rather than added/updated and/or 92 | * WGPEER_F_REPLACE_ALLOWEDIPS if all current allowed 93 | * IPs of this peer should be removed prior to adding 94 | * the list below and/or WGPEER_F_UPDATE_ONLY if the 95 | * peer should only be set if it already exists. 96 | * WGPEER_A_PRESHARED_KEY: len WG_KEY_LEN, all zeros to remove 97 | * WGPEER_A_ENDPOINT: struct sockaddr_in or struct sockaddr_in6 98 | * WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL: NLA_U16, 0 to disable 99 | * WGPEER_A_ALLOWEDIPS: NLA_NESTED 100 | * 0: NLA_NESTED 101 | * WGALLOWEDIP_A_FAMILY: NLA_U16 102 | * WGALLOWEDIP_A_IPADDR: struct in_addr or struct in6_addr 103 | * WGALLOWEDIP_A_CIDR_MASK: NLA_U8 104 | * 0: NLA_NESTED 105 | * ... 106 | * 0: NLA_NESTED 107 | * ... 108 | * ... 109 | * WGPEER_A_PROTOCOL_VERSION: NLA_U32, should not be set or used at 110 | * all by most users of this API, as the 111 | * most recent protocol will be used when 112 | * this is unset. Otherwise, must be set 113 | * to 1. 114 | * 0: NLA_NESTED 115 | * ... 116 | * ... 117 | * 118 | * It is possible that the amount of configuration data exceeds that of 119 | * the maximum message length accepted by the kernel. In that case, several 120 | * messages should be sent one after another, with each successive one 121 | * filling in information not contained in the prior. Note that if 122 | * WGDEVICE_F_REPLACE_PEERS is specified in the first message, it probably 123 | * should not be specified in fragments that come after, so that the list 124 | * of peers is only cleared the first time but appended after. Likewise for 125 | * peers, if WGPEER_F_REPLACE_ALLOWEDIPS is specified in the first message 126 | * of a peer, it likely should not be specified in subsequent fragments. 127 | * 128 | * If an error occurs, NLMSG_ERROR will reply containing an errno. 129 | */ 130 | 131 | #ifndef _WG_UAPI_WIREGUARD_H 132 | #define _WG_UAPI_WIREGUARD_H 133 | 134 | #define WG_GENL_NAME "wireguard" 135 | #define WG_GENL_VERSION 1 136 | 137 | #define WG_KEY_LEN 32 138 | 139 | enum wg_cmd { 140 | WG_CMD_GET_DEVICE, 141 | WG_CMD_SET_DEVICE, 142 | __WG_CMD_MAX 143 | }; 144 | #define WG_CMD_MAX (__WG_CMD_MAX - 1) 145 | 146 | enum wgdevice_flag { 147 | WGDEVICE_F_REPLACE_PEERS = 1U << 0, 148 | __WGDEVICE_F_ALL = WGDEVICE_F_REPLACE_PEERS 149 | }; 150 | enum wgdevice_attribute { 151 | WGDEVICE_A_UNSPEC, 152 | WGDEVICE_A_IFINDEX, 153 | WGDEVICE_A_IFNAME, 154 | WGDEVICE_A_PRIVATE_KEY, 155 | WGDEVICE_A_PUBLIC_KEY, 156 | WGDEVICE_A_FLAGS, 157 | WGDEVICE_A_LISTEN_PORT, 158 | WGDEVICE_A_FWMARK, 159 | WGDEVICE_A_PEERS, 160 | __WGDEVICE_A_LAST 161 | }; 162 | #define WGDEVICE_A_MAX (__WGDEVICE_A_LAST - 1) 163 | 164 | enum wgpeer_flag { 165 | WGPEER_F_REMOVE_ME = 1U << 0, 166 | WGPEER_F_REPLACE_ALLOWEDIPS = 1U << 1, 167 | WGPEER_F_UPDATE_ONLY = 1U << 2, 168 | __WGPEER_F_ALL = WGPEER_F_REMOVE_ME | WGPEER_F_REPLACE_ALLOWEDIPS | 169 | WGPEER_F_UPDATE_ONLY 170 | }; 171 | enum wgpeer_attribute { 172 | WGPEER_A_UNSPEC, 173 | WGPEER_A_PUBLIC_KEY, 174 | WGPEER_A_PRESHARED_KEY, 175 | WGPEER_A_FLAGS, 176 | WGPEER_A_ENDPOINT, 177 | WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, 178 | WGPEER_A_LAST_HANDSHAKE_TIME, 179 | WGPEER_A_RX_BYTES, 180 | WGPEER_A_TX_BYTES, 181 | WGPEER_A_ALLOWEDIPS, 182 | WGPEER_A_PROTOCOL_VERSION, 183 | __WGPEER_A_LAST 184 | }; 185 | #define WGPEER_A_MAX (__WGPEER_A_LAST - 1) 186 | 187 | enum wgallowedip_attribute { 188 | WGALLOWEDIP_A_UNSPEC, 189 | WGALLOWEDIP_A_FAMILY, 190 | WGALLOWEDIP_A_IPADDR, 191 | WGALLOWEDIP_A_CIDR_MASK, 192 | __WGALLOWEDIP_A_LAST 193 | }; 194 | #define WGALLOWEDIP_A_MAX (__WGALLOWEDIP_A_LAST - 1) 195 | 196 | #endif /* _WG_UAPI_WIREGUARD_H */ 197 | -------------------------------------------------------------------------------- /sha512.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2024 Felix Fietkau 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | /* SHA512 18 | * Daniel Beer , 22 Apr 2014 19 | * 20 | * This file is in the public domain. 21 | */ 22 | 23 | #include "sha512.h" 24 | 25 | static const uint64_t sha512_initial_state[8] = { 26 | 0x6a09e667f3bcc908LL, 0xbb67ae8584caa73bLL, 27 | 0x3c6ef372fe94f82bLL, 0xa54ff53a5f1d36f1LL, 28 | 0x510e527fade682d1LL, 0x9b05688c2b3e6c1fLL, 29 | 0x1f83d9abfb41bd6bLL, 0x5be0cd19137e2179LL, 30 | }; 31 | 32 | static const uint64_t round_k[80] = { 33 | 0x428a2f98d728ae22LL, 0x7137449123ef65cdLL, 34 | 0xb5c0fbcfec4d3b2fLL, 0xe9b5dba58189dbbcLL, 35 | 0x3956c25bf348b538LL, 0x59f111f1b605d019LL, 36 | 0x923f82a4af194f9bLL, 0xab1c5ed5da6d8118LL, 37 | 0xd807aa98a3030242LL, 0x12835b0145706fbeLL, 38 | 0x243185be4ee4b28cLL, 0x550c7dc3d5ffb4e2LL, 39 | 0x72be5d74f27b896fLL, 0x80deb1fe3b1696b1LL, 40 | 0x9bdc06a725c71235LL, 0xc19bf174cf692694LL, 41 | 0xe49b69c19ef14ad2LL, 0xefbe4786384f25e3LL, 42 | 0x0fc19dc68b8cd5b5LL, 0x240ca1cc77ac9c65LL, 43 | 0x2de92c6f592b0275LL, 0x4a7484aa6ea6e483LL, 44 | 0x5cb0a9dcbd41fbd4LL, 0x76f988da831153b5LL, 45 | 0x983e5152ee66dfabLL, 0xa831c66d2db43210LL, 46 | 0xb00327c898fb213fLL, 0xbf597fc7beef0ee4LL, 47 | 0xc6e00bf33da88fc2LL, 0xd5a79147930aa725LL, 48 | 0x06ca6351e003826fLL, 0x142929670a0e6e70LL, 49 | 0x27b70a8546d22ffcLL, 0x2e1b21385c26c926LL, 50 | 0x4d2c6dfc5ac42aedLL, 0x53380d139d95b3dfLL, 51 | 0x650a73548baf63deLL, 0x766a0abb3c77b2a8LL, 52 | 0x81c2c92e47edaee6LL, 0x92722c851482353bLL, 53 | 0xa2bfe8a14cf10364LL, 0xa81a664bbc423001LL, 54 | 0xc24b8b70d0f89791LL, 0xc76c51a30654be30LL, 55 | 0xd192e819d6ef5218LL, 0xd69906245565a910LL, 56 | 0xf40e35855771202aLL, 0x106aa07032bbd1b8LL, 57 | 0x19a4c116b8d2d0c8LL, 0x1e376c085141ab53LL, 58 | 0x2748774cdf8eeb99LL, 0x34b0bcb5e19b48a8LL, 59 | 0x391c0cb3c5c95a63LL, 0x4ed8aa4ae3418acbLL, 60 | 0x5b9cca4f7763e373LL, 0x682e6ff3d6b2b8a3LL, 61 | 0x748f82ee5defb2fcLL, 0x78a5636f43172f60LL, 62 | 0x84c87814a1f0ab72LL, 0x8cc702081a6439ecLL, 63 | 0x90befffa23631e28LL, 0xa4506cebde82bde9LL, 64 | 0xbef9a3f7b2c67915LL, 0xc67178f2e372532bLL, 65 | 0xca273eceea26619cLL, 0xd186b8c721c0c207LL, 66 | 0xeada7dd6cde0eb1eLL, 0xf57d4f7fee6ed178LL, 67 | 0x06f067aa72176fbaLL, 0x0a637dc5a2c898a6LL, 68 | 0x113f9804bef90daeLL, 0x1b710b35131c471bLL, 69 | 0x28db77f523047d84LL, 0x32caab7b40c72493LL, 70 | 0x3c9ebe0a15c9bebcLL, 0x431d67c49c100d4cLL, 71 | 0x4cc5d4becb3e42b6LL, 0x597f299cfc657e2aLL, 72 | 0x5fcb6fab3ad6faecLL, 0x6c44198c4a475817LL, 73 | }; 74 | 75 | static inline uint64_t load64(const uint8_t *x) 76 | { 77 | uint64_t r; 78 | 79 | r = *(x++); 80 | r = (r << 8) | *(x++); 81 | r = (r << 8) | *(x++); 82 | r = (r << 8) | *(x++); 83 | r = (r << 8) | *(x++); 84 | r = (r << 8) | *(x++); 85 | r = (r << 8) | *(x++); 86 | r = (r << 8) | *(x++); 87 | 88 | return r; 89 | } 90 | 91 | static inline void store64(uint8_t *x, uint64_t v) 92 | { 93 | x += 7; 94 | *(x--) = v; 95 | v >>= 8; 96 | *(x--) = v; 97 | v >>= 8; 98 | *(x--) = v; 99 | v >>= 8; 100 | *(x--) = v; 101 | v >>= 8; 102 | *(x--) = v; 103 | v >>= 8; 104 | *(x--) = v; 105 | v >>= 8; 106 | *(x--) = v; 107 | v >>= 8; 108 | *(x--) = v; 109 | } 110 | 111 | static inline uint64_t rot64(uint64_t x, int bits) 112 | { 113 | return (x >> bits) | (x << (64 - bits)); 114 | } 115 | 116 | static void 117 | sha512_block(struct sha512_state *s, const uint8_t *blk) 118 | { 119 | uint64_t w[16]; 120 | uint64_t a, b, c, d, e, f, g, h; 121 | int i; 122 | 123 | for (i = 0; i < 16; i++) { 124 | w[i] = load64(blk); 125 | blk += 8; 126 | } 127 | 128 | /* Load state */ 129 | a = s->h[0]; 130 | b = s->h[1]; 131 | c = s->h[2]; 132 | d = s->h[3]; 133 | e = s->h[4]; 134 | f = s->h[5]; 135 | g = s->h[6]; 136 | h = s->h[7]; 137 | 138 | for (i = 0; i < 80; i++) { 139 | /* Compute value of w[i + 16]. w[wrap(i)] is currently w[i] */ 140 | const uint64_t wi = w[i & 15]; 141 | const uint64_t wi15 = w[(i + 1) & 15]; 142 | const uint64_t wi2 = w[(i + 14) & 15]; 143 | const uint64_t wi7 = w[(i + 9) & 15]; 144 | const uint64_t s0 = 145 | rot64(wi15, 1) ^ rot64(wi15, 8) ^ (wi15 >> 7); 146 | const uint64_t s1 = 147 | rot64(wi2, 19) ^ rot64(wi2, 61) ^ (wi2 >> 6); 148 | 149 | /* Round calculations */ 150 | const uint64_t S0 = rot64(a, 28) ^ rot64(a, 34) ^ rot64(a, 39); 151 | const uint64_t S1 = rot64(e, 14) ^ rot64(e, 18) ^ rot64(e, 41); 152 | const uint64_t ch = (e & f) ^ ((~e) & g); 153 | const uint64_t temp1 = h + S1 + ch + round_k[i] + wi; 154 | const uint64_t maj = (a & b) ^ (a & c) ^ (b & c); 155 | const uint64_t temp2 = S0 + maj; 156 | 157 | /* Update round state */ 158 | h = g; 159 | g = f; 160 | f = e; 161 | e = d + temp1; 162 | d = c; 163 | c = b; 164 | b = a; 165 | a = temp1 + temp2; 166 | 167 | /* w[wrap(i)] becomes w[i + 16] */ 168 | w[i & 15] = wi + s0 + wi7 + s1; 169 | } 170 | 171 | /* Store state */ 172 | s->h[0] += a; 173 | s->h[1] += b; 174 | s->h[2] += c; 175 | s->h[3] += d; 176 | s->h[4] += e; 177 | s->h[5] += f; 178 | s->h[6] += g; 179 | s->h[7] += h; 180 | } 181 | 182 | void sha512_init(struct sha512_state *s) 183 | { 184 | memcpy(s->h, &sha512_initial_state, sizeof(s->h)); 185 | s->len = 0; 186 | } 187 | 188 | void sha512_add(struct sha512_state *s, const void *data, size_t len) 189 | { 190 | unsigned int partial = s->len & (SHA512_BLOCK_SIZE - 1); 191 | 192 | if (partial) { 193 | unsigned int cur = SHA512_BLOCK_SIZE - partial; 194 | 195 | if (cur > len) 196 | cur = len; 197 | 198 | memcpy(&s->partial[partial], data, cur); 199 | 200 | s->len += cur; 201 | data += cur; 202 | len -= cur; 203 | 204 | partial = s->len & (SHA512_BLOCK_SIZE - 1); 205 | if (!partial) 206 | sha512_block(s, s->partial); 207 | } 208 | 209 | while (len >= SHA512_BLOCK_SIZE) { 210 | sha512_block(s, data); 211 | 212 | s->len += SHA512_BLOCK_SIZE; 213 | data += SHA512_BLOCK_SIZE; 214 | len -= SHA512_BLOCK_SIZE; 215 | } 216 | 217 | if (!len) 218 | return; 219 | 220 | memcpy(s->partial, data, len); 221 | s->len += len; 222 | } 223 | 224 | void sha512_final(struct sha512_state *s, uint8_t *hash) 225 | { 226 | size_t last_size = s->len & (SHA512_BLOCK_SIZE - 1); 227 | unsigned int len = SHA512_HASH_SIZE; 228 | int i = 0; 229 | 230 | s->partial[last_size++] = 0x80; 231 | if (last_size < SHA512_BLOCK_SIZE) 232 | memset(&s->partial[last_size], 0, 233 | SHA512_BLOCK_SIZE - last_size); 234 | 235 | if (last_size > (SHA512_BLOCK_SIZE - 16)) { 236 | sha512_block(s, s->partial); 237 | memset(s->partial, 0, sizeof(s->partial)); 238 | } 239 | 240 | /* Note: we assume total_size fits in 61 bits */ 241 | store64(s->partial + SHA512_BLOCK_SIZE - 8, s->len << 3); 242 | sha512_block(s, s->partial); 243 | 244 | /* Read out whole words */ 245 | while (len >= 8) { 246 | store64(hash, s->h[i++]); 247 | hash += 8; 248 | len -= 8; 249 | } 250 | 251 | /* Read out bytes */ 252 | if (len) { 253 | uint8_t tmp[8]; 254 | 255 | store64(tmp, s->h[i]); 256 | memcpy(hash, tmp, len); 257 | } 258 | } 259 | 260 | void hmac_sha512(void *dest, const void *key, size_t key_len, 261 | const void *data, size_t data_len) 262 | { 263 | uint8_t k_pad[2 * SHA512_HASH_SIZE] = {}; 264 | struct sha512_state s; 265 | 266 | if (key_len > 128) { 267 | sha512_init(&s); 268 | sha512_add(&s, key, key_len); 269 | sha512_final(&s, k_pad); 270 | } else { 271 | memcpy(k_pad, key, key_len); 272 | } 273 | 274 | for (size_t i = 0; i < sizeof(k_pad); i++) 275 | k_pad[i] ^= 0x36; 276 | 277 | sha512_init(&s); 278 | sha512_add(&s, k_pad, sizeof(k_pad)); 279 | sha512_add(&s, data, data_len); 280 | sha512_final(&s, dest); 281 | 282 | for (size_t i = 0; i < sizeof(k_pad); i++) 283 | k_pad[i] ^= 0x36 ^ 0x5c; 284 | 285 | sha512_init(&s); 286 | sha512_add(&s, k_pad, sizeof(k_pad)); 287 | sha512_add(&s, dest, SHA512_HASH_SIZE); 288 | sha512_final(&s, dest); 289 | } 290 | -------------------------------------------------------------------------------- /ed25519.c: -------------------------------------------------------------------------------- 1 | /* Edwards curve operations 2 | * Daniel Beer , 9 Jan 2014 3 | * 4 | * This file is in the public domain. 5 | */ 6 | 7 | #include "ed25519.h" 8 | 9 | /* Base point is (numbers wrapped): 10 | * 11 | * x = 151122213495354007725011514095885315114 12 | * 54012693041857206046113283949847762202 13 | * y = 463168356949264781694283940034751631413 14 | * 07993866256225615783033603165251855960 15 | * 16 | * y is derived by transforming the original Montgomery base (u=9). x 17 | * is the corresponding positive coordinate for the new curve equation. 18 | * t is x*y. 19 | */ 20 | const struct ed25519_pt ed25519_base = { 21 | .x = { 22 | 0x1a, 0xd5, 0x25, 0x8f, 0x60, 0x2d, 0x56, 0xc9, 23 | 0xb2, 0xa7, 0x25, 0x95, 0x60, 0xc7, 0x2c, 0x69, 24 | 0x5c, 0xdc, 0xd6, 0xfd, 0x31, 0xe2, 0xa4, 0xc0, 25 | 0xfe, 0x53, 0x6e, 0xcd, 0xd3, 0x36, 0x69, 0x21 26 | }, 27 | .y = { 28 | 0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 29 | 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 30 | 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 31 | 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 32 | }, 33 | .t = { 34 | 0xa3, 0xdd, 0xb7, 0xa5, 0xb3, 0x8a, 0xde, 0x6d, 35 | 0xf5, 0x52, 0x51, 0x77, 0x80, 0x9f, 0xf0, 0x20, 36 | 0x7d, 0xe3, 0xab, 0x64, 0x8e, 0x4e, 0xea, 0x66, 37 | 0x65, 0x76, 0x8b, 0xd7, 0x0f, 0x5f, 0x87, 0x67 38 | }, 39 | .z = {1, 0} 40 | }; 41 | 42 | static const struct ed25519_pt ed25519_neutral = { 43 | .x = {0}, 44 | .y = {1, 0}, 45 | .t = {0}, 46 | .z = {1, 0} 47 | }; 48 | 49 | /* Conversion to and from projective coordinates */ 50 | void ed25519_project(struct ed25519_pt *p, 51 | const uint8_t *x, const uint8_t *y) 52 | { 53 | f25519_copy(p->x, x); 54 | f25519_copy(p->y, y); 55 | f25519_load(p->z, 1); 56 | f25519_mul__distinct(p->t, x, y); 57 | } 58 | 59 | void ed25519_unproject(uint8_t *x, uint8_t *y, 60 | const struct ed25519_pt *p) 61 | { 62 | uint8_t z1[F25519_SIZE]; 63 | 64 | f25519_inv__distinct(z1, p->z); 65 | f25519_mul__distinct(x, p->x, z1); 66 | f25519_mul__distinct(y, p->y, z1); 67 | 68 | f25519_normalize(x); 69 | f25519_normalize(y); 70 | } 71 | 72 | /* Compress/uncompress points. We compress points by storing the x 73 | * coordinate and the parity of the y coordinate. 74 | * 75 | * Rearranging the curve equation, we obtain explicit formulae for the 76 | * coordinates: 77 | * 78 | * x = sqrt((y^2-1) / (1+dy^2)) 79 | * y = sqrt((x^2+1) / (1-dx^2)) 80 | * 81 | * Where d = (-121665/121666), or: 82 | * 83 | * d = 370957059346694393431380835087545651895 84 | * 42113879843219016388785533085940283555 85 | */ 86 | 87 | static const uint8_t ed25519_d[F25519_SIZE] = { 88 | 0xa3, 0x78, 0x59, 0x13, 0xca, 0x4d, 0xeb, 0x75, 89 | 0xab, 0xd8, 0x41, 0x41, 0x4d, 0x0a, 0x70, 0x00, 90 | 0x98, 0xe8, 0x79, 0x77, 0x79, 0x40, 0xc7, 0x8c, 91 | 0x73, 0xfe, 0x6f, 0x2b, 0xee, 0x6c, 0x03, 0x52 92 | }; 93 | 94 | void ed25519_pack(uint8_t *c, const uint8_t *x, const uint8_t *y) 95 | { 96 | uint8_t tmp[F25519_SIZE]; 97 | uint8_t parity; 98 | 99 | f25519_copy(tmp, x); 100 | f25519_normalize(tmp); 101 | parity = (tmp[0] & 1) << 7; 102 | 103 | f25519_copy(c, y); 104 | f25519_normalize(c); 105 | c[31] |= parity; 106 | } 107 | 108 | uint8_t ed25519_try_unpack(uint8_t *x, uint8_t *y, const uint8_t *comp) 109 | { 110 | const int parity = comp[31] >> 7; 111 | uint8_t a[F25519_SIZE]; 112 | uint8_t b[F25519_SIZE]; 113 | uint8_t c[F25519_SIZE]; 114 | 115 | /* Unpack y */ 116 | f25519_copy(y, comp); 117 | y[31] &= 127; 118 | 119 | /* Compute c = y^2 */ 120 | f25519_mul__distinct(c, y, y); 121 | 122 | /* Compute b = (1+dy^2)^-1 */ 123 | f25519_mul__distinct(b, c, ed25519_d); 124 | f25519_add(a, b, f25519_one); 125 | f25519_inv__distinct(b, a); 126 | 127 | /* Compute a = y^2-1 */ 128 | f25519_sub(a, c, f25519_one); 129 | 130 | /* Compute c = a*b = (y^2-1)/(1-dy^2) */ 131 | f25519_mul__distinct(c, a, b); 132 | 133 | /* Compute a, b = +/-sqrt(c), if c is square */ 134 | f25519_sqrt(a, c); 135 | f25519_neg(b, a); 136 | 137 | /* Select one of them, based on the compressed parity bit */ 138 | f25519_select(x, a, b, (a[0] ^ parity) & 1); 139 | 140 | /* Verify that x^2 = c */ 141 | f25519_mul__distinct(a, x, x); 142 | f25519_normalize(a); 143 | f25519_normalize(c); 144 | 145 | return f25519_eq(a, c); 146 | } 147 | 148 | /* k = 2d */ 149 | static const uint8_t ed25519_k[F25519_SIZE] = { 150 | 0x59, 0xf1, 0xb2, 0x26, 0x94, 0x9b, 0xd6, 0xeb, 151 | 0x56, 0xb1, 0x83, 0x82, 0x9a, 0x14, 0xe0, 0x00, 152 | 0x30, 0xd1, 0xf3, 0xee, 0xf2, 0x80, 0x8e, 0x19, 153 | 0xe7, 0xfc, 0xdf, 0x56, 0xdc, 0xd9, 0x06, 0x24 154 | }; 155 | 156 | void ed25519_add(struct ed25519_pt *r, 157 | const struct ed25519_pt *p1, const struct ed25519_pt *p2) 158 | { 159 | /* Explicit formulas database: add-2008-hwcd-3 160 | * 161 | * source 2008 Hisil--Wong--Carter--Dawson, 162 | * http://eprint.iacr.org/2008/522, Section 3.1 163 | * appliesto extended-1 164 | * parameter k 165 | * assume k = 2 d 166 | * compute A = (Y1-X1)(Y2-X2) 167 | * compute B = (Y1+X1)(Y2+X2) 168 | * compute C = T1 k T2 169 | * compute D = Z1 2 Z2 170 | * compute E = B - A 171 | * compute F = D - C 172 | * compute G = D + C 173 | * compute H = B + A 174 | * compute X3 = E F 175 | * compute Y3 = G H 176 | * compute T3 = E H 177 | * compute Z3 = F G 178 | */ 179 | uint8_t a[F25519_SIZE]; 180 | uint8_t b[F25519_SIZE]; 181 | uint8_t c[F25519_SIZE]; 182 | uint8_t d[F25519_SIZE]; 183 | uint8_t e[F25519_SIZE]; 184 | uint8_t f[F25519_SIZE]; 185 | uint8_t g[F25519_SIZE]; 186 | uint8_t h[F25519_SIZE]; 187 | 188 | /* A = (Y1-X1)(Y2-X2) */ 189 | f25519_sub(c, p1->y, p1->x); 190 | f25519_sub(d, p2->y, p2->x); 191 | f25519_mul__distinct(a, c, d); 192 | 193 | /* B = (Y1+X1)(Y2+X2) */ 194 | f25519_add(c, p1->y, p1->x); 195 | f25519_add(d, p2->y, p2->x); 196 | f25519_mul__distinct(b, c, d); 197 | 198 | /* C = T1 k T2 */ 199 | f25519_mul__distinct(d, p1->t, p2->t); 200 | f25519_mul__distinct(c, d, ed25519_k); 201 | 202 | /* D = Z1 2 Z2 */ 203 | f25519_mul__distinct(d, p1->z, p2->z); 204 | f25519_add(d, d, d); 205 | 206 | /* E = B - A */ 207 | f25519_sub(e, b, a); 208 | 209 | /* F = D - C */ 210 | f25519_sub(f, d, c); 211 | 212 | /* G = D + C */ 213 | f25519_add(g, d, c); 214 | 215 | /* H = B + A */ 216 | f25519_add(h, b, a); 217 | 218 | /* X3 = E F */ 219 | f25519_mul__distinct(r->x, e, f); 220 | 221 | /* Y3 = G H */ 222 | f25519_mul__distinct(r->y, g, h); 223 | 224 | /* T3 = E H */ 225 | f25519_mul__distinct(r->t, e, h); 226 | 227 | /* Z3 = F G */ 228 | f25519_mul__distinct(r->z, f, g); 229 | } 230 | 231 | static void ed25519_double(struct ed25519_pt *r, const struct ed25519_pt *p) 232 | { 233 | /* Explicit formulas database: dbl-2008-hwcd 234 | * 235 | * source 2008 Hisil--Wong--Carter--Dawson, 236 | * http://eprint.iacr.org/2008/522, Section 3.3 237 | * compute A = X1^2 238 | * compute B = Y1^2 239 | * compute C = 2 Z1^2 240 | * compute D = a A 241 | * compute E = (X1+Y1)^2-A-B 242 | * compute G = D + B 243 | * compute F = G - C 244 | * compute H = D - B 245 | * compute X3 = E F 246 | * compute Y3 = G H 247 | * compute T3 = E H 248 | * compute Z3 = F G 249 | */ 250 | uint8_t a[F25519_SIZE]; 251 | uint8_t b[F25519_SIZE]; 252 | uint8_t c[F25519_SIZE]; 253 | uint8_t e[F25519_SIZE]; 254 | uint8_t f[F25519_SIZE]; 255 | uint8_t g[F25519_SIZE]; 256 | uint8_t h[F25519_SIZE]; 257 | 258 | /* A = X1^2 */ 259 | f25519_mul__distinct(a, p->x, p->x); 260 | 261 | /* B = Y1^2 */ 262 | f25519_mul__distinct(b, p->y, p->y); 263 | 264 | /* C = 2 Z1^2 */ 265 | f25519_mul__distinct(c, p->z, p->z); 266 | f25519_add(c, c, c); 267 | 268 | /* D = a A (alter sign) */ 269 | /* E = (X1+Y1)^2-A-B */ 270 | f25519_add(f, p->x, p->y); 271 | f25519_mul__distinct(e, f, f); 272 | f25519_sub(e, e, a); 273 | f25519_sub(e, e, b); 274 | 275 | /* G = D + B */ 276 | f25519_sub(g, b, a); 277 | 278 | /* F = G - C */ 279 | f25519_sub(f, g, c); 280 | 281 | /* H = D - B */ 282 | f25519_neg(h, b); 283 | f25519_sub(h, h, a); 284 | 285 | /* X3 = E F */ 286 | f25519_mul__distinct(r->x, e, f); 287 | 288 | /* Y3 = G H */ 289 | f25519_mul__distinct(r->y, g, h); 290 | 291 | /* T3 = E H */ 292 | f25519_mul__distinct(r->t, e, h); 293 | 294 | /* Z3 = F G */ 295 | f25519_mul__distinct(r->z, f, g); 296 | } 297 | 298 | void ed25519_smult(struct ed25519_pt *r_out, const struct ed25519_pt *p, 299 | const uint8_t *e) 300 | { 301 | struct ed25519_pt r; 302 | int i; 303 | 304 | ed25519_copy(&r, &ed25519_neutral); 305 | 306 | for (i = 255; i >= 0; i--) { 307 | const uint8_t bit = (e[i >> 3] >> (i & 7)) & 1; 308 | struct ed25519_pt s; 309 | 310 | ed25519_double(&r, &r); 311 | ed25519_add(&s, &r, p); 312 | 313 | f25519_select(r.x, r.x, s.x, bit); 314 | f25519_select(r.y, r.y, s.y, bit); 315 | f25519_select(r.z, r.z, s.z, bit); 316 | f25519_select(r.t, r.t, s.t, bit); 317 | } 318 | 319 | ed25519_copy(r_out, &r); 320 | } 321 | -------------------------------------------------------------------------------- /wg-linux.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2022 Felix Fietkau 4 | * 5 | * Based on wireguard-tools: 6 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 7 | */ 8 | #define _GNU_SOURCE 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "linux/wireguard.h" 29 | #include "unetd.h" 30 | 31 | struct timespec64 { 32 | int64_t tv_sec; 33 | int64_t tv_nsec; 34 | }; 35 | 36 | struct wg_linux_peer_req { 37 | struct nl_msg *msg; 38 | 39 | struct nlattr *peers, *entry, *ips; 40 | }; 41 | 42 | static struct unl unl; 43 | 44 | static int 45 | wg_nl_init(void) 46 | { 47 | int ret; 48 | 49 | if (unl.sock) 50 | return 0; 51 | 52 | ret = unl_genl_init(&unl, "wireguard"); 53 | if (ret) 54 | return ret; 55 | 56 | nl_socket_set_buffer_size(unl.sock, 32768, 32768); 57 | nlmsg_set_default_size(32768); 58 | 59 | return 0; 60 | } 61 | 62 | static struct nl_msg * 63 | wg_genl_msg(struct network *net, bool set) 64 | { 65 | struct nl_msg *msg; 66 | 67 | msg = unl_genl_msg(&unl, set ? WG_CMD_SET_DEVICE : WG_CMD_GET_DEVICE, !set); 68 | nla_put_string(msg, WGDEVICE_A_IFNAME, network_name(net)); 69 | 70 | return msg; 71 | } 72 | 73 | static int 74 | wg_genl_call(struct nl_msg *msg) 75 | { 76 | return unl_request(&unl, msg, NULL, NULL); 77 | } 78 | 79 | static int 80 | __wg_linux_init(struct network *net, void *key) 81 | { 82 | struct nl_msg *msg; 83 | 84 | msg = wg_genl_msg(net, true); 85 | nla_put(msg, WGDEVICE_A_PRIVATE_KEY, WG_KEY_LEN, key); 86 | nla_put_u32(msg, WGDEVICE_A_FLAGS, WGDEVICE_F_REPLACE_PEERS); 87 | 88 | return wg_genl_call(msg); 89 | } 90 | 91 | static void 92 | wg_linux_cleanup(struct network *net) 93 | { 94 | uint8_t key[WG_KEY_LEN] = {}; 95 | 96 | __wg_linux_init(net, key); 97 | } 98 | 99 | static int 100 | wg_linux_init(struct network *net) 101 | { 102 | if (wg_nl_init()) 103 | return -1; 104 | 105 | return __wg_linux_init(net, net->config.key); 106 | } 107 | 108 | static int 109 | wg_linux_init_local(struct network *net, struct network_peer *peer) 110 | { 111 | struct nl_msg *msg; 112 | 113 | msg = wg_genl_msg(net, true); 114 | nla_put_u16(msg, WGDEVICE_A_LISTEN_PORT, peer ? peer->port : 0); 115 | 116 | return wg_genl_call(msg); 117 | } 118 | 119 | static void 120 | wg_linux_msg_add_ip(struct nl_msg *msg, int af, void *addr, int mask) 121 | { 122 | struct nlattr *ip; 123 | int len; 124 | 125 | if (af == AF_INET6) 126 | len = sizeof(struct in6_addr); 127 | else 128 | len = sizeof(struct in_addr); 129 | 130 | ip = nla_nest_start(msg, 0); 131 | nla_put_u16(msg, WGALLOWEDIP_A_FAMILY, af); 132 | nla_put(msg, WGALLOWEDIP_A_IPADDR, len, addr); 133 | nla_put_u8(msg, WGALLOWEDIP_A_CIDR_MASK, mask); 134 | nla_nest_end(msg, ip); 135 | } 136 | 137 | static struct nl_msg * 138 | wg_linux_peer_req_init(struct network *net, struct network_peer *peer, 139 | struct wg_linux_peer_req *req) 140 | { 141 | req->msg = wg_genl_msg(net, true); 142 | 143 | req->peers = nla_nest_start(req->msg, WGDEVICE_A_PEERS); 144 | req->entry = nla_nest_start(req->msg, 0); 145 | nla_put(req->msg, WGPEER_A_PUBLIC_KEY, WG_KEY_LEN, peer->key); 146 | 147 | return req->msg; 148 | } 149 | 150 | static int 151 | wg_linux_peer_req_done(struct wg_linux_peer_req *req) 152 | { 153 | nla_nest_end(req->msg, req->entry); 154 | nla_nest_end(req->msg, req->peers); 155 | 156 | return wg_genl_call(req->msg); 157 | } 158 | 159 | static struct nl_msg * 160 | wg_linux_peer_msg_size_check(struct wg_linux_peer_req *req, struct network *net, 161 | struct network_peer *peer) 162 | { 163 | if (nlmsg_get_max_size(req->msg) > 164 | nlmsg_total_size(nlmsg_hdr(req->msg)->nlmsg_len) + 256) 165 | return req->msg; 166 | 167 | nla_nest_end(req->msg, req->ips); 168 | wg_linux_peer_req_done(req); 169 | 170 | wg_linux_peer_req_init(net, peer, req); 171 | req->ips = nla_nest_start(req->msg, WGPEER_A_ALLOWEDIPS); 172 | 173 | return req->msg; 174 | } 175 | 176 | static void 177 | wg_linux_peer_msg_add_allowed_ip(struct wg_linux_peer_req *req, struct network *net, 178 | struct network_peer *peer) 179 | { 180 | struct nl_msg *msg = req->msg; 181 | struct blob_attr *cur; 182 | int rem; 183 | 184 | wg_linux_msg_add_ip(msg, AF_INET6, &peer->local_addr.in6, 128); 185 | msg = wg_linux_peer_msg_size_check(req, net, peer); 186 | 187 | blobmsg_for_each_attr(cur, peer->ipaddr, rem) { 188 | const char *str = blobmsg_get_string(cur); 189 | struct in6_addr in6; 190 | int af, mask; 191 | 192 | if (strchr(str, ':')) { 193 | af = AF_INET6; 194 | mask = 128; 195 | } else { 196 | af = AF_INET; 197 | mask = 32; 198 | } 199 | 200 | if (inet_pton(af, str, &in6) != 1) 201 | continue; 202 | 203 | wg_linux_msg_add_ip(msg, af, &in6, mask); 204 | msg = wg_linux_peer_msg_size_check(req, net, peer); 205 | } 206 | 207 | blobmsg_for_each_attr(cur, peer->subnet, rem) { 208 | const char *str = blobmsg_get_string(cur); 209 | union network_addr addr; 210 | int mask; 211 | int af; 212 | 213 | af = strchr(str, ':') ? AF_INET6 : AF_INET; 214 | if (network_get_subnet(af, &addr, &mask, str)) 215 | continue; 216 | 217 | wg_linux_msg_add_ip(msg, af, &addr, mask); 218 | msg = wg_linux_peer_msg_size_check(req, net, peer); 219 | } 220 | } 221 | 222 | static int 223 | wg_linux_peer_update(struct network *net, struct network_peer *peer, enum wg_update_cmd cmd) 224 | { 225 | struct wg_linux_peer_req req; 226 | struct network_host *host; 227 | 228 | wg_linux_peer_req_init(net, peer, &req); 229 | 230 | if (cmd == WG_PEER_DELETE) { 231 | nla_put_u32(req.msg, WGPEER_A_FLAGS, WGPEER_F_REMOVE_ME); 232 | goto out; 233 | } 234 | 235 | nla_put_u32(req.msg, WGPEER_A_FLAGS, WGPEER_F_REPLACE_ALLOWEDIPS); 236 | 237 | req.ips = nla_nest_start(req.msg, WGPEER_A_ALLOWEDIPS); 238 | 239 | wg_linux_peer_msg_add_allowed_ip(&req, net, peer); 240 | 241 | for_each_routed_host(host, net, peer) 242 | wg_linux_peer_msg_add_allowed_ip(&req, net, &host->peer); 243 | 244 | nla_nest_end(req.msg, req.ips); 245 | 246 | out: 247 | return wg_linux_peer_req_done(&req); 248 | } 249 | 250 | static void 251 | wg_linux_parse_peer(struct network *net, struct nlattr *data, time_t now) 252 | { 253 | struct network_peer *peer = NULL; 254 | struct nlattr *tb[__WGPEER_A_LAST]; 255 | struct nlattr *cur; 256 | 257 | nla_parse_nested(tb, WGPEER_A_MAX, data, NULL); 258 | 259 | cur = tb[WGPEER_A_PUBLIC_KEY]; 260 | if (!cur) 261 | return; 262 | 263 | peer = wg_peer_update_start(net, nla_data(cur)); 264 | if (!peer) 265 | return; 266 | 267 | if ((cur = tb[WGPEER_A_LAST_HANDSHAKE_TIME]) != NULL) { 268 | struct timespec64 *tv = nla_data(cur); 269 | 270 | wg_peer_set_last_handshake(net, peer, now, tv->tv_sec); 271 | } 272 | 273 | if ((cur = tb[WGPEER_A_RX_BYTES]) != NULL) 274 | wg_peer_set_rx_bytes(net, peer, nla_get_u64(cur)); 275 | 276 | if ((cur = tb[WGPEER_A_TX_BYTES]) != NULL) 277 | wg_peer_set_tx_bytes(net, peer, nla_get_u64(cur)); 278 | 279 | if ((cur = tb[WGPEER_A_ENDPOINT]) != NULL) 280 | wg_peer_set_endpoint(net, peer, nla_data(cur), nla_len(cur)); 281 | 282 | wg_peer_update_done(net, peer); 283 | } 284 | 285 | static void 286 | wg_linux_parse_peer_list(struct network *net, struct nlattr *data, time_t now) 287 | { 288 | struct nlattr *cur; 289 | int rem; 290 | 291 | if (!data) 292 | return; 293 | 294 | nla_for_each_nested(cur, data, rem) 295 | wg_linux_parse_peer(net, cur, now); 296 | } 297 | 298 | static int 299 | wg_linux_get_cb(struct nl_msg *msg, void *arg) 300 | { 301 | struct nlmsghdr *nh = nlmsg_hdr(msg); 302 | struct network *net = arg; 303 | struct nlattr *tb[__WGDEVICE_A_LAST]; 304 | time_t now = time(NULL); 305 | 306 | nlmsg_parse(nh, sizeof(struct genlmsghdr), tb, __WGDEVICE_A_LAST, NULL); 307 | wg_linux_parse_peer_list(net, tb[WGDEVICE_A_PEERS], now); 308 | 309 | return NL_SKIP; 310 | } 311 | 312 | static int 313 | wg_linux_peer_refresh(struct network *net) 314 | { 315 | struct nl_msg *msg = wg_genl_msg(net, false); 316 | 317 | return unl_request(&unl, msg, wg_linux_get_cb, net); 318 | } 319 | 320 | static int 321 | wg_linux_peer_connect(struct network *net, struct network_peer *peer, 322 | union network_endpoint *ep) 323 | { 324 | struct wg_linux_peer_req req; 325 | struct nl_msg *msg; 326 | int len; 327 | 328 | msg = wg_linux_peer_req_init(net, peer, &req); 329 | 330 | if (net->net_config.keepalive) { 331 | nla_put_u16(msg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, 0); 332 | wg_linux_peer_req_done(&req); 333 | 334 | msg = wg_linux_peer_req_init(net, peer, &req); 335 | nla_put_u16(msg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, 336 | net->net_config.keepalive); 337 | } 338 | 339 | if (ep->in.sin_family == AF_INET6) 340 | len = sizeof(ep->in6); 341 | else 342 | len = sizeof(ep->in); 343 | nla_put(msg, WGPEER_A_ENDPOINT, len, &ep->in6); 344 | 345 | return wg_linux_peer_req_done(&req); 346 | } 347 | 348 | const struct wg_ops wg_linux_ops = { 349 | .name = "user", 350 | .init = wg_linux_init, 351 | .cleanup = wg_linux_cleanup, 352 | .init_local = wg_linux_init_local, 353 | .peer_update = wg_linux_peer_update, 354 | .peer_refresh = wg_linux_peer_refresh, 355 | .peer_connect = wg_linux_peer_connect, 356 | }; 357 | -------------------------------------------------------------------------------- /pex-stun.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "unetd.h" 11 | 12 | static inline int avl_stun_cmp(const void *k1, const void *k2, void *priv) 13 | { 14 | return memcmp(k1, k2, 12); 15 | } 16 | 17 | static bool has_connected_peer(struct network *net, bool pex) 18 | { 19 | struct network_peer *peer; 20 | 21 | vlist_for_each_element(&net->peers, peer, node) { 22 | if (pex && !peer->pex_port) 23 | continue; 24 | 25 | if (peer->state.connected || peer->indirect) 26 | return true; 27 | } 28 | 29 | return false; 30 | } 31 | 32 | void network_stun_server_add(struct network *net, const char *host) 33 | { 34 | struct network_stun *stun = &net->stun; 35 | struct network_stun_server *s; 36 | char *name_buf; 37 | 38 | s = calloc_a(sizeof(*s), &name_buf, strlen(host) + 1); 39 | s->pending_node.key = s->req.transaction; 40 | s->host = strcpy(name_buf, host); 41 | 42 | list_add_tail(&s->list, &stun->servers); 43 | } 44 | 45 | static void 46 | network_stun_close_socket(struct network *net) 47 | { 48 | struct network_host *local = net->net_config.local_host; 49 | struct network_stun *stun = &net->stun; 50 | 51 | if (!stun->wgport_disabled) 52 | return; 53 | 54 | D_NET(net, "close STUN socket"); 55 | uloop_fd_delete(&stun->socket); 56 | close(stun->socket.fd); 57 | wg_init_local(net, &local->peer); 58 | stun->wgport_disabled = false; 59 | } 60 | 61 | static void 62 | network_stun_socket_cb(struct uloop_fd *fd, unsigned int events) 63 | { 64 | struct network_stun *stun = container_of(fd, struct network_stun, socket); 65 | struct network *net = container_of(stun, struct network, stun); 66 | char buf[1024]; 67 | ssize_t len; 68 | 69 | while (1) { 70 | len = recv(fd->fd, buf, sizeof(buf), 0); 71 | if (len < 0) { 72 | if (errno == EAGAIN) 73 | break; 74 | if (errno == EINTR) 75 | continue; 76 | 77 | perror("recv"); 78 | network_stun_close_socket(net); 79 | return; 80 | } 81 | 82 | if (!stun_msg_is_valid(buf, len)) 83 | continue; 84 | 85 | network_stun_rx_packet(net, buf, len); 86 | } 87 | } 88 | 89 | static void 90 | network_stun_open_socket(struct network *net) 91 | { 92 | struct network_host *local = net->net_config.local_host; 93 | struct network_stun *stun = &net->stun; 94 | int fd; 95 | 96 | if (stun->wgport_disabled) 97 | return; 98 | 99 | D_NET(net, "open STUN socket"); 100 | wg_init_local(net, NULL); 101 | 102 | fd = usock(USOCK_SERVER | USOCK_UDP | USOCK_IPV4ONLY | USOCK_NONBLOCK, 103 | NULL, usock_port(stun->port_local)); 104 | if (fd < 0) { 105 | wg_init_local(net, &local->peer); 106 | return; 107 | } 108 | 109 | stun->socket.fd = fd; 110 | uloop_fd_add(&stun->socket, ULOOP_READ); 111 | stun->wgport_disabled = true; 112 | } 113 | 114 | static bool 115 | network_stun_query_next(struct network *net) 116 | { 117 | struct network_stun *stun = &net->stun; 118 | struct network_stun_server *s; 119 | char addrstr[INET6_ADDRSTRLEN]; 120 | union network_endpoint ep; 121 | uint16_t res_port = 0; 122 | const void *msg; 123 | ssize_t ret; 124 | size_t len; 125 | 126 | s = list_first_entry(&stun->servers, struct network_stun_server, list); 127 | if (s->pending) 128 | return false; 129 | 130 | /* send next query */ 131 | if (network_get_endpoint(&ep, AF_INET, s->host, 0, s->seq++) < 0) { 132 | D_NET(net, "lookup failed for STUN host %s", s->host); 133 | goto out; 134 | } 135 | 136 | if (ep.sa.sa_family != AF_INET || !ep.in.sin_port) 137 | goto out; 138 | 139 | if (!stun->wgport_disabled && stun->auth_port_ext) 140 | res_port = stun->auth_port_ext; 141 | 142 | D_NET(net, "Send STUN query to %s, res_port=%d, wg_disabled=%d", 143 | inet_ntop(ep.sa.sa_family, network_endpoint_addr(&ep, NULL), 144 | addrstr, sizeof(addrstr)), res_port, stun->wgport_disabled); 145 | msg = stun_msg_request_prepare(&s->req, &len, res_port); 146 | if (!msg) 147 | goto out; 148 | 149 | retry: 150 | s->req_auth_port = false; 151 | if (stun->wgport_disabled) { 152 | ret = sendto(stun->socket.fd, msg, len, 0, &ep.sa, sizeof(ep.in)); 153 | } else if (!stun->auth_port_ext) { 154 | s->req_auth_port = true; 155 | ret = sendto(pex_socket(), msg, len, 0, &ep.sa, sizeof(ep.in)); 156 | } else { 157 | struct { 158 | struct ip ip; 159 | struct udphdr udp; 160 | } packet_hdr = {}; 161 | union network_addr local_addr = {}; 162 | 163 | network_get_local_addr(&local_addr, &ep); 164 | packet_hdr.ip = (struct ip){ 165 | .ip_hl = 5, 166 | .ip_v = 4, 167 | .ip_ttl = 64, 168 | .ip_p = IPPROTO_UDP, 169 | .ip_src = local_addr.in, 170 | .ip_dst = ep.in.sin_addr, 171 | }; 172 | packet_hdr.udp = (struct udphdr){ 173 | .uh_sport = htons(stun->port_local), 174 | .uh_dport = ep.in.sin_port, 175 | }; 176 | ep.in.sin_port = 0; 177 | 178 | ret = sendto_rawudp(pex_raw_socket(AF_INET), &ep, 179 | &packet_hdr, sizeof(packet_hdr), 180 | msg, len); 181 | } 182 | 183 | if (ret < 0 && errno == EINTR) 184 | goto retry; 185 | 186 | out: 187 | avl_insert(&stun->pending, &s->pending_node); 188 | s->pending = true; 189 | 190 | if (!list_is_last(&s->list, &stun->servers)) 191 | list_move_tail(&s->list, &stun->servers); 192 | 193 | return true; 194 | } 195 | 196 | static void 197 | network_stun_query_clear_pending(struct network *net) 198 | { 199 | struct network_stun *stun = &net->stun; 200 | struct network_stun_server *s; 201 | 202 | list_for_each_entry(s, &stun->servers, list) { 203 | if (!s->pending) 204 | continue; 205 | 206 | avl_delete(&stun->pending, &s->pending_node); 207 | s->pending = false; 208 | } 209 | } 210 | 211 | void network_stun_rx_packet(struct network *net, const void *data, size_t len) 212 | { 213 | struct network_stun *stun = &net->stun; 214 | const struct stun_msg_hdr *hdr = data; 215 | struct network_stun_server *s; 216 | 217 | s = avl_find_element(&stun->pending, hdr->transaction, s, pending_node); 218 | if (!s) 219 | return; 220 | 221 | if (!stun_msg_request_complete(&s->req, data, len)) 222 | return; 223 | 224 | if (!s->req.port) 225 | return; 226 | 227 | network_stun_update_port(net, s->req_auth_port, s->req.port); 228 | if (s->req_auth_port) 229 | stun->state = STUN_STATE_STUN_QUERY_SEND; 230 | else 231 | stun->state = STUN_STATE_IDLE; 232 | 233 | network_stun_query_clear_pending(net); 234 | 235 | uloop_timeout_set(&stun->timer, 1); 236 | } 237 | 238 | static void 239 | network_stun_timer_cb(struct uloop_timeout *t) 240 | { 241 | struct network_stun *stun = container_of(t, struct network_stun, timer); 242 | struct network *net = container_of(stun, struct network, stun); 243 | unsigned int next = 0; 244 | 245 | restart: 246 | switch (stun->state) { 247 | case STUN_STATE_IDLE: 248 | network_stun_close_socket(net); 249 | next = 15 * 60 * 1000; 250 | stun->state = STUN_STATE_STUN_QUERY_SEND; 251 | D_NET(net, "STUN idle"); 252 | break; 253 | case STUN_STATE_PEX_QUERY_WAIT: 254 | stun->state = STUN_STATE_STUN_QUERY_SEND; 255 | fallthrough; 256 | case STUN_STATE_STUN_QUERY_SEND: 257 | if (network_stun_query_next(net)) { 258 | next = 50; 259 | break; 260 | } 261 | 262 | stun->state = STUN_STATE_STUN_QUERY_WAIT; 263 | D_NET(net, "wait for STUN server responses"); 264 | next = 1000; 265 | break; 266 | case STUN_STATE_STUN_QUERY_WAIT: 267 | D_NET(net, "timeout waiting for STUN server responses, retry=%d", stun->retry); 268 | network_stun_query_clear_pending(net); 269 | if (stun->retry > 0) { 270 | stun->retry--; 271 | stun->state = STUN_STATE_STUN_QUERY_SEND; 272 | goto restart; 273 | } 274 | 275 | if (!stun->port_ext && !stun->wgport_disabled) { 276 | network_stun_open_socket(net); 277 | stun->state = STUN_STATE_STUN_QUERY_SEND; 278 | stun->retry = 2; 279 | } else { 280 | stun->state = STUN_STATE_IDLE; 281 | } 282 | goto restart; 283 | } 284 | 285 | if (next) 286 | uloop_timeout_set(t, next); 287 | } 288 | 289 | void network_stun_update_port(struct network *net, bool auth, uint16_t val) 290 | { 291 | struct network_stun *stun = &net->stun; 292 | uint16_t *port = auth ? &stun->auth_port_ext : &stun->port_ext; 293 | 294 | D_NET(net, "Update external %s port: %d", auth ? "auth" : "data", val); 295 | *port = val; 296 | } 297 | 298 | void network_stun_start(struct network *net) 299 | { 300 | struct network_host *local = net->net_config.local_host; 301 | struct network_stun *stun = &net->stun; 302 | unsigned int next = 1; 303 | 304 | if (!local || list_empty(&stun->servers)) 305 | return; 306 | 307 | if (local->peer.port != stun->port_local) { 308 | stun->port_ext = 0; 309 | stun->port_local = local->peer.port; 310 | } 311 | 312 | if (!stun->port_ext && has_connected_peer(net, true)) { 313 | D_NET(net, "wait for port information from PEX"); 314 | stun->state = STUN_STATE_PEX_QUERY_WAIT; 315 | next = 60 * 1000; 316 | } else { 317 | if (!stun->port_ext && !has_connected_peer(net, false)) 318 | network_stun_open_socket(net); 319 | 320 | stun->state = STUN_STATE_STUN_QUERY_SEND; 321 | stun->retry = 2; 322 | } 323 | 324 | uloop_timeout_set(&stun->timer, next); 325 | } 326 | 327 | void network_stun_init(struct network *net) 328 | { 329 | struct network_stun *stun = &net->stun; 330 | 331 | stun->socket.cb = network_stun_socket_cb; 332 | stun->timer.cb = network_stun_timer_cb; 333 | INIT_LIST_HEAD(&stun->servers); 334 | avl_init(&stun->pending, avl_stun_cmp, true, NULL); 335 | } 336 | 337 | void network_stun_free(struct network *net) 338 | { 339 | struct network_stun *stun = &net->stun; 340 | struct network_stun_server *s, *tmp; 341 | 342 | uloop_timeout_cancel(&stun->timer); 343 | network_stun_close_socket(net); 344 | 345 | avl_remove_all_elements(&stun->pending, s, pending_node, tmp) 346 | s->pending = false; 347 | 348 | list_for_each_entry_safe(s, tmp, &stun->servers, list) { 349 | list_del(&s->list); 350 | free(s); 351 | } 352 | } 353 | -------------------------------------------------------------------------------- /vxlan.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2022 Felix Fietkau 4 | */ 5 | #define _GNU_SOURCE 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "unetd.h" 17 | 18 | struct vxlan_tunnel { 19 | struct network *net; 20 | struct network_service *s; 21 | char ifname[IFNAMSIZ + 1]; 22 | int ifindex; 23 | uint16_t mtu; 24 | uint16_t port; 25 | uint32_t vni; 26 | uint32_t *forward_ports; 27 | uint32_t *cur_forward_ports; 28 | bool active; 29 | }; 30 | 31 | static uint32_t 32 | vxlan_tunnel_id(struct vxlan_tunnel *vt) 33 | { 34 | siphash_key_t key = {}; 35 | const char *name = network_service_name(vt->s); 36 | uint64_t val; 37 | 38 | if (vt->vni != ~0) 39 | return vt->vni; 40 | 41 | siphash_to_le64(&val, name, strlen(name), &key); 42 | 43 | return val & 0x00ffffff; 44 | } 45 | 46 | static struct nl_msg *vxlan_rtnl_msg(const char *ifname, int type, int flags) 47 | { 48 | struct ifinfomsg iim = { 49 | .ifi_family = AF_UNSPEC, 50 | }; 51 | struct nl_msg *msg; 52 | 53 | msg = nlmsg_alloc_simple(type, flags | NLM_F_REQUEST); 54 | if (!msg) 55 | return NULL; 56 | 57 | nlmsg_append(msg, &iim, sizeof(iim), 0); 58 | nla_put_string(msg, IFLA_IFNAME, ifname); 59 | 60 | return msg; 61 | } 62 | 63 | static int 64 | vxlan_update_host_fdb_entry(struct vxlan_tunnel *vt, struct network_host *host, bool add) 65 | { 66 | struct ndmsg ndmsg = { 67 | .ndm_family = PF_BRIDGE, 68 | .ndm_state = NUD_NOARP | NUD_PERMANENT, 69 | .ndm_flags = NTF_SELF, 70 | .ndm_ifindex = vt->ifindex, 71 | }; 72 | unsigned int flags = NLM_F_REQUEST; 73 | uint8_t lladdr[ETH_ALEN] = {}; 74 | struct nl_msg *msg; 75 | 76 | if (add) 77 | flags |= NLM_F_CREATE | NLM_F_APPEND; 78 | 79 | msg = nlmsg_alloc_simple(add ? RTM_NEWNEIGH : RTM_DELNEIGH, flags); 80 | nlmsg_append(msg, &ndmsg, sizeof(ndmsg), 0); 81 | nla_put(msg, NDA_LLADDR, ETH_ALEN, lladdr); 82 | nla_put(msg, NDA_DST, sizeof(struct in6_addr), &host->peer.local_addr); 83 | nla_put_u32(msg, NDA_IFINDEX, vt->net->ifindex); 84 | 85 | return rtnl_call(msg); 86 | } 87 | 88 | static void 89 | vxlan_update_fdb_hosts(struct vxlan_tunnel *vt) 90 | { 91 | struct network_service *s = vt->s; 92 | bool active; 93 | int i; 94 | 95 | if (!vt->active) 96 | return; 97 | 98 | for (i = 0; i < s->n_members; i++) { 99 | if (s->members[i] == vt->net->net_config.local_host) 100 | continue; 101 | 102 | if (vt->forward_ports && !bitmask_test(vt->forward_ports, i)) 103 | continue; 104 | 105 | active = s->members[i]->peer.state.connected; 106 | if (active == bitmask_test(vt->cur_forward_ports, i)) 107 | continue; 108 | 109 | if (!vxlan_update_host_fdb_entry(vt, s->members[i], active)) 110 | bitmask_set_val(vt->cur_forward_ports, i, active); 111 | } 112 | } 113 | 114 | static void 115 | vxlan_peer_update(struct network *net, struct network_service *s, struct network_peer *peer) 116 | { 117 | if (!s->vxlan) 118 | return; 119 | 120 | vxlan_update_fdb_hosts(s->vxlan); 121 | } 122 | 123 | static void 124 | vxlan_tunnel_init(struct vxlan_tunnel *vt) 125 | { 126 | struct network_peer *local = &vt->net->net_config.local_host->peer; 127 | struct nlattr *linkinfo, *data; 128 | struct nl_msg *msg; 129 | struct in6_addr group_addr; 130 | int mtu; 131 | 132 | if (rtnl_init()) 133 | return; 134 | 135 | memset(&group_addr, 0xff, sizeof(group_addr)); 136 | msg = vxlan_rtnl_msg(vt->ifname, RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL); 137 | 138 | linkinfo = nla_nest_start(msg, IFLA_LINKINFO); 139 | nla_put_string(msg, IFLA_INFO_KIND, "vxlan"); 140 | nla_put_u32(msg, IFLA_MTU, vt->mtu); 141 | 142 | data = nla_nest_start(msg, IFLA_INFO_DATA); 143 | nla_put_u32(msg, IFLA_VXLAN_ID, vxlan_tunnel_id(vt)); 144 | nla_put(msg, IFLA_VXLAN_LOCAL6, sizeof(struct in6_addr), &local->local_addr); 145 | nla_put(msg, IFLA_VXLAN_GROUP6, sizeof(struct in6_addr), &group_addr); 146 | nla_put_u16(msg, IFLA_VXLAN_PORT, htons(vt->port)); 147 | nla_put_u8(msg, IFLA_VXLAN_LEARNING, 1); 148 | nla_put_u32(msg, IFLA_VXLAN_LINK, vt->net->ifindex); 149 | nla_nest_end(msg, data); 150 | 151 | nla_nest_end(msg, linkinfo); 152 | 153 | if (rtnl_call(msg) < 0) 154 | return; 155 | 156 | vt->ifindex = if_nametoindex(vt->ifname); 157 | if (!vt->ifindex) { 158 | D_SERVICE(vt->net, vt->s, "failed to get ifindex for device %s", vt->ifname); 159 | return; 160 | } 161 | 162 | vt->active = true; 163 | vxlan_update_fdb_hosts(vt); 164 | 165 | mtu = 1420 - sizeof(struct ipv6hdr) - sizeof(struct udphdr) - 8; 166 | unetd_attach_mssfix(vt->ifindex, mtu); 167 | } 168 | 169 | static void 170 | vxlan_tunnel_teardown(struct vxlan_tunnel *vt) 171 | { 172 | struct nl_msg *msg; 173 | 174 | vt->active = false; 175 | msg = vxlan_rtnl_msg(vt->ifname, RTM_DELLINK, 0); 176 | rtnl_call(msg); 177 | } 178 | 179 | static const char * 180 | vxlan_find_ifname(struct network *net, const char *service) 181 | { 182 | struct blob_attr *cur; 183 | int rem; 184 | 185 | if (!net->config.tunnels) 186 | return NULL; 187 | 188 | blobmsg_for_each_attr(cur, net->config.tunnels, rem) { 189 | const char *name; 190 | 191 | if (!blobmsg_check_attr(cur, true) || 192 | blobmsg_type(cur) != BLOBMSG_TYPE_STRING) 193 | continue; 194 | 195 | if (strcmp(blobmsg_get_string(cur), service) != 0) 196 | continue; 197 | 198 | name = blobmsg_name(cur); 199 | if (strlen(name) > IFNAMSIZ) 200 | break; 201 | 202 | return name; 203 | } 204 | 205 | return NULL; 206 | } 207 | 208 | static void 209 | __vxlan_mark_forward_host(struct vxlan_tunnel *vt, struct network_host *host) 210 | { 211 | struct network_service *s = vt->s; 212 | unsigned int i; 213 | 214 | for (i = 0; i < s->n_members; i++) { 215 | if (s->members[i] != host) 216 | continue; 217 | 218 | bitmask_set(vt->forward_ports, i); 219 | break; 220 | } 221 | } 222 | 223 | static void 224 | vxlan_mark_forward_host(struct vxlan_tunnel *vt, const char *name) 225 | { 226 | struct network *net = vt->net; 227 | struct network_host *host; 228 | 229 | host = avl_find_element(&net->hosts, name, host, node); 230 | if (!host) 231 | return; 232 | 233 | __vxlan_mark_forward_host(vt, host); 234 | } 235 | 236 | static void 237 | vxlan_mark_forward_group(struct vxlan_tunnel *vt, const char *name) 238 | { 239 | struct network *net = vt->net; 240 | struct network_group *group; 241 | int i; 242 | 243 | group = avl_find_element(&net->groups, name, group, node); 244 | if (!group) 245 | return; 246 | 247 | for (i = 0; i < group->n_members; i++) 248 | __vxlan_mark_forward_host(vt, group->members[i]); 249 | } 250 | 251 | static void 252 | vxlan_init_forward_ports(struct vxlan_tunnel *vt, struct blob_attr *data) 253 | { 254 | unsigned int len = bitmask_size(vt->s->n_members); 255 | struct blob_attr *cur; 256 | int rem; 257 | 258 | vt->cur_forward_ports = realloc(vt->cur_forward_ports, len); 259 | memset(vt->cur_forward_ports, 0, len); 260 | 261 | if (!data || blobmsg_check_array(data, BLOBMSG_TYPE_STRING) <= 0) { 262 | free(vt->forward_ports); 263 | vt->forward_ports = NULL; 264 | return; 265 | } 266 | 267 | vt->forward_ports = realloc(vt->forward_ports, len); 268 | memset(vt->forward_ports, 0, len); 269 | blobmsg_for_each_attr(cur, data, rem) { 270 | const char *name = blobmsg_get_string(cur); 271 | 272 | if (name[0] == '@') 273 | vxlan_mark_forward_group(vt, name + 1); 274 | else 275 | vxlan_mark_forward_host(vt, name); 276 | } 277 | } 278 | 279 | static bool 280 | vxlan_config_equal(struct network_service *s1, struct network_service *s2) 281 | { 282 | int i; 283 | 284 | if (!blob_attr_equal(s1->config, s2->config)) 285 | return false; 286 | 287 | if (s1->n_members != s2->n_members) 288 | return false; 289 | 290 | for (i = 0; i < s1->n_members; i++) 291 | if (memcmp(s1->members[i]->peer.key, s2->members[i]->peer.key, 292 | CURVE25519_KEY_SIZE) != 0) 293 | return false; 294 | 295 | return true; 296 | } 297 | 298 | static void 299 | vxlan_init(struct network *net, struct network_service *s, 300 | struct network_service *s_old) 301 | { 302 | enum { 303 | VXCFG_ATTR_FWD_PORTS, 304 | VXCFG_ATTR_ID, 305 | VXCFG_ATTR_PORT, 306 | VXCFG_ATTR_MTU, 307 | __VXCFG_ATTR_MAX 308 | }; 309 | static const struct blobmsg_policy policy[__VXCFG_ATTR_MAX] = { 310 | [VXCFG_ATTR_FWD_PORTS] = { "forward_ports", BLOBMSG_TYPE_ARRAY }, 311 | [VXCFG_ATTR_ID] = { "id", BLOBMSG_TYPE_INT32 }, 312 | [VXCFG_ATTR_PORT] = { "port", BLOBMSG_TYPE_INT32 }, 313 | [VXCFG_ATTR_MTU] = { "mtu", BLOBMSG_TYPE_INT32 }, 314 | }; 315 | struct blob_attr *tb[__VXCFG_ATTR_MAX] = {}; 316 | struct blob_attr *cur; 317 | struct vxlan_tunnel *vt = s->vxlan; 318 | const char *name; 319 | 320 | if (s_old) { 321 | vt = s_old->vxlan; 322 | s_old->vxlan = NULL; 323 | if (!vt) 324 | return; 325 | 326 | if (vxlan_config_equal(s, s_old)) { 327 | s->vxlan = vt; 328 | vt->s = s; 329 | return; 330 | } 331 | 332 | vxlan_tunnel_teardown(vt); 333 | goto init; 334 | } 335 | 336 | name = vxlan_find_ifname(net, network_service_name(s)); 337 | if (!name) { 338 | D_SERVICE(net, s, "no configured tunnel ifname"); 339 | return; 340 | } 341 | 342 | vt = calloc(1, sizeof(*s->vxlan)); 343 | snprintf(vt->ifname, sizeof(vt->ifname), "%s", name); 344 | vt->net = net; 345 | 346 | init: 347 | s->vxlan = vt; 348 | vt->s = s; 349 | if (s->config) 350 | blobmsg_parse(policy, __VXCFG_ATTR_MAX, tb, blobmsg_data(s->config), 351 | blobmsg_len(s->config)); 352 | 353 | vxlan_init_forward_ports(vt, tb[VXCFG_ATTR_FWD_PORTS]); 354 | if ((cur = tb[VXCFG_ATTR_ID]) != NULL) 355 | vt->vni = blobmsg_get_u32(cur) & 0x00ffffff; 356 | else 357 | vt->vni = ~0; 358 | 359 | if ((cur = tb[VXCFG_ATTR_PORT]) != NULL) 360 | vt->port = blobmsg_get_u32(cur); 361 | else 362 | vt->port = 4789; 363 | 364 | if ((cur = tb[VXCFG_ATTR_MTU]) != NULL) 365 | vt->mtu = blobmsg_get_u32(cur); 366 | else 367 | vt->mtu = 1500; 368 | 369 | vxlan_tunnel_init(vt); 370 | } 371 | 372 | static void 373 | vxlan_free(struct network *net, struct network_service *s) 374 | { 375 | struct vxlan_tunnel *vt = s->vxlan; 376 | 377 | if (!vt) 378 | return; 379 | 380 | vxlan_tunnel_teardown(vt); 381 | s->vxlan = NULL; 382 | free(vt->forward_ports); 383 | free(vt); 384 | } 385 | 386 | const struct service_ops vxlan_ops = { 387 | .init = vxlan_init, 388 | .free = vxlan_free, 389 | .peer_update = vxlan_peer_update, 390 | }; 391 | -------------------------------------------------------------------------------- /wg-user.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2022 Felix Fietkau 4 | * 5 | * Based on wireguard-tools: 6 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "unetd.h" 24 | 25 | #define SOCK_PATH RUNSTATEDIR "/wireguard/" 26 | #define SOCK_SUFFIX ".sock" 27 | 28 | struct wg_req { 29 | FILE *f; 30 | 31 | char *buf; 32 | size_t buf_len; 33 | 34 | char *key, *value; 35 | 36 | int ret; 37 | }; 38 | 39 | static void 40 | key_to_hex(char hex[static WG_KEY_LEN_HEX], const uint8_t key[static WG_KEY_LEN]) 41 | { 42 | unsigned int i; 43 | 44 | for (i = 0; i < WG_KEY_LEN; ++i) { 45 | hex[i * 2] = 87U + (key[i] >> 4) + ((((key[i] >> 4) - 10U) >> 8) & ~38U); 46 | hex[i * 2 + 1] = 87U + (key[i] & 0xf) + ((((key[i] & 0xf) - 10U) >> 8) & ~38U); 47 | } 48 | 49 | hex[i * 2] = '\0'; 50 | } 51 | 52 | static bool 53 | key_from_hex(uint8_t key[static WG_KEY_LEN], const char *hex) 54 | { 55 | uint8_t c, c_acc, c_alpha0, c_alpha, c_num0, c_num, c_val; 56 | volatile uint8_t ret = 0; 57 | 58 | if (strlen(hex) != WG_KEY_LEN_HEX - 1) 59 | return false; 60 | 61 | for (unsigned int i = 0; i < WG_KEY_LEN_HEX - 1; i += 2) { 62 | c = (uint8_t)hex[i]; 63 | c_num = c ^ 48U; 64 | c_num0 = (c_num - 10U) >> 8; 65 | c_alpha = (c & ~32U) - 55U; 66 | c_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8; 67 | ret |= ((c_num0 | c_alpha0) - 1) >> 8; 68 | c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha); 69 | c_acc = c_val * 16U; 70 | 71 | c = (uint8_t)hex[i + 1]; 72 | c_num = c ^ 48U; 73 | c_num0 = (c_num - 10U) >> 8; 74 | c_alpha = (c & ~32U) - 55U; 75 | c_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8; 76 | ret |= ((c_num0 | c_alpha0) - 1) >> 8; 77 | c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha); 78 | key[i / 2] = c_acc | c_val; 79 | } 80 | 81 | return 1 & ((ret - 1) >> 8); 82 | } 83 | 84 | static bool wg_user_check(struct network *net) 85 | { 86 | struct sockaddr_un addr = { .sun_family = AF_UNIX }; 87 | struct stat sbuf; 88 | int fd, ret; 89 | 90 | if (snprintf(addr.sun_path, sizeof(addr.sun_path), SOCK_PATH "%s" SOCK_SUFFIX, network_name(net)) < 0) 91 | return false; 92 | if (stat(addr.sun_path, &sbuf) < 0) 93 | return false; 94 | if (!S_ISSOCK(sbuf.st_mode)) 95 | return false; 96 | ret = fd = socket(AF_UNIX, SOCK_STREAM, 0); 97 | if (ret < 0) 98 | return false; 99 | ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); 100 | if (ret < 0 && errno == ECONNREFUSED) { /* If the process is gone, we try to clean up the socket. */ 101 | close(fd); 102 | unlink(addr.sun_path); 103 | return false; 104 | } 105 | close(fd); 106 | return true; 107 | } 108 | 109 | static FILE *wg_user_file(struct network *net) 110 | { 111 | struct stat sbuf; 112 | struct sockaddr_un addr = { .sun_family = AF_UNIX }; 113 | int fd = -1, ret; 114 | FILE *f = NULL; 115 | 116 | errno = EINVAL; 117 | ret = snprintf(addr.sun_path, sizeof(addr.sun_path), SOCK_PATH "%s" SOCK_SUFFIX, network_name(net)); 118 | if (ret < 0) 119 | goto out; 120 | ret = stat(addr.sun_path, &sbuf); 121 | if (ret < 0) 122 | goto out; 123 | errno = EBADF; 124 | if (!S_ISSOCK(sbuf.st_mode)) 125 | goto out; 126 | 127 | ret = fd = socket(AF_UNIX, SOCK_STREAM, 0); 128 | if (ret < 0) 129 | goto out; 130 | 131 | ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); 132 | if (ret < 0) { 133 | if (errno == ECONNREFUSED) /* If the process is gone, we try to clean up the socket. */ 134 | unlink(addr.sun_path); 135 | goto out; 136 | } 137 | f = fdopen(fd, "r+"); 138 | if (f) 139 | errno = 0; 140 | out: 141 | ret = -errno; 142 | if (ret) { 143 | if (fd >= 0) 144 | close(fd); 145 | errno = -ret; 146 | return NULL; 147 | } 148 | return f; 149 | } 150 | 151 | static void wg_req_set(struct wg_req *req, const char *key, const char *value) 152 | { 153 | fprintf(req->f, "%s=%s\n", key, value); 154 | } 155 | 156 | static void wg_req_set_int(struct wg_req *req, const char *key, int value) 157 | { 158 | fprintf(req->f, "%s=%d\n", key, value); 159 | } 160 | 161 | #define wg_req_printf(req, name, format, ...) fprintf((req)->f, "%s=" format "\n", name, ##__VA_ARGS__) 162 | 163 | static int wg_req_init(struct wg_req *req, struct network *net, bool set) 164 | { 165 | memset(req, 0, sizeof(*req)); 166 | req->ret = -1; 167 | req->f = wg_user_file(net); 168 | if (!req->f) 169 | return -1; 170 | 171 | wg_req_set(req, set ? "set" : "get", "1"); 172 | 173 | return 0; 174 | } 175 | 176 | static bool wg_req_fetch(struct wg_req *req) 177 | { 178 | int len; 179 | 180 | if (!req->buf) { 181 | fprintf(req->f, "\n"); 182 | fflush(req->f); 183 | } 184 | 185 | if (getline(&req->buf, &req->buf_len, req->f) <= 0) 186 | return false; 187 | 188 | req->key = req->buf; 189 | len = strlen(req->key); 190 | if (len == 1 && req->key[0] == '\n') 191 | return false; 192 | 193 | req->value = strchr(req->key, '='); 194 | if (!req->value || !len || req->key[len - 1] != '\n') 195 | return false; 196 | 197 | *(req->value++) = req->key[--len] = 0; 198 | if (!strcmp(req->key, "errno")) 199 | req->ret = atoi(req->value); 200 | 201 | return true; 202 | } 203 | 204 | static void wg_req_complete(struct wg_req *req) 205 | { 206 | while (wg_req_fetch(req)); 207 | } 208 | 209 | static int wg_req_done(struct wg_req *req) 210 | { 211 | if (!req->buf) 212 | wg_req_complete(req); 213 | 214 | if (req->f) 215 | fclose(req->f); 216 | free(req->buf); 217 | 218 | return -req->ret; 219 | } 220 | 221 | static int 222 | wg_user_test(struct network *net) 223 | { 224 | struct wg_req req; 225 | 226 | if (wg_req_init(&req, net, false)) 227 | return -1; 228 | 229 | return wg_req_done(&req); 230 | } 231 | 232 | static int 233 | wg_network_reset(struct network *net, uint8_t *key) 234 | { 235 | struct wg_req req; 236 | char key_str[WG_KEY_LEN_HEX]; 237 | 238 | if (wg_req_init(&req, net, true)) 239 | return -1; 240 | 241 | wg_req_set(&req, "replace_peers", "true"); 242 | 243 | key_to_hex(key_str, key); 244 | wg_req_set(&req, "private_key", key_str); 245 | 246 | return wg_req_done(&req); 247 | } 248 | 249 | static int 250 | wg_user_init(struct network *net) 251 | { 252 | int err; 253 | 254 | err = wg_user_test(net); 255 | if (err) 256 | return err; 257 | 258 | return wg_network_reset(net, net->config.key); 259 | } 260 | 261 | static void 262 | wg_user_cleanup(struct network *net) 263 | { 264 | uint8_t key[WG_KEY_LEN] = {}; 265 | 266 | wg_network_reset(net, key); 267 | } 268 | 269 | static int 270 | wg_user_init_local(struct network *net, struct network_peer *peer) 271 | { 272 | struct wg_req req; 273 | 274 | if (wg_req_init(&req, net, true)) 275 | return -1; 276 | 277 | wg_req_set_int(&req, "listen_port", peer ? peer->port : 0); 278 | 279 | return wg_req_done(&req); 280 | } 281 | 282 | static void 283 | wg_user_peer_req_add_allowed_ip(struct wg_req *req, struct network_peer *peer) 284 | { 285 | char addr[INET6_ADDRSTRLEN]; 286 | struct blob_attr *cur; 287 | int rem; 288 | 289 | inet_ntop(AF_INET6, &peer->local_addr.in6, addr, sizeof(addr)); 290 | wg_req_printf(req, "allowed_ip", "%s/128", addr); 291 | 292 | blobmsg_for_each_attr(cur, peer->ipaddr, rem) { 293 | const char *str = blobmsg_get_string(cur); 294 | struct in6_addr in6; 295 | int af, mask; 296 | 297 | if (strchr(str, ':')) { 298 | af = AF_INET6; 299 | mask = 128; 300 | } else { 301 | af = AF_INET; 302 | mask = 32; 303 | } 304 | 305 | if (inet_pton(af, str, &in6) != 1) 306 | continue; 307 | 308 | wg_req_printf(req, "allowed_ip", "%s/%d", str, mask); 309 | } 310 | 311 | blobmsg_for_each_attr(cur, peer->subnet, rem) { 312 | const char *str = blobmsg_get_string(cur); 313 | char buf[INET6_ADDRSTRLEN]; 314 | union network_addr addr; 315 | int mask; 316 | int af; 317 | 318 | af = strchr(str, ':') ? AF_INET6 : AF_INET; 319 | if (network_get_subnet(af, &addr, &mask, str)) 320 | continue; 321 | 322 | inet_ntop(af, &addr, buf, sizeof(buf)); 323 | wg_req_printf(req, "allowed_ip", "%s/%d", buf, mask); 324 | } 325 | } 326 | 327 | static int 328 | wg_user_peer_update(struct network *net, struct network_peer *peer, enum wg_update_cmd cmd) 329 | { 330 | struct network_host *host; 331 | struct wg_req req; 332 | char key[WG_KEY_LEN_HEX]; 333 | 334 | if (wg_req_init(&req, net, true)) 335 | return -1; 336 | 337 | key_to_hex(key, peer->key); 338 | wg_req_set(&req, "public_key", key); 339 | 340 | if (cmd == WG_PEER_DELETE) { 341 | wg_req_set(&req, "remove", "true"); 342 | goto out; 343 | } 344 | 345 | wg_req_set(&req, "replace_allowed_ips", "true"); 346 | wg_user_peer_req_add_allowed_ip(&req, peer); 347 | for_each_routed_host(host, net, peer) 348 | wg_user_peer_req_add_allowed_ip(&req, &host->peer); 349 | 350 | out: 351 | return wg_req_done(&req); 352 | } 353 | 354 | static int 355 | wg_user_peer_refresh(struct network *net) 356 | { 357 | struct network_peer *peer = NULL; 358 | struct wg_req req; 359 | uint8_t key[WG_KEY_LEN]; 360 | time_t now = time(NULL); 361 | 362 | if (wg_req_init(&req, net, false)) 363 | return -1; 364 | 365 | while (wg_req_fetch(&req)) { 366 | if (!strcmp(req.key, "public_key")) { 367 | if (peer) 368 | wg_peer_update_done(net, peer); 369 | if (key_from_hex(key, req.value)) 370 | peer = wg_peer_update_start(net, key); 371 | else 372 | peer = NULL; 373 | continue; 374 | } 375 | 376 | if (!peer) 377 | continue; 378 | 379 | if (!strcmp(req.key, "last_handshake_time_sec")) { 380 | uint64_t sec = strtoull(req.value, NULL, 0); 381 | 382 | wg_peer_set_last_handshake(net, peer, now, sec); 383 | continue; 384 | } 385 | 386 | if (!strcmp(req.key, "rx_bytes")) { 387 | uint64_t bytes = strtoull(req.value, NULL, 0); 388 | 389 | wg_peer_set_rx_bytes(net, peer, bytes); 390 | continue; 391 | } 392 | 393 | if (!strcmp(req.key, "tx_bytes")) { 394 | uint64_t bytes = strtoull(req.value, NULL, 0); 395 | 396 | wg_peer_set_tx_bytes(net, peer, bytes); 397 | continue; 398 | } 399 | 400 | if (!strcmp(req.key, "endpoint")) { 401 | struct addrinfo *resolved; 402 | struct addrinfo hints = { 403 | .ai_family = AF_UNSPEC, 404 | .ai_socktype = SOCK_DGRAM, 405 | .ai_protocol = IPPROTO_UDP, 406 | }; 407 | char *port; 408 | 409 | if (!strlen(req.value)) 410 | continue; 411 | 412 | if (req.value[0] == '[') { 413 | req.value++; 414 | port = strchr(req.value, ']'); 415 | if (!port) 416 | continue; 417 | 418 | *port++ = 0; 419 | if (*port++ != ':') 420 | continue; 421 | } else { 422 | port = strchr(req.value, ':'); 423 | if (!port) 424 | continue; 425 | 426 | *port++ = 0; 427 | } 428 | 429 | if (!*port) 430 | continue; 431 | 432 | if (getaddrinfo(req.value, port, &hints, &resolved) != 0) 433 | continue; 434 | 435 | if ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) || 436 | (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6))) 437 | wg_peer_set_endpoint(net, peer, resolved->ai_addr, resolved->ai_addrlen); 438 | 439 | freeaddrinfo(resolved); 440 | continue; 441 | } 442 | } 443 | 444 | if (peer) 445 | wg_peer_update_done(net, peer); 446 | 447 | return wg_req_done(&req); 448 | } 449 | 450 | static int 451 | wg_user_peer_connect(struct network *net, struct network_peer *peer, 452 | union network_endpoint *ep) 453 | { 454 | struct wg_req req; 455 | char addr[INET6_ADDRSTRLEN]; 456 | char key[WG_KEY_LEN_HEX]; 457 | const void *ip; 458 | int port; 459 | 460 | if (wg_req_init(&req, net, true)) 461 | return -1; 462 | 463 | key_to_hex(key, peer->key); 464 | wg_req_set(&req, "public_key", key); 465 | 466 | if (ep->in.sin_family == AF_INET6) 467 | ip = &ep->in6.sin6_addr; 468 | else 469 | ip = &ep->in.sin_addr; 470 | 471 | inet_ntop(ep->in.sin_family, ip, addr, sizeof(addr)); 472 | port = ntohs(ep->in.sin_port); 473 | 474 | if (ep->in.sin_family == AF_INET6) 475 | wg_req_printf(&req, "endpoint", "[%s]:%d", addr, port); 476 | else 477 | wg_req_printf(&req, "endpoint", "%s:%d", addr, port); 478 | 479 | if (net->net_config.keepalive) { 480 | wg_req_set_int(&req, "persistent_keepalive_interval", 0); 481 | wg_req_set_int(&req, "persistent_keepalive_interval", 482 | net->net_config.keepalive); 483 | } 484 | 485 | return wg_req_done(&req); 486 | } 487 | 488 | const struct wg_ops wg_user_ops = { 489 | .name = "user", 490 | .check = wg_user_check, 491 | .init = wg_user_init, 492 | .cleanup = wg_user_cleanup, 493 | .init_local = wg_user_init_local, 494 | .peer_update = wg_user_peer_update, 495 | .peer_refresh = wg_user_peer_refresh, 496 | .peer_connect = wg_user_peer_connect, 497 | }; 498 | -------------------------------------------------------------------------------- /host.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (C) 2022 Felix Fietkau 4 | */ 5 | #include 6 | #include 7 | #include "unetd.h" 8 | 9 | static LIST_HEAD(old_hosts); 10 | static struct blob_buf b; 11 | 12 | static int avl_key_cmp(const void *k1, const void *k2, void *ptr) 13 | { 14 | return memcmp(k1, k2, CURVE25519_KEY_SIZE); 15 | } 16 | 17 | static bool 18 | network_peer_equal(struct network_peer *p1, struct network_peer *p2) 19 | { 20 | return !memcmp(&p1->local_addr, &p2->local_addr, sizeof(p1->local_addr)) && 21 | blob_attr_equal(p1->ipaddr, p2->ipaddr) && 22 | blob_attr_equal(p1->subnet, p2->subnet) && 23 | p1->port == p2->port; 24 | } 25 | 26 | static void 27 | network_peer_update(struct vlist_tree *tree, 28 | struct vlist_node *node_new, 29 | struct vlist_node *node_old) 30 | { 31 | struct network *net = container_of(tree, struct network, peers); 32 | struct network_peer *h_new = container_of_safe(node_new, struct network_peer, node); 33 | struct network_peer *h_old = container_of_safe(node_old, struct network_peer, node); 34 | int ret; 35 | 36 | if (h_new && h_old) { 37 | memcpy(&h_new->state, &h_old->state, sizeof(h_new->state)); 38 | 39 | if (network_peer_equal(h_new, h_old)) 40 | return; 41 | } 42 | 43 | if ((h_new ? h_new : h_old)->indirect) 44 | return; 45 | 46 | if (h_new) 47 | ret = wg_peer_update(net, h_new, h_old ? WG_PEER_UPDATE : WG_PEER_CREATE); 48 | else 49 | ret = wg_peer_update(net, h_old, WG_PEER_DELETE); 50 | 51 | if (ret) 52 | fprintf(stderr, "Failed to %s peer on network %s: %s\n", 53 | h_new ? "update" : "delete", network_name(net), 54 | strerror(-ret)); 55 | } 56 | 57 | static struct network_group * 58 | network_group_get(struct network *net, const char *name) 59 | { 60 | struct network_group *group; 61 | char *name_buf; 62 | 63 | group = avl_find_element(&net->groups, name, group, node); 64 | if (group) 65 | return group; 66 | 67 | group = calloc_a(sizeof(*group), &name_buf, strlen(name) + 1); 68 | group->node.key = strcpy(name_buf, name); 69 | avl_insert(&net->groups, &group->node); 70 | 71 | return group; 72 | } 73 | 74 | static void 75 | network_host_add_group(struct network *net, struct network_host *host, 76 | const char *name) 77 | { 78 | struct network_group *group; 79 | int i; 80 | 81 | group = network_group_get(net, name); 82 | for (i = 0; i < group->n_members; i++) 83 | if (group->members[i] == host) 84 | return; 85 | 86 | group->n_members++; 87 | group->members = realloc(group->members, group->n_members * sizeof(*group->members)); 88 | group->members[group->n_members - 1] = host; 89 | } 90 | 91 | enum { 92 | NETWORK_HOST_KEY, 93 | NETWORK_HOST_GROUPS, 94 | NETWORK_HOST_IPADDR, 95 | NETWORK_HOST_SUBNET, 96 | NETWORK_HOST_PORT, 97 | NETWORK_HOST_PEX_PORT, 98 | NETWORK_HOST_ENDPOINT, 99 | NETWORK_HOST_GATEWAY, 100 | NETWORK_HOST_META, 101 | __NETWORK_HOST_MAX 102 | }; 103 | 104 | static const struct blobmsg_policy host_policy[__NETWORK_HOST_MAX] = { 105 | [NETWORK_HOST_KEY] = { "key", BLOBMSG_TYPE_STRING }, 106 | [NETWORK_HOST_GROUPS] = { "groups", BLOBMSG_TYPE_ARRAY }, 107 | [NETWORK_HOST_IPADDR] = { "ipaddr", BLOBMSG_TYPE_ARRAY }, 108 | [NETWORK_HOST_SUBNET] = { "subnet", BLOBMSG_TYPE_ARRAY }, 109 | [NETWORK_HOST_PORT] = { "port", BLOBMSG_TYPE_INT32 }, 110 | [NETWORK_HOST_PEX_PORT] = { "peer-exchange-port", BLOBMSG_TYPE_INT32 }, 111 | [NETWORK_HOST_ENDPOINT] = { "endpoint", BLOBMSG_TYPE_STRING }, 112 | [NETWORK_HOST_GATEWAY] = { "gateway", BLOBMSG_TYPE_STRING }, 113 | [NETWORK_HOST_META] = { "meta", BLOBMSG_TYPE_TABLE }, 114 | }; 115 | 116 | static void 117 | network_host_create(struct network *net, struct blob_attr *attr, bool dynamic) 118 | { 119 | struct blob_attr *tb[__NETWORK_HOST_MAX]; 120 | struct blob_attr *cur, *ipaddr, *subnet, *meta; 121 | uint8_t key[CURVE25519_KEY_SIZE]; 122 | struct network_host *host = NULL; 123 | struct network_peer *peer; 124 | int ipaddr_len, subnet_len, meta_len; 125 | const char *endpoint, *gateway; 126 | char *endpoint_buf, *gateway_buf; 127 | int rem; 128 | 129 | blobmsg_parse(host_policy, __NETWORK_HOST_MAX, tb, blobmsg_data(attr), blobmsg_len(attr)); 130 | 131 | if (!tb[NETWORK_HOST_KEY]) 132 | return; 133 | 134 | ipaddr_len = tb[NETWORK_HOST_IPADDR] ? blob_pad_len(tb[NETWORK_HOST_IPADDR]) : 0; 135 | if (ipaddr_len && 136 | blobmsg_check_array(tb[NETWORK_HOST_IPADDR], BLOBMSG_TYPE_STRING) < 0) 137 | ipaddr_len = 0; 138 | 139 | subnet_len = tb[NETWORK_HOST_SUBNET] ? blob_pad_len(tb[NETWORK_HOST_SUBNET]) : 0; 140 | if (subnet_len && 141 | blobmsg_check_array(tb[NETWORK_HOST_SUBNET], BLOBMSG_TYPE_STRING) < 0) 142 | subnet_len = 0; 143 | 144 | meta_len = tb[NETWORK_HOST_META] ? blob_pad_len(tb[NETWORK_HOST_META]) : 0; 145 | if (meta_len && 146 | blobmsg_check_array(tb[NETWORK_HOST_META], BLOBMSG_TYPE_STRING) < 0) 147 | meta_len = 0; 148 | 149 | if ((cur = tb[NETWORK_HOST_ENDPOINT]) != NULL) 150 | endpoint = blobmsg_get_string(cur); 151 | else 152 | endpoint = NULL; 153 | 154 | if (!dynamic && (cur = tb[NETWORK_HOST_GATEWAY]) != NULL) 155 | gateway = blobmsg_get_string(cur); 156 | else 157 | gateway = NULL; 158 | 159 | if (b64_decode(blobmsg_get_string(tb[NETWORK_HOST_KEY]), key, 160 | sizeof(key)) != sizeof(key)) 161 | return; 162 | 163 | if (dynamic) { 164 | struct network_dynamic_peer *dyn_peer; 165 | 166 | /* don't override/alter hosts configured via network data */ 167 | peer = vlist_find(&net->peers, key, peer, node); 168 | if (peer && !peer->dynamic && 169 | peer->node.version == net->peers.version) 170 | return; 171 | 172 | dyn_peer = calloc_a(sizeof(*dyn_peer), 173 | &ipaddr, ipaddr_len, 174 | &subnet, subnet_len, 175 | &endpoint_buf, endpoint ? strlen(endpoint) + 1 : 0); 176 | list_add_tail(&dyn_peer->list, &net->dynamic_peers); 177 | peer = &dyn_peer->peer; 178 | } else { 179 | const char *name; 180 | char *name_buf; 181 | 182 | name = blobmsg_name(attr); 183 | host = avl_find_element(&net->hosts, name, host, node); 184 | if (host) 185 | return; 186 | 187 | host = calloc_a(sizeof(*host), 188 | &name_buf, strlen(name) + 1, 189 | &ipaddr, ipaddr_len, 190 | &subnet, subnet_len, 191 | &meta, meta_len, 192 | &endpoint_buf, endpoint ? strlen(endpoint) + 1 : 0, 193 | &gateway_buf, gateway ? strlen(gateway) + 1 : 0); 194 | host->node.key = strcpy(name_buf, name); 195 | peer = &host->peer; 196 | } 197 | 198 | peer->dynamic = dynamic; 199 | if ((cur = tb[NETWORK_HOST_IPADDR]) != NULL && ipaddr_len) 200 | peer->ipaddr = memcpy(ipaddr, cur, ipaddr_len); 201 | if ((cur = tb[NETWORK_HOST_SUBNET]) != NULL && subnet_len) 202 | peer->subnet = memcpy(subnet, cur, subnet_len); 203 | if ((cur = tb[NETWORK_HOST_PORT]) != NULL) 204 | peer->port = blobmsg_get_u32(cur); 205 | else 206 | peer->port = net->net_config.port; 207 | if ((cur = tb[NETWORK_HOST_PEX_PORT]) != NULL) 208 | peer->pex_port = blobmsg_get_u32(cur); 209 | else 210 | peer->pex_port = net->net_config.pex_port; 211 | if (endpoint) 212 | peer->endpoint = strcpy(endpoint_buf, endpoint); 213 | if ((cur = tb[NETWORK_HOST_META]) != NULL && meta_len) 214 | peer->meta = memcpy(meta, cur, meta_len); 215 | memcpy(peer->key, key, sizeof(key)); 216 | 217 | memcpy(&peer->local_addr.network_id, 218 | &net->net_config.addr.network_id, 219 | sizeof(peer->local_addr.network_id)); 220 | network_fill_host_addr(&peer->local_addr, peer->key); 221 | 222 | if (!host) 223 | return; 224 | 225 | if (gateway) 226 | host->gateway = strcpy(gateway_buf, gateway); 227 | 228 | blobmsg_for_each_attr(cur, tb[NETWORK_HOST_GROUPS], rem) { 229 | if (!blobmsg_check_attr(cur, false) || 230 | blobmsg_type(cur) != BLOBMSG_TYPE_STRING) 231 | continue; 232 | 233 | network_host_add_group(net, host, blobmsg_get_string(cur)); 234 | } 235 | 236 | avl_insert(&net->hosts, &host->node); 237 | if (!memcmp(peer->key, net->config.pubkey, sizeof(key))) { 238 | if (!net->prev_local_host || 239 | !network_peer_equal(&net->prev_local_host->peer, &host->peer)) 240 | net->net_config.local_host_changed = true; 241 | 242 | net->net_config.local_host = host; 243 | } 244 | } 245 | 246 | static void 247 | network_hosts_load_dynamic_file(struct network *net, const char *file) 248 | { 249 | struct blob_attr *cur; 250 | int rem; 251 | 252 | blob_buf_init(&b, 0); 253 | 254 | if (!blobmsg_add_json_from_file(&b, file)) 255 | return; 256 | 257 | blob_for_each_attr(cur, b.head, rem) 258 | network_host_create(net, cur, true); 259 | } 260 | 261 | static void 262 | network_hosts_load_dynamic_peers(struct network *net) 263 | { 264 | struct network_dynamic_peer *dyn; 265 | struct blob_attr *cur; 266 | int rem; 267 | 268 | if (!net->config.peer_data) 269 | return; 270 | 271 | blobmsg_for_each_attr(cur, net->config.peer_data, rem) 272 | network_hosts_load_dynamic_file(net, blobmsg_get_string(cur)); 273 | 274 | blob_buf_free(&b); 275 | 276 | list_for_each_entry(dyn, &net->dynamic_peers, list) 277 | vlist_add(&net->peers, &dyn->peer.node, &dyn->peer.key); 278 | } 279 | 280 | static void 281 | network_host_free_dynamic_peers(struct list_head *list) 282 | { 283 | struct network_dynamic_peer *dyn, *dyn_tmp; 284 | 285 | list_for_each_entry_safe(dyn, dyn_tmp, list, list) { 286 | list_del(&dyn->list); 287 | free(dyn); 288 | } 289 | } 290 | 291 | void network_hosts_reload_dynamic_peers(struct network *net) 292 | { 293 | struct network_peer *peer; 294 | LIST_HEAD(old_entries); 295 | 296 | if (!net->config.peer_data) 297 | return; 298 | 299 | list_splice_init(&net->dynamic_peers, &old_entries); 300 | 301 | vlist_for_each_element(&net->peers, peer, node) 302 | if (peer->dynamic) 303 | peer->node.version = net->peers.version - 1; 304 | 305 | network_hosts_load_dynamic_peers(net); 306 | 307 | vlist_flush(&net->peers); 308 | 309 | network_host_free_dynamic_peers(&old_entries); 310 | } 311 | 312 | void network_hosts_update_start(struct network *net) 313 | { 314 | struct network_host *host, *htmp; 315 | struct network_group *group, *gtmp; 316 | 317 | avl_remove_all_elements(&net->hosts, host, node, htmp) 318 | list_add_tail(&host->node.list, &old_hosts); 319 | 320 | avl_remove_all_elements(&net->groups, group, node, gtmp) { 321 | free(group->members); 322 | free(group); 323 | } 324 | 325 | vlist_update(&net->peers); 326 | } 327 | 328 | static void 329 | __network_hosts_update_done(struct network *net, bool free_net) 330 | { 331 | struct network_host *local, *host, *tmp; 332 | LIST_HEAD(old_dynamic); 333 | const char *local_name; 334 | 335 | list_splice_init(&net->dynamic_peers, &old_dynamic); 336 | if (free_net) 337 | goto out; 338 | 339 | local = net->net_config.local_host; 340 | if (!local) 341 | goto out; 342 | 343 | local_name = network_host_name(local); 344 | 345 | if (net->net_config.local_host_changed) 346 | wg_init_local(net, &local->peer); 347 | 348 | avl_for_each_element(&net->hosts, host, node) { 349 | if (host == local) 350 | continue; 351 | host->peer.indirect = false; 352 | if (host->gateway && strcmp(host->gateway, local_name) != 0) 353 | host->peer.indirect = true; 354 | if (local->gateway && strcmp(local->gateway, network_host_name(host)) != 0) 355 | host->peer.indirect = true; 356 | vlist_add(&net->peers, &host->peer.node, host->peer.key); 357 | } 358 | 359 | network_hosts_load_dynamic_peers(net); 360 | 361 | out: 362 | vlist_flush(&net->peers); 363 | 364 | network_host_free_dynamic_peers(&old_dynamic); 365 | 366 | list_for_each_entry_safe(host, tmp, &old_hosts, node.list) { 367 | list_del(&host->node.list); 368 | free(host); 369 | } 370 | } 371 | 372 | void network_hosts_update_done(struct network *net) 373 | { 374 | return __network_hosts_update_done(net, false); 375 | } 376 | 377 | static union network_endpoint * 378 | network_peer_next_endpoint(struct network_peer *peer) 379 | { 380 | union network_endpoint *ep; 381 | int i; 382 | 383 | for (i = 0; i < __ENDPOINT_TYPE_MAX; i++) { 384 | int cur = peer->state.next_endpoint_idx; 385 | 386 | if (++peer->state.next_endpoint_idx == __ENDPOINT_TYPE_MAX) 387 | peer->state.next_endpoint_idx = 0; 388 | 389 | ep = &peer->state.next_endpoint[cur]; 390 | if (cur == ENDPOINT_TYPE_STATIC && 391 | (!peer->endpoint || 392 | network_get_endpoint(ep, AF_UNSPEC, peer->endpoint, peer->port, 393 | peer->state.connect_attempt++))) 394 | continue; 395 | 396 | if (!ep->sa.sa_family) 397 | continue; 398 | 399 | return ep; 400 | } 401 | 402 | return NULL; 403 | } 404 | 405 | 406 | static void 407 | network_hosts_connect_cb(struct uloop_timeout *t) 408 | { 409 | struct network *net = container_of(t, struct network, connect_timer); 410 | struct network_host *host; 411 | struct network_peer *peer; 412 | union network_endpoint *ep; 413 | 414 | avl_for_each_element(&net->hosts, host, node) 415 | host->peer.state.num_net_queries = 0; 416 | net->num_net_queries = 0; 417 | 418 | if (!net->net_config.keepalive || !net->net_config.local_host) 419 | return; 420 | 421 | wg_peer_refresh(net); 422 | 423 | vlist_for_each_element(&net->peers, peer, node) { 424 | if (peer->state.connected || peer->indirect) 425 | continue; 426 | 427 | ep = network_peer_next_endpoint(peer); 428 | if (!ep) 429 | continue; 430 | 431 | if (memcmp(ep, &peer->state.endpoint, sizeof(*ep)) != 0 && 432 | !network_skip_endpoint_route(net, ep)) 433 | unetd_ubus_netifd_add_route(net, ep); 434 | 435 | wg_peer_connect(net, peer, ep); 436 | } 437 | 438 | network_pex_event(net, NULL, PEX_EV_QUERY); 439 | 440 | uloop_timeout_set(t, 1000); 441 | } 442 | 443 | void network_hosts_add(struct network *net, struct blob_attr *hosts) 444 | { 445 | struct blob_attr *cur; 446 | int rem; 447 | 448 | blobmsg_for_each_attr(cur, hosts, rem) 449 | network_host_create(net, cur, false); 450 | } 451 | 452 | void network_hosts_init(struct network *net) 453 | { 454 | INIT_LIST_HEAD(&net->dynamic_peers); 455 | avl_init(&net->hosts, avl_strcmp, false, NULL); 456 | vlist_init(&net->peers, avl_key_cmp, network_peer_update); 457 | avl_init(&net->groups, avl_strcmp, false, NULL); 458 | net->connect_timer.cb = network_hosts_connect_cb; 459 | } 460 | 461 | void network_hosts_free(struct network *net) 462 | { 463 | uloop_timeout_cancel(&net->connect_timer); 464 | network_hosts_update_start(net); 465 | __network_hosts_update_done(net, true); 466 | } 467 | --------------------------------------------------------------------------------