diff --git a/.clang-format b/.clang-format index 3f5427ffb5..3c029071f0 100644 --- a/.clang-format +++ b/.clang-format @@ -50,7 +50,7 @@ NamespaceIndentation: All # shorties AllowShortBlocksOnASingleLine: 'false' AllowShortCaseLabelsOnASingleLine: 'false' -AllowShortFunctionsOnASingleLine: Inline +AllowShortFunctionsOnASingleLine: All AllowShortIfStatementsOnASingleLine: 'false' AllowShortLoopsOnASingleLine: 'false' diff --git a/CMakeLists.txt b/CMakeLists.txt index 5bb6f298f6..5ee1217f63 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -301,7 +301,7 @@ if(WITH_HIVE) add_definitions(-DLOKINET_HIVE) endif() -add_subdirectory(crypto) +#add_subdirectory(crypto) add_subdirectory(llarp) if(BUILD_DAEMON) add_subdirectory(daemon) diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt deleted file mode 100644 index b39422f28b..0000000000 --- a/crypto/CMakeLists.txt +++ /dev/null @@ -1,66 +0,0 @@ - -add_library(lokinet-libntrup - STATIC - libntrup/src/ntru.cpp - libntrup/src/ref/randomsmall.c - libntrup/src/ref/swap.c - libntrup/src/ref/rq_round3.c - libntrup/src/ref/rq_recip3.c - libntrup/src/ref/small.c - libntrup/src/ref/rq_mult.c - libntrup/src/ref/randomweightw.c - libntrup/src/ref/random32.c - libntrup/src/ref/dec.c - libntrup/src/ref/r3_mult.c - libntrup/src/ref/r3_recip.c - libntrup/src/ref/keypair.c - libntrup/src/ref/rq_rounded.c - libntrup/src/ref/enc.c - libntrup/src/ref/int32_sort.c - libntrup/src/ref/rq.c -) - -target_include_directories(lokinet-libntrup PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/libntrup/include) - -# The avx implementation uses runtime CPU feature detection to enable itself, so we *always* want to -# compile it with avx2/fma support when supported by the compiler even if we aren't compiling with -# general AVX2 enabled. -set(NTRU_AVX_SRC - libntrup/src/avx/randomsmall.c - libntrup/src/avx/weight.c - libntrup/src/avx/swap.c - libntrup/src/avx/rq_round3.c - libntrup/src/avx/rq_recip3.c - libntrup/src/avx/small.c - libntrup/src/avx/randomweightw.c - libntrup/src/avx/dec.c - libntrup/src/avx/r3_recip.c - libntrup/src/avx/keypair.c - libntrup/src/avx/rq_rounded.c - libntrup/src/avx/mult.c - libntrup/src/avx/enc.c - libntrup/src/avx/int32_sort.c - libntrup/src/avx/rq.c - libntrup/src/avx/rq_mod3.c -) - -include(CheckCXXCompilerFlag) -check_cxx_compiler_flag(-mavx2 COMPILER_SUPPORTS_AVX2) -check_cxx_compiler_flag(-mfma COMPILER_SUPPORTS_FMA) -if(COMPILER_SUPPORTS_AVX2 AND COMPILER_SUPPORTS_FMA AND (NOT ANDROID)) - target_sources(lokinet-libntrup PRIVATE ${NTRU_AVX_SRC}) - set_property(SOURCE ${NTRU_AVX_SRC} APPEND PROPERTY COMPILE_FLAGS "-mavx2 -mfma") - message(STATUS "Building libntrup with runtime AVX2/FMA support") -else() - target_sources(lokinet-libntrup PRIVATE libntrup/src/noavx-stubs.c) - message(STATUS "Not building with libntrup runtime AVX2/FMA support (either this architecture doesn't support them, or your compile doesn't support the -mavx2 -mfma flags") -endif() - -enable_lto(lokinet-libntrup) - -if (WARNINGS_AS_ERRORS) - target_compile_options(lokinet-libntrup PUBLIC -Wall -Wextra -Werror) -endif() - -target_link_libraries(lokinet-libntrup PUBLIC sodium) - diff --git a/crypto/libntrup/include/libntrup/ntru.h b/crypto/libntrup/include/libntrup/ntru.h deleted file mode 100644 index 390f06f0a7..0000000000 --- a/crypto/libntrup/include/libntrup/ntru.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef LIBNTRUP_NTRU_H -#define LIBNTRUP_NTRU_H -#ifdef __cplusplus -extern "C" -{ -#endif - -#include "ntru_api.h" - - void - ntru_init(int force_no_avx2); - - int - crypto_kem_enc(unsigned char *cstr, unsigned char *k, - const unsigned char *pk); - - int - crypto_kem_dec(unsigned char *k, const unsigned char *cstr, - const unsigned char *sk); - - int - crypto_kem_keypair(unsigned char *pk, unsigned char *sk); - -#define crypto_kem_SECRETKEYBYTES 1600 -#define crypto_kem_PUBLICKEYBYTES 1218 -#define crypto_kem_CIPHERTEXTBYTES 1047 - -#define NTRU_SECRETKEYBYTES CRYPTO_SECRETKEYBYTES -#define NTRU_PUBLICKEYBYTES CRYPTO_PUBLICKEYBYTES -#define NTRU_CIPHERTEXTBYTES CRYPTO_CIPHERTEXTBYTES - -#define CRYPTO_BYTES 32 - -#ifdef __cplusplus -} -#endif -#endif diff --git a/crypto/libntrup/include/libntrup/ntru_api.h b/crypto/libntrup/include/libntrup/ntru_api.h deleted file mode 100644 index 9753891d93..0000000000 --- a/crypto/libntrup/include/libntrup/ntru_api.h +++ /dev/null @@ -1,22 +0,0 @@ - -int -crypto_kem_enc_ref(unsigned char *cstr, unsigned char *k, - const unsigned char *pk); - -int -crypto_kem_dec_ref(unsigned char *k, const unsigned char *cstr, - const unsigned char *sk); - -int -crypto_kem_keypair_ref(unsigned char *pk, unsigned char *sk); - -int -crypto_kem_enc_avx2(unsigned char *cstr, unsigned char *k, - const unsigned char *pk); - -int -crypto_kem_dec_avx2(unsigned char *k, const unsigned char *cstr, - const unsigned char *sk); - -int -crypto_kem_keypair_avx2(unsigned char *pk, unsigned char *sk); diff --git a/crypto/libntrup/include/sodium/crypto_int16.h b/crypto/libntrup/include/sodium/crypto_int16.h deleted file mode 100644 index fd4e8f5c01..0000000000 --- a/crypto/libntrup/include/sodium/crypto_int16.h +++ /dev/null @@ -1,2 +0,0 @@ -#include -typedef int16_t crypto_int16; \ No newline at end of file diff --git a/crypto/libntrup/include/sodium/crypto_int32.h b/crypto/libntrup/include/sodium/crypto_int32.h deleted file mode 100644 index 9e5d7ff3ba..0000000000 --- a/crypto/libntrup/include/sodium/crypto_int32.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once -#include -typedef int32_t crypto_int32; diff --git a/crypto/libntrup/include/sodium/crypto_int64.h b/crypto/libntrup/include/sodium/crypto_int64.h deleted file mode 100644 index 0e87ad3a77..0000000000 --- a/crypto/libntrup/include/sodium/crypto_int64.h +++ /dev/null @@ -1,2 +0,0 @@ -#include -typedef int64_t crypto_int64; \ No newline at end of file diff --git a/crypto/libntrup/include/sodium/crypto_int8.h b/crypto/libntrup/include/sodium/crypto_int8.h deleted file mode 100644 index 1ad6096428..0000000000 --- a/crypto/libntrup/include/sodium/crypto_int8.h +++ /dev/null @@ -1,2 +0,0 @@ -#include -typedef int8_t crypto_int8; \ No newline at end of file diff --git a/crypto/libntrup/include/sodium/crypto_kem.h b/crypto/libntrup/include/sodium/crypto_kem.h deleted file mode 100644 index 8618072a3b..0000000000 --- a/crypto/libntrup/include/sodium/crypto_kem.h +++ /dev/null @@ -1 +0,0 @@ -#include \ No newline at end of file diff --git a/crypto/libntrup/include/sodium/crypto_uint16.h b/crypto/libntrup/include/sodium/crypto_uint16.h deleted file mode 100644 index 60eda75c45..0000000000 --- a/crypto/libntrup/include/sodium/crypto_uint16.h +++ /dev/null @@ -1,2 +0,0 @@ -#include -typedef uint16_t crypto_uint16; \ No newline at end of file diff --git a/crypto/libntrup/include/sodium/crypto_uint32.h b/crypto/libntrup/include/sodium/crypto_uint32.h deleted file mode 100644 index 0147a9867e..0000000000 --- a/crypto/libntrup/include/sodium/crypto_uint32.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once -#include -typedef uint32_t crypto_uint32; diff --git a/crypto/libntrup/src/avx/api.h b/crypto/libntrup/src/avx/api.h deleted file mode 100644 index 94d75538bc..0000000000 --- a/crypto/libntrup/src/avx/api.h +++ /dev/null @@ -1,4 +0,0 @@ -#define CRYPTO_SECRETKEYBYTES 1600 -#define CRYPTO_PUBLICKEYBYTES 1218 -#define CRYPTO_CIPHERTEXTBYTES 1047 -#define CRYPTO_BYTES 32 diff --git a/crypto/libntrup/src/avx/dec.c b/crypto/libntrup/src/avx/dec.c deleted file mode 100644 index bc189e9926..0000000000 --- a/crypto/libntrup/src/avx/dec.c +++ /dev/null @@ -1,73 +0,0 @@ -#ifdef KAT -#include -#endif -#include -#include -#include - -#include "params.h" -#include "small.h" -#include "mod3.h" -#include "rq.h" -#include "r3.h" - -#ifndef __AVX2__ -#error "This file requires compilation with AVX2 support" -#endif - -int -crypto_kem_dec_avx2(unsigned char *k, const unsigned char *cstr, - const unsigned char *sk) -{ - small f[768]; - modq h[768]; - small grecip[768]; - modq c[768]; - modq t[768]; - small t3[768]; - small r[768]; - modq hr[768]; - unsigned char rstr[small_encode_len]; - unsigned char hash[64]; - int i; - int result = 0; - - small_decode(f, sk); - small_decode(grecip, sk + small_encode_len); - rq_decode(h, sk + 2 * small_encode_len); - - rq_decoderounded(c, cstr + 32); - - rq_mult(t, c, f); - rq_mod3(t3, t); - - r3_mult(r, t3, grecip); - -#ifdef KAT - { - int j; - printf("decrypt r:"); - for(j = 0; j < p; ++j) - if(r[j] == 1) - printf(" +%d", j); - else if(r[j] == -1) - printf(" -%d", j); - printf("\n"); - } -#endif - - result |= r3_weightw_mask(r); - - rq_mult(hr, h, r); - rq_round3(hr, hr); - for(i = 0; i < p; ++i) - result |= modq_nonzero_mask(hr[i] - c[i]); - - small_encode(rstr, r); - crypto_hash_sha512(hash, rstr, sizeof rstr); - result |= crypto_verify_32(hash, cstr); - - for(i = 0; i < 32; ++i) - k[i] = (hash[32 + i] & ~result); - return result; -} diff --git a/crypto/libntrup/src/avx/enc.c b/crypto/libntrup/src/avx/enc.c deleted file mode 100644 index 85ac4d946b..0000000000 --- a/crypto/libntrup/src/avx/enc.c +++ /dev/null @@ -1,52 +0,0 @@ -#ifdef KAT -#include -#endif - -#include -#include "params.h" -#include "small.h" -#include "rq.h" -#include -#include - -#ifndef __AVX2__ -#error "This file requires compilation with AVX2 support" -#endif - -int -crypto_kem_enc_avx2(unsigned char *cstr, unsigned char *k, - const unsigned char *pk) -{ - small r[768]; - modq h[768]; - modq c[768]; - unsigned char rstr[small_encode_len]; - unsigned char hash[64]; - - small_random_weightw(r); - -#ifdef KAT - { - int i; - printf("encrypt r:"); - for(i = 0; i < p; ++i) - if(r[i] == 1) - printf(" +%d", i); - else if(r[i] == -1) - printf(" -%d", i); - printf("\n"); - } -#endif - - small_encode(rstr, r); - crypto_hash_sha512(hash, rstr, sizeof rstr); - - rq_decode(h, pk); - rq_mult(c, h, r); - - memcpy(k, hash + 32, 32); - memcpy(cstr, hash, 32); - rq_roundencode(cstr + 32, c); - - return 0; -} diff --git a/crypto/libntrup/src/avx/implementors b/crypto/libntrup/src/avx/implementors deleted file mode 100644 index 51ac31ea2e..0000000000 --- a/crypto/libntrup/src/avx/implementors +++ /dev/null @@ -1,5 +0,0 @@ -Alphabetical order: -Daniel J. Bernstein -Chitchanok Chuengsatiansup -Tanja Lange -Christine van Vredendaal diff --git a/crypto/libntrup/src/avx/int32_sort.c b/crypto/libntrup/src/avx/int32_sort.c deleted file mode 100644 index ea836d0b8c..0000000000 --- a/crypto/libntrup/src/avx/int32_sort.c +++ /dev/null @@ -1,466 +0,0 @@ -#ifdef __AVX2__ -#include "int32_sort.h" -#include - -typedef crypto_int32 int32; - -static inline void -minmax(int32 *x, int32 *y) -{ - __asm__("movl (%0),%%eax;movl (%1),%%ebx;cmpl %%ebx,%%eax;mov %%eax,%%edx;cmovg " - "%%ebx,%%eax;cmovg %%edx,%%ebx;movl %%eax,(%0);movl %%ebx,(%1)" - : - : "r"(x), "r"(y) - : "%eax", "%ebx", "%edx"); -} - -/* sort x0,x2; sort x1,x3; ... sort x13, x15 */ -static inline void -minmax02through1315(int32 *x) -{ - __m256i a = _mm256_loadu_si256((__m256i *)x); - __m256i b = _mm256_loadu_si256((__m256i *)(x + 8)); - __m256i c = _mm256_unpacklo_epi64(a, b); /* a01b01a45b45 */ - __m256i d = _mm256_unpackhi_epi64(a, b); /* a23b23a67b67 */ - __m256i g = _mm256_min_epi32(c, d); - __m256i h = _mm256_max_epi32(c, d); - a = _mm256_unpacklo_epi64(g, h); - b = _mm256_unpackhi_epi64(g, h); - _mm256_storeu_si256((__m256i *)x, a); - _mm256_storeu_si256((__m256i *)(x + 8), b); -} - -/* sort x0,x2; sort x1,x3; sort x4,x6; sort x5,x7 */ -static inline void -minmax02134657(int32 *x) -{ - __m256i a = _mm256_loadu_si256((__m256i *)x); - __m256i b = _mm256_shuffle_epi32(a, 0x4e); - __m256i c = _mm256_cmpgt_epi32(a, b); - c = _mm256_shuffle_epi32(c, 0x44); - __m256i abc = c & (a ^ b); - a ^= abc; - _mm256_storeu_si256((__m256i *)x, a); -} - -static void -multiminmax2plus2(int32 *x, int n) -{ - while(n >= 16) - { - minmax02through1315(x); - n -= 16; - x += 16; - } - if(n >= 8) - { - minmax02134657(x); - n -= 8; - x += 8; - } - if(n >= 4) - { - minmax(x, x + 2); - minmax(x + 1, x + 3); - n -= 4; - x += 4; - } - if(n > 0) - { - minmax(x, x + 2); - if(n > 1) - minmax(x + 1, x + 3); - } -} - -static void -multiminmax2plus6(int32 *x, int n) -{ - while(n >= 4) - { - minmax(x, x + 6); - minmax(x + 1, x + 7); - n -= 4; - x += 4; - } - if(n > 0) - { - minmax(x, x + 6); - if(n > 1) - minmax(x + 1, x + 7); - } -} - -static void -multiminmax2plus14(int32 *x, int n) -{ - while(n >= 8) - { - minmax(x, x + 14); - minmax(x + 1, x + 15); - minmax(x + 4, x + 18); - minmax(x + 5, x + 19); - n -= 8; - x += 8; - } - if(n >= 4) - { - minmax(x, x + 14); - minmax(x + 1, x + 15); - n -= 4; - x += 4; - } - if(n > 0) - { - minmax(x, x + 14); - if(n > 1) - minmax(x + 1, x + 15); - } -} - -/* sort x[i],y[i] for i in 0,1,4,5,8,9,12,13 */ -/* all of x0...x15 and y0...y15 must exist; no aliasing */ -static inline void -minmax0145891213(int32 *x, int32 *y) -{ - __m256i a01234567 = _mm256_loadu_si256((__m256i *)x); - __m256i a89101112131415 = _mm256_loadu_si256((__m256i *)(x + 8)); - __m256i b01234567 = _mm256_loadu_si256((__m256i *)y); - __m256i b89101112131415 = _mm256_loadu_si256((__m256i *)(y + 8)); - - __m256i a0189451213 = _mm256_unpacklo_epi64(a01234567, a89101112131415); - __m256i b0189451213 = _mm256_unpacklo_epi64(b01234567, b89101112131415); - __m256i c0189451213 = _mm256_min_epi32(a0189451213, b0189451213); - __m256i d0189451213 = _mm256_max_epi32(a0189451213, b0189451213); - - __m256i c01234567 = _mm256_blend_epi32(a01234567, c0189451213, 0x33); - __m256i d01234567 = _mm256_blend_epi32(b01234567, d0189451213, 0x33); - __m256i c89101112131415 = _mm256_unpackhi_epi64(c0189451213, a89101112131415); - __m256i d89101112131415 = _mm256_unpackhi_epi64(d0189451213, b89101112131415); - - _mm256_storeu_si256((__m256i *)x, c01234567); - _mm256_storeu_si256((__m256i *)(x + 8), c89101112131415); - _mm256_storeu_si256((__m256i *)y, d01234567); - _mm256_storeu_si256((__m256i *)(y + 8), d89101112131415); -} - -/* offset >= 30 */ -static void -multiminmax2plusmore(int32 *x, int n, int offset) -{ - while(n >= 16) - { - minmax0145891213(x, x + offset); - n -= 16; - x += 16; - } - if(n >= 8) - { - minmax(x, x + offset); - minmax(x + 1, x + 1 + offset); - minmax(x + 4, x + 4 + offset); - minmax(x + 5, x + 5 + offset); - n -= 8; - x += 8; - } - if(n >= 4) - { - minmax(x, x + offset); - minmax(x + 1, x + 1 + offset); - n -= 4; - x += 4; - } - if(n > 0) - { - minmax(x, x + offset); - if(n > 1) - minmax(x + 1, x + 1 + offset); - } -} - -/* sort x0,x1; ... sort x14, x15 */ -static inline void -minmax01through1415(int32 *x) -{ - __m256i a = _mm256_loadu_si256((__m256i *)x); - __m256i b = _mm256_loadu_si256((__m256i *)(x + 8)); - __m256i c = _mm256_unpacklo_epi32(a, b); /* ab0ab1ab4ab5 */ - __m256i d = _mm256_unpackhi_epi32(a, b); /* ab2ab3ab6ab7 */ - __m256i e = _mm256_unpacklo_epi32(c, d); /* a02b02a46b46 */ - __m256i f = _mm256_unpackhi_epi32(c, d); /* a13b13a57b57 */ - __m256i g = _mm256_min_epi32(e, f); /* a02b02a46b46 */ - __m256i h = _mm256_max_epi32(e, f); /* a13b13a57b57 */ - a = _mm256_unpacklo_epi32(g, h); - b = _mm256_unpackhi_epi32(g, h); - _mm256_storeu_si256((__m256i *)x, a); - _mm256_storeu_si256((__m256i *)(x + 8), b); -} - -/* sort x0,x1; sort x2,x3; sort x4,x5; sort x6,x7 */ -static inline void -minmax01234567(int32 *x) -{ - __m256i a = _mm256_loadu_si256((__m256i *)x); - __m256i b = _mm256_shuffle_epi32(a, 0xb1); - __m256i c = _mm256_cmpgt_epi32(a, b); - c = _mm256_shuffle_epi32(c, 0xa0); - __m256i abc = c & (a ^ b); - a ^= abc; - _mm256_storeu_si256((__m256i *)x, a); -} - -static void -multiminmax1plus1(int32 *x, int n) -{ - while(n >= 16) - { - minmax01through1415(x); - n -= 16; - x += 16; - } - if(n >= 8) - { - minmax01234567(x); - n -= 8; - x += 8; - } - if(n >= 4) - { - minmax(x, x + 1); - minmax(x + 2, x + 3); - n -= 4; - x += 4; - } - if(n >= 2) - { - minmax(x, x + 1); - n -= 2; - x += 2; - } - if(n > 0) - minmax(x, x + 1); -} - -static void -multiminmax1(int32 *x, int n, int offset) -{ - while(n >= 16) - { - minmax(x, x + offset); - minmax(x + 2, x + 2 + offset); - minmax(x + 4, x + 4 + offset); - minmax(x + 6, x + 6 + offset); - minmax(x + 8, x + 8 + offset); - minmax(x + 10, x + 10 + offset); - minmax(x + 12, x + 12 + offset); - minmax(x + 14, x + 14 + offset); - n -= 16; - x += 16; - } - if(n >= 8) - { - minmax(x, x + offset); - minmax(x + 2, x + 2 + offset); - minmax(x + 4, x + 4 + offset); - minmax(x + 6, x + 6 + offset); - n -= 8; - x += 8; - } - if(n >= 4) - { - minmax(x, x + offset); - minmax(x + 2, x + 2 + offset); - n -= 4; - x += 4; - } - if(n >= 2) - { - minmax(x, x + offset); - n -= 2; - x += 2; - } - if(n > 0) - minmax(x, x + offset); -} - -/* sort x[i],y[i] for i in 0,2,4,6,8,10,12,14 */ -/* all of x0...x15 and y0...y15 must exist; no aliasing */ -static inline void -minmax02468101214(int32 *x, int32 *y) -{ - __m256i a01234567 = _mm256_loadu_si256((__m256i *)x); - __m256i a89101112131415 = _mm256_loadu_si256((__m256i *)(x + 8)); - __m256i b01234567 = _mm256_loadu_si256((__m256i *)y); - __m256i b89101112131415 = _mm256_loadu_si256((__m256i *)(y + 8)); - - __m256i a0819412513 = _mm256_unpacklo_epi32(a01234567, a89101112131415); - __m256i a210311614715 = _mm256_unpackhi_epi32(a01234567, a89101112131415); - __m256i a02810461214 = _mm256_unpacklo_epi32(a0819412513, a210311614715); - __m256i a13911571315 = _mm256_unpackhi_epi32(a0819412513, a210311614715); - - __m256i b0819412513 = _mm256_unpacklo_epi32(b01234567, b89101112131415); - __m256i b210311614715 = _mm256_unpackhi_epi32(b01234567, b89101112131415); - __m256i b02810461214 = _mm256_unpacklo_epi32(b0819412513, b210311614715); - __m256i b13911571315 = _mm256_unpackhi_epi32(b0819412513, b210311614715); - - __m256i c02810461214 = _mm256_min_epi32(a02810461214, b02810461214); - __m256i d02810461214 = _mm256_max_epi32(a02810461214, b02810461214); - - __m256i c01234567 = _mm256_unpacklo_epi32(c02810461214, a13911571315); - __m256i c89101112131415 = _mm256_unpackhi_epi32(c02810461214, a13911571315); - __m256i d01234567 = _mm256_unpacklo_epi32(d02810461214, b13911571315); - __m256i d89101112131415 = _mm256_unpackhi_epi32(d02810461214, b13911571315); - - _mm256_storeu_si256((__m256i *)x, c01234567); - _mm256_storeu_si256((__m256i *)(x + 8), c89101112131415); - _mm256_storeu_si256((__m256i *)y, d01234567); - _mm256_storeu_si256((__m256i *)(y + 8), d89101112131415); -} - -/* assumes offset >= 31 */ -static void -multiminmax1plusmore(int32 *x, int n, int offset) -{ - while(n >= 16) - { - minmax02468101214(x, x + offset); - n -= 16; - x += 16; - } - if(n >= 8) - { - minmax(x, x + offset); - minmax(x + 2, x + 2 + offset); - minmax(x + 4, x + 4 + offset); - minmax(x + 6, x + 6 + offset); - n -= 8; - x += 8; - } - if(n >= 4) - { - minmax(x, x + offset); - minmax(x + 2, x + 2 + offset); - n -= 4; - x += 4; - } - if(n >= 2) - { - minmax(x, x + offset); - n -= 2; - x += 2; - } - if(n > 0) - minmax(x, x + offset); -} - -/* sort x0,y0; sort x1,y1; ...; sort x7,y7 */ -static inline void -minmax8(int32 *x, int32 *y) -{ - __m256i a = _mm256_loadu_si256((__m256i *)x); - __m256i b = _mm256_loadu_si256((__m256i *)y); - _mm256_storeu_si256((__m256i *)x, _mm256_min_epi32(a, b)); - _mm256_storeu_si256((__m256i *)y, _mm256_max_epi32(a, b)); -} - -/* assumes p >= 8; implies offset >= 8 */ -static void -multiminmax_atleast8(int p, int32 *x, int n, int offset) -{ - int i; - while(n >= 2 * p) - { - for(i = 0; i < p; i += 8) - minmax8(x + i, x + i + offset); - n -= 2 * p; - x += 2 * p; - } - for(i = 0; i + 8 <= n; i += 8) - { - if(i & p) - return; - minmax8(x + i, x + i + offset); - } - for(; i < n; ++i) - { - if(i & p) - return; - minmax(x + i, x + i + offset); - } -} - -/* sort x0,y0; sort x1,y1; sort x2,y2; sort x3,y3 */ -static inline void -minmax4(int32 *x, int32 *y) -{ - __m128i a = _mm_loadu_si128((__m128i *)x); - __m128i b = _mm_loadu_si128((__m128i *)y); - _mm_storeu_si128((__m128i *)x, _mm_min_epi32(a, b)); - _mm_storeu_si128((__m128i *)y, _mm_max_epi32(a, b)); -} - -static void -multiminmax4(int32 *x, int n, int offset) -{ - int i; - while(n >= 8) - { - minmax4(x, x + offset); - n -= 8; - x += 8; - } - if(n >= 4) - minmax4(x, x + offset); - else - for(i = 0; i < n; ++i) - minmax(x + i, x + i + offset); -} - -void -int32_sort(int32 *x, int n) -{ - int top, p, q; - - if(n < 2) - return; - top = 1; - while(top < n - top) - top += top; - - for(p = top; p >= 8; p >>= 1) - { - multiminmax_atleast8(p, x, n - p, p); - for(q = top; q > p; q >>= 1) - multiminmax_atleast8(p, x + p, n - q, q - p); - } - if(p >= 4) - { - multiminmax4(x, n - 4, 4); - for(q = top; q > 4; q >>= 1) - multiminmax4(x + 4, n - q, q - 4); - } - if(p >= 2) - { - multiminmax2plus2(x, n - 2); - for(q = top; q >= 32; q >>= 1) - multiminmax2plusmore(x + 2, n - q, q - 2); - if(q >= 16) - multiminmax2plus14(x + 2, n - 16); - if(q >= 8) - multiminmax2plus6(x + 2, n - 8); - if(q >= 4) - multiminmax2plus2(x + 2, n - 4); - } - multiminmax1plus1(x, n - 1); - for(q = top; q >= 32; q >>= 1) - multiminmax1plusmore(x + 1, n - q, q - 1); - if(q >= 16) - multiminmax1(x + 1, n - 16, 15); - if(q >= 8) - multiminmax1(x + 1, n - 8, 7); - if(q >= 4) - multiminmax1(x + 1, n - 4, 3); - if(q >= 2) - multiminmax1plus1(x + 1, n - 2); -} -#endif diff --git a/crypto/libntrup/src/avx/int32_sort.h b/crypto/libntrup/src/avx/int32_sort.h deleted file mode 100644 index ea0cb05e50..0000000000 --- a/crypto/libntrup/src/avx/int32_sort.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef int32_sort_h -#define int32_sort_h - -#include - -#define int32_sort crypto_kem_sntrup4591761_avx_int32_sort -extern void -int32_sort(crypto_int32 *, int); - -#endif diff --git a/crypto/libntrup/src/avx/keypair.c b/crypto/libntrup/src/avx/keypair.c deleted file mode 100644 index 32aed4ac0a..0000000000 --- a/crypto/libntrup/src/avx/keypair.c +++ /dev/null @@ -1,45 +0,0 @@ -#include -#include "modq.h" -#include "params.h" -#include "r3.h" -#include "small.h" -#include "rq.h" -#include - -#if crypto_kem_PUBLICKEYBYTES != rq_encode_len -#error "crypto_kem_PUBLICKEYBYTES must match rq_encode_len" -#endif -#if crypto_kem_SECRETKEYBYTES != rq_encode_len + 2 * small_encode_len -#error \ - "crypto_kem_SECRETKEYBYTES must match rq_encode_len + 2 * small_encode_len" -#endif - -#ifndef __AVX2__ -#error "This file requires compilation with AVX2 support" -#endif - -int -crypto_kem_keypair_avx2(unsigned char *pk, unsigned char *sk) -{ - small g[768]; - small grecip[768]; - small f[768]; - modq f3recip[768]; - modq h[768]; - - do - small_random(g); - while(r3_recip(grecip, g) != 0); - - small_random_weightw(f); - rq_recip3(f3recip, f); - - rq_mult(h, f3recip, g); - - rq_encode(pk, h); - small_encode(sk, f); - small_encode(sk + small_encode_len, grecip); - memcpy(sk + 2 * small_encode_len, pk, rq_encode_len); - - return 0; -} diff --git a/crypto/libntrup/src/avx/mod3.h b/crypto/libntrup/src/avx/mod3.h deleted file mode 100644 index 97ec3b55fa..0000000000 --- a/crypto/libntrup/src/avx/mod3.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef mod3_h -#define mod3_h - -#include "small.h" -#include - -/* -1 if x is nonzero, 0 otherwise */ -static inline int -mod3_nonzero_mask(small x) -{ - return -x * x; -} - -/* input between -100000 and 100000 */ -/* output between -1 and 1 */ -static inline small -mod3_freeze(crypto_int32 a) -{ - a -= 3 * ((10923 * a) >> 15); - a -= 3 * ((89478485 * a + 134217728) >> 28); - return a; -} - -static inline small -mod3_minusproduct(small a, small b, small c) -{ - crypto_int32 A = a; - crypto_int32 B = b; - crypto_int32 C = c; - return mod3_freeze(A - B * C); -} - -static inline small -mod3_plusproduct(small a, small b, small c) -{ - crypto_int32 A = a; - crypto_int32 B = b; - crypto_int32 C = c; - return mod3_freeze(A + B * C); -} - -static inline small -mod3_product(small a, small b) -{ - return a * b; -} - -static inline small -mod3_sum(small a, small b) -{ - crypto_int32 A = a; - crypto_int32 B = b; - return mod3_freeze(A + B); -} - -static inline small -mod3_reciprocal(small a1) -{ - return a1; -} - -static inline small -mod3_quotient(small num, small den) -{ - return mod3_product(num, mod3_reciprocal(den)); -} - -#endif diff --git a/crypto/libntrup/src/avx/modq.h b/crypto/libntrup/src/avx/modq.h deleted file mode 100644 index ff64884c02..0000000000 --- a/crypto/libntrup/src/avx/modq.h +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef modq_h -#define modq_h - -#include -#include -#include - -typedef crypto_int16 modq; - -/* input between -9000000 and 9000000 */ -/* output between -2295 and 2295 */ -static inline modq -modq_freeze(crypto_int32 a) -{ - a -= 4591 * ((228 * a) >> 20); - a -= 4591 * ((58470 * a + 134217728) >> 28); - return a; -} - -static inline modq -modq_minusproduct(modq a, modq b, modq c) -{ - crypto_int32 A = a; - crypto_int32 B = b; - crypto_int32 C = c; - return modq_freeze(A - B * C); -} - -static inline modq -modq_plusproduct(modq a, modq b, modq c) -{ - crypto_int32 A = a; - crypto_int32 B = b; - crypto_int32 C = c; - return modq_freeze(A + B * C); -} - -static inline modq -modq_product(modq a, modq b) -{ - crypto_int32 A = a; - crypto_int32 B = b; - return modq_freeze(A * B); -} - -static inline modq -modq_square(modq a) -{ - crypto_int32 A = a; - return modq_freeze(A * A); -} - -static inline modq -modq_sum(modq a, modq b) -{ - crypto_int32 A = a; - crypto_int32 B = b; - return modq_freeze(A + B); -} - -static inline modq -modq_reciprocal(modq a1) -{ - modq a2 = modq_square(a1); - modq a3 = modq_product(a2, a1); - modq a4 = modq_square(a2); - modq a8 = modq_square(a4); - modq a16 = modq_square(a8); - modq a32 = modq_square(a16); - modq a35 = modq_product(a32, a3); - modq a70 = modq_square(a35); - modq a140 = modq_square(a70); - modq a143 = modq_product(a140, a3); - modq a286 = modq_square(a143); - modq a572 = modq_square(a286); - modq a1144 = modq_square(a572); - modq a1147 = modq_product(a1144, a3); - modq a2294 = modq_square(a1147); - modq a4588 = modq_square(a2294); - modq a4589 = modq_product(a4588, a1); - return a4589; -} - -static inline modq -modq_quotient(modq num, modq den) -{ - return modq_product(num, modq_reciprocal(den)); -} - -/* -1 if x is nonzero, 0 otherwise */ -static inline int -modq_nonzero_mask(modq x) -{ - crypto_int32 r = (crypto_uint16)x; - r = -r; - r >>= 30; - return r; -} - -#endif diff --git a/crypto/libntrup/src/avx/mult.c b/crypto/libntrup/src/avx/mult.c deleted file mode 100644 index 687acb46ac..0000000000 --- a/crypto/libntrup/src/avx/mult.c +++ /dev/null @@ -1,884 +0,0 @@ -#if __AVX2__ -#include -#include -#include "mod3.h" -#include "rq.h" -#include "r3.h" - -// 32-bit hosts: use compiler builtins and let compiler -// perform register allocation and/or spilling to core -// -// Confirmed working 3/10/18 -despair -// Less than 5% performance hit, -// all in register loads/stores to/from core -#ifndef __amd64__ -#define MULSTEP_gcc(j, h0, h1, h2, h3, h4) \ - gj = g[j]; \ - h0 += f0 * gj; \ - _mm256_storeu_ps((float *)&h[i + j], h0); \ - h1 += f1 * gj; \ - h2 += f2 * gj; \ - h3 += f3 * gj; \ - h4 += f4 * gj; \ - h0 = _mm256_loadu_ps((float *)&h[i + j + 5]); \ - h0 += f5 * gj; - -#define MULSTEP MULSTEP_gcc - -#define MULSTEP_noload(j, h0, h1, h2, h3, h4) \ - gj = g[j]; \ - h0 += gj * f0; \ - _mm256_storeu_ps((float *)&h[i + j], h0); \ - h1 += gj * f1; \ - h2 += gj * f2; \ - h3 += gj * f3; \ - h4 += gj * f4; \ - h0 = gj * f5; - -#define MULSTEP_fromzero(j, h0, h1, h2, h3, h4) \ - gj = g[j]; \ - h0 = gj * f0; \ - _mm256_storeu_ps((float *)&h[i + j], h0); \ - h1 = gj * f1; \ - h2 = gj * f2; \ - h3 = gj * f3; \ - h4 = gj * f4; \ - h0 = gj * f5; -#else -// 64-bit hosts: use inline asm as before -#define MULSTEP MULSTEP_asm -#define MULSTEP_asm(j, h0, h1, h2, h3, h4) \ - gj = g[j]; \ - __asm__( \ - "vfmadd231ps %5,%6,%0 \n\t" \ - "vmovups %0,%12 \n\t" \ - "vmovups %13,%0 \n\t" \ - "vfmadd231ps %5,%7,%1 \n\t" \ - "vfmadd231ps %5,%8,%2 \n\t" \ - "vfmadd231ps %5,%9,%3 \n\t" \ - "vfmadd231ps %5,%10,%4 \n\t" \ - "vfmadd231ps %5,%11,%0 \n\t" \ - : "+x"(h0), "+x"(h1), "+x"(h2), "+x"(h3), "+x"(h4) \ - : "x"(gj), "x"(f0), "x"(f1), "x"(f2), "x"(f3), "x"(f4), "x"(f5), \ - "m"(h[i + j]), "m"(h[i + j + 5])); - -#define MULSTEP_noload(j, h0, h1, h2, h3, h4) \ - gj = g[j]; \ - __asm__( \ - "vfmadd231ps %5,%6,%0 \n\t" \ - "vmovups %0,%12 \n\t" \ - "vfmadd231ps %5,%7,%1 \n\t" \ - "vfmadd231ps %5,%8,%2 \n\t" \ - "vfmadd231ps %5,%9,%3 \n\t" \ - "vfmadd231ps %5,%10,%4 \n\t" \ - "vmulps %5,%11,%0 \n\t" \ - : "+x"(h0), "+x"(h1), "+x"(h2), "+x"(h3), "+x"(h4) \ - : "x"(gj), "x"(f0), "x"(f1), "x"(f2), "x"(f3), "x"(f4), "x"(f5), \ - "m"(h[i + j])); - -#define MULSTEP_fromzero(j, h0, h1, h2, h3, h4) \ - gj = g[j]; \ - __asm__( \ - "vmulps %5,%6,%0 \n\t" \ - "vmovups %0,%12 \n\t" \ - "vmulps %5,%7,%1 \n\t" \ - "vmulps %5,%8,%2 \n\t" \ - "vmulps %5,%9,%3 \n\t" \ - "vmulps %5,%10,%4 \n\t" \ - "vmulps %5,%11,%0 \n\t" \ - : "=&x"(h0), "=&x"(h1), "=&x"(h2), "=&x"(h3), "=&x"(h4) \ - : "x"(gj), "x"(f0), "x"(f1), "x"(f2), "x"(f3), "x"(f4), "x"(f5), \ - "m"(h[i + j])); -#endif - -static inline __m128i -_mm_load_cvtepi8_epi16(const long long *x) -{ - __m128i result; - __asm__("vpmovsxbw %1, %0" : "=x"(result) : "m"(*x)); - return result; -} - -#define v0 _mm256_set1_epi32(0) -#define v0_128 _mm_set1_epi32(0) -#define v7 _mm256_set1_epi16(7) -#define v4591_16 _mm256_set1_epi16(4591) -#define v2296_16 _mm256_set1_epi16(2296) - -#define alpha_32 _mm256_set1_epi32(0x4b400000) -#define alpha_32_128 _mm_set1_epi32(0x4b400000) -#define alpha_float _mm256_set1_ps(12582912.0) - -#define v0_float _mm256_set1_ps(0) -#define v1_float _mm256_set1_ps(1) -#define vm1_float _mm256_set1_ps(-1) -#define vm4591_float _mm256_set1_ps(-4591) -#define recip4591_float \ - _mm256_set1_ps(0.00021781746896101067305597908952297974298) - -static inline __m256 -add(__m256 x, __m256 y) -{ - return x + y; -} - -static inline __m256 -fastadd(__m256 x, __m256 y) -{ - return _mm256_fmadd_ps(y, v1_float, x); -} - -static inline __m256 -fastsub(__m256 x, __m256 y) -{ - return _mm256_fmadd_ps(y, vm1_float, x); -} - -static inline __m256 -reduce(__m256 x) -{ - __m256 q = x * recip4591_float; - q = _mm256_round_ps(q, 8); - return _mm256_fmadd_ps(q, vm4591_float, x); -} - -static inline __m256i -squeeze(__m256i x) -{ - __m256i q = _mm256_mulhrs_epi16(x, v7); - q = _mm256_mullo_epi16(q, v4591_16); - return _mm256_sub_epi16(x, q); -} - -static inline __m256i -squeezeadd16(__m256i x, __m256i y) -{ - __m256i q; - x = _mm256_add_epi16(x, y); - q = _mm256_mulhrs_epi16(x, v7); - q = _mm256_mullo_epi16(q, v4591_16); - return _mm256_sub_epi16(x, q); -} - -static inline __m256i -freeze(__m256i x) -{ - __m256i mask, x2296, x4591; - x4591 = _mm256_add_epi16(x, v4591_16); - mask = _mm256_srai_epi16(x, 15); - x = _mm256_blendv_epi8(x, x4591, mask); - x2296 = _mm256_sub_epi16(x, v2296_16); - mask = _mm256_srai_epi16(x2296, 15); - x4591 = _mm256_sub_epi16(x, v4591_16); - x = _mm256_blendv_epi8(x4591, x, mask); - return x; -} - -/* 24*8*float32 f inputs between -10000 and 10000 */ -/* 24*8*float32 g inputs between -32 and 32 */ -/* 48*8*float32 h outputs between -7680000 and 7680000 */ -static void -mult24x8_float(__m256 h[48], const __m256 f[24], const __m256 g[24]) -{ - int i, j; - __m256 f0, f1, f2, f3, f4, f5, gj, h0, h1, h2, h3, h4; - - i = 0; - f0 = f[i]; - f1 = f[i + 1]; - f2 = f[i + 2]; - f3 = f[i + 3]; - f4 = f[i + 4]; - f5 = f[i + 5]; - MULSTEP_fromzero(0, h0, h1, h2, h3, h4); - for(j = 0; j < 20; j += 5) - { - MULSTEP_noload(j + 1, h1, h2, h3, h4, h0); - MULSTEP_noload(j + 2, h2, h3, h4, h0, h1); - MULSTEP_noload(j + 3, h3, h4, h0, h1, h2); - MULSTEP_noload(j + 4, h4, h0, h1, h2, h3); - MULSTEP_noload(j + 5, h0, h1, h2, h3, h4); - } - MULSTEP_noload(j + 1, h1, h2, h3, h4, h0); - MULSTEP_noload(j + 2, h2, h3, h4, h0, h1); - MULSTEP_noload(j + 3, h3, h4, h0, h1, h2); - - h[i + j + 4] = h4; - h[i + j + 5] = h0; - h[i + j + 6] = h1; - h[i + j + 7] = h2; - h[i + j + 8] = h3; - - for(i = 6; i < 24; i += 6) - { - f0 = f[i]; - f1 = f[i + 1]; - f2 = f[i + 2]; - f3 = f[i + 3]; - f4 = f[i + 4]; - f5 = f[i + 5]; - h0 = h[i]; - h1 = h[i + 1]; - h2 = h[i + 2]; - h3 = h[i + 3]; - h4 = h[i + 4]; - for(j = 0; j < 15; j += 5) - { - MULSTEP(j + 0, h0, h1, h2, h3, h4); - MULSTEP(j + 1, h1, h2, h3, h4, h0); - MULSTEP(j + 2, h2, h3, h4, h0, h1); - MULSTEP(j + 3, h3, h4, h0, h1, h2); - MULSTEP(j + 4, h4, h0, h1, h2, h3); - } - MULSTEP(j + 0, h0, h1, h2, h3, h4); - MULSTEP(j + 1, h1, h2, h3, h4, h0); - MULSTEP(j + 2, h2, h3, h4, h0, h1); - MULSTEP_noload(j + 3, h3, h4, h0, h1, h2); - MULSTEP_noload(j + 4, h4, h0, h1, h2, h3); - MULSTEP_noload(j + 5, h0, h1, h2, h3, h4); - MULSTEP_noload(j + 6, h1, h2, h3, h4, h0); - MULSTEP_noload(j + 7, h2, h3, h4, h0, h1); - MULSTEP_noload(j + 8, h3, h4, h0, h1, h2); - h[i + j + 9] = h4; - h[i + j + 10] = h0; - h[i + j + 11] = h1; - h[i + j + 12] = h2; - h[i + j + 13] = h3; - } - - h[47] = v0_float; -} - -/* 48*8*float32 f inputs between -5000 and 5000 */ -/* 48*8*float32 g inputs between -16 and 16 */ -/* 96*8*float32 h outputs between -3840000 and 3840000 */ -static void -mult48x8_float(__m256 h[96], const __m256 f[48], const __m256 g[48]) -{ - __m256 h01[48]; - __m256 g01[24]; - __m256 *f01 = h01 + 24; - int i; - - for(i = 24; i > 0;) - { - i -= 2; - f01[i] = f[i] + f[i + 24]; - g01[i] = g[i] + g[i + 24]; - f01[i + 1] = f[i + 1] + f[i + 1 + 24]; - g01[i + 1] = g[i + 1] + g[i + 1 + 24]; - } - - mult24x8_float(h, f, g); - mult24x8_float(h + 48, f + 24, g + 24); - mult24x8_float(h01, f01, g01); - - for(i = 0; i < 24; ++i) - { - __m256 h0i = h[i]; - __m256 h0itop = h[i + 24]; - __m256 h1i = h[i + 48]; - __m256 h1itop = h[i + 72]; - __m256 h01i = h01[i]; - __m256 h01itop = h01[i + 24]; - __m256 c = fastsub(h0itop, h1i); - h[i + 24] = c + fastsub(h01i, h0i); - h[i + 48] = fastsub(h01itop, h1itop) - c; - } -} - -/* 96*8*float32 f inputs between -2500 and 2500 */ -/* 96*8*float32 g inputs between -8 and 8 */ -/* 192*8*float32 h outputs between -1920000 and 1920000 */ -static void -mult96x8_float(__m256 h[192], const __m256 f[96], const __m256 g[96]) -{ - __m256 h01[96]; - __m256 g01[48]; - __m256 *f01 = h01 + 48; - int i; - - for(i = 48; i > 0;) - { - i -= 4; - f01[i] = f[i] + f[i + 48]; - g01[i] = g[i] + g[i + 48]; - f01[i + 1] = f[i + 1] + f[i + 1 + 48]; - g01[i + 1] = g[i + 1] + g[i + 1 + 48]; - f01[i + 2] = f[i + 2] + f[i + 2 + 48]; - g01[i + 2] = g[i + 2] + g[i + 2 + 48]; - f01[i + 3] = f[i + 3] + f[i + 3 + 48]; - g01[i + 3] = g[i + 3] + g[i + 3 + 48]; - } - - mult48x8_float(h, f, g); - mult48x8_float(h + 96, f + 48, g + 48); - mult48x8_float(h01, f01, g01); - - for(i = 0; i < 48; ++i) - { - __m256 h0i = h[i]; - __m256 h0itop = h[i + 48]; - __m256 h1i = h[i + 96]; - __m256 h1itop = h[i + 144]; - __m256 h01i = h01[i]; - __m256 h01itop = h01[i + 48]; - __m256 c = fastsub(h0itop, h1i); - h[i + 48] = c + fastsub(h01i, h0i); - h[i + 96] = fastsub(h01itop, h1itop) - c; - } -} - -/* 96*16*int16 f inputs between -2500 and 2500 */ -/* 96*(16*int8 stored in 32*int8) g inputs between -8 and 8 */ -/* 192*16*int16 h outputs between -2400 and 2400 */ -static void -mult96x16(__m256i * h, const __m256i * f, const __m256i * g) -{ - __m256 hfloat[192]; - __m256 gfloat[96]; - __m256 *ffloat = hfloat + 96; - int i, p; - - for(p = 0; p < 2; ++p) - { - for(i = 96; i > 0;) - { - i -= 2; - __m256i fi = - _mm256_cvtepi16_epi32(_mm_loadu_si128(p + (const __m128i *)&f[i])); - __m256i gi = _mm256_cvtepi16_epi32( - _mm_load_cvtepi8_epi16(p + (const long long *)&g[i])); - __m256 storage; - *(__m256i *)&storage = _mm256_add_epi32(fi, alpha_32); - ffloat[i] = storage - alpha_float; - *(__m256i *)&storage = _mm256_add_epi32(gi, alpha_32); - gfloat[i] = storage - alpha_float; - fi = _mm256_cvtepi16_epi32( - _mm_loadu_si128(p + (const __m128i *)&f[i + 1])); - gi = _mm256_cvtepi16_epi32( - _mm_load_cvtepi8_epi16(p + (const long long *)&g[i + 1])); - *(__m256i *)&storage = _mm256_add_epi32(fi, alpha_32); - ffloat[i + 1] = storage - alpha_float; - *(__m256i *)&storage = _mm256_add_epi32(gi, alpha_32); - gfloat[i + 1] = storage - alpha_float; - } - mult96x8_float(hfloat, ffloat, gfloat); - for(i = 192; i > 0;) - { - __m128i h0, h1; - i -= 4; - hfloat[i] = add(alpha_float, reduce(hfloat[i])); - hfloat[i + 1] = fastadd(alpha_float, reduce(hfloat[i + 1])); - hfloat[i + 2] = add(alpha_float, reduce(hfloat[i + 2])); - hfloat[i + 3] = fastadd(alpha_float, reduce(hfloat[i + 3])); - h0 = 0 [(__m128i *)&hfloat[i]]; - h0 = _mm_sub_epi32(h0, alpha_32_128); - h1 = 1 [(__m128i *)&hfloat[i]]; - h1 = _mm_sub_epi32(h1, alpha_32_128); - _mm_storeu_si128(p + (__m128i *)&h[i], _mm_packs_epi32(h0, h1)); - h0 = 0 [(__m128i *)&hfloat[i + 1]]; - h0 = _mm_sub_epi32(h0, alpha_32_128); - h1 = 1 [(__m128i *)&hfloat[i + 1]]; - h1 = _mm_sub_epi32(h1, alpha_32_128); - _mm_storeu_si128(p + (__m128i *)&h[i + 1], _mm_packs_epi32(h0, h1)); - h0 = 0 [(__m128i *)&hfloat[i + 2]]; - h0 = _mm_sub_epi32(h0, alpha_32_128); - h1 = 1 [(__m128i *)&hfloat[i + 2]]; - h1 = _mm_sub_epi32(h1, alpha_32_128); - _mm_storeu_si128(p + (__m128i *)&h[i + 2], _mm_packs_epi32(h0, h1)); - h0 = 0 [(__m128i *)&hfloat[i + 3]]; - h0 = _mm_sub_epi32(h0, alpha_32_128); - h1 = 1 [(__m128i *)&hfloat[i + 3]]; - h1 = _mm_sub_epi32(h1, alpha_32_128); - _mm_storeu_si128(p + (__m128i *)&h[i + 3], _mm_packs_epi32(h0, h1)); - } - } -} - -/* int16 i of output x[j] is int16 j of input x[i] */ -static void -transpose16(__m256i x[16]) -{ - static const int rev[4] = {0, 4, 2, 6}; - int i; - __m256i y[16]; - - for(i = 0; i < 16; i += 4) - { - __m256i a0 = x[i]; - __m256i a1 = x[i + 1]; - __m256i a2 = x[i + 2]; - __m256i a3 = x[i + 3]; - __m256i b0 = _mm256_unpacklo_epi16(a0, a1); - __m256i b1 = _mm256_unpackhi_epi16(a0, a1); - __m256i b2 = _mm256_unpacklo_epi16(a2, a3); - __m256i b3 = _mm256_unpackhi_epi16(a2, a3); - __m256i c0 = _mm256_unpacklo_epi32(b0, b2); - __m256i c2 = _mm256_unpackhi_epi32(b0, b2); - __m256i c1 = _mm256_unpacklo_epi32(b1, b3); - __m256i c3 = _mm256_unpackhi_epi32(b1, b3); - y[i] = c0; - y[i + 2] = c2; - y[i + 1] = c1; - y[i + 3] = c3; - } - for(i = 0; i < 4; ++i) - { - int r = rev[i]; - __m256i c0 = y[i]; - __m256i c4 = y[i + 4]; - __m256i c8 = y[i + 8]; - __m256i c12 = y[i + 12]; - __m256i d0 = _mm256_unpacklo_epi64(c0, c4); - __m256i d4 = _mm256_unpackhi_epi64(c0, c4); - __m256i d8 = _mm256_unpacklo_epi64(c8, c12); - __m256i d12 = _mm256_unpackhi_epi64(c8, c12); - __m256i e0 = _mm256_permute2x128_si256(d0, d8, 0x20); - __m256i e8 = _mm256_permute2x128_si256(d0, d8, 0x31); - __m256i e4 = _mm256_permute2x128_si256(d4, d12, 0x20); - __m256i e12 = _mm256_permute2x128_si256(d4, d12, 0x31); - x[r] = e0; - x[r + 8] = e8; - x[r + 1] = e4; - x[r + 9] = e12; - } -} - -/* byte i of output x[j] is byte j of input x[i] */ -static void -transpose32(__m256i x[32]) -{ - static const int rev[4] = {0, 8, 4, 12}; - int i; - __m256i y[32]; - - for(i = 0; i < 32; i += 4) - { - __m256i a0 = x[i]; - __m256i a1 = x[i + 1]; - __m256i a2 = x[i + 2]; - __m256i a3 = x[i + 3]; - __m256i b0 = _mm256_unpacklo_epi8(a0, a1); - __m256i b1 = _mm256_unpackhi_epi8(a0, a1); - __m256i b2 = _mm256_unpacklo_epi8(a2, a3); - __m256i b3 = _mm256_unpackhi_epi8(a2, a3); - __m256i c0 = _mm256_unpacklo_epi16(b0, b2); - __m256i c2 = _mm256_unpackhi_epi16(b0, b2); - __m256i c1 = _mm256_unpacklo_epi16(b1, b3); - __m256i c3 = _mm256_unpackhi_epi16(b1, b3); - y[i] = c0; - y[i + 2] = c2; - y[i + 1] = c1; - y[i + 3] = c3; - } - for(i = 0; i < 4; ++i) - { - int r = rev[i]; - __m256i c0 = y[i]; - __m256i c8 = y[i + 8]; - __m256i c16 = y[i + 16]; - __m256i c24 = y[i + 24]; - __m256i c4 = y[i + 4]; - __m256i c12 = y[i + 12]; - __m256i c20 = y[i + 20]; - __m256i c28 = y[i + 28]; - __m256i d0 = _mm256_unpacklo_epi32(c0, c4); - __m256i d4 = _mm256_unpackhi_epi32(c0, c4); - __m256i d8 = _mm256_unpacklo_epi32(c8, c12); - __m256i d12 = _mm256_unpackhi_epi32(c8, c12); - __m256i d16 = _mm256_unpacklo_epi32(c16, c20); - __m256i d20 = _mm256_unpackhi_epi32(c16, c20); - __m256i d24 = _mm256_unpacklo_epi32(c24, c28); - __m256i d28 = _mm256_unpackhi_epi32(c24, c28); - __m256i e0 = _mm256_unpacklo_epi64(d0, d8); - __m256i e8 = _mm256_unpackhi_epi64(d0, d8); - __m256i e16 = _mm256_unpacklo_epi64(d16, d24); - __m256i e24 = _mm256_unpackhi_epi64(d16, d24); - __m256i e4 = _mm256_unpacklo_epi64(d4, d12); - __m256i e12 = _mm256_unpackhi_epi64(d4, d12); - __m256i e20 = _mm256_unpacklo_epi64(d20, d28); - __m256i e28 = _mm256_unpackhi_epi64(d20, d28); - __m256i f0 = _mm256_permute2x128_si256(e0, e16, 0x20); - __m256i f16 = _mm256_permute2x128_si256(e0, e16, 0x31); - __m256i f8 = _mm256_permute2x128_si256(e8, e24, 0x20); - __m256i f24 = _mm256_permute2x128_si256(e8, e24, 0x31); - __m256i f4 = _mm256_permute2x128_si256(e4, e20, 0x20); - __m256i f20 = _mm256_permute2x128_si256(e4, e20, 0x31); - __m256i f12 = _mm256_permute2x128_si256(e12, e28, 0x20); - __m256i f28 = _mm256_permute2x128_si256(e12, e28, 0x31); - x[r] = f0; - x[r + 16] = f16; - x[r + 1] = f8; - x[r + 17] = f24; - x[r + 2] = f4; - x[r + 18] = f20; - x[r + 3] = f12; - x[r + 19] = f28; - } -} - -/* 48*16*int16 f inputs between -2295 and 2295 */ -/* 24*32*int8 g inputs between -1 and 1 */ -/* 96*16*int16 h outputs between -2295 and 2295 */ -static void -mult768_mix2_m256i(__m256i h[96], const __m256i f[48], const __m256i g[24]) -{ - __m256i hkara[24][16]; - __m256i gkara[3][32]; -#define fkara hkara - int i; - - for(i = 6; i-- > 0;) - { - __m256i f0, f1, f2, f3, f4, f5, f6, f7; - __m256i f01, f23, f45, f67; - __m256i f02, f46, f04, f26, f0426; - __m256i f13, f57, f15, f37, f1537; - __m256i f0213, f4657, f04261537, f0415, f2637; - - f0 = _mm256_loadu_si256(&f[i + 0]); - f1 = _mm256_loadu_si256(&f[i + 6]); - f2 = _mm256_loadu_si256(&f[i + 12]); - f3 = _mm256_loadu_si256(&f[i + 18]); - f4 = _mm256_loadu_si256(&f[i + 24]); - f5 = _mm256_loadu_si256(&f[i + 30]); - f6 = _mm256_loadu_si256(&f[i + 36]); - f7 = _mm256_loadu_si256(&f[i + 42]); - f01 = squeezeadd16(f0, f1); - fkara[i][8] = f01; - f23 = squeezeadd16(f2, f3); - fkara[i][9] = f23; - f45 = squeezeadd16(f4, f5); - fkara[i][10] = f45; - f67 = squeezeadd16(f6, f7); - fkara[i][11] = f67; - - fkara[i][0] = f0; - fkara[i][2] = f2; - fkara[i][4] = f4; - fkara[i][6] = f6; - - f02 = squeezeadd16(f0, f2); - fkara[i + 6][0] = f02; - f04 = squeezeadd16(f0, f4); - fkara[i + 6][6] = f04; - f46 = squeezeadd16(f4, f6); - fkara[i + 6][3] = f46; - f26 = squeezeadd16(f2, f6); - fkara[i + 6][8] = f26; - - fkara[i][1] = f1; - fkara[i][3] = f3; - fkara[i][5] = f5; - fkara[i][7] = f7; - - f13 = squeezeadd16(f1, f3); - fkara[i + 6][1] = f13; - f15 = squeezeadd16(f1, f5); - fkara[i + 6][7] = f15; - f57 = squeezeadd16(f5, f7); - fkara[i + 6][4] = f57; - f37 = squeezeadd16(f3, f7); - fkara[i + 6][9] = f37; - - f0426 = squeezeadd16(f04, f26); - fkara[i + 6][12] = f0426; - f1537 = squeezeadd16(f15, f37); - fkara[i + 6][13] = f1537; - f0213 = squeezeadd16(f02, f13); - fkara[i + 6][2] = f0213; - f4657 = squeezeadd16(f46, f57); - fkara[i + 6][5] = f4657; - f0415 = squeezeadd16(f04, f15); - fkara[i + 6][10] = f0415; - f2637 = squeezeadd16(f26, f37); - fkara[i + 6][11] = f2637; - f04261537 = squeezeadd16(f0426, f1537); - fkara[i + 6][14] = f04261537; - - fkara[i][12] = v0; - fkara[i][13] = v0; - fkara[i][14] = v0; - fkara[i][15] = v0; - fkara[i + 6][15] = v0; - } - - for(i = 3; i-- > 0;) - { - __m256i g0, g1, g2, g3, g4, g5, g6, g7; - __m256i g01, g23, g45, g67; - __m256i g02, g46, g04, g26, g0426; - __m256i g13, g57, g15, g37, g1537; - __m256i g0213, g4657, g04261537, g0415, g2637; - - g0 = _mm256_loadu_si256(&g[i + 0]); - g1 = _mm256_loadu_si256(&g[i + 3]); - g2 = _mm256_loadu_si256(&g[i + 6]); - g3 = _mm256_loadu_si256(&g[i + 9]); - g4 = _mm256_loadu_si256(&g[i + 12]); - g5 = _mm256_loadu_si256(&g[i + 15]); - g6 = _mm256_loadu_si256(&g[i + 18]); - g7 = _mm256_loadu_si256(&g[i + 21]); - g01 = _mm256_add_epi8(g0, g1); - gkara[i][8] = g01; - g23 = _mm256_add_epi8(g2, g3); - gkara[i][9] = g23; - g45 = _mm256_add_epi8(g4, g5); - gkara[i][10] = g45; - g67 = _mm256_add_epi8(g6, g7); - gkara[i][11] = g67; - - gkara[i][0] = g0; - gkara[i][2] = g2; - gkara[i][4] = g4; - gkara[i][6] = g6; - - g02 = _mm256_add_epi8(g0, g2); - gkara[i][16] = g02; - g04 = _mm256_add_epi8(g0, g4); - gkara[i][22] = g04; - g46 = _mm256_add_epi8(g4, g6); - gkara[i][19] = g46; - g26 = _mm256_add_epi8(g2, g6); - gkara[i][24] = g26; - - gkara[i][1] = g1; - gkara[i][3] = g3; - gkara[i][5] = g5; - gkara[i][7] = g7; - - g13 = _mm256_add_epi8(g1, g3); - gkara[i][17] = g13; - g15 = _mm256_add_epi8(g1, g5); - gkara[i][23] = g15; - g57 = _mm256_add_epi8(g5, g7); - gkara[i][20] = g57; - g37 = _mm256_add_epi8(g3, g7); - gkara[i][25] = g37; - - g0426 = _mm256_add_epi8(g04, g26); - gkara[i][28] = g0426; - g1537 = _mm256_add_epi8(g15, g37); - gkara[i][29] = g1537; - g0213 = _mm256_add_epi8(g02, g13); - gkara[i][18] = g0213; - g4657 = _mm256_add_epi8(g46, g57); - gkara[i][21] = g4657; - g0415 = _mm256_add_epi8(g04, g15); - gkara[i][26] = g0415; - g2637 = _mm256_add_epi8(g26, g37); - gkara[i][27] = g2637; - g04261537 = _mm256_add_epi8(g0426, g1537); - gkara[i][30] = g04261537; - - gkara[i][12] = v0; - gkara[i][13] = v0; - gkara[i][14] = v0; - gkara[i][15] = v0; - gkara[i][31] = v0; - } - - for(i = 12; i-- > 0;) - transpose16(fkara[i]); - for(i = 3; i-- > 0;) - transpose32(gkara[i]); - - mult96x16(hkara[12], fkara[6], (__m256i *)(1 + (__m128i *)gkara)); - mult96x16(hkara[0], fkara[0], gkara[0]); - - for(i = 24; i-- > 0;) - transpose16(hkara[i]); - - for(i = 6; i-- > 0;) - { - __m256i h0, h1, h2, h3, h4, h5, h6, h7, h8, h9; - __m256i h10, h11, h12, h13, h14, h15, h16, h17, h18, h19; - __m256i h20, h21, h22, h23; - __m256i h32, h33, h34, h35, h36, h37, h38, h39; - __m256i h40, h41, h42, h43, h44, h45, h46, h47, h48, h49; - __m256i h50, h51, h52, h53, h54, h55, h56, h57, h58, h59; - __m256i h60, h61; - __m256i c; - -#define COMBINE(h0, h1, h2, h3, x0, x1) \ - c = _mm256_sub_epi16(h1, h2); \ - h1 = _mm256_sub_epi16(_mm256_add_epi16(c, x0), h0); \ - h2 = _mm256_sub_epi16(x1, _mm256_add_epi16(c, h3)); \ - h1 = squeeze(h1); \ - h2 = squeeze(h2); - - h56 = hkara[i + 12][12]; - h57 = hkara[i + 18][12]; - h58 = hkara[i + 12][13]; - h59 = hkara[i + 18][13]; - h60 = hkara[i + 12][14]; - h61 = hkara[i + 18][14]; - COMBINE(h56, h57, h58, h59, h60, h61) - - h44 = hkara[i + 12][6]; - h45 = hkara[i + 18][6]; - h46 = hkara[i + 12][7]; - h47 = hkara[i + 18][7]; - h52 = hkara[i + 12][10]; - h53 = hkara[i + 18][10]; - COMBINE(h44, h45, h46, h47, h52, h53) - - h48 = hkara[i + 12][8]; - h49 = hkara[i + 18][8]; - h50 = hkara[i + 12][9]; - h51 = hkara[i + 18][9]; - h54 = hkara[i + 12][11]; - h55 = hkara[i + 18][11]; - COMBINE(h48, h49, h50, h51, h54, h55) - COMBINE(h44, h46, h48, h50, h56, h58) - COMBINE(h45, h47, h49, h51, h57, h59) - - h0 = hkara[i][0]; - h1 = hkara[i + 6][0]; - h2 = hkara[i][1]; - h3 = hkara[i + 6][1]; - h16 = hkara[i][8]; - h17 = hkara[i + 6][8]; - COMBINE(h0, h1, h2, h3, h16, h17) - - h4 = hkara[i][2]; - h5 = hkara[i + 6][2]; - h6 = hkara[i][3]; - h7 = hkara[i + 6][3]; - h18 = hkara[i][9]; - h19 = hkara[i + 6][9]; - COMBINE(h4, h5, h6, h7, h18, h19) - - h32 = hkara[i + 12][0]; - h33 = hkara[i + 18][0]; - h34 = hkara[i + 12][1]; - h35 = hkara[i + 18][1]; - h36 = hkara[i + 12][2]; - h37 = hkara[i + 18][2]; - COMBINE(h32, h33, h34, h35, h36, h37) - COMBINE(h1, h3, h5, h7, h33, h35) - COMBINE(h0, h2, h4, h6, h32, h34) - - h8 = hkara[i][4]; - h9 = hkara[i + 6][4]; - h10 = hkara[i][5]; - h11 = hkara[i + 6][5]; - h20 = hkara[i][10]; - h21 = hkara[i + 6][10]; - COMBINE(h8, h9, h10, h11, h20, h21) - - h12 = hkara[i][6]; - h13 = hkara[i + 6][6]; - h14 = hkara[i][7]; - h15 = hkara[i + 6][7]; - h22 = hkara[i][11]; - h23 = hkara[i + 6][11]; - COMBINE(h12, h13, h14, h15, h22, h23) - - h38 = hkara[i + 12][3]; - h39 = hkara[i + 18][3]; - h40 = hkara[i + 12][4]; - h41 = hkara[i + 18][4]; - h42 = hkara[i + 12][5]; - h43 = hkara[i + 18][5]; - COMBINE(h38, h39, h40, h41, h42, h43) - COMBINE(h8, h10, h12, h14, h38, h40) - COMBINE(h9, h11, h13, h15, h39, h41) - - COMBINE(h0, h4, h8, h12, h44, h48) - h0 = freeze(h0); - h4 = freeze(h4); - h8 = freeze(h8); - h12 = freeze(h12); - _mm256_storeu_si256(&h[i + 0], h0); - _mm256_storeu_si256(&h[i + 24], h4); - _mm256_storeu_si256(&h[i + 48], h8); - _mm256_storeu_si256(&h[i + 72], h12); - - COMBINE(h1, h5, h9, h13, h45, h49) - h1 = freeze(h1); - h5 = freeze(h5); - h9 = freeze(h9); - h13 = freeze(h13); - _mm256_storeu_si256(&h[i + 6], h1); - _mm256_storeu_si256(&h[i + 30], h5); - _mm256_storeu_si256(&h[i + 54], h9); - _mm256_storeu_si256(&h[i + 78], h13); - - COMBINE(h2, h6, h10, h14, h46, h50) - h2 = freeze(h2); - h6 = freeze(h6); - h10 = freeze(h10); - h14 = freeze(h14); - _mm256_storeu_si256(&h[i + 12], h2); - _mm256_storeu_si256(&h[i + 36], h6); - _mm256_storeu_si256(&h[i + 60], h10); - _mm256_storeu_si256(&h[i + 84], h14); - - COMBINE(h3, h7, h11, h15, h47, h51) - h3 = freeze(h3); - h7 = freeze(h7); - h11 = freeze(h11); - h15 = freeze(h15); - _mm256_storeu_si256(&h[i + 18], h3); - _mm256_storeu_si256(&h[i + 42], h7); - _mm256_storeu_si256(&h[i + 66], h11); - _mm256_storeu_si256(&h[i + 90], h15); - } -} - -#define p 761 - -/* 761 f inputs between -2295 and 2295 */ -/* 761 g inputs between -1 and 1 */ -/* 761 h outputs between -2295 and 2295 */ -void -rq_mult(modq *h, const modq *f, const small *g) -{ - __m256i fgvec[96]; - modq *fg; - int i; - - mult768_mix2_m256i(fgvec, (__m256i *)f, (__m256i *)g); - fg = (modq *)fgvec; - - h[0] = modq_freeze(fg[0] + fg[p]); - for(i = 1; i < 9; ++i) - h[i] = modq_freeze(fg[i] + fg[i + p - 1] + fg[i + p]); - for(i = 9; i < 761; i += 16) - { - __m256i fgi = _mm256_loadu_si256((__m256i *)&fg[i]); - __m256i fgip = _mm256_loadu_si256((__m256i *)&fg[i + p]); - __m256i fgip1 = _mm256_loadu_si256((__m256i *)&fg[i + p - 1]); - __m256i x = _mm256_add_epi16(fgi, _mm256_add_epi16(fgip, fgip1)); - x = freeze(squeeze(x)); - _mm256_storeu_si256((__m256i *)&h[i], x); - } - for(i = 761; i < 768; ++i) - h[i] = 0; -} - -void -r3_mult(small *h, const small *f, const small *g) -{ - __m256i fgvec[96]; - __m256i fvec[48]; - modq *fg; - int i; - - memset(fvec, 0, sizeof fvec); - - for(i = 0; i < 761; ++i) - i[(modq *)fvec] = f[i]; - - mult768_mix2_m256i(fgvec, fvec, (__m256i *)g); - fg = (modq *)fgvec; - - h[0] = mod3_freeze(fg[0] + fg[p]); - for(i = 1; i < p; ++i) - h[i] = mod3_freeze(fg[i] + fg[i + p - 1] + fg[i + p]); - for(i = p; i < 768; ++i) - h[i] = 0; -} -#endif diff --git a/crypto/libntrup/src/avx/params.h b/crypto/libntrup/src/avx/params.h deleted file mode 100644 index 655e6ec095..0000000000 --- a/crypto/libntrup/src/avx/params.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef params_h -#define params_h - -#define q 4591 -/* XXX: also built into modq in various ways */ - -#define qshift 2295 -#define p 761 -#define w 286 - -#define rq_encode_len 1218 -#define small_encode_len 191 - -#endif diff --git a/crypto/libntrup/src/avx/r3.h b/crypto/libntrup/src/avx/r3.h deleted file mode 100644 index aa0b036074..0000000000 --- a/crypto/libntrup/src/avx/r3.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef r3_h -#define r3_h - -#include "small.h" - -#define r3_mult crypto_kem_sntrup4591761_avx_r3_mult -extern void -r3_mult(small *, const small *, const small *); - -#define r3_recip crypto_kem_sntrup4591761_avx_r3_recip -extern int -r3_recip(small *, const small *); - -#define r3_weightw_mask crypto_kem_sntrup4591761_avx_r3_weightw_mask -extern int -r3_weightw_mask(const small *); - -#endif diff --git a/crypto/libntrup/src/avx/r3_recip.c b/crypto/libntrup/src/avx/r3_recip.c deleted file mode 100644 index 839bb2ce7f..0000000000 --- a/crypto/libntrup/src/avx/r3_recip.c +++ /dev/null @@ -1,221 +0,0 @@ -#if __AVX2__ -#include -#include "params.h" -#include "mod3.h" -#include "swap.h" -#include "r3.h" - -/* caller must ensure that x-y does not overflow */ -static int -smaller_mask(int x, int y) -{ - return (x - y) >> 31; -} - -static void -vectormod3_product(small *z, int len, const small *x, const small c) -{ - int i; - int minusmask = c; - int plusmask = -c; - __m256i minusvec, plusvec, zerovec; - - minusmask >>= 31; - plusmask >>= 31; - minusvec = _mm256_set1_epi32(minusmask); - plusvec = _mm256_set1_epi32(plusmask); - zerovec = _mm256_set1_epi32(0); - - while(len >= 32) - { - __m256i xi = _mm256_loadu_si256((__m256i *)x); - xi = (xi & plusvec) | (_mm256_sub_epi8(zerovec, xi) & minusvec); - _mm256_storeu_si256((__m256i *)z, xi); - x += 32; - z += 32; - len -= 32; - } - - for(i = 0; i < len; ++i) - z[i] = mod3_product(x[i], c); -} - -static void -vectormod3_minusproduct(small *z, int len, const small *x, const small *y, - const small c) -{ - int i; - int minusmask = c; - int plusmask = -c; - __m256i minusvec, plusvec, zerovec, twovec, fourvec; - - minusmask >>= 31; - plusmask >>= 31; - minusvec = _mm256_set1_epi32(minusmask); - plusvec = _mm256_set1_epi32(plusmask); - zerovec = _mm256_set1_epi32(0); - twovec = _mm256_set1_epi32(0x02020202); - fourvec = _mm256_set1_epi32(0x04040404); - - while(len >= 32) - { - __m256i xi = _mm256_loadu_si256((__m256i *)x); - __m256i yi = _mm256_loadu_si256((__m256i *)y); - __m256i r; - yi = (yi & plusvec) | (_mm256_sub_epi8(zerovec, yi) & minusvec); - xi = _mm256_sub_epi8(xi, yi); - - r = _mm256_add_epi8(xi, twovec); - r &= fourvec; - r = _mm256_srli_epi32(r, 2); - xi = _mm256_sub_epi8(xi, r); - r = _mm256_add_epi8(r, r); - xi = _mm256_sub_epi8(xi, r); - - r = _mm256_sub_epi8(twovec, xi); - r &= fourvec; - r = _mm256_srli_epi32(r, 2); - xi = _mm256_add_epi8(xi, r); - r = _mm256_add_epi8(r, r); - xi = _mm256_add_epi8(xi, r); - - _mm256_storeu_si256((__m256i *)z, xi); - x += 32; - y += 32; - z += 32; - len -= 32; - } - - for(i = 0; i < len; ++i) - z[i] = mod3_minusproduct(x[i], y[i], c); -} - -static void -vectormod3_shift(small *z, int len) -{ - int i; - while(len >= 33) - { - __m256i zi = _mm256_loadu_si256((__m256i *)(z + len - 33)); - _mm256_storeu_si256((__m256i *)(z + len - 32), zi); - len -= 32; - } - for(i = len - 1; i > 0; --i) - z[i] = z[i - 1]; - z[0] = 0; -} - -/* -r = s^(-1) mod m, returning 0, if s is invertible mod m -or returning -1 if s is not invertible mod m -r,s are polys of degree

= loops) - break; - - c = mod3_quotient(g[p], f[p]); - - vectormod3_minusproduct(g, 768, g, f, c); - vectormod3_shift(g, 769); - -#ifdef SIMPLER - vectormod3_minusproduct(v, 1536, v, u, c); - vectormod3_shift(v, 1537); -#else - if(loop < p) - { - vectormod3_minusproduct(v, loop + 1, v, u, c); - vectormod3_shift(v, loop + 2); - } - else - { - vectormod3_minusproduct(v + loop - p, p + 1, v + loop - p, u + loop - p, - c); - vectormod3_shift(v + loop - p, p + 2); - } -#endif - - e -= 1; - - ++loop; - - swapmask = smaller_mask(e, d) & mod3_nonzero_mask(g[p]); - swap(&e, &d, sizeof e, swapmask); - swap(f, g, (p + 1) * sizeof(small), swapmask); - -#ifdef SIMPLER - swap(u, v, 1536 * sizeof(small), swapmask); -#else - if(loop < p) - { - swap(u, v, (loop + 1) * sizeof(small), swapmask); - } - else - { - swap(u + loop - p, v + loop - p, (p + 1) * sizeof(small), swapmask); - } -#endif - } - - c = mod3_reciprocal(f[p]); - vectormod3_product(r, p, u + p, c); - for(i = p; i < 768; ++i) - r[i] = 0; - return smaller_mask(0, d); -} -#endif \ No newline at end of file diff --git a/crypto/libntrup/src/avx/randomsmall.c b/crypto/libntrup/src/avx/randomsmall.c deleted file mode 100644 index 7ebe5feb59..0000000000 --- a/crypto/libntrup/src/avx/randomsmall.c +++ /dev/null @@ -1,18 +0,0 @@ -#include "params.h" -#include -#include -#include "small.h" - -void -small_random(small *g) -{ - crypto_uint32 r[p]; - int i; - - randombytes((unsigned char *)r, sizeof r); - for(i = 0; i < p; ++i) - g[i] = (small)(((r[i] & 1073741823) * 3) >> 30) - 1; - /* bias is miniscule */ - for(i = p; i < 768; ++i) - g[i] = 0; -} diff --git a/crypto/libntrup/src/avx/randomweightw.c b/crypto/libntrup/src/avx/randomweightw.c deleted file mode 100644 index 888fb937c1..0000000000 --- a/crypto/libntrup/src/avx/randomweightw.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "params.h" -#include -#include "int32_sort.h" -#include "small.h" - -void -small_random_weightw(small *f) -{ - crypto_int32 r[p]; - int i; - - randombytes((unsigned char *)r, sizeof r); - for(i = 0; i < w; ++i) - r[i] &= -2; - for(i = w; i < p; ++i) - r[i] = (r[i] & -3) | 1; - int32_sort(r, p); - for(i = 0; i < p; ++i) - f[i] = ((small)(r[i] & 3)) - 1; - for(i = p; i < 768; ++i) - f[i] = 0; -} diff --git a/crypto/libntrup/src/avx/rq.c b/crypto/libntrup/src/avx/rq.c deleted file mode 100644 index a000e7d72f..0000000000 --- a/crypto/libntrup/src/avx/rq.c +++ /dev/null @@ -1,185 +0,0 @@ -#if __AVX2__ -#include -#include "params.h" -#include -#include -#include "rq.h" - -#define v2295_16 _mm256_set1_epi16(2295) -#define v2295_16_128 _mm_set1_epi16(2295) -#define alpha_top _mm256_set1_epi32(0x43380000) -#define alpha _mm256_set1_pd(6755399441055744.0) -#define alpha_64 _mm256_set1_epi64(0x4338000000000000) - -/* each reciprocal is rounded _up_ to nearest floating-point number */ -#define recip54 0.0185185185185185209599811884118025773204863071441650390625 -#define recip4591 \ - 0.000217817468961010681817447309782664888189174234867095947265625 -#define recip6144 \ - 0.0001627604166666666847367028747584072334575466811656951904296875 -#define recip331776 \ - 0.00000301408179012345704632478034235010255770248477347195148468017578125 -#define recip37748736 \ - 0.000000026490953233506946282623583451172610825352649044361896812915802001953125 - -#define broadcast(r) _mm256_set1_pd(r) -#define floor(x) _mm256_floor_pd(x) - -// 32-bit hosts only -#ifndef __amd64__ -#define _mm_extract_epi64(X, N) \ - (__extension__({ \ - __v2di __a = (__v2di)(X); \ - __a[N]; \ - })) -#endif - -void -rq_encode(unsigned char *c, const modq *f) -{ - crypto_int32 f0, f1, f2, f3, f4; - int i; - - for(i = 0; i < p / 5; ++i) - { - f0 = *f++ + qshift; - f1 = *f++ + qshift; - f2 = *f++ + qshift; - f3 = *f++ + qshift; - f4 = *f++ + qshift; - /* now want f0 + 6144*f1 + ... as a 64-bit integer */ - f1 *= 3; - f2 *= 9; - f3 *= 27; - f4 *= 81; - /* now want f0 + f1<<11 + f2<<22 + f3<<33 + f4<<44 */ - f0 += f1 << 11; - *c++ = f0; - f0 >>= 8; - *c++ = f0; - f0 >>= 8; - f0 += f2 << 6; - *c++ = f0; - f0 >>= 8; - *c++ = f0; - f0 >>= 8; - f0 += f3 << 1; - *c++ = f0; - f0 >>= 8; - f0 += f4 << 4; - *c++ = f0; - f0 >>= 8; - *c++ = f0; - f0 >>= 8; - *c++ = f0; - } - /* XXX: using p mod 5 = 1 */ - f0 = *f++ + qshift; - *c++ = f0; - f0 >>= 8; - *c++ = f0; -} - -void -rq_decode(modq *f, const unsigned char *c) -{ - crypto_uint32 c0, c1; - int i; - - for(i = 0; i < 152; i += 4) - { - __m256i abcd, ac, bd, abcd0, abcd1; - __m256d x0, x1, f4, f3, f2, f1, f0; - __m128i if4, if3, if2, if1, if0; - __m128i x01, x23, x02, x13, xab, xcd; - - /* f0 + f1*6144 + f2*6144^2 + f3*6144^3 + f4*6144^4 */ - /* = c0 + c1*256 + ... + c6*256^6 + c7*256^7 */ - /* with each f between 0 and 4590 */ - - /* could use _mm256_cvtepi32_pd instead; but beware uint32 */ - - abcd = _mm256_loadu_si256((__m256i *)c); /* a0 a1 b0 b1 c0 c1 d0 d1 */ - c += 32; - - ac = _mm256_unpacklo_epi32(abcd, alpha_top); /* a0 a1 c0 c1 */ - bd = _mm256_unpackhi_epi32(abcd, alpha_top); /* b0 b1 d0 d1 */ - abcd1 = _mm256_unpackhi_epi64(ac, bd); /* a1 b1 c1 d1 */ - abcd0 = _mm256_unpacklo_epi64(ac, bd); /* a0 b0 c0 d0 */ - x1 = *(__m256d *)&abcd1; - x0 = *(__m256d *)&abcd0; - - x1 -= alpha; - x0 -= alpha; - - /* x1 is [0,41] + [0,4590]*54 + f4*331776 */ - f4 = broadcast(recip331776) * x1; - f4 = floor(f4); - x1 -= broadcast(331776.0) * f4; - - /* x1 is [0,41] + f3*54 */ - f3 = broadcast(recip54) * x1; - f3 = floor(f3); - x1 -= broadcast(54.0) * f3; - - x0 += broadcast(4294967296.0) * x1; - - /* x0 is [0,4590] + [0,4590]*6144 + f2*6144^2 */ - f2 = broadcast(recip37748736) * x0; - f2 = floor(f2); - x0 -= broadcast(37748736.0) * f2; - - /* x0 is [0,4590] + f1*6144 */ - f1 = broadcast(recip6144) * x0; - f1 = floor(f1); - x0 -= broadcast(6144.0) * f1; - - f0 = x0; - - f4 -= broadcast(4591.0) * floor(broadcast(recip4591) * f4); - f3 -= broadcast(4591.0) * floor(broadcast(recip4591) * f3); - f2 -= broadcast(4591.0) * floor(broadcast(recip4591) * f2); - f1 -= broadcast(4591.0) * floor(broadcast(recip4591) * f1); - f0 -= broadcast(4591.0) * floor(broadcast(recip4591) * f0); - - if4 = _mm256_cvtpd_epi32(f4); /* a4 0 b4 0 c4 0 d4 0 */ - if3 = _mm256_cvtpd_epi32(f3); /* a3 0 b3 0 c3 0 d3 0 */ - if2 = _mm256_cvtpd_epi32(f2); /* a2 0 b2 0 c2 0 d2 0 */ - if1 = _mm256_cvtpd_epi32(f1); /* a1 0 b1 0 c1 0 d1 0 */ - if0 = _mm256_cvtpd_epi32(f0); /* a0 0 b0 0 c0 0 d0 0 */ - - if4 = _mm_sub_epi16(if4, v2295_16_128); - f[4] = _mm_extract_epi32(if4, 0); - f[9] = _mm_extract_epi32(if4, 1); - f[14] = _mm_extract_epi32(if4, 2); - f[19] = _mm_extract_epi32(if4, 3); - - x23 = _mm_packs_epi32(if2, if3); /* a2 b2 c2 d2 a3 b3 c3 d3 */ - x01 = _mm_packs_epi32(if0, if1); /* a0 b0 c0 d0 a1 b1 c1 d1 */ - x02 = _mm_unpacklo_epi16(x01, x23); /* a0 a2 b0 b2 c0 c2 d0 d2 */ - x13 = _mm_unpackhi_epi16(x01, x23); /* a1 a3 b1 b3 c1 c3 d1 d3 */ - xab = _mm_unpacklo_epi16(x02, x13); /* a0 a1 a2 a3 b0 b1 b2 b3 */ - xcd = _mm_unpackhi_epi16(x02, x13); /* c0 c1 c2 c3 d0 d1 d2 d3 */ - xab = _mm_sub_epi16(xab, v2295_16_128); - xcd = _mm_sub_epi16(xcd, v2295_16_128); - - *(crypto_int64 *)(f + 0) = _mm_extract_epi64(xab, 0); - *(crypto_int64 *)(f + 5) = _mm_extract_epi64(xab, 1); - *(crypto_int64 *)(f + 10) = _mm_extract_epi64(xcd, 0); - *(crypto_int64 *)(f + 15) = _mm_extract_epi64(xcd, 1); - f += 20; - } - - c0 = *c++; - c1 = *c++; - c0 += c1 << 8; - *f++ = modq_freeze(c0 + q - qshift); - *f++ = 0; - *f++ = 0; - *f++ = 0; - *f++ = 0; - *f++ = 0; - *f++ = 0; - *f++ = 0; -} -#endif diff --git a/crypto/libntrup/src/avx/rq.h b/crypto/libntrup/src/avx/rq.h deleted file mode 100644 index 0660af3507..0000000000 --- a/crypto/libntrup/src/avx/rq.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef rq_h -#define rq_h - -#include "modq.h" -#include "small.h" - -#define rq_encode crypto_kem_sntrup4591761_avx_rq_encode -extern void -rq_encode(unsigned char *, const modq *); - -#define rq_decode crypto_kem_sntrup4591761_avx_rq_decode -extern void -rq_decode(modq *, const unsigned char *); - -#define rq_roundencode crypto_kem_sntrup4591761_avx_rq_roundencode -extern void -rq_roundencode(unsigned char *, const modq *); - -#define rq_decoderounded crypto_kem_sntrup4591761_avx_rq_decoderounded -extern void -rq_decoderounded(modq *, const unsigned char *); - -#define rq_round3 crypto_kem_sntrup4591761_avx_rq_round3 -extern void -rq_round3(modq *, const modq *); - -#define rq_mod3 crypto_kem_sntrup4591761_avx_rq_mod3 -extern void -rq_mod3(small *, const modq *); - -#define rq_mult crypto_kem_sntrup4591761_avx_rq_mult -extern void -rq_mult(modq *, const modq *, const small *); - -#define rq_recip3 crypto_kem_sntrup4591761_avx_rq_recip3 -int -rq_recip3(modq *, const small *); - -#endif diff --git a/crypto/libntrup/src/avx/rq_mod3.c b/crypto/libntrup/src/avx/rq_mod3.c deleted file mode 100644 index 6a972acda0..0000000000 --- a/crypto/libntrup/src/avx/rq_mod3.c +++ /dev/null @@ -1,68 +0,0 @@ -#if __AVX2__ -#include -#include -#include "mod3.h" -#include "rq.h" - -#define v3 _mm256_set1_epi16(3) -#define v7 _mm256_set1_epi16(7) -#define v2296_16 _mm256_set1_epi16(2296) -#define v4591_16 _mm256_set1_epi16(4591) -#define v10923_16 _mm256_set1_epi16(10923) - -// 32-bit hosts only -#ifndef __amd64__ -#define _mm_extract_epi64(X, N) \ - (__extension__({ \ - __v2di __a = (__v2di)(X); \ - __a[N]; \ - })) -#endif - -static inline __m256i -squeeze(__m256i x) -{ - __m256i q = _mm256_mulhrs_epi16(x, v7); - q = _mm256_mullo_epi16(q, v4591_16); - return _mm256_sub_epi16(x, q); -} - -static inline __m256i -freeze(__m256i x) -{ - __m256i mask, x2296, x4591; - x4591 = _mm256_add_epi16(x, v4591_16); - mask = _mm256_srai_epi16(x, 15); - x = _mm256_blendv_epi8(x, x4591, mask); - x2296 = _mm256_sub_epi16(x, v2296_16); - mask = _mm256_srai_epi16(x2296, 15); - x4591 = _mm256_sub_epi16(x, v4591_16); - x = _mm256_blendv_epi8(x4591, x, mask); - return x; -} - -void -rq_mod3(small *g, const modq *f) -{ - int i; - - for(i = 0; i < 768; i += 16) - { - __m256i x = _mm256_loadu_si256((__m256i *)&f[i]); - __m256i q; - x = _mm256_mullo_epi16(x, v3); - x = squeeze(x); - x = freeze(x); - q = _mm256_mulhrs_epi16(x, v10923_16); - x = _mm256_sub_epi16(x, q); - q = _mm256_add_epi16(q, q); - x = _mm256_sub_epi16(x, q); /* g0 g1 ... g15 */ - x = _mm256_packs_epi16(x, - x); /* g0 ... g7 g0 ... g7 g8 ... g15 g8 ... g15 */ - 0 [(long long *)&g[i]] = - _mm_extract_epi64(_mm256_extracti128_si256(x, 0), 0); - 1 [(long long *)&g[i]] = - _mm_extract_epi64(_mm256_extracti128_si256(x, 1), 0); - } -} -#endif \ No newline at end of file diff --git a/crypto/libntrup/src/avx/rq_recip3.c b/crypto/libntrup/src/avx/rq_recip3.c deleted file mode 100644 index 5b4e96d6b9..0000000000 --- a/crypto/libntrup/src/avx/rq_recip3.c +++ /dev/null @@ -1,247 +0,0 @@ -#if __AVX2__ -#include -#include "params.h" -#include "swap.h" -#include "rq.h" - -#define v7 _mm256_set1_epi16(7) -#define v1827_16 _mm256_set1_epi16(1827) -#define v4591_16 _mm256_set1_epi16(4591) -#define v29234_16 _mm256_set1_epi16(29234) - -/* caller must ensure that x-y does not overflow */ -static int -smaller_mask(int x, int y) -{ - return (x - y) >> 31; -} - -static inline __m256i -product(__m256i x, __m256i y) -{ - __m256i lo, hi, r0, r1, t0, t1, t, s0, s1; - - lo = _mm256_mullo_epi16(x, y); - hi = _mm256_mulhi_epi16(x, y); - r0 = _mm256_unpacklo_epi16(lo, hi); - r1 = _mm256_unpackhi_epi16(lo, hi); - - t0 = _mm256_srai_epi32(r0, 16); - t1 = _mm256_srai_epi32(r1, 16); - t = _mm256_packs_epi32(t0, t1); - t = _mm256_mulhrs_epi16(t, v29234_16); - lo = _mm256_mullo_epi16(t, v4591_16); - hi = _mm256_mulhi_epi16(t, v4591_16); - s0 = _mm256_unpacklo_epi16(lo, hi); - s1 = _mm256_unpackhi_epi16(lo, hi); - s0 = _mm256_slli_epi32(s0, 4); - s1 = _mm256_slli_epi32(s1, 4); - r0 = _mm256_sub_epi32(r0, s0); - r1 = _mm256_sub_epi32(r1, s1); - - t0 = _mm256_srai_epi32(r0, 8); - t1 = _mm256_srai_epi32(r1, 8); - t = _mm256_packs_epi32(t0, t1); - t = _mm256_mulhrs_epi16(t, v1827_16); - lo = _mm256_mullo_epi16(t, v4591_16); - hi = _mm256_mulhi_epi16(t, v4591_16); - s0 = _mm256_unpacklo_epi16(lo, hi); - s1 = _mm256_unpackhi_epi16(lo, hi); - r0 = _mm256_sub_epi32(r0, s0); - r1 = _mm256_sub_epi32(r1, s1); - - x = _mm256_packs_epi32(r0, r1); - return x; -} - -static inline __m256i -minusproduct(__m256i x, __m256i y, __m256i z) -{ - __m256i t; - - x = _mm256_sub_epi16(x, product(y, z)); - t = _mm256_mulhrs_epi16(x, v7); - t = _mm256_mullo_epi16(t, v4591_16); - x = _mm256_sub_epi16(x, t); - return x; -} - -static void -vectormodq_product(modq *z, int len, const modq *x, const modq c) -{ - __m256i cvec = _mm256_set1_epi16(c); - while(len >= 16) - { - __m256i xi = _mm256_loadu_si256((__m256i *)x); - xi = product(xi, cvec); - _mm256_storeu_si256((__m256i *)z, xi); - x += 16; - z += 16; - len -= 16; - } - while(len > 0) - { - *z = modq_product(*x, c); - ++x; - ++z; - --len; - } -} - -static void -vectormodq_minusproduct(modq *z, int len, const modq *x, const modq *y, - const modq c) -{ - __m256i cvec = _mm256_set1_epi16(c); - while(len >= 16) - { - __m256i xi = _mm256_loadu_si256((__m256i *)x); - __m256i yi = _mm256_loadu_si256((__m256i *)y); - xi = minusproduct(xi, yi, cvec); - _mm256_storeu_si256((__m256i *)z, xi); - x += 16; - y += 16; - z += 16; - len -= 16; - } - while(len > 0) - { - *z = modq_minusproduct(*x, *y, c); - ++x; - ++y; - ++z; - --len; - } -} - -static void -vectormodq_shift(modq *z, int len) -{ - int i; - while(len >= 17) - { - __m256i zi = _mm256_loadu_si256((__m256i *)(z + len - 17)); - _mm256_storeu_si256((__m256i *)(z + len - 16), zi); - len -= 16; - } - for(i = len - 1; i > 0; --i) - z[i] = z[i - 1]; - z[0] = 0; -} - -/* -r = (3s)^(-1) mod m, returning 0, if s is invertible mod m -or returning -1 if s is not invertible mod m -r,s are polys of degree

= loops) - break; - - c = modq_quotient(g[p], f[p]); - - vectormodq_minusproduct(g, 768, g, f, c); - vectormodq_shift(g, 769); - -#ifdef SIMPLER - vectormodq_minusproduct(v, 1536, v, u, c); - vectormodq_shift(v, 1537); -#else - if(loop < p) - { - vectormodq_minusproduct(v, loop + 1, v, u, c); - vectormodq_shift(v, loop + 2); - } - else - { - vectormodq_minusproduct(v + loop - p, p + 1, v + loop - p, u + loop - p, - c); - vectormodq_shift(v + loop - p, p + 2); - } -#endif - - e -= 1; - - ++loop; - - swapmask = smaller_mask(e, d) & modq_nonzero_mask(g[p]); - swap(&e, &d, sizeof e, swapmask); - swap(f, g, 768 * sizeof(modq), swapmask); - -#ifdef SIMPLER - swap(u, v, 1536 * sizeof(modq), swapmask); -#else - if(loop < p) - { - swap(u, v, (loop + 1) * sizeof(modq), swapmask); - } - else - { - swap(u + loop - p, v + loop - p, (p + 1) * sizeof(modq), swapmask); - } -#endif - } - - c = modq_reciprocal(f[p]); - vectormodq_product(r, p, u + p, c); - for(i = 0; i < p; ++i) - r[i] = modq_freeze(r[i]); - for(i = p; i < 768; ++i) - r[i] = 0; - return smaller_mask(0, d); -} -#endif \ No newline at end of file diff --git a/crypto/libntrup/src/avx/rq_round3.c b/crypto/libntrup/src/avx/rq_round3.c deleted file mode 100644 index e6c6569b84..0000000000 --- a/crypto/libntrup/src/avx/rq_round3.c +++ /dev/null @@ -1,24 +0,0 @@ -#if __AVX2__ -#include -#include "params.h" -#include "rq.h" - -#define v3_16 _mm256_set1_epi16(3) -#define v10923_16 _mm256_set1_epi16(10923) - -void -rq_round3(modq *h, const modq *f) -{ - int i; - - for(i = 0; i < 768; i += 16) - { - __m256i x = _mm256_loadu_si256((__m256i *)&f[i]); - __m256i x2; - x = _mm256_mulhrs_epi16(x, v10923_16); - x2 = _mm256_add_epi16(x, x); - x = _mm256_add_epi16(x, x2); - _mm256_storeu_si256((__m256i *)&h[i], x); - } -} -#endif \ No newline at end of file diff --git a/crypto/libntrup/src/avx/rq_rounded.c b/crypto/libntrup/src/avx/rq_rounded.c deleted file mode 100644 index 512f597321..0000000000 --- a/crypto/libntrup/src/avx/rq_rounded.c +++ /dev/null @@ -1,297 +0,0 @@ -#if __AVX2__ -#include -#include "params.h" -#include -#include "rq.h" - -#define alpha_top _mm256_set1_epi32(0x43380000) -#define alpha _mm256_set1_pd(6755399441055744.0) -#define v10923_16 _mm256_set1_epi16(10923) -#define floor(x) _mm256_floor_pd(x) - -void -rq_roundencode(unsigned char *c, const modq *f) -{ - int i; - __m256i h[50]; - - for(i = 0; i < 208; i += 16) - { - __m256i a0, a1, a2, b0, b1, b2, c0, c1, c2, d0, d1, d2; - __m256i e0, e1, f0, f1, g0, g1; - a0 = _mm256_castsi128_si256(_mm_loadu_si128((__m128i *)&f[0])); - a1 = _mm256_castsi128_si256(_mm_loadu_si128((__m128i *)&f[8])); - a2 = _mm256_castsi128_si256(_mm_loadu_si128((__m128i *)&f[16])); - a0 = _mm256_inserti128_si256(a0, _mm_loadu_si128((__m128i *)&f[24]), 1); - a1 = _mm256_inserti128_si256(a1, _mm_loadu_si128((__m128i *)&f[32]), 1); - a2 = _mm256_inserti128_si256(a2, _mm_loadu_si128((__m128i *)&f[40]), 1); - f += 48; - - a0 = _mm256_mulhrs_epi16(a0, v10923_16); - a1 = _mm256_mulhrs_epi16(a1, v10923_16); - a2 = _mm256_mulhrs_epi16(a2, v10923_16); - - /* a0: a0 a1 a2 b0 b1 b2 c0 c1 and similar second half */ - /* a1: c2 d0 d1 d2 e0 e1 e2 f0 */ - /* a2: f1 f2 g0 g1 g2 h0 h1 h2 */ - - b1 = _mm256_blend_epi16(a2, a0, 0xf0); - b1 = _mm256_shuffle_epi32(b1, 0x4e); - b0 = _mm256_blend_epi16(a0, a1, 0xf0); - b2 = _mm256_blend_epi16(a1, a2, 0xf0); - /* XXX: use shufps instead? */ - - /* b0: a0 a1 a2 b0 e0 e1 e2 f0 */ - /* b1: b1 b2 c0 c1 f1 f2 g0 g1 */ - /* b2: c2 d0 d1 d2 g2 h0 h1 h2 */ - - c1 = _mm256_blend_epi16(b2, b0, 0xcc); - c1 = _mm256_shuffle_epi32(c1, 0xb1); - c0 = _mm256_blend_epi16(b0, b1, 0xcc); - c2 = _mm256_blend_epi16(b1, b2, 0xcc); - - /* c0: a0 a1 c0 c1 e0 e1 g0 g1 */ - /* c1: a2 b0 c2 d0 e2 f0 g2 h0 */ - /* c2: b1 b2 d1 d2 f1 f2 h1 h2 */ - - d1 = _mm256_blend_epi16(c2, c0, 0xaa); - d1 = _mm256_shufflelo_epi16(d1, 0xb1); - d1 = _mm256_shufflehi_epi16(d1, 0xb1); - d0 = _mm256_blend_epi16(c0, c1, 0xaa); - d2 = _mm256_blend_epi16(c1, c2, 0xaa); - - /* d0: a0 b0 c0 d0 e0 f0 g0 h0 */ - /* d1: a1 b1 c1 d1 e1 f1 g1 h1 */ - /* d2: a2 b2 c2 d2 e2 f2 g2 h2 */ - - d0 = _mm256_add_epi16(d0, _mm256_set1_epi16(765)); - d1 = _mm256_add_epi16(d1, _mm256_set1_epi16(765)); - d2 = _mm256_add_epi16(d2, _mm256_set1_epi16(765)); - /* want bytes of d0 + 1536*d1 + 1536*1536*d2 */ - - e0 = d0 & _mm256_set1_epi16(0xff); - d0 = _mm256_srli_epi16(d0, 8); - /* want e0, d0 + 6*d1 + 6*1536*d2 */ - - d1 = _mm256_mullo_epi16(d1, _mm256_set1_epi16(6)); - d0 = _mm256_add_epi16(d0, d1); - /* want e0, d0 + 6*1536*d2 */ - - e1 = _mm256_slli_epi16(d0, 8); - e0 = _mm256_add_epi16(e0, e1); - d0 = _mm256_srli_epi16(d0, 8); - /* want e0, d0 + 36*d2 */ - - d2 = _mm256_mullo_epi16(d2, _mm256_set1_epi16(36)); - e1 = _mm256_add_epi16(d0, d2); - /* want e0, e1 */ - - /* e0: out0 out1 out4 out5 out8 out9 ... */ - /* e1: out2 out3 out6 out7 out10 out11 ... */ - - f0 = _mm256_unpacklo_epi16(e0, e1); - f1 = _mm256_unpackhi_epi16(e0, e1); - - g0 = _mm256_permute2x128_si256(f0, f1, 0x20); - g1 = _mm256_permute2x128_si256(f0, f1, 0x31); - - _mm256_storeu_si256((__m256i *)c, g0); - _mm256_storeu_si256((__m256i *)(c + 32), g1); - c += 64; - } - - for(i = 0; i < 9; ++i) - { - __m256i x = _mm256_loadu_si256((__m256i *)&f[16 * i]); - _mm256_storeu_si256(&h[i], _mm256_mulhrs_epi16(x, v10923_16)); - } - f = (const modq *)h; - - for(i = 208; i < 253; ++i) - { - crypto_int32 f0, f1, f2; - f0 = *f++; - f1 = *f++; - f2 = *f++; - f0 += 1806037245; - f1 *= 3; - f2 *= 9; - f0 += f1 << 9; - f0 += f2 << 18; - *(crypto_int32 *)c = f0; - c += 4; - } - { - crypto_int32 f0, f1; - f0 = *f++; - f1 = *f++; - f0 += 1175805; - f1 *= 3; - f0 += f1 << 9; - *c++ = f0; - f0 >>= 8; - *c++ = f0; - f0 >>= 8; - *c++ = f0; - } -} - -void -rq_decoderounded(modq *f, const unsigned char *c) -{ - crypto_uint32 c0, c1, c2, c3; - crypto_uint32 f0, f1, f2; - int i; - - for(i = 0; i < 248; i += 8) - { - __m256i abcdefgh, todo[2]; - __m256d x, f2, f1, f0; - __m128i if2, if1, if0; - int j; - - abcdefgh = _mm256_loadu_si256((__m256i *)c); - c += 32; - - todo[0] = _mm256_unpacklo_epi32(abcdefgh, alpha_top); - todo[1] = _mm256_unpackhi_epi32(abcdefgh, alpha_top); - - for(j = 0; j < 2; ++j) - { - x = *(__m256d *)&todo[j]; - x -= alpha; - - /* x is f0 + f1*1536 + f2*1536^2 */ - /* with each f between 0 and 1530 */ - - f2 = - x - * _mm256_set1_pd( - 0.00000042385525173611114052197733521876177320564238470979034900665283203125); - f2 = floor(f2); - x -= f2 * _mm256_set1_pd(2359296.0); - - f1 = - x - * _mm256_set1_pd( - 0.00065104166666666673894681149903362893383018672466278076171875); - f1 = floor(f1); - x -= f1 * _mm256_set1_pd(1536.0); - - f0 = x; - - f2 -= - _mm256_set1_pd(1531.0) - * floor( - f2 - * _mm256_set1_pd( - 0.0006531678641410842804659875326933615724556148052215576171875)); - f1 -= - _mm256_set1_pd(1531.0) - * floor( - f1 - * _mm256_set1_pd( - 0.0006531678641410842804659875326933615724556148052215576171875)); - f0 -= - _mm256_set1_pd(1531.0) - * floor( - f0 - * _mm256_set1_pd( - 0.0006531678641410842804659875326933615724556148052215576171875)); - - f2 *= _mm256_set1_pd(3.0); - f2 -= _mm256_set1_pd(2295.0); - f1 *= _mm256_set1_pd(3.0); - f1 -= _mm256_set1_pd(2295.0); - f0 *= _mm256_set1_pd(3.0); - f0 -= _mm256_set1_pd(2295.0); - - if2 = _mm256_cvtpd_epi32(f2); /* a2 b2 e2 f2 */ - if1 = _mm256_cvtpd_epi32(f1); /* a1 b1 e1 f1 */ - if0 = _mm256_cvtpd_epi32(f0); /* a0 b0 e0 f0 */ - - f[6 * j + 0] = _mm_extract_epi32(if0, 0); - f[6 * j + 1] = _mm_extract_epi32(if1, 0); - f[6 * j + 2] = _mm_extract_epi32(if2, 0); - f[6 * j + 3] = _mm_extract_epi32(if0, 1); - f[6 * j + 4] = _mm_extract_epi32(if1, 1); - f[6 * j + 5] = _mm_extract_epi32(if2, 1); - - f[6 * j + 12] = _mm_extract_epi32(if0, 2); - f[6 * j + 13] = _mm_extract_epi32(if1, 2); - f[6 * j + 14] = _mm_extract_epi32(if2, 2); - f[6 * j + 15] = _mm_extract_epi32(if0, 3); - f[6 * j + 16] = _mm_extract_epi32(if1, 3); - f[6 * j + 17] = _mm_extract_epi32(if2, 3); - } - - f += 24; - } - - for(i = 248; i < 253; ++i) - { - c0 = *c++; - c1 = *c++; - c2 = *c++; - c3 = *c++; - - /* f0 + f1*1536 + f2*1536^2 */ - /* = c0 + c1*256 + c2*256^2 + c3*256^3 */ - /* with each f between 0 and 1530 */ - - /* f2 = (64/9)c3 + (1/36)c2 + (1/9216)c1 + (1/2359296)c0 - [0,0.99675] */ - /* claim: 2^21 f2 < x < 2^21(f2+1) */ - /* where x = 14913081*c3 + 58254*c2 + 228*(c1+2) */ - /* proof: x - 2^21 f2 = 456 - (8/9)c0 + (4/9)c1 - (2/9)c2 + (1/9)c3 + 2^21 - * [0,0.99675] */ - /* at least 456 - (8/9)255 - (2/9)255 > 0 */ - /* at most 456 + (4/9)255 + (1/9)255 + 2^21 0.99675 < 2^21 */ - f2 = (14913081 * c3 + 58254 * c2 + 228 * (c1 + 2)) >> 21; - - c2 += c3 << 8; - c2 -= (f2 * 9) << 2; - /* f0 + f1*1536 */ - /* = c0 + c1*256 + c2*256^2 */ - /* c2 <= 35 = floor((1530+1530*1536)/256^2) */ - /* f1 = (128/3)c2 + (1/6)c1 + (1/1536)c0 - (1/1536)f0 */ - /* claim: 2^21 f1 < x < 2^21(f1+1) */ - /* where x = 89478485*c2 + 349525*c1 + 1365*(c0+1) */ - /* proof: x - 2^21 f1 = 1365 - (1/3)c2 - (1/3)c1 - (1/3)c0 + (4096/3)f0 */ - /* at least 1365 - (1/3)35 - (1/3)255 - (1/3)255 > 0 */ - /* at most 1365 + (4096/3)1530 < 2^21 */ - f1 = (89478485 * c2 + 349525 * c1 + 1365 * (c0 + 1)) >> 21; - - c1 += c2 << 8; - c1 -= (f1 * 3) << 1; - - c0 += c1 << 8; - f0 = c0; - - *f++ = modq_freeze(f0 * 3 + q - qshift); - *f++ = modq_freeze(f1 * 3 + q - qshift); - *f++ = modq_freeze(f2 * 3 + q - qshift); - } - - c0 = *c++; - c1 = *c++; - c2 = *c++; - - f1 = (89478485 * c2 + 349525 * c1 + 1365 * (c0 + 1)) >> 21; - - c1 += c2 << 8; - c1 -= (f1 * 3) << 1; - - c0 += c1 << 8; - f0 = c0; - - *f++ = modq_freeze(f0 * 3 + q - qshift); - *f++ = modq_freeze(f1 * 3 + q - qshift); - *f++ = 0; - *f++ = 0; - *f++ = 0; - *f++ = 0; - *f++ = 0; - *f++ = 0; - *f++ = 0; -} -#endif diff --git a/crypto/libntrup/src/avx/small.c b/crypto/libntrup/src/avx/small.c deleted file mode 100644 index f296899607..0000000000 --- a/crypto/libntrup/src/avx/small.c +++ /dev/null @@ -1,54 +0,0 @@ -#if __AVX2__ -#include -#include "params.h" -#include "small.h" - -/* XXX: these functions rely on p mod 4 = 1 */ - -/* all coefficients in -1, 0, 1 */ -void -small_encode(unsigned char *c, const small *f) -{ - small c0; - int i; - - for(i = 0; i < p / 4; ++i) - { - c0 = *f++ + 1; - c0 += (*f++ + 1) << 2; - c0 += (*f++ + 1) << 4; - c0 += (*f++ + 1) << 6; - *c++ = c0; - } - c0 = *f++ + 1; - *c++ = c0; -} - -void -small_decode(small *f, const unsigned char *c) -{ - unsigned char c0; - int i; - - for(i = 0; i < p / 4; ++i) - { - c0 = *c++; - *f++ = ((small)(c0 & 3)) - 1; - c0 >>= 2; - *f++ = ((small)(c0 & 3)) - 1; - c0 >>= 2; - *f++ = ((small)(c0 & 3)) - 1; - c0 >>= 2; - *f++ = ((small)(c0 & 3)) - 1; - } - c0 = *c++; - *f++ = ((small)(c0 & 3)) - 1; - *f++ = 0; - *f++ = 0; - *f++ = 0; - *f++ = 0; - *f++ = 0; - *f++ = 0; - *f++ = 0; -} -#endif \ No newline at end of file diff --git a/crypto/libntrup/src/avx/small.h b/crypto/libntrup/src/avx/small.h deleted file mode 100644 index ca8f54bf1e..0000000000 --- a/crypto/libntrup/src/avx/small.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef small_h -#define small_h - -#include - -typedef crypto_int8 small; - -#define small_encode crypto_kem_sntrup4591761_avx_small_encode -extern void -small_encode(unsigned char *, const small *); - -#define small_decode crypto_kem_sntrup4591761_avx_small_decode -extern void -small_decode(small *, const unsigned char *); - -#define small_random crypto_kem_sntrup4591761_avx_small_random -extern void -small_random(small *); - -#define small_random_weightw crypto_kem_sntrup4591761_avx_small_random_weightw -extern void -small_random_weightw(small *); - -#endif diff --git a/crypto/libntrup/src/avx/swap.c b/crypto/libntrup/src/avx/swap.c deleted file mode 100644 index 569470639d..0000000000 --- a/crypto/libntrup/src/avx/swap.c +++ /dev/null @@ -1,37 +0,0 @@ -#if __AVX2__ -#include -#include "swap.h" - -void -swap(void *x, void *y, int bytes, int mask) -{ - char c = mask; - __m256i maskvec = _mm256_set1_epi32(mask); - - while(bytes >= 32) - { - __m256i xi = _mm256_loadu_si256(x); - __m256i yi = _mm256_loadu_si256(y); - __m256i xinew = _mm256_blendv_epi8(xi, yi, maskvec); - __m256i yinew = _mm256_blendv_epi8(yi, xi, maskvec); - _mm256_storeu_si256(x, xinew); - _mm256_storeu_si256(y, yinew); - x = 32 + (char *)x; - y = 32 + (char *)y; - bytes -= 32; - } - while(bytes > 0) - { - char xi = *(char *)x; - char yi = *(char *)y; - char t = c & (xi ^ yi); - xi ^= t; - yi ^= t; - *(char *)x = xi; - *(char *)y = yi; - ++x; - ++y; - --bytes; - } -} -#endif \ No newline at end of file diff --git a/crypto/libntrup/src/avx/swap.h b/crypto/libntrup/src/avx/swap.h deleted file mode 100644 index 1f56ccf1e0..0000000000 --- a/crypto/libntrup/src/avx/swap.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef swap_h -#define swap_h - -#define swap crypto_kem_sntrup4591761_avx_swap -extern void -swap(void *, void *, int, int); - -#endif diff --git a/crypto/libntrup/src/avx/weight.c b/crypto/libntrup/src/avx/weight.c deleted file mode 100644 index 9d22b10167..0000000000 --- a/crypto/libntrup/src/avx/weight.c +++ /dev/null @@ -1,34 +0,0 @@ -#if __AVX2__ -#include -#include "params.h" -#include "r3.h" -#include -#include - -int -r3_weightw_mask(const small *r) -{ - int weight; - int i; - __m256i tally = _mm256_set1_epi32(0); - - for(i = 0; i < 768; i += 16) - { - __m256i x = _mm256_cvtepi8_epi16(_mm_loadu_si128((__m128i *)&r[i])); - x &= _mm256_set1_epi32(0x00010001); - tally = _mm256_add_epi16(tally, x); - } - - tally = _mm256_hadd_epi16(tally, tally); - tally = _mm256_hadd_epi16(tally, tally); - tally = _mm256_hadd_epi16(tally, tally); - - weight = _mm_extract_epi16(_mm256_extracti128_si256(tally, 0), 0) - + _mm_extract_epi16(_mm256_extracti128_si256(tally, 1), 0); - - weight -= w; - - return (-(crypto_int32)(crypto_uint16)weight) >> 30; -} - -#endif diff --git a/crypto/libntrup/src/files.txt b/crypto/libntrup/src/files.txt deleted file mode 100644 index 6e10fee3d7..0000000000 --- a/crypto/libntrup/src/files.txt +++ /dev/null @@ -1,32 +0,0 @@ -crypto/libntrup/src/ref/randomsmall.c -crypto/libntrup/src/ref/swap.c -crypto/libntrup/src/ref/rq_round3.c -crypto/libntrup/src/ref/rq_recip3.c -crypto/libntrup/src/ref/small.c -crypto/libntrup/src/ref/rq_mult.c -crypto/libntrup/src/ref/randomweightw.c -crypto/libntrup/src/ref/random32.c -crypto/libntrup/src/ref/dec.c -crypto/libntrup/src/ref/r3_mult.c -crypto/libntrup/src/ref/r3_recip.c -crypto/libntrup/src/ref/keypair.c -crypto/libntrup/src/ref/rq_rounded.c -crypto/libntrup/src/ref/enc.c -crypto/libntrup/src/ref/int32_sort.c -crypto/libntrup/src/ref/rq.c -crypto/libntrup/src/avx/randomsmall.c -crypto/libntrup/src/avx/weight.c -crypto/libntrup/src/avx/swap.c -crypto/libntrup/src/avx/rq_round3.c -crypto/libntrup/src/avx/rq_recip3.c -crypto/libntrup/src/avx/small.c -crypto/libntrup/src/avx/randomweightw.c -crypto/libntrup/src/avx/dec.c -crypto/libntrup/src/avx/r3_recip.c -crypto/libntrup/src/avx/keypair.c -crypto/libntrup/src/avx/rq_rounded.c -crypto/libntrup/src/avx/mult.c -crypto/libntrup/src/avx/enc.c -crypto/libntrup/src/avx/int32_sort.c -crypto/libntrup/src/avx/rq.c -crypto/libntrup/src/avx/rq_mod3.c diff --git a/crypto/libntrup/src/noavx-stubs.c b/crypto/libntrup/src/noavx-stubs.c deleted file mode 100644 index 023354c40c..0000000000 --- a/crypto/libntrup/src/noavx-stubs.c +++ /dev/null @@ -1,29 +0,0 @@ -// Stubs for compilers/builds without avx2 support -// -int -crypto_kem_enc_avx2(unsigned char *cstr, unsigned char *k, - const unsigned char *pk) -{ - (void)(cstr); - (void)(k); - (void)(pk); - return -1; -} - -int -crypto_kem_dec_avx2(unsigned char *k, const unsigned char *cstr, - const unsigned char *sk) -{ - (void)(k); - (void)(sk); - (void)(cstr); - return -1; -} - -int -crypto_kem_keypair_avx2(unsigned char *pk, unsigned char *sk) -{ - (void)(pk); - (void)(sk); - return -1; -} diff --git a/crypto/libntrup/src/ntru.cpp b/crypto/libntrup/src/ntru.cpp deleted file mode 100644 index 44b95cd4ec..0000000000 --- a/crypto/libntrup/src/ntru.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include -#include - -#ifdef __x86_64__ -#include -#include - -bool -supports_avx2() -{ - std::array< int, 4 > cpuinfo; - __cpuid(0, cpuinfo[0], cpuinfo[1], cpuinfo[2], cpuinfo[3]); - if (cpuinfo[0] < 7) - return false; - - __cpuid_count(7, 0, cpuinfo[0], cpuinfo[1], cpuinfo[2], cpuinfo[3]); - return cpuinfo[1] & (1 << 5); -} - -#else - -bool -supports_avx2() -{ - return false; -} - -#endif - -int (*__crypto_kem_enc)(unsigned char *cstr, unsigned char *k, - const unsigned char *pk); - -int (*__crypto_kem_dec)(unsigned char *k, const unsigned char *cstr, - const unsigned char *sk); - -int (*__crypto_kem_keypair)(unsigned char *pk, unsigned char *sk); - -extern "C" -{ - void - ntru_init(int force_no_avx2) - { - if(supports_avx2() && !force_no_avx2) - { - __crypto_kem_dec = &crypto_kem_dec_avx2; - __crypto_kem_enc = &crypto_kem_enc_avx2; - __crypto_kem_keypair = &crypto_kem_keypair_avx2; - } - else - { - __crypto_kem_dec = &crypto_kem_dec_ref; - __crypto_kem_enc = &crypto_kem_enc_ref; - __crypto_kem_keypair = &crypto_kem_keypair_ref; - } - } - - int - crypto_kem_enc(unsigned char *cstr, unsigned char *k, const unsigned char *pk) - { - return __crypto_kem_enc(cstr, k, pk); - } - - int - crypto_kem_dec(unsigned char *k, const unsigned char *cstr, - const unsigned char *sk) - { - return __crypto_kem_dec(k, cstr, sk); - } - - int - crypto_kem_keypair(unsigned char *pk, unsigned char *sk) - { - return __crypto_kem_keypair(pk, sk); - } -} diff --git a/crypto/libntrup/src/ref/README b/crypto/libntrup/src/ref/README deleted file mode 100644 index 30265ef710..0000000000 --- a/crypto/libntrup/src/ref/README +++ /dev/null @@ -1,32 +0,0 @@ -This is a reference implementation of Streamlined NTRU Prime 4591^761. -This implementation is designed primarily for clarity, subject to the -following constraints: - - * The implementation is written in C. The Sage implementation in the - NTRU Prime paper is considerably more concise (and compatible). - - * The implementation avoids data-dependent branches and array - indices. For example, conditional swaps are computed by arithmetic - rather than by branches. - - * The implementation avoids other C operations that often take - variable time. For example, divisions by 3 are computed via - multiplications and shifts. - -This implementation does _not_ sacrifice clarity for speed. - -This implementation has not yet been reviewed for correctness or for -constant-time behavior. It does pass various tests and has no known -bugs, but there are at least some platforms where multiplications take -variable time, and fixing this requires platform-specific effort; see -https://www.bearssl.org/ctmul.html and http://repository.tue.nl/800603. - -This implementation allows "benign malleability" of ciphertexts, as -defined in http://www.shoup.net/papers/iso-2_1.pdf. Specifically, each -32-bit ciphertext word encodes three integers between 0 and 1530; if -larger integers appear then they are silently reduced modulo 1531. -Similar comments apply to public keys. - -There is a separate "avx" implementation where similar comments apply, -except that "avx" _does_ sacrifice clarity for speed on CPUs with AVX2 -instructions. diff --git a/crypto/libntrup/src/ref/api.h b/crypto/libntrup/src/ref/api.h deleted file mode 100644 index 94d75538bc..0000000000 --- a/crypto/libntrup/src/ref/api.h +++ /dev/null @@ -1,4 +0,0 @@ -#define CRYPTO_SECRETKEYBYTES 1600 -#define CRYPTO_PUBLICKEYBYTES 1218 -#define CRYPTO_CIPHERTEXTBYTES 1047 -#define CRYPTO_BYTES 32 diff --git a/crypto/libntrup/src/ref/dec.c b/crypto/libntrup/src/ref/dec.c deleted file mode 100644 index c110465b2e..0000000000 --- a/crypto/libntrup/src/ref/dec.c +++ /dev/null @@ -1,75 +0,0 @@ -#ifdef KAT -#include -#endif -#include -#include -#include - -#include "params.h" -#include "small.h" -#include "mod3.h" -#include "rq.h" -#include "r3.h" - -int -crypto_kem_dec_ref(unsigned char *k, const unsigned char *cstr, - const unsigned char *sk) -{ - small f[p]; - modq h[p]; - small grecip[p]; - modq c[p]; - modq t[p]; - small t3[p]; - small r[p]; - modq hr[p]; - unsigned char rstr[small_encode_len]; - unsigned char hash[64]; - int i; - int result = 0; - int weight; - - small_decode(f, sk); - small_decode(grecip, sk + small_encode_len); - rq_decode(h, sk + 2 * small_encode_len); - - rq_decoderounded(c, cstr + 32); - - rq_mult(t, c, f); - for(i = 0; i < p; ++i) - t3[i] = mod3_freeze(modq_freeze(3 * t[i])); - - r3_mult(r, t3, grecip); - -#ifdef KAT - { - int j; - printf("decrypt r:"); - for(j = 0; j < p; ++j) - if(r[j] == 1) - printf(" +%d", j); - else if(r[j] == -1) - printf(" -%d", j); - printf("\n"); - } -#endif - - weight = 0; - for(i = 0; i < p; ++i) - weight += (1 & r[i]); - weight -= w; - result |= modq_nonzero_mask(weight); /* XXX: puts limit on p */ - - rq_mult(hr, h, r); - rq_round3(hr, hr); - for(i = 0; i < p; ++i) - result |= modq_nonzero_mask(hr[i] - c[i]); - - small_encode(rstr, r); - crypto_hash_sha512(hash, rstr, sizeof rstr); - result |= crypto_verify_32(hash, cstr); - - for(i = 0; i < 32; ++i) - k[i] = (hash[32 + i] & ~result); - return result; -} diff --git a/crypto/libntrup/src/ref/enc.c b/crypto/libntrup/src/ref/enc.c deleted file mode 100644 index ccebf73c6d..0000000000 --- a/crypto/libntrup/src/ref/enc.c +++ /dev/null @@ -1,49 +0,0 @@ -#ifdef KAT -#include -#endif - -#include -#include "params.h" -#include "small.h" -#include "rq.h" -#include -#include - -int -crypto_kem_enc_ref(unsigned char *cstr, unsigned char *k, - const unsigned char *pk) -{ - small r[p]; - modq h[p]; - modq c[p]; - unsigned char rstr[small_encode_len]; - unsigned char hash[64]; - - small_random_weightw(r); - -#ifdef KAT - { - int i; - printf("encrypt r:"); - for(i = 0; i < p; ++i) - if(r[i] == 1) - printf(" +%d", i); - else if(r[i] == -1) - printf(" -%d", i); - printf("\n"); - } -#endif - - small_encode(rstr, r); - crypto_hash_sha512(hash, rstr, sizeof rstr); - - rq_decode(h, pk); - rq_mult(c, h, r); - rq_round3(c, c); - - memcpy(k, hash + 32, 32); - memcpy(cstr, hash, 32); - rq_encoderounded(cstr + 32, c); - - return 0; -} diff --git a/crypto/libntrup/src/ref/implementors b/crypto/libntrup/src/ref/implementors deleted file mode 100644 index 51ac31ea2e..0000000000 --- a/crypto/libntrup/src/ref/implementors +++ /dev/null @@ -1,5 +0,0 @@ -Alphabetical order: -Daniel J. Bernstein -Chitchanok Chuengsatiansup -Tanja Lange -Christine van Vredendaal diff --git a/crypto/libntrup/src/ref/int32_sort.c b/crypto/libntrup/src/ref/int32_sort.c deleted file mode 100644 index 21d9cf2389..0000000000 --- a/crypto/libntrup/src/ref/int32_sort.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "int32_sort.h" -#include - -static void -minmax(crypto_int32 *x, crypto_int32 *y) -{ - crypto_uint32 xi = *x; - crypto_uint32 yi = *y; - crypto_uint32 xy = xi ^ yi; - crypto_uint32 c = yi - xi; - c ^= xy & (c ^ yi); - c >>= 31; - c = -c; - c &= xy; - *x = xi ^ c; - *y = yi ^ c; -} - -void -int32_sort(crypto_int32 *x, int n) -{ - int top, p, q, i; - - if(n < 2) - return; - top = 1; - while(top < n - top) - top += top; - - for(p = top; p > 0; p >>= 1) - { - for(i = 0; i < n - p; ++i) - if(!(i & p)) - minmax(x + i, x + i + p); - for(q = top; q > p; q >>= 1) - for(i = 0; i < n - q; ++i) - if(!(i & p)) - minmax(x + i + p, x + i + q); - } -} diff --git a/crypto/libntrup/src/ref/int32_sort.h b/crypto/libntrup/src/ref/int32_sort.h deleted file mode 100644 index 8639a146a0..0000000000 --- a/crypto/libntrup/src/ref/int32_sort.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef int32_sort_h -#define int32_sort_h - -#include - -#define int32_sort crypto_kem_sntrup4591761_ref_int32_sort -extern void -int32_sort(crypto_int32 *, int); - -#endif diff --git a/crypto/libntrup/src/ref/keypair.c b/crypto/libntrup/src/ref/keypair.c deleted file mode 100644 index 4e5d0522c7..0000000000 --- a/crypto/libntrup/src/ref/keypair.c +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include "modq.h" -#include "params.h" -#include "r3.h" -#include "small.h" -#include "rq.h" -#include - -#if crypto_kem_PUBLICKEYBYTES != rq_encode_len -#error "crypto_kem_PUBLICKEYBYTES must match rq_encode_len" -#endif -#if crypto_kem_SECRETKEYBYTES != rq_encode_len + 2 * small_encode_len -#error \ - "crypto_kem_SECRETKEYBYTES must match rq_encode_len + 2 * small_encode_len" -#endif - -int -crypto_kem_keypair_ref(unsigned char *pk, unsigned char *sk) -{ - small g[p]; - small grecip[p]; - small f[p]; - modq f3recip[p]; - modq h[p]; - - do - small_random(g); - while(r3_recip(grecip, g) != 0); - - small_random_weightw(f); - rq_recip3(f3recip, f); - - rq_mult(h, f3recip, g); - - rq_encode(pk, h); - small_encode(sk, f); - small_encode(sk + small_encode_len, grecip); - memcpy(sk + 2 * small_encode_len, pk, rq_encode_len); - - return 0; -} diff --git a/crypto/libntrup/src/ref/mod3.h b/crypto/libntrup/src/ref/mod3.h deleted file mode 100644 index 97ec3b55fa..0000000000 --- a/crypto/libntrup/src/ref/mod3.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef mod3_h -#define mod3_h - -#include "small.h" -#include - -/* -1 if x is nonzero, 0 otherwise */ -static inline int -mod3_nonzero_mask(small x) -{ - return -x * x; -} - -/* input between -100000 and 100000 */ -/* output between -1 and 1 */ -static inline small -mod3_freeze(crypto_int32 a) -{ - a -= 3 * ((10923 * a) >> 15); - a -= 3 * ((89478485 * a + 134217728) >> 28); - return a; -} - -static inline small -mod3_minusproduct(small a, small b, small c) -{ - crypto_int32 A = a; - crypto_int32 B = b; - crypto_int32 C = c; - return mod3_freeze(A - B * C); -} - -static inline small -mod3_plusproduct(small a, small b, small c) -{ - crypto_int32 A = a; - crypto_int32 B = b; - crypto_int32 C = c; - return mod3_freeze(A + B * C); -} - -static inline small -mod3_product(small a, small b) -{ - return a * b; -} - -static inline small -mod3_sum(small a, small b) -{ - crypto_int32 A = a; - crypto_int32 B = b; - return mod3_freeze(A + B); -} - -static inline small -mod3_reciprocal(small a1) -{ - return a1; -} - -static inline small -mod3_quotient(small num, small den) -{ - return mod3_product(num, mod3_reciprocal(den)); -} - -#endif diff --git a/crypto/libntrup/src/ref/modq.h b/crypto/libntrup/src/ref/modq.h deleted file mode 100644 index 6484a4f96a..0000000000 --- a/crypto/libntrup/src/ref/modq.h +++ /dev/null @@ -1,101 +0,0 @@ -#ifndef modq_h -#define modq_h - -#include -#include -#include -#include - -typedef crypto_int16 modq; - -/* -1 if x is nonzero, 0 otherwise */ -static inline int -modq_nonzero_mask(modq x) -{ - crypto_int32 r = (crypto_uint16)x; - r = -r; - r >>= 30; - return r; -} - -/* input between -9000000 and 9000000 */ -/* output between -2295 and 2295 */ -static inline modq -modq_freeze(crypto_int32 a) -{ - a -= 4591 * ((228 * a) >> 20); - a -= 4591 * ((58470 * a + 134217728) >> 28); - return a; -} - -static inline modq -modq_minusproduct(modq a, modq b, modq c) -{ - crypto_int32 A = a; - crypto_int32 B = b; - crypto_int32 C = c; - return modq_freeze(A - B * C); -} - -static inline modq -modq_plusproduct(modq a, modq b, modq c) -{ - crypto_int32 A = a; - crypto_int32 B = b; - crypto_int32 C = c; - return modq_freeze(A + B * C); -} - -static inline modq -modq_product(modq a, modq b) -{ - crypto_int32 A = a; - crypto_int32 B = b; - return modq_freeze(A * B); -} - -static inline modq -modq_square(modq a) -{ - crypto_int32 A = a; - return modq_freeze(A * A); -} - -static inline modq -modq_sum(modq a, modq b) -{ - crypto_int32 A = a; - crypto_int32 B = b; - return modq_freeze(A + B); -} - -static inline modq -modq_reciprocal(modq a1) -{ - modq a2 = modq_square(a1); - modq a3 = modq_product(a2, a1); - modq a4 = modq_square(a2); - modq a8 = modq_square(a4); - modq a16 = modq_square(a8); - modq a32 = modq_square(a16); - modq a35 = modq_product(a32, a3); - modq a70 = modq_square(a35); - modq a140 = modq_square(a70); - modq a143 = modq_product(a140, a3); - modq a286 = modq_square(a143); - modq a572 = modq_square(a286); - modq a1144 = modq_square(a572); - modq a1147 = modq_product(a1144, a3); - modq a2294 = modq_square(a1147); - modq a4588 = modq_square(a2294); - modq a4589 = modq_product(a4588, a1); - return a4589; -} - -static inline modq -modq_quotient(modq num, modq den) -{ - return modq_product(num, modq_reciprocal(den)); -} - -#endif diff --git a/crypto/libntrup/src/ref/params.h b/crypto/libntrup/src/ref/params.h deleted file mode 100644 index c55d435e19..0000000000 --- a/crypto/libntrup/src/ref/params.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef params_h -#define params_h - -#define q 4591 -/* XXX: also built into modq in various ways */ - -#define qshift 2295 -#define p 761 -#ifdef _MSC_VER -#define LOOPS 2 * p + 1 -#endif -#define w 286 - -#define rq_encode_len 1218 -#define small_encode_len 191 - -#endif diff --git a/crypto/libntrup/src/ref/r3.h b/crypto/libntrup/src/ref/r3.h deleted file mode 100644 index 07a5fb4b96..0000000000 --- a/crypto/libntrup/src/ref/r3.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef r3_h -#define r3_h - -#include "small.h" - -#define r3_mult crypto_kem_sntrup4591761_ref_r3_mult -extern void -r3_mult(small *, const small *, const small *); - -#define r3_recip crypto_kem_sntrup4591761_ref_r3_recip -extern int -r3_recip(small *, const small *); - -#endif diff --git a/crypto/libntrup/src/ref/r3_mult.c b/crypto/libntrup/src/ref/r3_mult.c deleted file mode 100644 index 8569315b1c..0000000000 --- a/crypto/libntrup/src/ref/r3_mult.c +++ /dev/null @@ -1,35 +0,0 @@ -#include "params.h" -#include "mod3.h" -#include "r3.h" - -void -r3_mult(small *h, const small *f, const small *g) -{ - small fg[p + p - 1]; - small result; - int i, j; - - for(i = 0; i < p; ++i) - { - result = 0; - for(j = 0; j <= i; ++j) - result = mod3_plusproduct(result, f[j], g[i - j]); - fg[i] = result; - } - for(i = p; i < p + p - 1; ++i) - { - result = 0; - for(j = i - p + 1; j < p; ++j) - result = mod3_plusproduct(result, f[j], g[i - j]); - fg[i] = result; - } - - for(i = p + p - 2; i >= p; --i) - { - fg[i - p] = mod3_sum(fg[i - p], fg[i]); - fg[i - p + 1] = mod3_sum(fg[i - p + 1], fg[i]); - } - - for(i = 0; i < p; ++i) - h[i] = fg[i]; -} diff --git a/crypto/libntrup/src/ref/r3_recip.c b/crypto/libntrup/src/ref/r3_recip.c deleted file mode 100644 index 567c6a30c3..0000000000 --- a/crypto/libntrup/src/ref/r3_recip.c +++ /dev/null @@ -1,151 +0,0 @@ -#include "params.h" -#include "mod3.h" -#include "swap.h" -#include "r3.h" - -/* caller must ensure that x-y does not overflow */ -static int -smaller_mask(int x, int y) -{ - return (x - y) >> 31; -} - -static void -vectormod3_product(small *z, int len, const small *x, const small c) -{ - int i; - for(i = 0; i < len; ++i) - z[i] = mod3_product(x[i], c); -} - -static void -vectormod3_minusproduct(small *z, int len, const small *x, const small *y, - const small c) -{ - int i; - for(i = 0; i < len; ++i) - z[i] = mod3_minusproduct(x[i], y[i], c); -} - -static void -vectormod3_shift(small *z, int len) -{ - int i; - for(i = len - 1; i > 0; --i) - z[i] = z[i - 1]; - z[0] = 0; -} - -/* -r = s^(-1) mod m, returning 0, if s is invertible mod m -or returning -1 if s is not invertible mod m -r,s are polys of degree

= LOOPS) - break; - - c = mod3_quotient(g[p], f[p]); - - vectormod3_minusproduct(g, p + 1, g, f, c); - vectormod3_shift(g, p + 1); - -#ifdef SIMPLER - vectormod3_minusproduct(v, loops + 1, v, u, c); - vectormod3_shift(v, loops + 1); -#else - if(loop < p) - { - vectormod3_minusproduct(v, loop + 1, v, u, c); - vectormod3_shift(v, loop + 2); - } - else - { - vectormod3_minusproduct(v + loop - p, p + 1, v + loop - p, u + loop - p, - c); - vectormod3_shift(v + loop - p, p + 2); - } -#endif - - e -= 1; - - ++loop; - - swapmask = smaller_mask(e, d) & mod3_nonzero_mask(g[p]); - swap(&e, &d, sizeof e, swapmask); - swap(f, g, (p + 1) * sizeof(small), swapmask); - -#ifdef SIMPLER - swap(u, v, (loops + 1) * sizeof(small), swapmask); -#else - if(loop < p) - { - swap(u, v, (loop + 1) * sizeof(small), swapmask); - } - else - { - swap(u + loop - p, v + loop - p, (p + 1) * sizeof(small), swapmask); - } -#endif - } - - c = mod3_reciprocal(f[p]); - vectormod3_product(r, p, u + p, c); - return smaller_mask(0, d); -} diff --git a/crypto/libntrup/src/ref/random32.c b/crypto/libntrup/src/ref/random32.c deleted file mode 100644 index be1a764a7c..0000000000 --- a/crypto/libntrup/src/ref/random32.c +++ /dev/null @@ -1,28 +0,0 @@ -#include -#include "small.h" - -#ifdef KAT -/* NIST KAT generator fails to provide chunk-independence */ -static unsigned char x[4 * 761]; -static long long pos = 4 * 761; -#endif - -crypto_int32 -small_random32(void) -{ -#ifdef KAT - if(pos == 4 * 761) - { - randombytes(x, sizeof x); - pos = 0; - } - pos += 4; - return x[pos - 4] + (x[pos - 3] << 8) + (x[pos - 2] << 16) - + (x[pos - 1] << 24); -#else - unsigned char x[4]; - randombytes(x, 4); - uint32_t x4 = x[3] << 24; - return x[0] + (x[1] << 8) + (x[2] << 16) + x4; -#endif -} diff --git a/crypto/libntrup/src/ref/randomsmall.c b/crypto/libntrup/src/ref/randomsmall.c deleted file mode 100644 index 1c417ff259..0000000000 --- a/crypto/libntrup/src/ref/randomsmall.c +++ /dev/null @@ -1,16 +0,0 @@ -#include "params.h" -#include -#include -#include "small.h" - -void -small_random(small *g) -{ - int i; - - for(i = 0; i < p; ++i) - { - crypto_uint32 r = small_random32(); - g[i] = (small)(((1073741823 & r) * 3) >> 30) - 1; - } -} diff --git a/crypto/libntrup/src/ref/randomweightw.c b/crypto/libntrup/src/ref/randomweightw.c deleted file mode 100644 index 75a0f0f36e..0000000000 --- a/crypto/libntrup/src/ref/randomweightw.c +++ /dev/null @@ -1,21 +0,0 @@ -#include "params.h" -#include -#include "int32_sort.h" -#include "small.h" - -void -small_random_weightw(small *f) -{ - crypto_int32 r[p]; - int i; - - for(i = 0; i < p; ++i) - r[i] = small_random32(); - for(i = 0; i < w; ++i) - r[i] &= -2; - for(i = w; i < p; ++i) - r[i] = (r[i] & -3) | 1; - int32_sort(r, p); - for(i = 0; i < p; ++i) - f[i] = ((small)(r[i] & 3)) - 1; -} diff --git a/crypto/libntrup/src/ref/rq.c b/crypto/libntrup/src/ref/rq.c deleted file mode 100644 index 36c21ed7d2..0000000000 --- a/crypto/libntrup/src/ref/rq.c +++ /dev/null @@ -1,142 +0,0 @@ -#include "params.h" -#include "rq.h" - -void -rq_encode(unsigned char *c, const modq *f) -{ - crypto_int32 f0, f1, f2, f3, f4; - int i; - - for(i = 0; i < p / 5; ++i) - { - f0 = *f++ + qshift; - f1 = *f++ + qshift; - f2 = *f++ + qshift; - f3 = *f++ + qshift; - f4 = *f++ + qshift; - /* now want f0 + 6144*f1 + ... as a 64-bit integer */ - f1 *= 3; - f2 *= 9; - f3 *= 27; - f4 *= 81; - /* now want f0 + f1<<11 + f2<<22 + f3<<33 + f4<<44 */ - f0 += f1 << 11; - *c++ = f0; - f0 >>= 8; - *c++ = f0; - f0 >>= 8; - f0 += f2 << 6; - *c++ = f0; - f0 >>= 8; - *c++ = f0; - f0 >>= 8; - f0 += f3 << 1; - *c++ = f0; - f0 >>= 8; - f0 += f4 << 4; - *c++ = f0; - f0 >>= 8; - *c++ = f0; - f0 >>= 8; - *c++ = f0; - } - /* XXX: using p mod 5 = 1 */ - f0 = *f++ + qshift; - *c++ = f0; - f0 >>= 8; - *c++ = f0; -} - -void -rq_decode(modq *f, const unsigned char *c) -{ - crypto_uint32 c0, c1, c2, c3, c4, c5, c6, c7; - crypto_uint32 f0, f1, f2, f3, f4; - int i; - - for(i = 0; i < p / 5; ++i) - { - c0 = *c++; - c1 = *c++; - c2 = *c++; - c3 = *c++; - c4 = *c++; - c5 = *c++; - c6 = *c++; - c7 = *c++; - - /* f0 + f1*6144 + f2*6144^2 + f3*6144^3 + f4*6144^4 */ - /* = c0 + c1*256 + ... + c6*256^6 + c7*256^7 */ - /* with each f between 0 and 4590 */ - - c6 += c7 << 8; - /* c6 <= 23241 = floor(4591*6144^4/2^48) */ - /* f4 = (16/81)c6 + (1/1296)(c5+[0,1]) - [0,0.75] */ - /* claim: 2^19 f4 < x < 2^19(f4+1) */ - /* where x = 103564 c6 + 405(c5+1) */ - /* proof: x - 2^19 f4 = (76/81)c6 + (37/81)c5 + 405 - (32768/81)[0,1] + - * 2^19[0,0.75] */ - /* at least 405 - 32768/81 > 0 */ - /* at most (76/81)23241 + (37/81)255 + 405 + 2^19 0.75 < 2^19 */ - f4 = (103564 * c6 + 405 * (c5 + 1)) >> 19; - - c5 += c6 << 8; - c5 -= (f4 * 81) << 4; - c4 += c5 << 8; - - /* f0 + f1*6144 + f2*6144^2 + f3*6144^3 */ - /* = c0 + c1*256 + c2*256^2 + c3*256^3 + c4*256^4 */ - /* c4 <= 247914 = floor(4591*6144^3/2^32) */ - /* f3 = (1/54)(c4+[0,1]) - [0,0.75] */ - /* claim: 2^19 f3 < x < 2^19(f3+1) */ - /* where x = 9709(c4+2) */ - /* proof: x - 2^19 f3 = 19418 - (1/27)c4 - (262144/27)[0,1] + 2^19[0,0.75] - */ - /* at least 19418 - 247914/27 - 262144/27 > 0 */ - /* at most 19418 + 2^19 0.75 < 2^19 */ - f3 = (9709 * (c4 + 2)) >> 19; - - c4 -= (f3 * 27) << 1; - c3 += c4 << 8; - /* f0 + f1*6144 + f2*6144^2 */ - /* = c0 + c1*256 + c2*256^2 + c3*256^3 */ - /* c3 <= 10329 = floor(4591*6144^2/2^24) */ - /* f2 = (4/9)c3 + (1/576)c2 + (1/147456)c1 + (1/37748736)c0 - [0,0.75] */ - /* claim: 2^19 f2 < x < 2^19(f2+1) */ - /* where x = 233017 c3 + 910(c2+2) */ - /* proof: x - 2^19 f2 = 1820 + (1/9)c3 - (2/9)c2 - (32/9)c1 - (1/72)c0 + - * 2^19[0,0.75] */ - /* at least 1820 - (2/9)255 - (32/9)255 - (1/72)255 > 0 */ - /* at most 1820 + (1/9)10329 + 2^19 0.75 < 2^19 */ - f2 = (233017 * c3 + 910 * (c2 + 2)) >> 19; - - c2 += c3 << 8; - c2 -= (f2 * 9) << 6; - c1 += c2 << 8; - /* f0 + f1*6144 */ - /* = c0 + c1*256 */ - /* c1 <= 110184 = floor(4591*6144/2^8) */ - /* f1 = (1/24)c1 + (1/6144)c0 - (1/6144)f0 */ - /* claim: 2^19 f1 < x < 2^19(f1+1) */ - /* where x = 21845(c1+2) + 85 c0 */ - /* proof: x - 2^19 f1 = 43690 - (1/3)c1 - (1/3)c0 + 2^19 [0,0.75] */ - /* at least 43690 - (1/3)110184 - (1/3)255 > 0 */ - /* at most 43690 + 2^19 0.75 < 2^19 */ - f1 = (21845 * (c1 + 2) + 85 * c0) >> 19; - - c1 -= (f1 * 3) << 3; - c0 += c1 << 8; - f0 = c0; - - *f++ = modq_freeze(f0 + q - qshift); - *f++ = modq_freeze(f1 + q - qshift); - *f++ = modq_freeze(f2 + q - qshift); - *f++ = modq_freeze(f3 + q - qshift); - *f++ = modq_freeze(f4 + q - qshift); - } - - c0 = *c++; - c1 = *c++; - c0 += c1 << 8; - *f++ = modq_freeze(c0 + q - qshift); -} diff --git a/crypto/libntrup/src/ref/rq.h b/crypto/libntrup/src/ref/rq.h deleted file mode 100644 index 69d800d95d..0000000000 --- a/crypto/libntrup/src/ref/rq.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef rq_h -#define rq_h - -#include "modq.h" -#include "small.h" - -#define rq_encode crypto_kem_sntrup4591761_ref_rq_encode -extern void -rq_encode(unsigned char *, const modq *); - -#define rq_decode crypto_kem_sntrup4591761_ref_rq_decode -extern void -rq_decode(modq *, const unsigned char *); - -#define rq_encoderounded crypto_kem_sntrup4591761_ref_rq_encoderounded -extern void -rq_encoderounded(unsigned char *, const modq *); - -#define rq_decoderounded crypto_kem_sntrup4591761_ref_rq_decoderounded -extern void -rq_decoderounded(modq *, const unsigned char *); - -#define rq_round3 crypto_kem_sntrup4591761_ref_rq_round -extern void -rq_round3(modq *, const modq *); - -#define rq_mult crypto_kem_sntrup4591761_ref_rq_mult -extern void -rq_mult(modq *, const modq *, const small *); - -#define rq_recip3 crypto_kem_sntrup4591761_ref_rq_recip3 -int -rq_recip3(modq *, const small *); - -#endif diff --git a/crypto/libntrup/src/ref/rq_mult.c b/crypto/libntrup/src/ref/rq_mult.c deleted file mode 100644 index e8be2f2cf1..0000000000 --- a/crypto/libntrup/src/ref/rq_mult.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "params.h" -#include "rq.h" - -void -rq_mult(modq *h, const modq *f, const small *g) -{ - modq fg[p + p - 1]; - modq result; - int i, j; - - for(i = 0; i < p; ++i) - { - result = 0; - for(j = 0; j <= i; ++j) - result = modq_plusproduct(result, f[j], g[i - j]); - fg[i] = result; - } - for(i = p; i < p + p - 1; ++i) - { - result = 0; - for(j = i - p + 1; j < p; ++j) - result = modq_plusproduct(result, f[j], g[i - j]); - fg[i] = result; - } - - for(i = p + p - 2; i >= p; --i) - { - fg[i - p] = modq_sum(fg[i - p], fg[i]); - fg[i - p + 1] = modq_sum(fg[i - p + 1], fg[i]); - } - - for(i = 0; i < p; ++i) - h[i] = fg[i]; -} diff --git a/crypto/libntrup/src/ref/rq_recip3.c b/crypto/libntrup/src/ref/rq_recip3.c deleted file mode 100644 index 78e8c63d64..0000000000 --- a/crypto/libntrup/src/ref/rq_recip3.c +++ /dev/null @@ -1,151 +0,0 @@ -#include "params.h" -#include "swap.h" -#include "rq.h" - -/* caller must ensure that x-y does not overflow */ -static int -smaller_mask(int x, int y) -{ - return (x - y) >> 31; -} - -static void -vectormodq_product(modq *z, int len, const modq *x, const modq c) -{ - int i; - for(i = 0; i < len; ++i) - z[i] = modq_product(x[i], c); -} - -static void -vectormodq_minusproduct(modq *z, int len, const modq *x, const modq *y, - const modq c) -{ - int i; - for(i = 0; i < len; ++i) - z[i] = modq_minusproduct(x[i], y[i], c); -} - -static void -vectormodq_shift(modq *z, int len) -{ - int i; - for(i = len - 1; i > 0; --i) - z[i] = z[i - 1]; - z[0] = 0; -} - -/* -r = (3s)^(-1) mod m, returning 0, if s is invertible mod m -or returning -1 if s is not invertible mod m -r,s are polys of degree

= LOOPS) - break; - - c = modq_quotient(g[p], f[p]); - - vectormodq_minusproduct(g, p + 1, g, f, c); - vectormodq_shift(g, p + 1); - -#ifdef SIMPLER - vectormodq_minusproduct(v, loops + 1, v, u, c); - vectormodq_shift(v, loops + 1); -#else - if(loop < p) - { - vectormodq_minusproduct(v, loop + 1, v, u, c); - vectormodq_shift(v, loop + 2); - } - else - { - vectormodq_minusproduct(v + loop - p, p + 1, v + loop - p, u + loop - p, - c); - vectormodq_shift(v + loop - p, p + 2); - } -#endif - - e -= 1; - - ++loop; - - swapmask = smaller_mask(e, d) & modq_nonzero_mask(g[p]); - swap(&e, &d, sizeof e, swapmask); - swap(f, g, (p + 1) * sizeof(modq), swapmask); - -#ifdef SIMPLER - swap(u, v, (loops + 1) * sizeof(modq), swapmask); -#else - if(loop < p) - { - swap(u, v, (loop + 1) * sizeof(modq), swapmask); - } - else - { - swap(u + loop - p, v + loop - p, (p + 1) * sizeof(modq), swapmask); - } -#endif - } - - c = modq_reciprocal(f[p]); - vectormodq_product(r, p, u + p, c); - return smaller_mask(0, d); -} diff --git a/crypto/libntrup/src/ref/rq_round3.c b/crypto/libntrup/src/ref/rq_round3.c deleted file mode 100644 index 41853a30a1..0000000000 --- a/crypto/libntrup/src/ref/rq_round3.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "params.h" -#include "rq.h" - -void -rq_round3(modq *h, const modq *f) -{ - int i; - - for(i = 0; i < p; ++i) - h[i] = ((21846 * (f[i] + 2295) + 32768) >> 16) * 3 - 2295; -} diff --git a/crypto/libntrup/src/ref/rq_rounded.c b/crypto/libntrup/src/ref/rq_rounded.c deleted file mode 100644 index f524f3b3c6..0000000000 --- a/crypto/libntrup/src/ref/rq_rounded.c +++ /dev/null @@ -1,111 +0,0 @@ -#include "params.h" -#include -#include "rq.h" - -void -rq_encoderounded(unsigned char *c, const modq *f) -{ - crypto_int32 f0, f1, f2; - int i; - - for(i = 0; i < p / 3; ++i) - { - f0 = *f++ + qshift; - f1 = *f++ + qshift; - f2 = *f++ + qshift; - f0 = (21846 * f0) >> 16; - f1 = (21846 * f1) >> 16; - f2 = (21846 * f2) >> 16; - /* now want f0 + f1*1536 + f2*1536^2 as a 32-bit integer */ - f2 *= 3; - f1 += f2 << 9; - f1 *= 3; - f0 += f1 << 9; - *c++ = f0; - f0 >>= 8; - *c++ = f0; - f0 >>= 8; - *c++ = f0; - f0 >>= 8; - *c++ = f0; - } - /* XXX: using p mod 3 = 2 */ - f0 = *f++ + qshift; - f1 = *f++ + qshift; - f0 = (21846 * f0) >> 16; - f1 = (21846 * f1) >> 16; - f1 *= 3; - f0 += f1 << 9; - *c++ = f0; - f0 >>= 8; - *c++ = f0; - f0 >>= 8; - *c++ = f0; -} - -void -rq_decoderounded(modq *f, const unsigned char *c) -{ - crypto_uint32 c0, c1, c2, c3; - crypto_uint32 f0, f1, f2; - int i; - - for(i = 0; i < p / 3; ++i) - { - c0 = *c++; - c1 = *c++; - c2 = *c++; - c3 = *c++; - - /* f0 + f1*1536 + f2*1536^2 */ - /* = c0 + c1*256 + c2*256^2 + c3*256^3 */ - /* with each f between 0 and 1530 */ - - /* f2 = (64/9)c3 + (1/36)c2 + (1/9216)c1 + (1/2359296)c0 - [0,0.99675] */ - /* claim: 2^21 f2 < x < 2^21(f2+1) */ - /* where x = 14913081*c3 + 58254*c2 + 228*(c1+2) */ - /* proof: x - 2^21 f2 = 456 - (8/9)c0 + (4/9)c1 - (2/9)c2 + (1/9)c3 + 2^21 - * [0,0.99675] */ - /* at least 456 - (8/9)255 - (2/9)255 > 0 */ - /* at most 456 + (4/9)255 + (1/9)255 + 2^21 0.99675 < 2^21 */ - f2 = (14913081 * c3 + 58254 * c2 + 228 * (c1 + 2)) >> 21; - - c2 += c3 << 8; - c2 -= (f2 * 9) << 2; - /* f0 + f1*1536 */ - /* = c0 + c1*256 + c2*256^2 */ - /* c2 <= 35 = floor((1530+1530*1536)/256^2) */ - /* f1 = (128/3)c2 + (1/6)c1 + (1/1536)c0 - (1/1536)f0 */ - /* claim: 2^21 f1 < x < 2^21(f1+1) */ - /* where x = 89478485*c2 + 349525*c1 + 1365*(c0+1) */ - /* proof: x - 2^21 f1 = 1365 - (1/3)c2 - (1/3)c1 - (1/3)c0 + (4096/3)f0 */ - /* at least 1365 - (1/3)35 - (1/3)255 - (1/3)255 > 0 */ - /* at most 1365 + (4096/3)1530 < 2^21 */ - f1 = (89478485 * c2 + 349525 * c1 + 1365 * (c0 + 1)) >> 21; - - c1 += c2 << 8; - c1 -= (f1 * 3) << 1; - - c0 += c1 << 8; - f0 = c0; - - *f++ = modq_freeze(f0 * 3 + q - qshift); - *f++ = modq_freeze(f1 * 3 + q - qshift); - *f++ = modq_freeze(f2 * 3 + q - qshift); - } - - c0 = *c++; - c1 = *c++; - c2 = *c++; - - f1 = (89478485 * c2 + 349525 * c1 + 1365 * (c0 + 1)) >> 21; - - c1 += c2 << 8; - c1 -= (f1 * 3) << 1; - - c0 += c1 << 8; - f0 = c0; - - *f++ = modq_freeze(f0 * 3 + q - qshift); - *f++ = modq_freeze(f1 * 3 + q - qshift); -} diff --git a/crypto/libntrup/src/ref/small.c b/crypto/libntrup/src/ref/small.c deleted file mode 100644 index 209de23693..0000000000 --- a/crypto/libntrup/src/ref/small.c +++ /dev/null @@ -1,44 +0,0 @@ -#include "params.h" -#include "small.h" - -/* XXX: these functions rely on p mod 4 = 1 */ - -/* all coefficients in -1, 0, 1 */ -void -small_encode(unsigned char *c, const small *f) -{ - small c0; - int i; - - for(i = 0; i < p / 4; ++i) - { - c0 = *f++ + 1; - c0 += (*f++ + 1) << 2; - c0 += (*f++ + 1) << 4; - c0 += (*f++ + 1) << 6; - *c++ = c0; - } - c0 = *f++ + 1; - *c++ = c0; -} - -void -small_decode(small *f, const unsigned char *c) -{ - unsigned char c0; - int i; - - for(i = 0; i < p / 4; ++i) - { - c0 = *c++; - *f++ = ((small)(c0 & 3)) - 1; - c0 >>= 2; - *f++ = ((small)(c0 & 3)) - 1; - c0 >>= 2; - *f++ = ((small)(c0 & 3)) - 1; - c0 >>= 2; - *f++ = ((small)(c0 & 3)) - 1; - } - c0 = *c++; - *f++ = ((small)(c0 & 3)) - 1; -} diff --git a/crypto/libntrup/src/ref/small.h b/crypto/libntrup/src/ref/small.h deleted file mode 100644 index de58ef9d05..0000000000 --- a/crypto/libntrup/src/ref/small.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef small_h -#define small_h - -#include -#include - -typedef crypto_int8 small; - -#define small_encode crypto_kem_sntrup4591761_ref_small_encode -extern void -small_encode(unsigned char *, const small *); - -#define small_decode crypto_kem_sntrup4591761_ref_small_decode -extern void -small_decode(small *, const unsigned char *); - -#define small_random32 crypto_kem_sntrup4591761_ref_small_random32 -extern crypto_int32 -small_random32(void); - -#define small_random crypto_kem_sntrup4591761_ref_small_random -extern void -small_random(small *); - -#define small_random_weightw crypto_kem_sntrup4591761_ref_small_random_weightw -extern void -small_random_weightw(small *); - -#endif diff --git a/crypto/libntrup/src/ref/swap.c b/crypto/libntrup/src/ref/swap.c deleted file mode 100644 index 2051f1c5d8..0000000000 --- a/crypto/libntrup/src/ref/swap.c +++ /dev/null @@ -1,21 +0,0 @@ -#include "swap.h" - -void -swap(void *x, void *y, int bytes, int mask) -{ - int i; - char xi, yi, c, t; - - c = mask; - - for(i = 0; i < bytes; ++i) - { - xi = i[(char *)x]; - yi = i[(char *)y]; - t = c & (xi ^ yi); - xi ^= t; - yi ^= t; - i[(char *)x] = xi; - i[(char *)y] = yi; - } -} diff --git a/crypto/libntrup/src/ref/swap.h b/crypto/libntrup/src/ref/swap.h deleted file mode 100644 index 2c3f3a8a55..0000000000 --- a/crypto/libntrup/src/ref/swap.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef swap_h -#define swap_h - -#define swap crypto_kem_sntrup4591761_ref_swap -extern void -swap(void *, void *, int, int); - -#endif diff --git a/crypto/ntruprime-20171206/crypto_kem/sntrup4591761/checksumbig b/crypto/ntruprime-20171206/crypto_kem/sntrup4591761/checksumbig deleted file mode 100644 index a366c4e704..0000000000 --- a/crypto/ntruprime-20171206/crypto_kem/sntrup4591761/checksumbig +++ /dev/null @@ -1 +0,0 @@ -83705d49d3a8cb2e16028b86ea6bd44a969b51c2e5114ee02767cf2ddf1aac26 diff --git a/crypto/ntruprime-20171206/crypto_kem/sntrup4591761/checksumsmall b/crypto/ntruprime-20171206/crypto_kem/sntrup4591761/checksumsmall deleted file mode 100644 index d87bd217b8..0000000000 --- a/crypto/ntruprime-20171206/crypto_kem/sntrup4591761/checksumsmall +++ /dev/null @@ -1 +0,0 @@ -336647fe0ed2f6e0d4b15d05e68faec67a81312d769ad3cbee8e0f2de83c2dde diff --git a/crypto/ntruprime-20171206/crypto_kem/sntrup4591761/description b/crypto/ntruprime-20171206/crypto_kem/sntrup4591761/description deleted file mode 100644 index 7827a166d0..0000000000 --- a/crypto/ntruprime-20171206/crypto_kem/sntrup4591761/description +++ /dev/null @@ -1 +0,0 @@ -Streamlined NTRU Prime 4591^761 diff --git a/crypto/ntruprime-20171206/crypto_kem/sntrup4591761/designers b/crypto/ntruprime-20171206/crypto_kem/sntrup4591761/designers deleted file mode 100644 index 51ac31ea2e..0000000000 --- a/crypto/ntruprime-20171206/crypto_kem/sntrup4591761/designers +++ /dev/null @@ -1,5 +0,0 @@ -Alphabetical order: -Daniel J. Bernstein -Chitchanok Chuengsatiansup -Tanja Lange -Christine van Vredendaal diff --git a/daemon/CMakeLists.txt b/daemon/CMakeLists.txt index 772e665327..84b37d9894 100644 --- a/daemon/CMakeLists.txt +++ b/daemon/CMakeLists.txt @@ -59,8 +59,7 @@ foreach(exe ${exetargets}) elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") target_link_directories(${exe} PRIVATE /usr/local/lib) endif() - # target_link_libraries(${exe} PUBLIC lokinet-amalgum hax_and_shims_for_cmake) - target_link_libraries(${exe} PUBLIC lokinet-core hax_and_shims_for_cmake) + target_link_libraries(${exe} PUBLIC lokinet-base lokinet-core) if(STRIP_SYMBOLS) add_custom_command(TARGET ${exe} POST_BUILD diff --git a/daemon/lokinet.cpp b/daemon/lokinet.cpp index c731abc853..7397ae7b5a 100644 --- a/daemon/lokinet.cpp +++ b/daemon/lokinet.cpp @@ -1,11 +1,9 @@ #include #include // for ensure_config -// #include #include #include #include #include -// #include #include #include @@ -14,14 +12,9 @@ #include #include -// #include -// #include #include -// #include #include -// #include #include -// #include #ifdef _WIN32 #include diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in index 95afd645fb..4348e09953 100644 --- a/docs/Doxyfile.in +++ b/docs/Doxyfile.in @@ -8,7 +8,7 @@ ALIASES = "rst=\verbatim embed:rst" ALIASES += "endrst=\endverbatim" BUILTIN_STL_SUPPORT = YES INPUT = @lokinet_doc_sources_spaced@ -INCLUDE_PATH = @PROJECT_SOURCE_DIR@/include @PROJECT_SOURCE_DIR@/llarp @PROJECT_SOURCE_DIR@/crypto/libntrup/include @PROJECT_SOURCE_DIR@/external/ghc-filesystem/include/ +INCLUDE_PATH = @PROJECT_SOURCE_DIR@/include @PROJECT_SOURCE_DIR@/llarp @PROJECT_SOURCE_DIR@/external/ghc-filesystem/include/ RECURSIVE = YES CLANG_ASSISTED_PARSING = NO #CLANG_OPTIONS = -std=c++17 -Wno-pragma-once-outside-header @@ -17,4 +17,4 @@ HTML_OUTPUT = doxyhtml GENERATE_LATEX = NO GENERATE_XML = YES XML_OUTPUT = doxyxml -MACRO_EXPANSION = YES \ No newline at end of file +MACRO_EXPANSION = YES diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 59d18a9fe0..33ce97f481 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -73,7 +73,7 @@ add_library(libevent::threads ALIAS event_pthreads) add_library(libevent::extra ALIAS event_extra) -system_or_submodule(OXENC oxenc liboxenc>=1.0.10 oxen-encoding) +system_or_submodule(OXENC oxenc liboxenc>=1.1.0 oxen-encoding) system_or_submodule(OXENMQ oxenmq liboxenmq>=1.2.14 oxen-mq) @@ -103,8 +103,6 @@ endif() # we get oxen-logging from libquic; forcing submodule ensures fmt>10.0.0 -#set(OXEN_LOGGING_FMT_HEADER_ONLY ON CACHE INTERNAL "") -#set(OXEN_LOGGING_SPDLOG_HEADER_ONLY ON CACHE INTERNAL "") set(OXEN_LOGGING_FORCE_SUBMODULES ON CACHE INTERNAL "") add_subdirectory(oxen-libquic) diff --git a/external/oxen-encoding b/external/oxen-encoding index 7c8ab72db8..510e5259bd 160000 --- a/external/oxen-encoding +++ b/external/oxen-encoding @@ -1 +1 @@ -Subproject commit 7c8ab72db826f3d77f7fc2e58b029b1d2e67d52e +Subproject commit 510e5259bd639d28813fe6c940c33701233e994f diff --git a/external/oxen-libquic b/external/oxen-libquic index d837250265..a23573d6cf 160000 --- a/external/oxen-libquic +++ b/external/oxen-libquic @@ -1 +1 @@ -Subproject commit d83725026529c259395d2abc2d3e91840d67cdc6 +Subproject commit a23573d6cf37f1021debbfeaf0db0cac44e89c4e diff --git a/include/llarp.hpp b/include/llarp.hpp index 8acbca2f48..7699dd9223 100644 --- a/include/llarp.hpp +++ b/include/llarp.hpp @@ -17,7 +17,7 @@ namespace llarp class EventLoop; struct Config; - struct RouterContact; + struct RelayContact; struct Config; struct Router; class NodeDB; diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index 877b9215ac..2e62b6cdeb 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -19,8 +19,6 @@ lokinet_add_library(lokinet-cryptography # Functional objects use by lokinet-core and other libraries # needed by vpn/ router/ rpc/ handlers/ net/ link/ lokinet_add_library(lokinet-core-utils - # endpoint_base.cpp - auth/file_auth.cpp auth/rpc_auth.cpp auth/session_auth.cpp @@ -28,12 +26,6 @@ lokinet_add_library(lokinet-core-utils handlers/session.cpp handlers/tun.cpp - service/identity.cpp - service/info.cpp - service/intro.cpp # path - service/intro_set.cpp - service/name.cpp - vpn/egres_packet_router.cpp ) @@ -46,8 +38,6 @@ lokinet_add_library(lokinet-core router/router.cpp router/route_poker.cpp - - service/types.cpp session/session.cpp ) @@ -62,7 +52,6 @@ lokinet_add_library(lokinet-rpc lokinet_add_library(lokinet-wire link/connection.cpp - link/contacts.cpp link/link_manager.cpp link/tunnel.cpp ) @@ -72,7 +61,6 @@ lokinet_add_library(lokinet-utils ${CMAKE_CURRENT_BINARY_DIR}/constants/version.cpp util/buffer.cpp util/file.cpp -# util/logging/buffer.cpp util/mem.cpp util/str.cpp util/thread/threading.cpp @@ -82,13 +70,31 @@ lokinet_add_library(lokinet-utils add_dependencies(lokinet-utils genversion) +lokinet_add_library(lokinet-contact + contact/client_contact.cpp + contact/client_intro.cpp + contact/contactdb.cpp + contact/keys.cpp + contact/relay_contact.cpp + contact/relay_contact_local.cpp + contact/relay_contact_remote.cpp + contact/router_id.cpp + contact/sns.cpp + contact/tag.cpp +) + +# Network interface files used for mediating ip traffic +lokinet_add_library(lokinet-ip + net/ip_packet.cpp + net/policy.cpp + net/utils.cpp +) + # Addressing and event loop files used by lokinet-core and other libraries -# needed by rpc/ link/ service/ config/ path/ dht/ +# needed by rpc/ link/ config/ path/ dht/ lokinet_add_library(lokinet-addressing address/address.cpp - address/ip_packet.cpp address/ip_range.cpp - address/keys.cpp address/utils.cpp ev/loop.cpp @@ -96,21 +102,11 @@ lokinet_add_library(lokinet-addressing ev/types.cpp ev/udp.cpp - net/ip.cpp - net/net_int.cpp - - router_contact.cpp # TODO: move these + router_id.cpp to lokinet-core-utils..? - router_contact_local.cpp - router_contact_remote.cpp - router_id.cpp router_version.cpp # to be deleted shortly - - service/tag.cpp ) # lokinet-platform holds all platform specific code lokinet_add_library(lokinet-platform - net/interface_info.cpp vpn/packet_router.cpp vpn/platform.cpp ) @@ -173,7 +169,6 @@ endif() # nodedb data published to the network and versions of it stored locally lokinet_add_library(lokinet-nodedb bootstrap.cpp # config, router.hpp - net/traffic_policy.cpp # config, intro_set nodedb.cpp profiling.cpp # path, router, service::endpoint ) @@ -213,24 +208,20 @@ lokinet_add_library(lokinet-path ) # Link libraries to their internals -target_link_libraries(lokinet-core-utils PUBLIC lokinet-dns) -target_link_libraries(lokinet-core PUBLIC lokinet-core-utils) - -# Link lokinet-dns to alternate libraries -target_link_libraries(lokinet-dns PUBLIC lokinet-dns-platform) - target_link_libraries(lokinet-wire PUBLIC lokinet-addressing) -target_link_libraries(lokinet-dns PUBLIC lokinet-utils lokinet-cryptography lokinet-config) target_link_libraries(lokinet-nodedb PUBLIC lokinet-addressing lokinet-cryptography) -target_link_libraries(lokinet-platform PUBLIC lokinet-cryptography) -target_link_libraries(lokinet-rpc PUBLIC lokinet-wire) -target_link_libraries(lokinet-addressing PUBLIC lokinet-utils lokinet-cryptography) +target_link_libraries(lokinet-platform PUBLIC lokinet-cryptography lokinet-ip) +target_link_libraries(lokinet-rpc PUBLIC lokinet-wire lokinet-contact) +target_link_libraries(lokinet-addressing PUBLIC lokinet-utils lokinet-cryptography lokinet-contact lokinet-ip) target_link_libraries(lokinet-wire PUBLIC lokinet-cryptography) target_link_libraries(lokinet-config PUBLIC lokinet-cryptography) target_link_libraries(lokinet-dns PUBLIC lokinet-addressing + lokinet-cryptography + lokinet-config + lokinet-dns-platform ) target_link_libraries(lokinet-path @@ -240,6 +231,7 @@ target_link_libraries(lokinet-path target_link_libraries(lokinet-core-utils PUBLIC + lokinet-dns lokinet-config lokinet-platform lokinet-rpc @@ -249,7 +241,6 @@ target_link_libraries(lokinet-core-utils target_link_libraries(lokinet-cryptography PUBLIC lokinet-libcrypt - lokinet-libntrup ) target_link_libraries(lokinet-utils @@ -260,6 +251,7 @@ target_link_libraries(lokinet-utils # cross linkage target_link_libraries(lokinet-core PUBLIC + lokinet-core-utils lokinet-cryptography lokinet-nodedb lokinet-path @@ -287,17 +279,6 @@ target_link_libraries(lokinet-base ) -# target_link_libraries(lokinet-rpc PUBLIC oxenmq::oxenmq) -# target_link_libraries(lokinet-core PUBLIC oxenmq::oxenmq) -# target_link_libraries(lokinet-config PUBLIC oxenmq::oxenmq) -# target_link_libraries(lokinet-nodedb PUBLIC oxenmq::oxenmq) -# target_link_libraries(lokinet-path PUBLIC oxenmq::oxenmq) -# target_link_libraries(lokinet-platform PUBLIC oxenmq::oxenmq Threads::Threads) -# target_link_libraries(lokinet-cryptography PUBLIC sodium) -# target_link_libraries(lokinet-dns PUBLIC libunbound) -# target_link_libraries(lokinet-utils PUBLIC nlohmann_json::nlohmann_json) -# target_link_libraries(lokinet-wire PUBLIC oxenmq::oxenmq quic) - if(WITH_EMBEDDED_LOKINET) include(GNUInstallDirs) add_library(lokinet-shared SHARED lokinet_shared.cpp) diff --git a/llarp/address/address.cpp b/llarp/address/address.cpp index 6a11496c68..0e1a092e49 100644 --- a/llarp/address/address.cpp +++ b/llarp/address/address.cpp @@ -6,7 +6,7 @@ namespace llarp { static auto logcat = log::Cat("address"); - std::optional NetworkAddress::from_network_addr(const std::string& arg) + std::optional NetworkAddress::from_network_addr(std::string_view arg) { std::optional ret = std::nullopt; @@ -47,10 +47,7 @@ namespace llarp return std::tie(_pubkey, _is_client) == std::tie(other._pubkey, other._is_client); } - bool NetworkAddress::operator!=(const NetworkAddress& other) const - { - return !(*this == other); - } + bool NetworkAddress::operator!=(const NetworkAddress& other) const { return !(*this == other); } std::optional RelayAddress::from_relay_addr(std::string arg) { @@ -73,18 +70,9 @@ namespace llarp throw std::invalid_argument{"Invalid pubkey passed to RelayAddress constructor: {}"_format(arg)}; } - bool RelayAddress::operator<(const RelayAddress& other) const - { - return _pubkey < other._pubkey; - } + bool RelayAddress::operator<(const RelayAddress& other) const { return _pubkey < other._pubkey; } - bool RelayAddress::operator==(const RelayAddress& other) const - { - return _pubkey == other._pubkey; - } + bool RelayAddress::operator==(const RelayAddress& other) const { return _pubkey == other._pubkey; } - bool RelayAddress::operator!=(const RelayAddress& other) const - { - return !(*this == other); - } + bool RelayAddress::operator!=(const RelayAddress& other) const { return !(*this == other); } } // namespace llarp diff --git a/llarp/address/address.hpp b/llarp/address/address.hpp index 3b54d178d1..4d1acf7724 100644 --- a/llarp/address/address.hpp +++ b/llarp/address/address.hpp @@ -1,12 +1,11 @@ #pragma once -#include "keys.hpp" #include "utils.hpp" -#include -#include +#include +#include +#include #include -#include #include @@ -40,7 +39,9 @@ namespace llarp explicit NetworkAddress(std::string_view addr, std::string_view tld); // This private constructor expects NO '.snode' or '.loki' suffix - explicit NetworkAddress(RouterID rid, bool is_client) : _pubkey{std::move(rid)}, _is_client{is_client} {} + explicit NetworkAddress(RouterID rid, bool is_client) + : _pubkey{std::move(rid)}, _is_client{is_client}, _tld{_is_client ? TLD::LOKI : TLD::SNODE} + {} public: NetworkAddress() = default; @@ -60,7 +61,7 @@ namespace llarp // Will throw invalid_argument with bad input. Assumes that the network address terminates in either '.loki' // or '.snode' - static std::optional from_network_addr(const std::string& arg); + static std::optional from_network_addr(std::string_view arg); // Assumes that the pubkey passed is NOT terminated in either a '.loki' or '.snode' suffix static NetworkAddress from_pubkey(const RouterID& rid, bool is_client); @@ -80,7 +81,6 @@ namespace llarp std::string name() const { return _pubkey.to_string(); } std::string to_string() const { return name().append(_tld); } - static constexpr bool to_string_formattable{true}; }; diff --git a/llarp/address/ip_range.cpp b/llarp/address/ip_range.cpp index c47069af65..f26604f8b3 100644 --- a/llarp/address/ip_range.cpp +++ b/llarp/address/ip_range.cpp @@ -8,15 +8,15 @@ namespace llarp { if (_is_ipv4) { - _base_ip = _addr.to_ipv4().to_base(_mask); - _ip_range = ipv4_range{std::get(_base_ip), _mask}; - _max_ip = std::get(_ip_range).max_ip(); + _ip_net = _addr.to_ipv4() % _mask; + _base_ip = _ipv4_range().ip; + _max_ip = _ipv4_net().max_ip(); } else { - _base_ip = _addr.to_ipv6().to_base(_mask); - _ip_range = ipv6_range{std::get(_base_ip), _mask}; - _max_ip = std::get(_ip_range).max_ip(); + _ip_net = _addr.to_ipv6() % _mask; + _base_ip = _ipv6_range().ip; + _max_ip = _ipv6_net().max_ip(); } } @@ -64,15 +64,9 @@ namespace llarp return _contains(std::get(other.base_ip())); } - bool IPRange::_contains(const ipv4& other) const - { - return _ipv4_range().contains(other); - } + bool IPRange::_contains(const ipv4& other) const { return _ipv4_range().contains(other); } - bool IPRange::_contains(const ipv6& other) const - { - return _ipv6_range().contains(other); - } + bool IPRange::_contains(const ipv6& other) const { return _ipv6_range().contains(other); } bool IPRange::contains(const ip_v& other) const { @@ -82,15 +76,34 @@ namespace llarp return is_ipv4() ? _contains(std::get(other)) : _contains(std::get(other)); } + bool IPRange::contains(const ip_net_v& other) const + { + if (is_ipv4() ^ std::holds_alternative(other)) + return false; + + return is_ipv4() ? _contains(std::get(other).to_range().ip) + : _contains(std::get(other).to_range().ip); + } + + ip_v IPRange::net_ip() const + { + if (is_ipv4()) + return _ipv4_net().ip; + return _ipv6_net().ip; + } + std::optional IPRange::find_private_range(const std::list& excluding, bool ipv6_enabled) { if (excluding.empty()) return std::nullopt; - auto filter = [&excluding](const ip_range_v& range) -> bool { + auto filter = [&excluding](const ip_net_v& range) -> bool { for (const auto& e : excluding) - if (e == range) + { + if (e.contains(range)) return false; + } + log::trace(logcat, "{}", std::get(range).ip); return true; }; @@ -109,7 +122,7 @@ namespace llarp { for (size_t n = 0; n < num_ipv6_private; ++n) { - if (auto v6 = ipv6(0xfd2e, 0x6c6f, 0x6b69, n) / 64; filter(v6)) + if (auto v6 = ipv6(0xfd2e, 0x6c6f, 0x6b69, n, 0x0000, 0x0000, 0x0000, 0x0001) % 64; filter(v6)) return v6; } } diff --git a/llarp/address/ip_range.hpp b/llarp/address/ip_range.hpp index 101b01545c..b5ec3caa71 100644 --- a/llarp/address/ip_range.hpp +++ b/llarp/address/ip_range.hpp @@ -2,8 +2,6 @@ #include "utils.hpp" -#include - #include namespace llarp @@ -14,12 +12,13 @@ namespace llarp struct IPRange { private: + ip_net_v _ip_net; + oxen::quic::Address _addr; uint8_t _mask; bool _is_ipv4; ip_v _base_ip; - ip_range_v _ip_range; ip_v _max_ip; void _init_ip(); @@ -29,10 +28,13 @@ namespace llarp bool _contains(const ipv6& other) const; // getters to DRY out variant access - ipv4_range& _ipv4_range() { return std::get(_ip_range); } - const ipv4_range& _ipv4_range() const { return std::get(_ip_range); } - ipv6_range& _ipv6_range() { return std::get(_ip_range); } - const ipv6_range& _ipv6_range() const { return std::get(_ip_range); } + ipv4_net& _ipv4_net() { return std::get(_ip_net); } + const ipv4_net& _ipv4_net() const { return std::get(_ip_net); } + ipv4_range _ipv4_range() const { return _ipv4_net().to_range(); } + + ipv6_net& _ipv6_net() { return std::get(_ip_net); } + const ipv6_net& _ipv6_net() const { return std::get(_ip_net); } + ipv6_range _ipv6_range() const { return _ipv6_net().to_range(); } public: IPRange() : IPRange{oxen::quic::Address{}, 0} {} @@ -44,21 +46,21 @@ namespace llarp _init_ip(); } - IPRange(const ipv4_range& ipv4) - : _addr{ipv4.base}, - _mask{ipv4.mask}, + IPRange(const ipv4_net& ipv4) + : _ip_net{ipv4}, + _addr{_ipv4_net().ip}, + _mask{_ipv4_net().mask}, _is_ipv4{true}, - _base_ip{ipv4.base}, - _ip_range{ipv4}, - _max_ip{ipv4.max_ip()} + _base_ip{_ipv4_range().ip}, + _max_ip{_ipv4_net().max_ip()} {} - IPRange(const ipv6_range& ipv6) - : _addr{ipv6.base}, + IPRange(const ipv6_net& ipv6) + : _ip_net{ipv6}, + _addr{ipv6.ip}, _mask{ipv6.mask}, _is_ipv4{false}, - _base_ip{ipv6.base}, - _ip_range{ipv6}, + _base_ip{ipv6.ip.to_base(_mask)}, _max_ip{ipv6.max_ip()} {} @@ -67,16 +69,19 @@ namespace llarp void bt_encode(oxenc::bt_list_producer& btlp) const { btlp.append(to_string()); } - std::string to_string() const { return is_ipv4() ? _ipv4_range().to_string() : _ipv6_range().to_string(); } + std::string to_string() const { return is_ipv4() ? _ipv4_net().to_string() : _ipv6_net().to_string(); } static std::optional from_string(std::string arg); bool contains(const IPRange& other) const; bool contains(const ip_v& other) const; + bool contains(const ip_net_v& other) const; bool is_ipv4() const { return _is_ipv4; } - ip_range_v get_ip_range() const { return _ip_range; } + ip_net_v get_ip_net() const { return _ip_net; } + + ip_v net_ip() const; ip_v base_ip() const { return _base_ip; } @@ -85,6 +90,11 @@ namespace llarp const uint8_t& mask() const { return _mask; } uint8_t mask() { return _mask; } + oxen::quic::Address base_address() const + { + return is_ipv4() ? oxen::quic::Address{_ipv4_range().ip} : oxen::quic::Address{_ipv6_range().ip}; + } + const oxen::quic::Address& address() const { return _addr; } oxen::quic::Address address() { return _addr; } @@ -98,12 +108,12 @@ namespace llarp return std::tie(_addr, _mask) == std::tie(other._addr, other._mask); } - bool operator==(const ip_range_v& other) const + bool operator==(const ip_net_v& other) const { - if (_is_ipv4 and std::holds_alternative(other)) - return _ipv4_range() == std::get(other); - if (not _is_ipv4 and std::holds_alternative(other)) - return _ipv6_range() == std::get(other); + if (_is_ipv4 and std::holds_alternative(other)) + return _ipv4_net() == std::get(other); + if (not _is_ipv4 and std::holds_alternative(other)) + return _ipv6_net() == std::get(other); return false; } @@ -165,7 +175,7 @@ namespace llarp IPRangeIterator() = default; IPRangeIterator(const IPRange& range) - : _ip_range{range}, _is_ipv4{range.is_ipv4()}, _current_ip{range.base_ip()}, _max_ip{range.max_ip()} + : _ip_range{range}, _is_ipv4{range.is_ipv4()}, _current_ip{range.net_ip()}, _max_ip{range.max_ip()} {} // Returns the next ip address in the iterating range; returns std::nullopt if range is exhausted @@ -212,7 +222,7 @@ namespace std template <> struct hash { - size_t operator()(const llarp::IPRange& r) const + size_t operator()(const llarp::IPRange& r) const noexcept { size_t h; diff --git a/llarp/address/types.hpp b/llarp/address/types.hpp index 3120423559..58f94fb281 100644 --- a/llarp/address/types.hpp +++ b/llarp/address/types.hpp @@ -9,9 +9,11 @@ namespace llarp using ipv4 = oxen::quic::ipv4; using ipv6 = oxen::quic::ipv6; using ip_v = std::variant; - using ipv4_range = oxen::quic::ipv4_net; - using ipv6_range = oxen::quic::ipv6_net; - using ip_range_v = std::variant; + using ipv4_range = oxen::quic::ipv4_range; + using ipv6_range = oxen::quic::ipv6_range; + using ipv4_net = oxen::quic::ipv4_net; + using ipv6_net = oxen::quic::ipv6_net; + using ip_net_v = std::variant; namespace concepts { @@ -19,114 +21,10 @@ namespace llarp concept IPType = std::is_same_v || std::is_same_v; template - concept IPRangeType = std::is_same_v || std::is_same_v; + concept IPRangeType = std::is_same_v || std::is_same_v; } // namespace concepts using KeyedAddress = oxen::quic::RemoteAddress; - - inline constexpr uint32_t ipv6_flowlabel_mask = 0b0000'0000'0000'1111'1111'1111'1111'1111; - - inline constexpr size_t ICMP_HEADER_SIZE{8}; - - // Compares the given ip variant against a quic address - // Returns: - // - true : ip == address - // - false : ip != address - // Error: - // - throws : ip and address are mismatched ipv4 vs ipv6 - inline bool ip_equals_address(const ip_v& ip, const oxen::quic::Address& addr, bool compare_v4) - { - if (compare_v4 and std::holds_alternative(ip)) - return std::get(ip) == addr.to_ipv4(); - - if (not compare_v4 and std::holds_alternative(ip)) - return std::get(ip) == addr.to_ipv6(); - - throw std::invalid_argument{ - "Failed to compare ip variant in desired {} scheme!"_format(compare_v4 ? "ipv4" : "ipv6")}; - } - - struct ip_header_le - { - uint8_t header_len : 4; - uint8_t version : 4; - uint8_t service_type; - uint16_t total_len; - uint16_t id; - uint16_t frag_off; - uint8_t ttl; - uint8_t protocol; - uint16_t checksum; - uint32_t src; - uint32_t dest; - }; - - struct ip_header_be - { - uint8_t version : 4; - uint8_t header_len : 4; - uint8_t service_type; - uint16_t total_len; - uint16_t id; - uint16_t frag_off; - uint8_t ttl; - uint8_t protocol; - uint16_t checksum; - uint32_t src; - uint32_t dest; - }; - - using ip_header = std::conditional_t; - - static_assert(sizeof(ip_header) == 20); - - struct ipv6_header_preamble_le - { - unsigned char pad_small : 4; - unsigned char version : 4; - uint8_t pad[3]; - }; - - struct ipv6_header_preamble_be - { - unsigned char version : 4; - unsigned char pad_small : 4; - uint8_t pad[3]; - }; - - using ipv6_header_preamble = - std::conditional_t; - - static_assert(sizeof(ipv6_header_preamble) == 4); - - struct ipv6_header - { - union - { - ipv6_header_preamble preamble; - uint32_t flowlabel; - } preamble; - - uint16_t payload_len; - uint8_t protocol; - uint8_t hoplimit; - in6_addr srcaddr; - in6_addr dstaddr; - - /// Returns the flowlabel (stored in network order) in HOST ORDER - uint32_t set_flowlabel() const { return ntohl(preamble.flowlabel & htonl(ipv6_flowlabel_mask)); } - - /// Sets a flowlabel in network order. Takes in a label in HOST ORDER - void set_flowlabel(uint32_t label) - { - // the ipv6 flow label is the last 20 bits in the first 32 bits of the header - preamble.flowlabel = - (htonl(ipv6_flowlabel_mask) & htonl(label)) | (preamble.flowlabel & htonl(~ipv6_flowlabel_mask)); - } - }; - - static_assert(sizeof(ipv6_header) == 40); - } // namespace llarp namespace std @@ -134,13 +32,13 @@ namespace std template <> struct hash { - size_t operator()(const llarp::ipv4& obj) const { return hash{}(obj.addr); } + size_t operator()(const llarp::ipv4& obj) const noexcept { return hash{}(obj.addr); } }; template <> struct hash { - size_t operator()(const llarp::ipv6& obj) const + size_t operator()(const llarp::ipv6& obj) const noexcept { auto h = hash{}(obj.hi); h ^= hash{}(obj.lo) + oxen::quic::inverse_golden_ratio + (h << 6) + (h >> 2); @@ -151,7 +49,7 @@ namespace std template <> struct hash { - size_t operator()(const llarp::ip_v& obj) const + size_t operator()(const llarp::ip_v& obj) const noexcept { if (auto maybe_v4 = std::get_if(&obj)) return hash{}(*maybe_v4); diff --git a/llarp/address/utils.cpp b/llarp/address/utils.cpp index a0aa2152ed..be4cae5975 100644 --- a/llarp/address/utils.cpp +++ b/llarp/address/utils.cpp @@ -2,147 +2,70 @@ namespace llarp { - static auto logcat = log::Cat("Address-utils"); + static auto logcat = log::Cat("address-utils"); - uint16_t from_32_to_16(uint32_t x) + namespace detail { - /* add up 16-bit and 16-bit for 16+c bit */ - x = (x & 0xffff) + (x >> 16); - /* add up carry.. */ - x = (x & 0xffff) + (x >> 16); - return x; - } + std::optional parse_addr_string(std::string_view arg, std::string_view tld) + { + std::optional ret = std::nullopt; - uint32_t from_64_to_32(uint64_t x) - { - /* add up 32-bit and 32-bit for 32+c bit */ - x = (x & 0xffffffff) + (x >> 32); - /* add up carry.. */ - x = (x & 0xffffffff) + (x >> 32); - return x; - } - uint16_t fold_csum(uint32_t csum) - { - auto sum = csum; - sum = (sum & 0xffff) + (sum >> 16); - sum = (sum & 0xffff) + (sum >> 16); - return static_cast(~sum); - } + if (auto pos = arg.find_first_of('.'); pos != std::string_view::npos) + { + auto _prefix = arg.substr(0, pos); + // check the pubkey prefix is the right length + if (_prefix.length() != PUBKEYSIZE) + return ret; - uint32_t ipv4_checksum_magic(const uint8_t *buf, uint16_t len) - { - uint16_t odd{1}, result{0}; + // verify the tld is allowed + auto _tld = arg.substr(pos); - odd &= (unsigned long)buf; + if (_tld == tld and TLD::allowed.count(_tld)) + ret = _prefix; + } - if (odd) - { - result += oxenc::little_endian ? (*buf << 8) : *buf; - --len; - ++buf; - } + return ret; + }; - if (len >= 2) + std::pair parse_addr(std::string_view addr, std::optional default_port) { - if (2 & (unsigned long)buf) + std::pair result; + auto &[host, port] = result; + + if (auto p = addr.find_last_not_of(DIGITS); + p != std::string_view::npos && p + 2 <= addr.size() && addr[p] == ':') { - result += *(unsigned short *)buf; - len -= 2; - buf += 2; + if (!parse_int(addr.substr(p + 1), port)) + throw std::invalid_argument{"Invalid address: could not parse port"}; + addr.remove_suffix(addr.size() - p); } - - if (len >= 4) + else if (default_port.has_value()) // use ::has_value() in case default_port is set but is == 0 { - const unsigned char *end = buf + ((unsigned)len & ~3); + port = *default_port; + } + else + throw std::invalid_argument{ + "Invalid address: argument contains no port and no default was specified (input:{})"_format(addr)}; - unsigned int carry = 0; - do - { - unsigned int w = *(unsigned int *)buf; - buf += 4; - result += carry; - result += w; - carry = (w > result); - } while (buf < end); + bool had_sq_brackets = false; - result += carry; - result = (result & 0xffff) + (result >> 16); + if (!addr.empty() && addr.front() == '[' && addr.back() == ']') + { + addr.remove_prefix(1); + addr.remove_suffix(1); + had_sq_brackets = true; } - if (len & 2) + if (auto p = addr.find_first_not_of(PDIGITS); p != std::string_view::npos) { - result += *(unsigned short *)buf; - buf += 2; + if (auto q = addr.find_first_not_of(ALDIGITS); q != std::string_view::npos) + throw std::invalid_argument{"Invalid address: does not look like IPv4 or IPv6!"}; + if (!had_sq_brackets) + throw std::invalid_argument{"Invalid address: IPv6 addresses require [...] square brackets"}; } - } - - if (len & 1) - result += oxenc::little_endian ? *buf : (*buf << 8); - - result = from_32_to_16(result); - if (odd) - result = ((result >> 8) & 0xff) | ((result & 0xff) << 8); - - return result; - } - - uint16_t checksum_ipv4(const void *header, uint8_t header_len) - { - return ~ipv4_checksum_magic(static_cast(header), header_len * 4); - } - - uint32_t tcpudp_checksum_ipv4(uint32_t src, uint32_t dest, uint32_t len, uint8_t proto, uint32_t sum) - { - auto _sum = static_cast(sum); - - _sum += src; - _sum += dest; - - _sum += oxenc::big_endian ? proto + len : (proto + len) << 8; - - return from_64_to_32(_sum); - } - - uint16_t ipv6_checksum_magic( - const struct in6_addr *saddr, const struct in6_addr *daddr, uint32_t len, uint8_t proto, uint32_t csum) - { - uint32_t sum = csum; - - for (size_t i = 0; i < 4; ++i) - { - auto val = static_cast(saddr->s6_addr32[i]); - sum += val; - sum += (sum < val); - } - - for (size_t i = 0; i < 4; ++i) - { - auto val = static_cast(daddr->s6_addr32[i]); - sum += val; - sum += (sum < val); + host = addr; + return result; } - - uint32_t ulen = htonl(len); - uint32_t uproto = htonl(proto); - - sum += ulen; - sum += (sum < ulen); - - sum += uproto; - sum += (sum < uproto); - - return fold_csum(sum); - } - - uint32_t tcp_checksum_ipv6(const struct in6_addr *saddr, const struct in6_addr *daddr, uint32_t len, uint32_t csum) - { - return ~ipv6_checksum_magic(saddr, daddr, len, IPPROTO_TCP, csum); - } - - uint32_t udp_checksum_ipv6(const struct in6_addr *saddr, const struct in6_addr *daddr, uint32_t len, uint32_t csum) - { - return ~ipv6_checksum_magic(saddr, daddr, len, IPPROTO_UDP, csum); - } - + } // namespace detail } // namespace llarp diff --git a/llarp/address/utils.hpp b/llarp/address/utils.hpp index 14c6fde7b4..d78994a5fd 100644 --- a/llarp/address/utils.hpp +++ b/llarp/address/utils.hpp @@ -29,98 +29,28 @@ namespace llarp static std::set allowed = {SNODE, LOKI}; } // namespace TLD - uint16_t checksum_ipv4(const void *header, uint8_t header_len); - - uint32_t tcpudp_checksum_ipv4(uint32_t src, uint32_t dest, uint32_t len, uint8_t proto, uint32_t sum); - - uint32_t tcp_checksum_ipv6(const struct in6_addr *saddr, const struct in6_addr *daddr, uint32_t len, uint32_t csum); - - uint32_t udp_checksum_ipv6(const struct in6_addr *saddr, const struct in6_addr *daddr, uint32_t len, uint32_t csum); - namespace detail { // inline auto utilcat = log::Cat("addrutils"); - inline static std::optional parse_addr_string(std::string_view arg, std::string_view tld) - { - std::optional ret = std::nullopt; + std::optional parse_addr_string(std::string_view arg, std::string_view tld); - if (auto pos = arg.find_first_of('.'); pos != std::string_view::npos) - { - auto _prefix = arg.substr(0, pos); - // check the pubkey prefix is the right length - if (_prefix.length() != PUBKEYSIZE) - return ret; - - // verify the tld is allowed - auto _tld = arg.substr(pos); - - if (_tld == tld and TLD::allowed.count(_tld)) - ret = _prefix; - } - - return ret; - }; + inline constexpr auto DIGITS = "0123456789"sv; + inline constexpr auto PDIGITS = "0123456789."sv; + inline constexpr auto ALDIGITS = "0123456789abcdef:."sv; - inline static std::pair parse_addr( - std::string_view addr, std::optional default_port) - { - std::pair result; - - if (auto p = addr.find_last_not_of("0123456789"); - p != std::string_view::npos && p + 2 <= addr.size() && addr[p] == ':') - { - if (!parse_int(addr.substr(p + 1), result.second)) - throw std::invalid_argument{"Invalid address: could not parse port"}; - addr.remove_suffix(addr.size() - p); - } - else if (default_port) - { - // log::critical(utilcat, "Setting default port for addr parse!"); - result.second = *default_port; - } - else - { - throw std::invalid_argument{"Invalid address: no port was specified and there is no default"}; - } - - bool had_sq_brackets = false; - - if (!addr.empty() && addr.front() == '[' && addr.back() == ']') - { - addr.remove_prefix(1); - addr.remove_suffix(1); - had_sq_brackets = true; - } - - if (auto p = addr.find_first_not_of("0123456789."); p != std::string_view::npos) - { - if (auto q = addr.find_first_not_of("0123456789abcdef:."); q != std::string_view::npos) - throw std::invalid_argument{"Invalid address: does not look like IPv4 or IPv6!"}; - else if (!had_sq_brackets) - throw std::invalid_argument{"Invalid address: IPv6 addresses require [...] square brackets"}; - } - - // if (addr.empty()) - // { - // log::critical(utilcat, "addr is empty, tough titties buddy"); // TESTNET: remove this log please - // // addr = "::"; - // } - - result.first = addr; - return result; - } + std::pair parse_addr(std::string_view addr, std::optional default_port); inline constexpr size_t num_ipv4_private{272}; - inline constexpr std::array generate_private_ipv4() + inline constexpr std::array generate_private_ipv4() { - std::array ret{}; + std::array ret{}; for (size_t n = 16; n < 32; ++n) - ret[n - 16] = ipv4(172, n, 0, 0) / 16; + ret[n - 16] = ipv4(172, n, 0, 1) % 16; for (size_t n = 0; n < 256; ++n) - ret[n + 16] = ipv4(10, n, 0, 0) / 16; + ret[n + 16] = ipv4(10, n, 0, 1) % 16; return ret; } diff --git a/llarp/android/ifaddrs.c b/llarp/android/ifaddrs.c index 31784383d4..bbd78c55e6 100644 --- a/llarp/android/ifaddrs.c +++ b/llarp/android/ifaddrs.c @@ -237,10 +237,7 @@ static NetlinkList* getResultList(int p_socket, int p_request) return l_list; } -static size_t maxSize(size_t a, size_t b) -{ - return (a > b ? a : b); -} +static size_t maxSize(size_t a, size_t b) { return (a > b ? a : b); } static size_t calcAddrLen(sa_family_t p_family, int p_dataSize) { diff --git a/llarp/apple/PacketTunnelProvider.m b/llarp/apple/PacketTunnelProvider.m index 629f980143..0916a5aa58 100644 --- a/llarp/apple/PacketTunnelProvider.m +++ b/llarp/apple/PacketTunnelProvider.m @@ -32,10 +32,7 @@ - (void)updateNetworkSettings; @end -static void nslogger(const char* msg) -{ - NSLog(@"%s", msg); -} +static void nslogger(const char* msg) { NSLog(@"%s", msg); } static void packet_writer(int af, const void* data, size_t size, void* ctx) { diff --git a/llarp/apple/context_wrapper.cpp b/llarp/apple/context_wrapper.cpp index 5c5f846640..37ea1b1a35 100644 --- a/llarp/apple/context_wrapper.cpp +++ b/llarp/apple/context_wrapper.cpp @@ -3,15 +3,13 @@ #include "context.hpp" #include "vpn_interface.hpp" -#include #include #include +#include #include #include #include -// #include - #include #include #include diff --git a/llarp/apple/vpn_interface.cpp b/llarp/apple/vpn_interface.cpp index 4ffa535650..31d0ce2b59 100644 --- a/llarp/apple/vpn_interface.cpp +++ b/llarp/apple/vpn_interface.cpp @@ -31,10 +31,7 @@ namespace llarp::apple // } - int VPNInterface::PollFD() const - { - return -1; - } + int VPNInterface::PollFD() const { return -1; } IPPacket VPNInterface::read_next_packet() { diff --git a/llarp/auth/auth.hpp b/llarp/auth/auth.hpp index 85b23cf7fe..fddde53027 100644 --- a/llarp/auth/auth.hpp +++ b/llarp/auth/auth.hpp @@ -3,10 +3,9 @@ #include "types.hpp" #include +#include +#include #include -#include -#include -#include #include #include @@ -86,7 +85,7 @@ namespace llarp const std::set _files; const AuthFileType _type; mutable util::Mutex _m; - std::unordered_set _pending; + std::unordered_set _pending; /// returns an auth result for a auth info challange, opens every file until it finds a /// token matching it this is expected to be done in the IO thread AuthResult check_files(const AuthInfo& info) const; @@ -114,7 +113,7 @@ namespace llarp std::shared_ptr _omq; std::optional _omq_conn; - std::unordered_set _pending_sessions; + std::unordered_set _pending_sessions; }; } // namespace auth diff --git a/llarp/auth/session_auth.cpp b/llarp/auth/session_auth.cpp index 729ade273f..03406091ee 100644 --- a/llarp/auth/session_auth.cpp +++ b/llarp/auth/session_auth.cpp @@ -17,7 +17,7 @@ namespace llarp::auth if (_is_snode_service) _session_key = _router.identity(); else - crypto::identity_keygen(_session_key); + _session_key = crypto::generate_identity(); } std::optional SessionAuthPolicy::fetch_auth_token() @@ -31,9 +31,6 @@ namespace llarp::auth return ret; } - bool SessionAuthPolicy::load_identity_from_file(const char* fname) - { - return _session_key.load_from_file(fname); - } + bool SessionAuthPolicy::load_identity_from_file(const char* fname) { return _session_key.load_from_file(fname); } } // namespace llarp::auth diff --git a/llarp/bootstrap-fallbacks.cpp.in b/llarp/bootstrap-fallbacks.cpp.in index 51754db48f..71b657cf9a 100644 --- a/llarp/bootstrap-fallbacks.cpp.in +++ b/llarp/bootstrap-fallbacks.cpp.in @@ -12,7 +12,7 @@ namespace llarp @BOOTSTRAP_FALLBACKS@ }) { - if (network != RouterContact::ACTIVE_NETID) + if (network != RelayContact::ACTIVE_NETID) continue; auto& bsl = fallbacks[network]; diff --git a/llarp/bootstrap.cpp b/llarp/bootstrap.cpp index b6551f2c85..9d68264b22 100644 --- a/llarp/bootstrap.cpp +++ b/llarp/bootstrap.cpp @@ -32,7 +32,7 @@ namespace llarp try { - ret &= emplace(buf).second; + ret &= emplace(buf, true).second; } catch (...) { @@ -55,7 +55,7 @@ namespace llarp oxenc::bt_list_consumer btlc{buf}; while (not btlc.is_finished()) - ret &= emplace(btlc.consume_dict_data()).second; + ret &= emplace(btlc.consume_dict_data(), true).second; } catch (const std::exception& e) { @@ -78,10 +78,7 @@ namespace llarp return false; } - bool BootstrapList::contains(const RemoteRC& rc) const - { - return count(rc); - } + bool BootstrapList::contains(const RemoteRC& rc) const { return count(rc); } std::string_view BootstrapList::bt_encode() const { @@ -111,7 +108,7 @@ namespace llarp for (auto itr = begin(); itr != end(); ++itr) { - if (RouterContact::is_obsolete(*itr)) + if (RelayContact::is_obsolete(*itr)) { log::debug(logcat, "Deleting obsolete BootstrapRC (rid:{})", itr->router_id()); itr = erase(itr); @@ -126,7 +123,7 @@ namespace llarp log::critical(logcat, "BootstrapRC list force loading fallbacks..."); auto fallbacks = llarp::load_bootstrap_fallbacks(); - if (auto itr = fallbacks.find(RouterContact::ACTIVE_NETID); itr != fallbacks.end()) + if (auto itr = fallbacks.find(RelayContact::ACTIVE_NETID); itr != fallbacks.end()) { log::debug(logcat, "Loading {} default fallback bootstrap router(s)!", itr->second.size()); log::critical(logcat, "Fallback bootstrap loaded: {}", itr->second.current()); @@ -156,7 +153,7 @@ namespace llarp if (not fs::exists(fpath)) { - log::critical(logcat, "Bootstrap RC file non-existant at path:{}", fpath); + log::critical(logcat, "Bootstrap RC file non-existent at path:{}", fpath); return result; } diff --git a/llarp/bootstrap.hpp b/llarp/bootstrap.hpp index 83f973676d..4b444a38eb 100644 --- a/llarp/bootstrap.hpp +++ b/llarp/bootstrap.hpp @@ -1,7 +1,6 @@ #pragma once -#include "router_contact.hpp" - +#include #include #include diff --git a/llarp/bootstrap_fallbacks.cpp b/llarp/bootstrap_fallbacks.cpp index 14dcf6d818..63eb1b9bce 100644 --- a/llarp/bootstrap_fallbacks.cpp +++ b/llarp/bootstrap_fallbacks.cpp @@ -12,7 +12,7 @@ namespace llarp // }) { - if (network != RouterContact::ACTIVE_NETID) + if (network != RelayContact::ACTIVE_NETID) continue; auto& bsl = fallbacks[network]; diff --git a/llarp/config/config.cpp b/llarp/config/config.cpp index 74527912d8..8cee0faa70 100644 --- a/llarp/config/config.cpp +++ b/llarp/config/config.cpp @@ -5,8 +5,7 @@ #include #include -#include -#include +#include #include #include @@ -73,15 +72,16 @@ namespace llarp net_id = std::move(arg); }); - conf.define_option( + conf.define_option( "router", "relay-connections", Default{CLIENT_ROUTER_CONNECTIONS}, ClientOnly, Comment{ "Minimum number of routers lokinet client will attempt to maintain connections to.", - }, - [=, this](int arg) { + "If [network]:strict-connect is defined, the number of maintained client <-> router", + "connections set by [router]:relay-connections will be at MOST the number of pinned edges"}, + [=, this](size_t arg) { if (arg < CLIENT_ROUTER_CONNECTIONS) throw std::invalid_argument{ "Client relay connections must be >= {}"_format(CLIENT_ROUTER_CONNECTIONS)}; @@ -225,6 +225,147 @@ namespace llarp is_relay = params.is_relay; } + void ExitConfig::define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params) + { + (void)params; + + conf.define_option( + "exit", + "enable", + ClientOnly, + Default{false}, + assignment_acceptor(exit_enabled), + Comment{ + "Enable exit-node functionality for local lokinet instance.", + }); + + conf.define_option( + "exit", + "auth", + ClientOnly, + MultiValue, + Comment{ + "Specify an optional authentication token required to use a non-public exit node.", + "For example:", + " auth=myfavouriteexit.loki:abc", + "uses the authentication code `abc` whenever myfavouriteexit.loki is accessed.", + "Can be specified multiple times to store codes for different exit nodes.", + }, + [this](std::string arg) { + if (arg.empty()) + throw std::invalid_argument{"Empty argument passed to '[exit]:auth'"}; + + const auto pos = arg.find(":"); + + if (pos == std::string::npos) + { + throw std::invalid_argument( + "[exit]:auth invalid format, expects exit-address.loki:auth-token-goes-here"); + } + + const auto addr = arg.substr(0, pos); + auto auth = arg.substr(pos + 1); + + if (is_valid_sns(addr)) + { + ons_auth_tokens.emplace(std::move(addr), std::move(auth)); + } + else if (auto exit = NetworkAddress::from_network_addr(addr); exit->is_client()) + { + auth_tokens.emplace(std::move(*exit), std::move(auth)); + } + else + throw std::invalid_argument("[exit]:auth invalid exit address"); + }); + + conf.define_option( + "exit", + "policy", + MultiValue, + Comment{ + "Specifies the IP traffic accepted by the local exit node traffic policy. If any are", + "specified then only matched traffic will be allowed and all other traffic will be", + "dropped. Examples:", + " policy=tcp", + "would allow all TCP/IP packets (regardless of port);", + " policy=0x69", + "would allow IP traffic with IP protocol 0x69;", + " policy=udp/53", + "would allow UDP port 53; and", + " policy=tcp/smtp", + "would allow TCP traffic on the standard smtp port (21).", + }, + [this](std::string arg) { + // this will throw on error + exit_policy.protocols.emplace(arg); + }); + + conf.define_option( + "exit", + "reserved-range", + ClientOnly, + MultiValue, + Comment{ + "Reserve an ip range to use as an exit broker for a `.loki` address", + "Specify a `.loki` address and a reserved ip range to use as an exit broker.", + "Examples:", + " reserved-range=whatever.loki", + "would route all exit traffic through whatever.loki; and", + " reserved-range=stuff.loki:100.0.0.0/24", + "would route the IP range 100.0.0.0/24 through stuff.loki.", + "This option can be specified multiple times (to map different IP ranges).", + }, + [this](std::string arg) { + if (arg.empty()) + return; + + std::optional range; + + const auto pos = arg.find(":"); + + std::string input = (pos == std::string::npos) ? "0.0.0.0/0"s : arg.substr(pos + 1); + + range = IPRange::from_string(std::move(input)); + + if (not range.has_value()) + throw std::invalid_argument("[network]:exit-node invalid ip range for exit provided"); + + if (pos != std::string::npos) + arg = arg.substr(0, pos); + + if (is_valid_sns(arg)) + ons_ranges.emplace(std::move(arg), std::move(*range)); + else if (auto maybe_raddr = NetworkAddress::from_network_addr(arg); maybe_raddr) + ranges.emplace(std::move(*maybe_raddr), std::move(*range)); + else + throw std::invalid_argument{"[network]:exit-node bad address: {}"_format(arg)}; + }); + + conf.define_option( + "exit", + "routed-range", + MultiValue, + Comment{ + "Route local exit node traffic through the specified IP range. If omitted, the", + "default is ALL public ranges. Can be set to public to indicate that this exit", + "routes traffic to the public internet.", + "For example:", + " routed-range=10.0.0.0/16", + " routed-range=public", + "to advertise that this exit routes traffic to both the public internet, and to", + "10.0.x.y addresses.", + "", + "Note that this option does not automatically configure network routing; that", + "must be configured separately on the exit system to handle lokinet traffic.", + }, + [this](std::string arg) { + if (auto range = IPRange::from_string(arg)) + exit_policy.ranges.insert(std::move(*range)); + else + throw std::invalid_argument{"Bad IP range passed to routed-range:{}"_format(arg)}; + }); + } + void NetworkConfig::define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params) { (void)params; @@ -252,9 +393,9 @@ namespace llarp [this](std::string value) { RouterID router; if (not router.from_relay_address(value)) - throw std::invalid_argument{"bad snode value: " + value}; - if (not strict_connect.insert(router).second) - throw std::invalid_argument{"duplicate strict connect snode: " + value}; + throw std::invalid_argument{"bad .snode pubkey: {}"_format(value)}; + if (not pinned_edges.insert(router).second) + throw std::invalid_argument{"duplicate strict connect .snode: {}"_format(value)}; }, Comment{ "Public keys of routers which will act as pinned first-hops. This may be used to", @@ -380,7 +521,7 @@ namespace llarp ReachableDefault, assignment_acceptor(is_reachable), Comment{ - "Determines whether we will pubish our service's introset to the DHT.", + "Determines whether we will pubish our service's ClientContact to the DHT (client default: TRUE)", }); conf.define_option( @@ -413,19 +554,26 @@ namespace llarp conf.define_option( "network", "exit", + Hidden, ClientOnly, - Default{false}, - assignment_acceptor(allow_exit), + [this](bool arg) { + allow_exit = arg; + log::warning(logcat, "This option is deprecated! Use [exit]:enable instead!"); + }, Comment{ - "Whether or not we should act as an exit node. Beware that this increases demand", + "<< DEPRECATED -- use [exit]:enable instead >>\n", + "Whether or not we should act as an exit node. " + "Beware that this increases demand", "on the server and may pose liability concerns. Enable at your own risk.", }); conf.define_option( "network", "routed-range", + Hidden, MultiValue, Comment{ + "<< DEPRECATED -- use [exit]:routed-range instead >>\n", "When in exit mode announce one or more IP ranges that this exit node routes", "traffic for. If omitted, the default is all public ranges. Can be set to", "public to indicate that this exit routes traffic to the public internet.", @@ -439,8 +587,11 @@ namespace llarp "must be configured separately on the exit system to handle lokinet traffic.", }, [this](std::string arg) { + if (not traffic_policy) + traffic_policy = net::ExitPolicy{}; + if (auto range = IPRange::from_string(arg)) - _routed_ranges.insert(std::move(*range)); + traffic_policy->ranges.insert(std::move(*range)); else throw std::invalid_argument{"Bad IP range passed to routed-range:{}"_format(arg)}; }); @@ -448,8 +599,10 @@ namespace llarp conf.define_option( "network", "traffic-whitelist", + Hidden, MultiValue, Comment{ + "<< DEPRECATED -- use [exit]:policy instead >>\n", "Adds an IP traffic type whitelist; can be specified multiple times. If any are", "specified then only matched traffic will be allowed and all other traffic will be", "dropped. Examples:", @@ -464,7 +617,9 @@ namespace llarp }, [this](std::string arg) { if (not traffic_policy) - traffic_policy = net::TrafficPolicy{}; + traffic_policy = net::ExitPolicy{}; + + log::warning(logcat, "This option is deprecated! Use [exit]:policy instead!"); // this will throw on error traffic_policy->protocols.emplace(arg); @@ -473,9 +628,11 @@ namespace llarp conf.define_option( "network", "exit-node", + Hidden, ClientOnly, MultiValue, Comment{ + "<< DEPRECATED -- use [exit]:reserved-range instead >>\n", "Specify a `.loki` address and an ip range to use as an exit broker.", "Examples:", " exit-node=whatever.loki", @@ -488,6 +645,8 @@ namespace llarp if (arg.empty()) return; + log::warning(logcat, "This option is deprecated! Use [exit]:reserved-range instead!"); + std::optional range; const auto pos = arg.find(":"); @@ -502,7 +661,7 @@ namespace llarp if (pos != std::string::npos) arg = arg.substr(0, pos); - if (service::is_valid_ons(arg)) + if (is_valid_sns(arg)) _ons_ranges.emplace(std::move(arg), std::move(*range)); else if (auto maybe_raddr = NetworkAddress::from_network_addr(arg); maybe_raddr) _exit_ranges.emplace(std::move(*maybe_raddr), std::move(*range)); @@ -514,9 +673,12 @@ namespace llarp "network", "exit-auth", ClientOnly, + Hidden, MultiValue, Comment{ - "Specify an optional authentication code required to use a non-public exit node.", + "<< DEPRECATED -- use [exit]:auth instead >>\n", + "Specify an optional authentication code required to use ", + "a non-public exit node.", "For example:", " exit-auth=myfavouriteexit.loki:abc", "uses the authentication code `abc` whenever myfavouriteexit.loki is accessed.", @@ -526,6 +688,8 @@ namespace llarp if (arg.empty()) throw std::invalid_argument{"Empty argument passed to 'exit-auth'"}; + log::warning(logcat, "This option is deprecated! Use [exit]:auth instead!"); + const auto pos = arg.find(":"); if (pos == std::string::npos) @@ -537,7 +701,7 @@ namespace llarp const auto addr = arg.substr(0, pos); auto auth = arg.substr(pos + 1); - if (service::is_valid_ons(addr)) + if (is_valid_sns(addr)) { ons_exit_auths.emplace(std::move(addr), std::move(auth)); } @@ -661,7 +825,7 @@ namespace llarp auto addr_arg = arg.substr(0, pos); auto ip_arg = arg.substr(pos + 1); - if (service::is_valid_ons(addr_arg)) + if (is_valid_sns(addr_arg)) throw std::invalid_argument{"`mapaddr` cannot take an ONS entry: {}"_format(arg)}; if (auto maybe_raddr = NetworkAddress::from_network_addr(std::move(addr_arg)); maybe_raddr) @@ -719,7 +883,7 @@ namespace llarp if (not maybe_srv) throw std::invalid_argument{"Invalid SRV Record string: {}"_format(arg)}; - srv_records.push_back(std::move(*maybe_srv)); + srv_records.emplace(std::move(*maybe_srv)); }); conf.define_option( @@ -743,7 +907,6 @@ namespace llarp "network", "persist-addrmap-file", ClientOnly, - Default{fs::path{params.default_data_dir / "addrmap.dat"}}, Comment{ "If given this specifies a file in which to record mapped local tunnel addresses so", "the same local address will be used for the same lokinet address on reboot. If this", @@ -791,6 +954,7 @@ namespace llarp log::warning(logcat, "{} {}", err, load_file ? "NOT FOUND" : "STALE"); } + if (not data.empty()) { std::string_view bdata{data.data(), data.size()}; @@ -833,7 +997,7 @@ namespace llarp continue; } - if (service::is_valid_ons(*arg)) + if (is_valid_sns(*arg)) { log::warning(logcat, "{}: {}", addrmap_errorstr, "cannot accept ONS names!"); continue; @@ -1059,31 +1223,29 @@ namespace llarp auto parse_addr_for_link = [net_ptr](const std::string& arg, bool& given_port_only) { std::optional maybe = std::nullopt; - std::string_view arg_v{arg}, host; + std::string_view arg_v{arg}; + std::string host; uint16_t p{}; if (auto pos = arg_v.find(':'); pos != arg_v.npos) { // host = arg_v.substr(0, pos); - log::critical(logcat, "Parsing input: {}", arg); std::tie(host, p) = detail::parse_addr(arg_v, DEFAULT_LISTEN_PORT); - log::critical(logcat, "Parsed input = {}:{}", host, p); } if (host.empty()) { - log::critical( - logcat, "Host value empty, port:{}{}", p, p == DEFAULT_LISTEN_PORT ? "(DEFAULT PORT)" : ""); + log::debug(logcat, "Host value empty, port:{}{}", p, p == DEFAULT_LISTEN_PORT ? "(DEFAULT PORT)" : ""); given_port_only = p != DEFAULT_LISTEN_PORT; maybe = net_ptr->get_best_public_address(true, p); } else - maybe = oxen::quic::Address{std::string{host}, p}; + maybe = oxen::quic::Address{host, p}; if (maybe and maybe->is_loopback()) throw std::invalid_argument{"{} is a loopback address"_format(arg)}; - log::critical(logcat, "parsed address: {}", *maybe); + log::trace(logcat, "parsed address: {}", *maybe); return maybe; }; @@ -1313,7 +1475,7 @@ namespace llarp "add-node", MultiValue, Comment{ - "Specify a bootstrap file containing a list of signed RouterContacts of service " + "Specify a bootstrap file containing a list of signed RelayContacts of service " "nodes", "which can act as a bootstrap. Can be specified multiple times.", }, @@ -1380,7 +1542,8 @@ namespace llarp { (void)params; - constexpr Default DefaultUniqueCIDR{32}; + constexpr Default DefaultUniqueCIDR{24}; + conf.define_option( "paths", "unique-range-size", @@ -1539,14 +1702,12 @@ namespace llarp return load_config_data(ini, std::nullopt, isRelay); } - bool Config::load_default_config(bool isRelay) - { - return load_string("", isRelay); - } + bool Config::load_default_config(bool isRelay) { return load_string("", isRelay); } void Config::init_config(ConfigDefinition& conf, const ConfigGenParameters& params) { router.define_config_options(conf, params); + exit.define_config_options(conf, params); network.define_config_options(conf, params); paths.define_config_options(conf, params); dns.define_config_options(conf, params); diff --git a/llarp/config/config.hpp b/llarp/config/config.hpp index 51e8aae3d1..5aae2b004e 100644 --- a/llarp/config/config.hpp +++ b/llarp/config/config.hpp @@ -8,12 +8,11 @@ #include #include #include +#include #include #include #include -#include -#include -#include +#include #include #include @@ -34,8 +33,9 @@ namespace llarp using ConfigMap = llarp::ConfigParser::ConfigMap; inline constexpr uint16_t DEFAULT_LISTEN_PORT{1090}; + inline const oxen::quic::Address DEFAULT_CLIENT_LISTEN_ADDR{"0.0.0.0", DEFAULT_LISTEN_PORT}; inline constexpr uint16_t DEFAULT_DNS_PORT{53}; - inline constexpr int CLIENT_ROUTER_CONNECTIONS = 4; + inline constexpr size_t CLIENT_ROUTER_CONNECTIONS{4}; // TODO: don't use these maps. they're sloppy and difficult to follow /// Small struct to gather all parameters needed for config generation to reduce the number of @@ -57,7 +57,7 @@ namespace llarp struct RouterConfig { - int client_router_connections{CLIENT_ROUTER_CONNECTIONS}; + size_t client_router_connections{CLIENT_ROUTER_CONNECTIONS}; std::string net_id; @@ -87,20 +87,42 @@ namespace llarp /// i.e. 32 for every hop unique ip, 24 unique /24 per hop, etc uint8_t unique_hop_netmask; - /// set of countrys to exclude from path building (2 char country code) - std::unordered_set exclude_countries; - void define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params); /// return true if this set of router contacts is acceptable against this config bool check_rcs(const std::set& hops) const; }; + /** TODO: + - finalize supervenience of ExitConfig over deprecated config entries + */ + + /// Config options related to exit node services + struct ExitConfig + { + bool exit_enabled{false}; + + // Used by RemoteHandler to provide auth tokens for remote exits + std::unordered_map auth_tokens; + std::unordered_map ons_auth_tokens; + + net::ExitPolicy exit_policy; + + // Remote client ONS exit addresses mapped to local IP ranges pending ONS address resolution + // Reserved local IP ranges mapped to remote client ONS addresses (pending ONS resolution) + std::unordered_map ons_ranges; + + // Reserved local IP ranges mapped to remote client exit addresses + std::unordered_map ranges; + + void define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params); + }; + struct NetworkConfig { bool enable_profiling; bool save_profiles; - std::set strict_connect; + std::set pinned_edges; std::optional keyfile; @@ -108,16 +130,11 @@ namespace llarp std::optional paths; bool enable_ipv6{false}; - bool allow_exit{false}; bool is_reachable{false}; bool init_tun{true}; std::set snode_blacklist; - // Used by RemoteHandler to provide auth tokens for remote exits - std::unordered_map exit_auths; - std::unordered_map ons_exit_auths; - /* Auth specific config */ auth::AuthType auth_type = auth::AuthType::NONE; auth::AuthFileType auth_file_type = auth::AuthFileType::HASHES; @@ -131,9 +148,7 @@ namespace llarp std::set auth_files; - std::vector srv_records; - - std::optional traffic_policy; + std::unordered_set srv_records; std::optional path_alignment_timeout; @@ -163,15 +178,20 @@ namespace llarp // - when a session is created, check mapping when assigning IP's std::unordered_map _reserved_local_ips; + // TESTNET: moved into ExitConfig! + bool allow_exit{false}; + // Used by RemoteHandler to provide auth tokens for remote exits + std::unordered_map exit_auths; + std::unordered_map ons_exit_auths; + std::optional traffic_policy; // Remote client exit addresses mapped to local IP ranges std::unordered_map _exit_ranges; - // Remote client ONS exit addresses mapped to local IP ranges pending ONS address resolution std::unordered_map _ons_ranges; - // Used when in exit mode; pass down to LocalEndpoint - std::set _routed_ranges; + // std::set _routed_ranges; // moved into traffic_policy! + // TESTNET: move into ExitConfig! bool enable_route_poker; bool blackhole_routes; @@ -261,6 +281,7 @@ namespace llarp virtual std::unique_ptr make_gen_params() const; RouterConfig router; + ExitConfig exit; NetworkConfig network; PeerSelectionConfig paths; DnsConfig dns; diff --git a/llarp/config/ini.cpp b/llarp/config/ini.cpp index f1eb69e74d..4659ec8ded 100644 --- a/llarp/config/ini.cpp +++ b/llarp/config/ini.cpp @@ -50,10 +50,7 @@ namespace llarp _data.clear(); } - static bool whitespace(char ch) - { - return std::isspace(static_cast(ch)) != 0; - } + static bool whitespace(char ch) { return std::isspace(static_cast(ch)) != 0; } /// Differs from Parse() as ParseAll() does NOT skip comments /// ParseAll() is only used by RPC endpoint 'config' for diff --git a/llarp/consensus/reachability_testing.cpp b/llarp/consensus/reachability_testing.cpp index 8234f45b5f..39ef7e95e8 100644 --- a/llarp/consensus/reachability_testing.cpp +++ b/llarp/consensus/reachability_testing.cpp @@ -56,10 +56,7 @@ namespace llarp::consensus check_incoming_tests_impl("lokinet", now, startup, last); } - void reachability_testing::incoming_ping(const time_point_t& now) - { - last.last_test = now; - } + void reachability_testing::incoming_ping(const time_point_t& now) { last.last_test = now; } std::optional reachability_testing::next_random(Router* router, const time_point_t& now, bool requeue) { @@ -138,9 +135,6 @@ namespace llarp::consensus failing_queue.emplace(pk, steady_clock::now() + next_test_in, previous_failures + 1); } - void reachability_testing::remove_node_from_failing(const RouterID& pk) - { - failing.erase(pk); - } + void reachability_testing::remove_node_from_failing(const RouterID& pk) { failing.erase(pk); } } // namespace llarp::consensus diff --git a/llarp/consensus/reachability_testing.hpp b/llarp/consensus/reachability_testing.hpp index 5bad910c31..0315ba0608 100644 --- a/llarp/consensus/reachability_testing.hpp +++ b/llarp/consensus/reachability_testing.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include diff --git a/llarp/constants/files.hpp b/llarp/constants/files.hpp index 1feaca200a..4da3883682 100644 --- a/llarp/constants/files.hpp +++ b/llarp/constants/files.hpp @@ -39,19 +39,10 @@ namespace llarp return "C:\\ProgramData\\Lokinet"; } - inline fs::path GetDefaultConfigFilename() - { - return "lokinet.ini"; - } + inline fs::path GetDefaultConfigFilename() { return "lokinet.ini"; } - inline fs::path GetDefaultConfigPath() - { - return GetDefaultDataDir() / GetDefaultConfigFilename(); - } + inline fs::path GetDefaultConfigPath() { return GetDefaultDataDir() / GetDefaultConfigFilename(); } - inline fs::path GetDefaultBootstrap() - { - return GetDefaultDataDir() / "bootstrap.signed"; - } + inline fs::path GetDefaultBootstrap() { return GetDefaultDataDir() / "bootstrap.signed"; } } // namespace llarp diff --git a/llarp/constants/path.hpp b/llarp/constants/path.hpp index bbf3acbd99..7b2e1dbdcd 100644 --- a/llarp/constants/path.hpp +++ b/llarp/constants/path.hpp @@ -13,8 +13,9 @@ namespace llarp::path constexpr std::size_t DEFAULT_LEN = 4; /// pad messages to the nearest this many bytes constexpr std::size_t PAD_SIZE = 128; - /// default path lifetime in ms - constexpr std::chrono::milliseconds DEFAULT_LIFETIME = 20min; + /// default path lifetime in ms; + // TESTNET: reduced value + constexpr std::chrono::milliseconds DEFAULT_LIFETIME{10min}; /// minimum intro lifetime we will advertise constexpr std::chrono::milliseconds MIN_INTRO_LIFETIME = DEFAULT_LIFETIME / 2; /// number of slices of path lifetime to spread intros out via @@ -30,6 +31,10 @@ namespace llarp::path /// after this many ms a path build times out constexpr auto BUILD_TIMEOUT = 10s; + constexpr auto MIN_PATH_BUILD_INTERVAL{500ms}; + + constexpr auto PATH_BUILD_RATE{100ms}; + /// measure latency every this interval ms constexpr auto LATENCY_INTERVAL = 20s; /// if a path is inactive for this amount of time it's dead diff --git a/llarp/contact/client_contact.cpp b/llarp/contact/client_contact.cpp new file mode 100644 index 0000000000..caf49b32db --- /dev/null +++ b/llarp/contact/client_contact.cpp @@ -0,0 +1,279 @@ +#include "client_contact.hpp" + +#include + +namespace llarp +{ + static auto logcat = log::Cat("client-intro"); + + ClientContact::ClientContact( + Ed25519PrivateData private_data, + PubKey pk, + const std::unordered_set& srvs, + uint8_t proto_flags, + std::optional policy) + : derived_privatekey{std::move(private_data)}, + pubkey{std::move(pk)}, + SRVs{srvs.begin(), srvs.end()}, + protos{proto_flags}, + exit_policy{std::move(policy)} + {} + + ClientContact::ClientContact(std::string&& buf) { bt_decode(oxenc::bt_dict_consumer{buf}); } + + ClientContact ClientContact::generate( + Ed25519PrivateData&& private_data, + PubKey&& pk, + const std::unordered_set& srvs, + uint8_t proto_flags, + std::optional policy) + { + log::info(logcat, "Generating new ClientContact..."); + return ClientContact{std::move(private_data), std::move(pk), srvs, proto_flags, std::move(policy)}; + } + + void ClientContact::handle_updated_field(intro_set iset) + { + if (iset.empty()) + throw std::invalid_argument{"Cannot publish ClientContact with no ClientIntros!"}; + intros = std::move(iset); + log::debug(logcat, "ClientContact stored updated ClientIntros (n={})...", intros.size()); + } + + void ClientContact::handle_updated_field(std::unordered_set srvs) + { + log::trace(logcat, "ClientContact storing updated SRVs..."); + SRVs = std::move(srvs); + } + + void ClientContact::handle_updated_field(uint16_t proto) + { + log::trace(logcat, "ClientContact storing new protocol types..."); + protos = proto; + } + + void ClientContact::_regenerate() { log::debug(logcat, "ClientContact regenerated with updated fields!"); } + + void ClientContact::bt_encode(std::vector& buf) const + { + buf.resize(bt_encode(oxenc::bt_dict_producer{reinterpret_cast(buf.data()), buf.size()})); + } + + size_t ClientContact::bt_encode(oxenc::bt_dict_producer&& btdp) const + { + btdp.append("", ClientContact::CC_VERSION); + + btdp.append("a", pubkey.to_view()); + + if (exit_policy) + exit_policy->bt_encode(btdp.append_dict("e")); + + { + auto sublist = btdp.append_list("i"); + + for (auto& i : intros) + i.bt_encode(sublist.append_dict()); + } + + btdp.append("p", protos); + + if (not SRVs.empty()) + { + auto sublist = btdp.append_list("s"); + for (auto& s : SRVs) + s.bt_encode(sublist.append_dict()); + } + + return btdp.view().size(); + } + + void ClientContact::bt_decode(oxenc::bt_dict_consumer&& btdc) + { + auto version = btdc.require(""); + + if (ClientContact::CC_VERSION != version) + throw std::runtime_error{ + "Deserialized ClientContact with incorrect version! (Received:{}, expected:{})"_format( + version, ClientContact::CC_VERSION)}; + + pubkey.from_string(btdc.require("a")); + + if (btdc.skip_until("e")) + exit_policy->bt_decode(btdc.consume_dict_consumer()); + + btdc.required("i"); + + { + auto sublist = btdc.consume_list_consumer(); + + while (not sublist.is_finished()) + intros.emplace(sublist.consume_dict_consumer()); + } + + protos = btdc.require("p"); + + if (btdc.skip_until("s")) + { + auto sublist = btdc.consume_list_consumer(); + + while (not sublist.is_finished()) + SRVs.emplace(sublist.consume_dict_consumer()); + } + } + + session_tag ClientContact::generate_session_tag() const { return session_tag::make(protos & proto_mask); } + + bool ClientContact::is_expired(std::chrono::milliseconds now) const + { + // check the last intro to expire + return intros.rbegin()->is_expired(now); + } + + EncryptedClientContact ClientContact::encrypt_and_sign() const + { + EncryptedClientContact enc{}; + + try + { + enc.blinded_pubkey = derived_privatekey.to_pubkey(); + bt_encode(enc.encrypted); + + if (not crypto::xchacha20(enc.encrypted.data(), enc.encrypted.size(), pubkey.data(), enc.nonce.data())) + throw std::runtime_error{"Failed to encrypt ClientContact bt-payload!"}; + + enc.signed_at = llarp::time_now_ms(); + + oxenc::bt_dict_producer btdp; + enc.bt_encode(btdp); + + btdp.append_signature("~", [&](ustring_view to_sign) { + if (not crypto::sign(enc.sig, derived_privatekey, to_sign.data(), to_sign.size())) + throw std::runtime_error{"Failed to sign EncryptedClientContact payload!"}; + return enc.sig.to_view(); + }); + + enc._bt_payload = std::move(btdp).str(); + } + catch (const std::exception& e) + { + log::warning(logcat, "Exception encrypting and signing client contact: {}", e.what()); + throw; + } + + return enc; + } + + std::string ClientContact::to_string() const + { + return "CC:['a'={} | 'e'={} | 'i'={{{}}} | 'p'={} | 's'={}]"_format( + pubkey, exit_policy.has_value(), fmt::join(intros, ","), protos, not SRVs.empty()); + } + + EncryptedClientContact EncryptedClientContact::deserialize(std::string_view buf) + { + log::info(logcat, "Deserializing EncryptedClientContact..."); + return EncryptedClientContact{buf}; + } + + EncryptedClientContact::EncryptedClientContact(std::string_view buf) : _bt_payload{buf} + { + bt_decode(oxenc::bt_dict_consumer{_bt_payload}); + } + + std::string EncryptedClientContact::bt_encode() + { + oxenc::bt_dict_producer btdp; + bt_encode(btdp); + btdp.append("~", sig.to_view()); + _bt_payload = std::move(btdp).str(); + return _bt_payload; + } + + void EncryptedClientContact::bt_encode(oxenc::bt_dict_producer& btdp) const + { + btdp.append("i", blinded_pubkey.to_view()); + btdp.append("n", nonce.to_view()); + btdp.append("t", signed_at.count()); + btdp.append("x", std::string_view{reinterpret_cast(encrypted.data()), encrypted.size()}); + } + + /** EncryptedClientContact + "i" blinded local routerID + "n" nounce + "t" signing time + "x" encrypted payload + "~" signature + */ + void EncryptedClientContact::bt_decode(oxenc::bt_dict_consumer&& btdc) + { + try + { + blinded_pubkey.from_string(btdc.require("i")); + nonce.from_string(btdc.require("n")); + signed_at = std::chrono::milliseconds{btdc.require("t")}; + + // TESTNET: TOFIX: change this after oxenc span PR is merged + auto enc = btdc.require("x"); + encrypted.resize(enc.size()); + std::memcpy(encrypted.data(), enc.data(), enc.size()); + + sig.from_string(btdc.require("~")); + } + catch (const std::exception& e) + { + log::critical( + logcat, + "EncryptedClientContact deserialization failed: {} : payload: {}", + e.what(), + buffer_printer{_bt_payload}); + throw; + } + } + + std::optional EncryptedClientContact::decrypt(const PubKey& root) + { + std::optional cc = std::nullopt; + std::string payload{reinterpret_cast(encrypted.data()), encrypted.size()}; + + if (crypto::xchacha20( + reinterpret_cast(payload.data()), payload.size(), root.data(), nonce.data())) + { + log::debug(logcat, "EncryptedClientContact decrypted successfully..."); + cc = ClientContact{std::move(payload)}; + } + else + log::warning(logcat, "Failed to decrypt EncryptedClientContact!"); + + return cc; + } + + bool EncryptedClientContact::verify() const + { + try + { + oxenc::bt_dict_consumer btdc{_bt_payload}; + + btdc.require_signature("~", [this](ustring_view m, ustring_view s) { + if (s.size() != 64) + throw std::runtime_error{"Invalid signature: not 64 bytes"}; + + if (not crypto::verify(blinded_pubkey, m, s)) + throw std::runtime_error{"Failed to verify EncryptedClientContact signature!"}; + }); + } + catch (const std::exception& e) + { + log::warning(logcat, "Exception: {}", e.what()); + return false; + } + + log::info(logcat, "Successfully verified EncryptedClientContact!"); + + return true; + } + + bool EncryptedClientContact::is_expired(std::chrono::milliseconds now) const + { + return now >= signed_at + path::DEFAULT_LIFETIME; + } +} // namespace llarp diff --git a/llarp/contact/client_contact.hpp b/llarp/contact/client_contact.hpp new file mode 100644 index 0000000000..2a58f800a2 --- /dev/null +++ b/llarp/contact/client_contact.hpp @@ -0,0 +1,196 @@ +#pragma once + +#include "client_intro.hpp" +#include "router_id.hpp" +#include "tag.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace llarp +{ + struct EncryptedClientContact; + + namespace dht + { + struct CCNode; + } + + namespace handlers + { + class SessionEndpoint; + } + + // TESTNET: + inline static constexpr auto CC_PUBLISH_INTERVAL{30s}; + + /** ClientContact + On the wire we encode the data as a dict containing: + - "" : the CC format version, which must be == ClientContact::VERSION to be parsed successfully + - "a" : public key of the remote client instance + - "e" : (optional) exit policy containing sublists of accepted protocols and routed IP ranges + - "i" : list of client introductions corresponding to the different pivots through which paths can be built + to the client instance + - "p" : supported protocols indicating the traffic accepted by the client instance; this indicates if the + client is embedded and therefore requires a tunneled connection. Serialized as a bitwise flag of + protocol_flag enums (llarp/net/policy.hpp) + - "s" : (optional) SRV records for lokinet DNS lookup + */ + struct ClientContact + { + friend struct EncryptedClientContact; + friend class handlers::SessionEndpoint; + + inline static constexpr uint8_t CC_VERSION{0}; + inline static constexpr size_t MAX_CC_SIZE{4096}; + + ~ClientContact() = default; + + protected: + ClientContact() = default; + ClientContact(std::string&& buf); + + ClientContact( + Ed25519PrivateData private_data, + PubKey pk, + const std::unordered_set& srvs, + uint8_t proto_flags, + std::optional policy = std::nullopt); + + /** Parameters: + - `private_data` : derived private subkey data + - `pubkey` : master identity key pubkey + - `srvs` : SRV records (optional, can be empty) + - `proto_flags` : client-supported protocols + - `policy` : exit-related traffic policy (optional) + */ + static ClientContact generate( + Ed25519PrivateData&& private_data, + PubKey&& pubkey, + const std::unordered_set& srvs, + uint8_t proto_flags, + std::optional policy = std::nullopt); + + EncryptedClientContact encrypt_and_sign() const; + + template + void regenerate(intro_set iset, Opt&&... args) + { + handle_updated_field(std::forward(args)...); + regenerate(std::move(iset)); + } + + void regenerate(intro_set iset) + { + handle_updated_field(std::move(iset)); + _regenerate(); + } + + Ed25519PrivateData derived_privatekey; + + PubKey pubkey; + + intro_set intros; + std::unordered_set SRVs; + + uint8_t protos; + + // In exit mode, we advertise our policy for accepted traffic and the corresponding ranges + std::optional exit_policy; + + bool is_expired(std::chrono::milliseconds now = llarp::time_now_ms()) const; + + void bt_encode(std::vector& buf) const; + + size_t bt_encode(oxenc::bt_dict_producer&& btdp) const; + + // Throws like a MF (for now) + void bt_decode(std::string_view buf); + + // Throws if unsuccessful, must take BTDC in invocation + void bt_decode(oxenc::bt_dict_consumer&& btdc); + + session_tag generate_session_tag() const; + + private: + void _regenerate(); + + void handle_updated_field(uint16_t p); + void handle_updated_field(intro_set iset); + void handle_updated_field(std::unordered_set srvs); + + public: + bool operator==(const ClientContact& other) const + { + return std::tie(pubkey, intros, SRVs, protos, exit_policy) + == std::tie(other.pubkey, other.intros, other.SRVs, other.protos, other.exit_policy); + } + + bool operator!=(const ClientContact& other) const { return !(*this == other); } + + std::string to_string() const; + static constexpr bool to_string_formattable = true; + }; + + /** EncryptedClientContact + "i" blinded local PubKey (routerID) + "n" nounce + "t" signing time + "x" encrypted payload + "~" signature (signed with blinded derived scalar `b`) + */ + struct EncryptedClientContact + { + friend struct dht::CCNode; + friend struct ClientContact; + + explicit EncryptedClientContact() : nonce{SymmNonce::make_random()}, encrypted(ClientContact::MAX_CC_SIZE) {} + + static EncryptedClientContact deserialize(std::string_view buf); + + protected: + explicit EncryptedClientContact(std::string_view buf); + + PubKey blinded_pubkey; + SymmNonce nonce; + std::chrono::milliseconds signed_at{0s}; + std::vector encrypted; + Signature sig{}; + + std::string _bt_payload; + + // Does not encode signature, meant to be called prior to signing + void bt_encode(oxenc::bt_dict_producer& btdp) const; + + void bt_decode(oxenc::bt_dict_consumer&& btdc); + + std::string bt_encode(); + + public: + dht::Key_t key() const { return dht::Key_t{blinded_pubkey.data()}; } + + std::optional decrypt(const PubKey& root); + + std::string_view bt_payload() const { return _bt_payload; } + + bool verify() const; + + bool is_expired(std::chrono::milliseconds now = time_now_ms()) const; + }; +} // namespace llarp diff --git a/llarp/contact/client_intro.cpp b/llarp/contact/client_intro.cpp new file mode 100644 index 0000000000..87b73e0205 --- /dev/null +++ b/llarp/contact/client_intro.cpp @@ -0,0 +1,44 @@ +#include "client_contact.hpp" + +namespace llarp +{ + static auto logcat = log::Cat("client-intro"); + + ClientIntro::ClientIntro(oxenc::bt_dict_consumer&& btdc) { bt_decode(std::move(btdc)); } + + ClientIntro::ClientIntro(std::string_view buf) : ClientIntro{oxenc::bt_dict_consumer{buf}} {} + + void ClientIntro::bt_encode(oxenc::bt_dict_producer&& subdict) const + { + subdict.append("k", pivot_rid.to_view()); + subdict.append("p", pivot_txid.to_view()); + subdict.append("x", expiry.count()); + } + + bool ClientIntro::bt_decode(std::string_view buf) + { + try + { + bt_decode(oxenc::bt_dict_consumer{buf}); + } + catch (const std::exception& e) + { + log::critical(logcat, "ClientIntro deserialization failed: {}", e.what()); + return false; + } + + return true; + } + + void ClientIntro::bt_decode(oxenc::bt_dict_consumer&& btdc) + { + pivot_rid.from_string(btdc.require("k")); + pivot_txid.from_string(btdc.require("p")); + expiry = std::chrono::milliseconds{btdc.require("x")}; + } + + std::string ClientIntro::to_string() const + { + return "CI:['k'={} | 'p'={} | 'x'={}]"_format(pivot_rid, pivot_txid, expiry.count()); + } +} // namespace llarp diff --git a/llarp/contact/client_intro.hpp b/llarp/contact/client_intro.hpp new file mode 100644 index 0000000000..d701de913e --- /dev/null +++ b/llarp/contact/client_intro.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include +#include + +#include + +#include +#include + +namespace llarp +{ + struct ClientIntro + { + RouterID pivot_rid; + + HopID pivot_txid; // TXID at the pivot + std::chrono::milliseconds expiry{0s}; + uint64_t version{llarp::constants::proto_version}; + + ClientIntro() = default; + ClientIntro(oxenc::bt_dict_consumer&&); + ClientIntro(std::string_view buf); + + bool is_expired(std::chrono::milliseconds now = llarp::time_now_ms()) const { return now >= expiry; } + + void bt_encode(oxenc::bt_dict_producer&& subdict) const; + + // Does not throw, returns true/false + bool bt_decode(std::string_view buf); + + protected: + // Throws if unsuccessful, must take BTDC in invocation + void bt_decode(oxenc::bt_dict_consumer&& btdc); + + public: + auto operator<=>(const ClientIntro& other) const + { + return std::tie(pivot_rid, pivot_txid, expiry, version) + <=> std::tie(other.pivot_rid, other.pivot_txid, other.expiry, other.version); + } + + bool operator==(const ClientIntro& other) const { return (*this <=> other) == 0; } + + bool operator<(const ClientIntro& other) const + { + return std::tie(pivot_rid, pivot_txid, expiry, version) + < std::tie(other.pivot_rid, other.pivot_txid, other.expiry, other.version); + } + + std::string to_string() const; + static constexpr bool to_string_formattable = true; + }; + + struct ClientIntroComp + { + bool operator()(const ClientIntro& lhs, const ClientIntro& rhs) const { return lhs.expiry < rhs.expiry; } + }; + + using intro_queue = std::priority_queue, ClientIntroComp>; + using intro_set = std::set; + +} // namespace llarp + +namespace std +{ + template <> + struct hash + { + size_t operator()(const llarp::ClientIntro& i) const noexcept + { + return std::hash{}(i.pivot_rid) ^ std::hash{}(i.pivot_txid); + } + }; +} // namespace std diff --git a/llarp/contact/contactdb.cpp b/llarp/contact/contactdb.cpp new file mode 100644 index 0000000000..be47f0bd1c --- /dev/null +++ b/llarp/contact/contactdb.cpp @@ -0,0 +1,43 @@ +#include "contactdb.hpp" + +#include + +namespace llarp +{ + ContactDB::ContactDB(Router& r) : _router{r}, _local_key{dht::Key_t::derive_from_rid(r.local_rid())} + { + timer_keepalive = std::make_shared(0); + _cc_nodes = std::make_unique>(_local_key, csrng); + } + + std::optional ContactDB::get_decrypted_cc(RouterID remote) const + { + std::optional ret = std::nullopt; + + if (auto enc = get_encrypted_cc(dht::Key_t::derive_from_rid(remote))) + ret = enc->decrypt(remote); + + return ret; + } + + std::optional ContactDB::get_encrypted_cc(const dht::Key_t& key) const + { + std::optional enc = std::nullopt; + + auto& clientcontacts = _cc_nodes->nodes; + + if (auto itr = clientcontacts.find(key); itr != clientcontacts.end() && not itr->second.ecc.is_expired()) + enc = itr->second.ecc; + + return enc; + } + + nlohmann::json ContactDB::ExtractStatus() const + { + nlohmann::json obj{{"known_client_intros", _cc_nodes->ExtractStatus()}, {"local_key", _local_key.ToHex()}}; + return obj; + } + + void ContactDB::put_cc(EncryptedClientContact enc) { _cc_nodes->put_node(enc); } + +} // namespace llarp diff --git a/llarp/contact/contactdb.hpp b/llarp/contact/contactdb.hpp new file mode 100644 index 0000000000..16cbd8c78a --- /dev/null +++ b/llarp/contact/contactdb.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +namespace llarp +{ + struct Router; + + /** + ContactDB TODO: + - Store nearest-furthest expiry, trim + */ + + /// This class mediates storage, retrieval, and functionality for ClientContacts + struct ContactDB + { + private: + std::shared_ptr timer_keepalive; + Router& _router; + const dht::Key_t _local_key; + + std::unique_ptr> _cc_nodes; + + public: + explicit ContactDB(Router& r); + + std::optional get_decrypted_cc(RouterID remote) const; + + std::optional get_encrypted_cc(const dht::Key_t& key) const; + + nlohmann::json ExtractStatus() const; + + void put_cc(EncryptedClientContact enc); + + Router* router() const { return &_router; } + }; + +} // namespace llarp diff --git a/llarp/address/keys.cpp b/llarp/contact/keys.cpp similarity index 57% rename from llarp/address/keys.cpp rename to llarp/contact/keys.cpp index 1005244d12..d7a347d4f1 100644 --- a/llarp/address/keys.cpp +++ b/llarp/contact/keys.cpp @@ -1,5 +1,6 @@ #include "keys.hpp" +#include #include namespace llarp @@ -12,10 +13,7 @@ namespace llarp return true; } - std::string PubKey::to_string() const - { - return oxenc::to_hex(begin(), end()); - } + std::string PubKey::to_string() const { return oxenc::to_base32z(begin(), end()); } PubKey& PubKey::operator=(const uint8_t* ptr) { @@ -29,18 +27,9 @@ namespace llarp return *this; } - bool PubKey::operator<(const PubKey& other) const - { - return as_array() < other.as_array(); - } + bool PubKey::operator<(const PubKey& other) const { return as_array() < other.as_array(); } - bool PubKey::operator==(const PubKey& other) const - { - return as_array() == other.as_array(); - } + bool PubKey::operator==(const PubKey& other) const { return as_array() == other.as_array(); } - bool PubKey::operator!=(const PubKey& other) const - { - return !(*this == other); - } + bool PubKey::operator!=(const PubKey& other) const { return !(*this == other); } } // namespace llarp diff --git a/llarp/address/keys.hpp b/llarp/contact/keys.hpp similarity index 97% rename from llarp/address/keys.hpp rename to llarp/contact/keys.hpp index 36aa127dea..92ab9516fb 100644 --- a/llarp/address/keys.hpp +++ b/llarp/contact/keys.hpp @@ -3,11 +3,9 @@ #include #include #include -#include /** TODO: - re-configure string_view and ustring_view methods after deprecating RouterID - */ namespace llarp diff --git a/llarp/router_contact.cpp b/llarp/contact/relay_contact.cpp similarity index 76% rename from llarp/router_contact.cpp rename to llarp/contact/relay_contact.cpp index d16230f778..13a6b46438 100644 --- a/llarp/router_contact.cpp +++ b/llarp/contact/relay_contact.cpp @@ -1,18 +1,12 @@ -#include "router_contact.hpp" - -#include "constants/version.hpp" -#include "crypto/crypto.hpp" -#include "net/net.hpp" -#include "util/buffer.hpp" -#include "util/file.hpp" +#include "relay_contact.hpp" #include namespace llarp { - static auto logcat = log::Cat("RC"); + static auto logcat = log::Cat("relay-contact"); - void RouterContact::bt_verify(oxenc::bt_dict_consumer& btdc, bool reject_expired) const + void RelayContact::bt_verify(oxenc::bt_dict_consumer& btdc, bool reject_expired) const { btdc.require_signature("~", [this, reject_expired](ustring_view msg, ustring_view sig) { if (sig.size() != 64) @@ -38,9 +32,9 @@ namespace llarp btdc.finish(); } - void RouterContact::bt_load(oxenc::bt_dict_consumer& btdc) + void RelayContact::bt_load(oxenc::bt_dict_consumer& btdc) { - if (int rc_ver = btdc.require(""); rc_ver != RC_VERSION) + if (int rc_ver = btdc.require(""); rc_ver != RelayContact::VERSION) throw std::runtime_error{"Invalid RC: do not know how to parse v{} RCs"_format(rc_ver)}; auto ipv4_port = btdc.require("4"); @@ -104,7 +98,7 @@ namespace llarp _router_version[i] = ver[i]; } - bool RouterContact::write(const fs::path& fname) const + bool RelayContact::write(const fs::path& fname) const { auto bte = view(); @@ -120,7 +114,7 @@ namespace llarp return true; } - nlohmann::json RouterContact::extract_status() const + nlohmann::json RelayContact::extract_status() const { nlohmann::json obj{ {"lastUpdated", _timestamp.time_since_epoch().count()}, @@ -128,21 +122,10 @@ namespace llarp {"identity", _router_id.to_string()}, {"address", _addr.to_string()}}; - // if (routerVersion) - // { - // obj["routerVersion"] = routerVersion->to_string(); - // } - // std::vector srv; - // for (const auto& record : srvRecords) - // { - // srv.emplace_back(record.ExtractStatus()); - // } - // obj["srvRecords"] = srv; - return obj; } - bool RouterContact::is_public_addressable() const + bool RelayContact::is_public_addressable() const { if (_router_version.empty()) return false; @@ -150,24 +133,34 @@ namespace llarp return _addr.is_addressable(); } - bool RouterContact::is_expired(std::chrono::milliseconds now) const + bool RelayContact::has_ip_overlap(const RelayContact& other, uint8_t netmask) const + { + return (_addr.to_ipv4() / netmask).contains(other._addr.to_ipv4()); + } + + bool RelayContact::is_outdated(std::chrono::milliseconds now) const + { + return now >= _timestamp.time_since_epoch() + OUTDATED_AGE; + } + + bool RelayContact::is_expired(std::chrono::milliseconds now) const { - return age(now) >= _timestamp.time_since_epoch() + LIFETIME; + return now >= _timestamp.time_since_epoch() + LIFETIME; } - std::chrono::milliseconds RouterContact::time_to_expiry(std::chrono::milliseconds now) const + std::chrono::milliseconds RelayContact::time_to_expiry(std::chrono::milliseconds now) const { const auto expiry = _timestamp.time_since_epoch() + LIFETIME; return now < expiry ? expiry - now : 0s; } - std::chrono::milliseconds RouterContact::age(std::chrono::milliseconds now) const + std::chrono::milliseconds RelayContact::age(std::chrono::milliseconds now) const { auto delta = now - _timestamp.time_since_epoch(); return delta > 0s ? delta : 0s; } - bool RouterContact::expires_within_delta(std::chrono::milliseconds now, std::chrono::milliseconds dlt) const + bool RelayContact::expires_within_delta(std::chrono::milliseconds now, std::chrono::milliseconds dlt) const { return time_to_expiry(now) <= dlt; } @@ -177,7 +170,7 @@ namespace llarp "e6b3a6fe5e32c379b64212c72232d65b0b88ddf9bbaed4997409d329f8519e0b"sv, }; - bool RouterContact::is_obsolete_bootstrap() const + bool RelayContact::is_obsolete_bootstrap() const { for (const auto& k : obsolete_bootstraps) { @@ -187,7 +180,7 @@ namespace llarp return false; } - bool RouterContact::is_obsolete(const RouterContact& rc) + bool RelayContact::is_obsolete(const RelayContact& rc) { const auto& hex = rc._router_id.ToHex(); diff --git a/llarp/contact/relay_contact.hpp b/llarp/contact/relay_contact.hpp new file mode 100644 index 0000000000..2b6d87b334 --- /dev/null +++ b/llarp/contact/relay_contact.hpp @@ -0,0 +1,308 @@ +#pragma once + +#include "router_id.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace llarp +{ + inline static constexpr size_t NETID_SIZE{8}; + + /** RelayContact + On the wire we encode the data as a dict containing: + - "" : the RC format version, which must be == RelayContact::VERSION for us to attempt to + parse the reset of the fields. (Future versions might have backwards-compat support + for lower versions). + - "4" : 6 byte packed IPv4 address & port: 4 bytes of IPv4 address followed by 2 bytes of + port, both encoded in network (i.e. big-endian) order. + - "6" : optional 18 byte IPv6 address & port: 16 byte raw IPv6 address followed by 2 bytes + of port in network order. + - "i" : optional network ID string of up to 8 bytes; this is omitted for the default network + ID ("lokinet") but included for others (such as "testnet" for testnet). + - "p" : 32-byte router pubkey + - "t" : timestamp when this RC record was created (which also implicitly determines when it + goes stale and when it expires). + - "v" : lokinet version of the router; this is a three-byte packed value of + MAJOR, MINOR, PATCH, e.g. \x00\x0a\x03 for 0.10.3. + - "~" : signature of all of the previous serialized data, signed by "p" + */ + struct RelayContact + { + inline static constexpr uint8_t VERSION{0}; + + /// Unit tests disable this to allow private IP ranges in RCs, which normally get rejected. + inline static bool BLOCK_BOGONS{true}; + + inline static std::string ACTIVE_NETID{LOKINET_DEFAULT_NETID}; + + inline static constexpr size_t MAX_RC_SIZE{1024}; + + /// How long (from its signing time) before an RC becomes "outdated". Outdated records are + /// used (e.g. for path building) only if there are no newer records available, such as + /// might be the case when a client has been turned off for a while. + inline static constexpr auto OUTDATED_AGE{12h}; + + /// How long before an RC becomes invalid (and thus deleted). + inline static constexpr auto LIFETIME{30 * 24h}; + + ustring_view view() const { return _payload; } + + /// Getters for private attributes + const oxen::quic::Address& addr() const { return _addr; } + oxen::quic::Address addr() { return _addr; } + + const std::optional& addr6() const { return _addr6; } + + const RouterID& router_id() const { return _router_id; } + RouterID router_id() { return _router_id; } + + const rc_time& timestamp() const { return _timestamp; } + rc_time timestamp() { return _timestamp; } + + protected: + // advertised addresses + oxen::quic::Address _addr; // refactor all 15 uses to use addr() method + std::optional _addr6; // optional ipv6 + // public signing public key + RouterID _router_id; + + rc_time _timestamp{}; + + // Lokinet version at the time the RC was produced + std::array _router_version; + + // In both Remote and Local RC's, the entire bt-encoded payload given at construction is + // emplaced here. + // + // In a RemoteRC, this value will be held for the lifetime of the object + // s.t. it can be returned upon calls to ::bt_encode. + // In a LocalRC, this value will be supplanted any time a mutator is invoked, requiring + // the re-signing of the payload. + ustring _payload; + + public: + /// should we serialize the exit info? + static const bool serializeExit = true; + + nlohmann::json extract_status() const; + + nlohmann::json to_json() const { return extract_status(); } + + virtual std::string to_string() const + { + return "RC:['4'={} | 'i'='{}' | 'p'={} | 't'={} | v={}]"_format( + _addr.to_string(), ACTIVE_NETID, _router_id, _timestamp.time_since_epoch().count(), VERSION); + } + + bool write(const fs::path& fname) const; + + auto operator<=>(const RelayContact& other) const + { + return std::tie(_router_id, _addr, _addr6, _timestamp, _router_version) + <=> std::tie(other._router_id, other._addr, other._addr6, other._timestamp, other._router_version); + } + + bool operator==(const RelayContact& other) const { return (*this <=> other) == 0; } + + bool operator<(const RelayContact& other) const { return _router_id < other._router_id; } + + virtual void clear() {} + + bool is_public_addressable() const; + + bool has_ip_overlap(const RelayContact& other, uint8_t netmask) const; + + /// does this RC expire soon? default delta is 1 minute + bool expires_within_delta(std::chrono::milliseconds now, std::chrono::milliseconds dlt = 1min) const; + + /// returns true if this RC is outdated and should be fetched + bool is_outdated(std::chrono::milliseconds now = llarp::time_now_ms()) const; + + /// returns true if this RC is expired and should be removed + bool is_expired(std::chrono::milliseconds now) const; + + /// returns time in ms until we expire or 0 if we have expired + std::chrono::milliseconds time_to_expiry(std::chrono::milliseconds now) const; + + /// get the age of this RC in ms + std::chrono::milliseconds age(std::chrono::milliseconds now) const; + + bool other_is_newer(const RelayContact& other) const { return _timestamp < other._timestamp; } + + bool is_obsolete_bootstrap() const; + + static bool is_obsolete(const RelayContact& rc); + + void bt_verify(oxenc::bt_dict_consumer& data, bool reject_expired = false) const; + + void bt_load(oxenc::bt_dict_consumer& data); + + static constexpr bool to_string_formattable = true; + }; + + struct RemoteRC; + + /// Extension of RelayContact used to store a local "RC," and inserts a RelayContact by + /// re-parsing and sending it out. This sub-class contains a pubkey and all the other attributes + /// required for signing and serialization + struct LocalRC final : public RelayContact + { + static LocalRC make(Ed25519SecretKey secret, oxen::quic::Address local); + + private: + ustring _signature; + Ed25519SecretKey _secret_key; + + void bt_sign(oxenc::bt_dict_producer& btdp); + + void bt_encode(oxenc::bt_dict_producer& btdp); + + LocalRC(Ed25519SecretKey secret, oxen::quic::Address local); + + public: + LocalRC() = default; + ~LocalRC() = default; + + RemoteRC to_remote(); + + void resign(); + + void clear() override + { + _addr = oxen::quic::Address{}; + _addr6.reset(); + _router_id.zero(); + _timestamp = {}; + _router_version.fill(0); + _signature.clear(); + } + + auto operator<=>(const LocalRC& other) const + { + return std::tie(_router_id, _addr, _addr6, _timestamp, _router_version, _signature) <=> std::tie( + other._router_id, + other._addr, + other._addr6, + other._timestamp, + other._router_version, + other._signature); + } + + bool operator==(const LocalRC& other) const { return (*this <=> other) == 0; } + + bool operator<(const LocalRC& other) const { return _router_id < other._router_id; } + + /// Mutators for the private member attributes. Calling on the mutators + /// will clear the current signature and re-sign the RC + void set_addr(oxen::quic::Address new_addr) + { + _addr = std::move(new_addr); + resign(); + } + + void set_addr6(oxen::quic::Address new_addr) + { + _addr6 = std::move(new_addr); + resign(); + } + + void set_router_id(RouterID rid) + { + _router_id = std::move(rid); + resign(); + } + + void set_timestamp(std::chrono::milliseconds ts) + { + set_timestamp(rc_time{std::chrono::duration_cast(ts)}); + } + + void set_timestamp(rc_time ts) { _timestamp = ts; } + + /// Sets RC timestamp to current system clock time + void set_systime_timestamp() { set_timestamp(time_point_now()); } + }; + + /// Extension of RelayContact used in a "read-only" fashion. Parses the incoming RC to query + /// the data in the constructor, eliminating the need for a ::verify method/ + struct RemoteRC final : public RelayContact + { + private: + // this ctor is private because it doesn't set ::_payload + explicit RemoteRC(oxenc::bt_dict_consumer btdc, bool accept_expired = false); + + public: + RemoteRC() = default; + explicit RemoteRC(std::string_view data, bool accept_expired = false) + : RemoteRC{oxenc::bt_dict_consumer{data}, accept_expired} + { + _payload = {reinterpret_cast(data.data()), data.size()}; + } + explicit RemoteRC(ustring_view data, bool accept_expired = false) + : RemoteRC{oxenc::bt_dict_consumer{data}, accept_expired} + { + _payload = data; + } + ~RemoteRC() = default; + + std::string_view view() const { return {reinterpret_cast(_payload.data()), _payload.size()}; } + + bool verify() const; + + bool read(const fs::path& fname); + + void clear() override + { + _addr = oxen::quic::Address{}; + _addr6.reset(); + _router_id.zero(); + _timestamp = {}; + _router_version.fill(0); + } + + auto operator<=>(const RemoteRC& other) const + { + return std::tie(_router_id, _addr, _addr6, _timestamp, _router_version) + <=> std::tie(other._router_id, other._addr, other._addr6, other._timestamp, other._router_version); + } + + bool operator==(const RemoteRC& other) const { return (*this <=> other) == 0; } + + bool operator<(const RemoteRC& other) const { return _router_id < other._router_id; } + }; +} // namespace llarp + +namespace std +{ + template <> + struct hash + { + virtual size_t operator()(const llarp::RelayContact& r) const noexcept + { + return std::hash{}(r.router_id()); + } + }; + + template <> + struct hash : public hash + {}; + + template <> + struct hash : public hash + {}; +} // namespace std diff --git a/llarp/router_contact_local.cpp b/llarp/contact/relay_contact_local.cpp similarity index 88% rename from llarp/router_contact_local.cpp rename to llarp/contact/relay_contact_local.cpp index f8c4de51bb..0eea550ce0 100644 --- a/llarp/router_contact_local.cpp +++ b/llarp/contact/relay_contact_local.cpp @@ -1,10 +1,4 @@ -#include "constants/version.hpp" -#include "crypto/crypto.hpp" -#include "net/net.hpp" -#include "router_contact.hpp" -#include "util/buffer.hpp" -#include "util/file.hpp" -#include "util/time.hpp" +#include "relay_contact.hpp" #include @@ -37,7 +31,7 @@ namespace llarp btdp.append_signature("~", [this](ustring_view to_sign) { std::array sig; - if (!crypto::sign(const_cast(sig.data()), _secret_key, to_sign)) + if (!crypto::sign(sig.data(), _secret_key, to_sign)) throw std::runtime_error{"Failed to sign RC"}; _signature = {sig.data(), sig.size()}; @@ -49,7 +43,7 @@ namespace llarp void LocalRC::bt_encode(oxenc::bt_dict_producer& btdp) { - btdp.append("", RC_VERSION); + btdp.append("", VERSION); std::array buf; diff --git a/llarp/router_contact_remote.cpp b/llarp/contact/relay_contact_remote.cpp similarity index 71% rename from llarp/router_contact_remote.cpp rename to llarp/contact/relay_contact_remote.cpp index f2fc2c6169..6bffe3541f 100644 --- a/llarp/router_contact_remote.cpp +++ b/llarp/contact/relay_contact_remote.cpp @@ -1,23 +1,17 @@ -#include "constants/version.hpp" -#include "crypto/crypto.hpp" -#include "net/net.hpp" -#include "router_contact.hpp" -#include "util/buffer.hpp" -#include "util/file.hpp" -#include "util/time.hpp" +#include "relay_contact.hpp" #include namespace llarp { - static auto logcat = log::Cat("RC"); + static auto logcat = log::Cat("relay-contact"); - RemoteRC::RemoteRC(oxenc::bt_dict_consumer btdc) + RemoteRC::RemoteRC(oxenc::bt_dict_consumer btdc, bool accept_expired) { try { bt_load(btdc); - bt_verify(btdc, /*reject_expired=*/true); + bt_verify(btdc, not accept_expired); } catch (const std::exception& e) { @@ -38,7 +32,7 @@ namespace llarp log::trace(logcat, "{}B read from file (path:{})!", nread, fname); _payload.resize(nread); - oxenc::bt_dict_consumer btdc{_payload}; + oxenc::bt_dict_consumer btdc{ustring_view{_payload}}; bt_load(btdc); bt_verify(btdc); } @@ -53,7 +47,7 @@ namespace llarp bool RemoteRC::verify() const { - oxenc::bt_dict_consumer btdc{_payload}; + oxenc::bt_dict_consumer btdc{ustring_view{_payload}}; bt_verify(btdc); return true; } diff --git a/llarp/router_contact.hpp b/llarp/contact/router_contact.hpp similarity index 86% rename from llarp/router_contact.hpp rename to llarp/contact/router_contact.hpp index e90ff07088..5e349ae5f1 100644 --- a/llarp/router_contact.hpp +++ b/llarp/contact/router_contact.hpp @@ -1,12 +1,15 @@ #pragma once #include "router_id.hpp" -#include "router_version.hpp" #include -#include +#include #include +#include +#include #include +#include +#include #include #include @@ -21,7 +24,7 @@ namespace llarp inline static constexpr size_t NETID_SIZE{8}; /// On the wire we encode the data as a dict containing: - /// "" -- the RC format version, which must be == RouterContact::Version for us to attempt to + /// "" -- the RC format version, which must be == RelayContact::Version for us to attempt to /// parse the reset of the fields. (Future versions might have backwards-compat support /// for lower versions). /// "4" -- 6 byte packed IPv4 address & port: 4 bytes of IPv4 address followed by 2 bytes of @@ -37,29 +40,29 @@ namespace llarp /// MAJOR, MINOR, PATCH, e.g. \x00\x0a\x03 for 0.10.3. /// "~" -- signature of all of the previous serialized data, signed by "p" - /// RouterContact - struct RouterContact + /// RelayContact + struct RelayContact { - static constexpr uint8_t RC_VERSION = 0; + static constexpr uint8_t RC_VERSION{0}; /// Unit tests disable this to allow private IP ranges in RCs, which normally get rejected. - inline static bool BLOCK_BOGONS = true; + inline static bool BLOCK_BOGONS{true}; inline static std::string ACTIVE_NETID{LOKINET_DEFAULT_NETID}; - inline static constexpr size_t MAX_RC_SIZE = 1024; + inline static constexpr size_t MAX_RC_SIZE{1024}; /// How long (from its signing time) before an RC is considered "stale". Relays republish /// their RCs slightly more frequently than this so that ideally this won't happen. - static constexpr auto STALE_AGE = 6h; + static constexpr auto STALE_AGE{6h}; /// How long (from its signing time) before an RC becomes "outdated". Outdated records are /// used (e.g. for path building) only if there are no newer records available, such as /// might be the case when a client has been turned off for a while. - static constexpr auto OUTDATED_AGE = 12h; + static constexpr auto OUTDATED_AGE{12h}; /// How long before an RC becomes invalid (and thus deleted). - static constexpr auto LIFETIME = 30 * 24h; + static constexpr auto LIFETIME{30 * 24h}; ustring_view view() const { return _payload; } @@ -112,15 +115,15 @@ namespace llarp bool write(const fs::path& fname) const; - auto operator<=>(const RouterContact& other) const + auto operator<=>(const RelayContact& other) const { return std::tie(_router_id, _addr, _addr6, _timestamp, _router_version) <=> std::tie(other._router_id, other._addr, other._addr6, other._timestamp, other._router_version); } - bool operator==(const RouterContact& other) const { return (*this <=> other) == 0; } + bool operator==(const RelayContact& other) const { return (*this <=> other) == 0; } - bool operator<(const RouterContact& other) const { return _router_id < other._router_id; } + bool operator<(const RelayContact& other) const { return _router_id < other._router_id; } virtual void clear() {} @@ -138,11 +141,11 @@ namespace llarp /// get the age of this RC in ms std::chrono::milliseconds age(std::chrono::milliseconds now) const; - bool other_is_newer(const RouterContact& other) const { return _timestamp < other._timestamp; } + bool other_is_newer(const RelayContact& other) const { return _timestamp < other._timestamp; } bool is_obsolete_bootstrap() const; - static bool is_obsolete(const RouterContact& rc); + static bool is_obsolete(const RelayContact& rc); void bt_verify(oxenc::bt_dict_consumer& data, bool reject_expired = false) const; @@ -153,14 +156,14 @@ namespace llarp struct RemoteRC; - /// Extension of RouterContact used to store a local "RC," and inserts a RouterContact by + /// Extension of RelayContact used to store a local "RC," and inserts a RelayContact by /// re-parsing and sending it out. This sub-class contains a pubkey and all the other attributes /// required for signing and serialization /// /// Note: this class may be entirely superfluous, so it is used here as a placeholder until its /// marginal utility is determined. It may end up as a free-floating method that reads in /// parameters and outputs a bt-serialized string - struct LocalRC final : public RouterContact + struct LocalRC final : public RelayContact { static LocalRC make(Ed25519SecretKey secret, oxen::quic::Address local); @@ -238,9 +241,9 @@ namespace llarp void set_systime_timestamp() { set_timestamp(time_point_now()); } }; - /// Extension of RouterContact used in a "read-only" fashion. Parses the incoming RC to query + /// Extension of RelayContact used in a "read-only" fashion. Parses the incoming RC to query /// the data in the constructor, eliminating the need for a ::verify method/ - struct RemoteRC final : public RouterContact + struct RemoteRC final : public RelayContact { private: // this ctor is private because it doesn't set ::_payload @@ -285,19 +288,19 @@ namespace llarp namespace std { template <> - struct hash + struct hash { - virtual size_t operator()(const llarp::RouterContact& r) const + virtual size_t operator()(const llarp::RelayContact& r) const { return std::hash{}(r.router_id()); } }; template <> - struct hash : public hash + struct hash : public hash {}; template <> - struct hash final : public hash + struct hash final : public hash {}; } // namespace std diff --git a/llarp/router_id.cpp b/llarp/contact/router_id.cpp similarity index 100% rename from llarp/router_id.cpp rename to llarp/contact/router_id.cpp diff --git a/llarp/router_id.hpp b/llarp/contact/router_id.hpp similarity index 85% rename from llarp/router_id.hpp rename to llarp/contact/router_id.hpp index bc947b7632..b029271dfd 100644 --- a/llarp/router_id.hpp +++ b/llarp/contact/router_id.hpp @@ -1,7 +1,6 @@ #pragma once -#include "address/keys.hpp" - +#include #include #include @@ -13,14 +12,10 @@ namespace llarp { static constexpr size_t SIZE = 32; - using Data = std::array; - RouterID() = default; RouterID(const uint8_t* buf) : PubKey(buf) {} - RouterID(const Data& data) : PubKey(data) {} - RouterID(ustring_view data) : PubKey(data.data()) {} RouterID(std::string_view data) : RouterID(to_usv(data)) {} @@ -45,10 +40,7 @@ namespace llarp } }; - inline bool operator==(const RouterID& lhs, const RouterID& rhs) - { - return lhs.as_array() == rhs.as_array(); - } + inline bool operator==(const RouterID& lhs, const RouterID& rhs) { return lhs.as_array() == rhs.as_array(); } } // namespace llarp namespace std diff --git a/llarp/contact/sns.cpp b/llarp/contact/sns.cpp new file mode 100644 index 0000000000..64162f14f6 --- /dev/null +++ b/llarp/contact/sns.cpp @@ -0,0 +1,56 @@ +#include "sns.hpp" + +#include +#include + +namespace llarp +{ + static auto logcat = llarp::log::Cat("ONSRecord"); + + EncryptedSNSRecord EncryptedSNSRecord::deserialize(std::string_view bt) { return EncryptedSNSRecord{bt}; } + + EncryptedSNSRecord::EncryptedSNSRecord(std::string_view bt) : _bt_payload{bt} + { + bt_decode(oxenc::bt_dict_consumer{_bt_payload}); + } + + void EncryptedSNSRecord::bt_decode(oxenc::bt_dict_consumer&& btdc) + { + try + { + ciphertext = btdc.require("c"); + nonce.from_string(btdc.require("n")); + } + catch (...) + { + log::warning(logcat, "EncryptedSNSRecord exception"); + throw; + } + } + + std::string EncryptedSNSRecord::bt_encode() const + { + oxenc::bt_dict_producer btdp; + + btdp.append("c", ciphertext); + btdp.append("n", nonce.to_view()); + + return std::move(btdp).str(); + } + + std::optional EncryptedSNSRecord::decrypt(std::string_view ons_name) const + { + std::optional ret = std::nullopt; + + if (ciphertext.empty()) + return ret; + + if (auto maybe = crypto::maybe_decrypt_name(ciphertext, nonce, ons_name)) + { + auto _name = "{}.loki"_format(maybe->to_view()); + ret = NetworkAddress::from_network_addr(std::move(_name)); + } + + return ret; + } +} // namespace llarp diff --git a/llarp/contact/sns.hpp b/llarp/contact/sns.hpp new file mode 100644 index 0000000000..92fc72c393 --- /dev/null +++ b/llarp/contact/sns.hpp @@ -0,0 +1,95 @@ +#pragma once + +#include +#include + +#include + +namespace llarp +{ + struct NetworkAddress; + + /** Holds an entire SNS Record returned from a succfessful request to the `lookup_name` endpoint. + When transmitted over the wire back to the calling instance, it is bt-encoded and the SNS hash + ('ciphertext') is decrypted using the sns_name. + + bt-encoded keys: + 'c' : ciphertext + 'n' : nonce + */ + struct EncryptedSNSRecord + { + private: + explicit EncryptedSNSRecord(std::string_view bt); + void bt_decode(oxenc::bt_dict_consumer&& btdc); + + std::string _bt_payload; + + public: + SymmNonce nonce; + std::string ciphertext; + + EncryptedSNSRecord() = default; + + std::string_view bt_payload() const { return _bt_payload; } + + static EncryptedSNSRecord deserialize(std::string_view bt); + + std::string bt_encode() const; + + std::optional decrypt(std::string_view sns_name) const; + }; + + /// check if an sns name complies with the registration rules + inline bool is_valid_sns(std::string_view sns_name) + { + // make sure it ends with .loki because no fucking shit right? + if (not sns_name.ends_with(".loki")) + return false; + + // strip off .loki suffix + sns_name.remove_suffix(5); + + // ensure chars are sane + for (const auto ch : sns_name) + { + if (ch == '-') + continue; + if (ch == '.') + continue; + if (ch >= 'a' and ch <= 'z') + continue; + if (ch >= '0' and ch <= '9') + continue; + return false; + } + + // split into domain parts + const auto parts = split(sns_name, "."); + + // get root domain + const auto primaryName = parts[parts.size() - 1]; + constexpr size_t MaxNameLen = 32; + constexpr size_t MaxPunycodeNameLen = 63; + + // check against lns name blacklist + if (primaryName == "localhost") + return false; + if (primaryName == "loki") + return false; + if (primaryName == "snode") + return false; + // check for dashes + if (primaryName.find("-") == std::string_view::npos) + return primaryName.size() <= MaxNameLen; + // check for dashes and end or beginning + if (*primaryName.begin() == '-' or *(primaryName.end() - 1) == '-') + return false; + // check for punycode name length + if (primaryName.size() > MaxPunycodeNameLen) + return false; + // check for xn-- + return (primaryName[2] == '-' and primaryName[3] == '-') ? (primaryName[0] == 'x' and primaryName[1] == 'n') + : true; + } +} // namespace llarp diff --git a/llarp/contact/tag.cpp b/llarp/contact/tag.cpp new file mode 100644 index 0000000000..6704ba2239 --- /dev/null +++ b/llarp/contact/tag.cpp @@ -0,0 +1,39 @@ +#include "tag.hpp" + +#include +#include + +namespace llarp +{ + static auto logcat = log::Cat("session-tag"); + + session_tag::session_tag(uint8_t protocol) + { + randombytes(buf.data(), SIZE); + std::memcpy(buf.data(), &protocol, sizeof(uint8_t)); + log::debug(logcat, "new session tag generated: {}", buffer_printer{buf}); + } + + session_tag session_tag::make(uint8_t protocol) { return session_tag{protocol}; } + + std::tuple session_tag::proto_bits() const + { + auto& p = buf[0]; + return {p & meta::to_underlying(protocol_flag::EXIT), p & meta::to_underlying(protocol_flag::TCP2QUIC)}; + } + + void session_tag::read(std::string_view data) + { + if (data.size() != SIZE) + throw std::invalid_argument{ + "Buffer size mismatch (received: {}, expected: {}) reading in session tag!"_format(data.size(), SIZE)}; + + std::memcpy(buf.data(), data.data(), data.size()); + } + + std::string_view session_tag::view() const { return {reinterpret_cast(buf.data()), buf.size()}; } + + uspan session_tag::span() const { return buf; } + + std::string session_tag::to_string() const { return {reinterpret_cast(buf.data()), buf.size()}; } +} // namespace llarp diff --git a/llarp/contact/tag.hpp b/llarp/contact/tag.hpp new file mode 100644 index 0000000000..5bcde2a384 --- /dev/null +++ b/llarp/contact/tag.hpp @@ -0,0 +1,49 @@ +#pragma once + +// #include +#include +#include + +namespace llarp +{ + struct alignas(uint64_t) session_tag + { + static constexpr size_t SIZE{8}; + + std::array buf; + + session_tag() = default; + + private: + session_tag(uint8_t protocol); + + public: + static session_tag make(uint8_t protocol); + + std::tuple proto_bits() const; + + void read(std::string_view buf); + + std::string_view view() const; + uspan span() const; + + auto operator<=>(const session_tag& other) const { return buf <=> other.buf; } + bool operator==(const session_tag& other) const { return (*this <=> other) == 0; } + + std::string to_string() const; + static constexpr bool to_string_formattable = true; + }; + +} // namespace llarp + +namespace std +{ + template <> + struct hash + { + size_t operator()(const llarp::session_tag& tag) const noexcept + { + return std::hash{}(tag.view()); + } + }; +} // namespace std diff --git a/llarp/context.cpp b/llarp/context.cpp index d111543d47..8b81db2fa7 100644 --- a/llarp/context.cpp +++ b/llarp/context.cpp @@ -39,15 +39,9 @@ namespace llarp config = std::move(conf); } - bool Context::is_up() const - { - return router && router->is_running(); - } + bool Context::is_up() const { return router && router->is_running(); } - bool Context::looks_alive() const - { - return router && router->looks_alive(); - } + bool Context::looks_alive() const { return router && router->looks_alive(); } void Context::setup(const RuntimeOptions& opts) { @@ -127,10 +121,7 @@ namespace llarp close_waiter = std::make_unique>(); } - bool Context::is_stopping() const - { - return close_waiter.operator bool(); - } + bool Context::is_stopping() const { return close_waiter.operator bool(); } void Context::wait() { diff --git a/llarp/crypto/constants.hpp b/llarp/crypto/constants.hpp index 32cb142e9c..23c786a03a 100644 --- a/llarp/crypto/constants.hpp +++ b/llarp/crypto/constants.hpp @@ -1,22 +1,14 @@ #pragma once -#include - #include -static constexpr uint32_t PUBKEYSIZE = 32; -static constexpr uint32_t SECKEYSIZE = 64; -static constexpr uint32_t NONCESIZE = 24; -static constexpr uint32_t SHAREDKEYSIZE = 32; -static constexpr uint32_t HASHSIZE = 64; -static constexpr uint32_t SHORTHASHSIZE = 32; -static constexpr uint32_t HMACSECSIZE = 32; -static constexpr uint32_t SIGSIZE = 64; -static constexpr uint32_t TUNNONCESIZE = 32; -static constexpr uint32_t HMACSIZE = 32; -static constexpr uint32_t PATHIDSIZE = 16; - -static constexpr uint32_t PQ_CIPHERTEXTSIZE = crypto_kem_CIPHERTEXTBYTES; -static constexpr uint32_t PQ_PUBKEYSIZE = crypto_kem_PUBLICKEYBYTES; -static constexpr uint32_t PQ_SECRETKEYSIZE = crypto_kem_SECRETKEYBYTES; -static constexpr uint32_t PQ_KEYPAIRSIZE = (PQ_SECRETKEYSIZE + PQ_PUBKEYSIZE); +static constexpr uint32_t PATHIDSIZE{16}; +static constexpr uint32_t NONCESIZE{24}; +static constexpr uint32_t HMACSECSIZE{32}; +static constexpr uint32_t HMACSIZE{32}; +static constexpr uint32_t PUBKEYSIZE{32}; +static constexpr uint32_t SHAREDKEYSIZE{32}; +static constexpr uint32_t SHORTHASHSIZE{32}; +static constexpr uint32_t TUNNONCESIZE{32}; +static constexpr uint32_t SIGSIZE{64}; +static constexpr uint32_t SECKEYSIZE{64}; diff --git a/llarp/crypto/crypto.cpp b/llarp/crypto/crypto.cpp index 4e55af9a57..89843b6dfd 100644 --- a/llarp/crypto/crypto.cpp +++ b/llarp/crypto/crypto.cpp @@ -1,6 +1,7 @@ #include "crypto.hpp" -#include +#include +#include #include #include @@ -28,7 +29,7 @@ namespace llarp const PubKey& client_pk, const PubKey& server_pk, const uint8_t* themPub, - const Ed25519Hash& local_edhash) + const Ed25519PrivateData& local_edhash) { SharedSecret shared; crypto_generichash_state h; @@ -57,7 +58,7 @@ namespace llarp { SharedSecret dh_result; - if (dh(dh_result, sk.to_pubkey(), pk, pk.data(), sk.to_edhash())) + if (dh(dh_result, sk.to_pubkey(), pk, pk.data(), sk.to_eddata())) { return crypto_generichash_blake2b( shared.data(), shared.size(), n.data(), n.size(), dh_result.data(), dh_result.size()) @@ -72,7 +73,7 @@ namespace llarp { SharedSecret dh_result; - if (dh(dh_result, pk, sk.to_pubkey(), pk.data(), sk.to_edhash())) + if (dh(dh_result, pk, sk.to_pubkey(), pk.data(), sk.to_eddata())) { return crypto_generichash_blake2b( shared.data(), shared.size(), n.data(), n.size(), dh_result.data(), dh_result.size()) @@ -152,14 +153,9 @@ namespace llarp return crypto_generichash_blake2b(result.data(), ShortHash::SIZE, buf, size, nullptr, 0) != -1; } - bool crypto::hmac(uint8_t* result, uint8_t* buf, size_t size, const SharedSecret& secret) + bool crypto::hmac(uint8_t* result, const uint8_t* buf, size_t size, const SharedSecret& secret) { - return crypto_generichash_blake2b(result, HMACSIZE, buf, size, secret.data(), HMACSECSIZE) != -1; - } - - static bool hash(uint8_t* result, const llarp_buffer_t& buff) - { - return crypto_generichash_blake2b(result, HASHSIZE, buff.base, buff.sz, nullptr, 0) != -1; + return crypto_generichash_blake2b(result, HMACSIZE, buf, size, secret.data(), SharedSecret::SIZE) != -1; } bool crypto::sign(Signature& sig, const Ed25519SecretKey& secret, uint8_t* buf, size_t size) @@ -177,7 +173,7 @@ namespace llarp return crypto_sign_detached(sig, nullptr, buf.data(), buf.size(), sk.data()) != -1; } - bool crypto::sign(Signature& sig, const Ed25519Hash& privkey, uint8_t* buf, size_t size) + bool crypto::sign(Signature& sig, const Ed25519PrivateData& privkey, const uint8_t* buf, size_t size) { PubKey pubkey = privkey.to_pubkey(); @@ -225,7 +221,7 @@ namespace llarp : false; } - bool crypto::verify(const PubKey& pub, uint8_t* buf, size_t size, const Signature& sig) + bool crypto::verify(const PubKey& pub, const uint8_t* buf, size_t size, const Signature& sig) { return crypto_sign_verify_detached(sig.data(), buf, size, pub.data()) != -1; } @@ -237,17 +233,12 @@ namespace llarp : false; } - bool crypto::verify(uint8_t* pub, uint8_t* buf, size_t size, uint8_t* sig) - { - return crypto_sign_verify_detached(sig, buf, size, pub) != -1; - } - void crypto::derive_encrypt_outer_wrapping( const Ed25519SecretKey& shared_key, SharedSecret& secret, const SymmNonce& nonce, const RouterID& remote, - uspan payload) + std::span payload) { // derive shared key if (!crypto::dh_client(secret, remote, shared_key, nonce)) @@ -267,10 +258,13 @@ namespace llarp } void crypto::derive_decrypt_outer_wrapping( - const Ed25519SecretKey& local_sk, const PubKey& remote, const SymmNonce& nonce, uspan encrypted) + const Ed25519SecretKey& local_sk, + SharedSecret& shared, + const PubKey& remote, + const SymmNonce& nonce, + std::span encrypted) { - SharedSecret shared; - // derive shared secret using ephemeral pubkey and our secret key (and nonce) + // derive shared secret using shared secret and our secret key (and nonce) if (!crypto::dh_server(shared, remote, local_sk, nonce)) { auto err = "DH server failed during shared key derivation!"s; @@ -318,118 +312,52 @@ namespace llarp "can't in the and by be or then before so just face it this text hurts " "to read? lokinet yolo!"; - template - static bool make_scalar(AlignedBuffer<32>& out, const K& k, uint64_t i) + std::array crypto::make_scalar(const PubKey& k, uint64_t domain) { // b = BLIND-STRING || k || i - std::array buf; + std::array buf; std::copy(derived_key_hash_str, derived_key_hash_str + 160, buf.begin()); std::copy(k.begin(), k.end(), buf.begin() + 160); - oxenc::write_host_as_little(i, buf.data() + 160 + K::SIZE); + oxenc::write_host_as_little(domain, buf.data() + 160 + PubKey::SIZE); + // n = H(b) // h = make_point(n) - ShortHash n; - return -1 != crypto_generichash_blake2b(n.data(), ShortHash::SIZE, buf.data(), buf.size(), nullptr, 0) - && -1 != crypto_core_ed25519_from_uniform(out.data(), n.data()); - } - - static AlignedBuffer<32> zero; + std::array n; + std::array out; - bool crypto::derive_subkey( - PubKey& out_pubkey, const PubKey& root_pubkey, uint64_t key_n, const AlignedBuffer<32>* hash) - { - // scalar h = H( BLIND-STRING || root_pubkey || key_n ) - AlignedBuffer<32> h; - if (hash) - h = *hash; - else if (not make_scalar(h, root_pubkey, key_n)) - { - log::error(logcat, "cannot make scalar"); - return false; - } + crypto_generichash_blake2b(n.data(), n.size(), buf.data(), buf.size(), nullptr, 0); + crypto_core_ed25519_scalar_reduce(out.data(), n.data()); - return 0 == crypto_scalarmult_ed25519(out_pubkey.data(), h.data(), root_pubkey.data()); + return out; } - bool crypto::derive_subkey_private( - Ed25519Hash& out_key, const Ed25519SecretKey& root_key, uint64_t key_n, const AlignedBuffer<32>* hash) + bool crypto::derive_subkey(uint8_t* derived, size_t derived_len, const PubKey& root_pubkey, uint64_t key_n) { - // Derives a private subkey from a root key. - // - // The basic idea is: - // - // h = H( BLIND-STRING || A || key_n ) - // a - private key - // A = aB - public key - // s - signing hash - // a' = ah - derived private key - // A' = a'B = (ah)B - derived public key - // s' = H(h || s) - derived signing hash - // - // libsodium throws some wrenches in the mechanics which are a nuisance, - // the biggest of which is that sodium's secret key is *not* `a`; rather - // it is the seed. If you want to get the private key (i.e. "a"), you - // need to SHA-512 hash it and then clamp that. - // - // This also makes signature verification harder: we can't just use - // sodium's sign function because it wants to be given the seed rather - // than the private key, and moreover we can't actually *get* the seed to - // make libsodium happy because we only have `ah` above; thus we - // reimplemented most of sodium's detached signing function but without - // the hash step. - // - // Lastly, for the signing hash s', we need some value that is both - // different from the root s but also unknowable from the public key - // (since otherwise `r` in the signing function would be known), so we - // generate it from a hash of `h` and the root key's (psuedorandom) - // signing hash, `s`. - // - const auto root_pubkey = root_key.to_pubkey(); - - AlignedBuffer<32> h; - if (hash) - h = *hash; - else if (not make_scalar(h, root_pubkey, key_n)) + if (derived_len != PubKey::SIZE) { - log::error(logcat, "cannot make scalar"); + log::error(logcat, "Derived pubkey must be {}!", PubKey::SIZE); return false; } - h[0] &= 248; - h[31] &= 63; - h[31] |= 64; - - Ed25519Hash a = root_key.to_edhash(); - - // a' = ha - crypto_core_ed25519_scalar_mul(out_key.data(), h.data(), a.data()); - - // s' = H(h || s) - std::array buf; - std::copy(h.begin(), h.end(), buf.begin()); - std::copy(a.signing_hash().begin(), a.signing_hash().end(), buf.begin() + 32); - return -1 != crypto_generichash_blake2b(out_key.signing_hash().data(), 32, buf.data(), buf.size(), nullptr, 0); - - return true; + // scalar h = H( BLIND-STRING || root_pubkey || key_n ) + std::array h = crypto::make_scalar(root_pubkey, key_n); + return 0 == crypto_scalarmult_ed25519_noclamp(derived, h.data(), root_pubkey.data()); } - void crypto::randomize(uint8_t* buf, size_t len) - { - randombytes(buf, len); - } + void crypto::randomize(uint8_t* buf, size_t len) { randombytes(buf, len); } - void crypto::randbytes(uint8_t* ptr, size_t sz) - { - randombytes((unsigned char*)ptr, sz); - } + void crypto::randbytes(uint8_t* ptr, size_t sz) { randombytes((unsigned char*)ptr, sz); } - void crypto::identity_keygen(Ed25519SecretKey& keys) + Ed25519SecretKey crypto::generate_identity() { + Ed25519SecretKey ret{}; PubKey pk; - int result = crypto_sign_ed25519_keypair(pk.data(), keys.data()); + [[maybe_unused]] int result = crypto_sign_ed25519_keypair(pk.data(), ret.data()); assert(result != -1); - const PubKey sk_pk = keys.to_pubkey(); + const PubKey sk_pk = ret.to_pubkey(); + (void)sk_pk; assert(pk == sk_pk); + return ret; } bool crypto::check_identity_privkey(const Ed25519SecretKey& keys) @@ -444,28 +372,6 @@ namespace llarp return keys.to_pubkey() == pk && sk == keys; } - void crypto::encryption_keygen(Ed25519SecretKey& keys) - { - auto d = keys.data(); - randbytes(d, 32); - crypto_scalarmult_curve25519_base(d + 32, d); // expects xkey - } - - bool crypto::pqe_encrypt(PQCipherBlock& ciphertext, SharedSecret& sharedkey, const PQPubKey& pubkey) - { - return crypto_kem_enc(ciphertext.data(), sharedkey.data(), pubkey.data()) != -1; - } - bool crypto::pqe_decrypt(const PQCipherBlock& ciphertext, SharedSecret& sharedkey, const uint8_t* secretkey) - { - return crypto_kem_dec(sharedkey.data(), ciphertext.data(), secretkey) != -1; - } - - void crypto::pqe_keygen(PQKeyPair& keypair) - { - auto d = keypair.data(); - crypto_kem_keypair(d + PQ_SECRETKEYSIZE, d); - } - #ifdef HAVE_CRYPT bool crypto::check_passwd_hash(std::string pwhash, std::string challenge) { @@ -482,29 +388,9 @@ namespace llarp } #endif - const uint8_t* seckey_to_pubkey(const Ed25519SecretKey& sec) - { - return sec.data() + 32; - } + const uint8_t* seckey_to_pubkey(const Ed25519SecretKey& sec) { return sec.data() + 32; } - const uint8_t* pq_keypair_to_pubkey(const PQKeyPair& k) - { - return k.data() + PQ_SECRETKEYSIZE; - } - - const uint8_t* pq_keypair_to_seckey(const PQKeyPair& k) - { - return k.data(); - } - - uint64_t randint() - { - uint64_t i; - randombytes((uint8_t*)&i, sizeof(i)); - return i; - } - - // Called during static initialization to initialize libsodium and ntru. (The CSRNG return is + // Called during static initialization to initialize libsodium. (The CSRNG return is // not useful, but just here to get this called during static initialization of `csrng`). static CSRNG _initialize_crypto() { @@ -513,12 +399,9 @@ namespace llarp log::critical(logcat, "sodium_init() failed, unable to continue!"); std::abort(); } - char* avx2 = std::getenv("AVX2_FORCE_DISABLE"); - ntru_init(avx2 && avx2 == "1"sv); return CSRNG{}; } CSRNG csrng = _initialize_crypto(); - } // namespace llarp diff --git a/llarp/crypto/crypto.hpp b/llarp/crypto/crypto.hpp index 80d96f458a..5a35ffc396 100644 --- a/llarp/crypto/crypto.hpp +++ b/llarp/crypto/crypto.hpp @@ -2,8 +2,9 @@ #include "types.hpp" -#include +#include #include +#include #include @@ -42,7 +43,7 @@ namespace llarp bool shorthash(ShortHash&, uint8_t*, size_t size); /// blake2s 256 bit hmac - bool hmac(uint8_t*, uint8_t*, size_t, const SharedSecret&); + bool hmac(uint8_t*, const uint8_t*, size_t, const SharedSecret&); /// ed25519 sign bool sign(Signature&, const Ed25519SecretKey&, uint8_t* buf, size_t size); @@ -52,13 +53,12 @@ namespace llarp bool sign(uint8_t* sig, const Ed25519SecretKey& sk, ustring_view buf); /// ed25519 sign (custom with derived keys) - bool sign(Signature&, const Ed25519Hash&, uint8_t* buf, size_t size); + bool sign(Signature&, const Ed25519PrivateData&, const uint8_t* buf, size_t size); /// ed25519 verify bool verify(const PubKey&, ustring_view, ustring_view); - bool verify(const PubKey&, uint8_t*, size_t, const Signature&); + bool verify(const PubKey&, const uint8_t*, size_t, const Signature&); bool verify(ustring_view, ustring_view, ustring_view); - bool verify(uint8_t*, uint8_t*, size_t, uint8_t*); /// Used in path-build and session initiation messages. Derives a shared secret key for symmetric DH, encrypting /// the given payload in-place. Will throw on failure of either the client DH derivation or the xchacha20 @@ -68,26 +68,30 @@ namespace llarp SharedSecret& secret, const SymmNonce& nonce, const RouterID& remote, - uspan payload); + std::span payload); + + // void derive_encrypt_outer_wrapping( + // const Ed25519SecretKey& shared_key, + // SharedSecret& secret, + // const SymmNonce& nonce, + // const RouterID& remote, + // uspan payload); /// Used in receiving path-build and session initiation messages. Derives a shared secret key using an ephemeral /// pubkey and the provided nonce. The encrypted payload is mutated in-place. Will throw on failure of either /// the server DH derivation or the xchacha20 payload mutation void derive_decrypt_outer_wrapping( - const Ed25519SecretKey& local, const PubKey& remote, const SymmNonce& nonce, uspan encrypted); + const Ed25519SecretKey& local, + SharedSecret& shared, + const PubKey& remote, + const SymmNonce& nonce, + std::span encrypted); - /// derive sub keys for public keys. hash is really only intended for - /// testing ands key_n if given. - bool derive_subkey( - PubKey& derived, const PubKey& root, uint64_t key_n, const AlignedBuffer<32>* hash = nullptr); + std::array make_scalar(const PubKey& k, uint64_t domain); - /// derive sub keys for private keys. hash is really only intended for + /// derive sub keys for public keys. hash is really only intended for /// testing ands key_n if given. - bool derive_subkey_private( - Ed25519Hash& derived, - const Ed25519SecretKey& root, - uint64_t key_n, - const AlignedBuffer<32>* hash = nullptr); + bool derive_subkey(uint8_t* derived, size_t derived_len, const PubKey& root, uint64_t key_n); /// randomize buffer void randomize(uint8_t* buf, size_t len); @@ -95,20 +99,7 @@ namespace llarp /// randomizer memory void randbytes(uint8_t*, size_t); - /// generate signing keypair - void identity_keygen(Ed25519SecretKey&); - - /// generate encryption keypair - void encryption_keygen(Ed25519SecretKey&); - - /// generate post quantum encrytion key - void pqe_keygen(PQKeyPair&); - - /// post quantum decrypt (buffer, sharedkey_dst, sec) - bool pqe_decrypt(const PQCipherBlock&, SharedSecret&, const uint8_t*); - - /// post quantum encrypt (buffer, sharedkey_dst, pub) - bool pqe_encrypt(PQCipherBlock&, SharedSecret&, const PQPubKey&); + Ed25519SecretKey generate_identity(); bool check_identity_privkey(const Ed25519SecretKey&); @@ -120,22 +111,6 @@ namespace llarp const uint8_t* seckey_to_pubkey(const Ed25519SecretKey& secret); - const uint8_t* pq_keypair_to_pubkey(const PQKeyPair& keypair); - - const uint8_t* pq_keypair_to_seckey(const PQKeyPair& keypair); - /// rng type that uses llarp::randint(), which is cryptographically secure - struct CSRNG - { - using result_type = uint64_t; - - static constexpr uint64_t min() { return std::numeric_limits::min(); } - - static constexpr uint64_t max() { return std::numeric_limits::max(); } - - uint64_t operator()() { return llarp::randint(); } - }; - - extern CSRNG csrng; } // namespace llarp diff --git a/llarp/crypto/key_manager.cpp b/llarp/crypto/key_manager.cpp index 5655e51360..7541a1fa44 100644 --- a/llarp/crypto/key_manager.cpp +++ b/llarp/crypto/key_manager.cpp @@ -1,6 +1,6 @@ #include "key_manager.hpp" -#include "crypto.hpp" +#include "types.hpp" #include #include @@ -36,12 +36,13 @@ namespace llarp else { log::debug(logcat, "Client generating identity key..."); - crypto::identity_keygen(identity_key); + identity_key = crypto::generate_identity(); } + identity_data = identity_key.to_eddata(); public_key = seckey_to_pubkey(identity_key); - log::info(logcat, "Client public key: {}", public_key); + log::info(logcat, "Client public key: {}", public_key); is_initialized = true; } else @@ -70,7 +71,14 @@ namespace llarp void KeyManager::update_idkey(Ed25519SecretKey&& newkey) { identity_key = std::move(newkey); + identity_data = identity_key.to_eddata(); public_key = seckey_to_pubkey(identity_key); log::info(logcat, "Relay key manager updated secret key; new public key: {}", public_key); } + + Ed25519PrivateData KeyManager::derive_subkey(uint64_t domain) const + { + return identity_key.derive_private_subkey_data(domain); + } + } // namespace llarp diff --git a/llarp/crypto/key_manager.hpp b/llarp/crypto/key_manager.hpp index 067be56be0..d443ce1c85 100644 --- a/llarp/crypto/key_manager.hpp +++ b/llarp/crypto/key_manager.hpp @@ -2,7 +2,7 @@ #include "types.hpp" -#include +#include #include @@ -10,19 +10,21 @@ namespace llarp { struct Config; + namespace handlers + { + class SessionEndpoint; + } + // KeyManager manages the cryptographic keys stored on disk for the local // node. This includes private keys as well as the self-signed router contact // file (e.g. "self.signed"). // // Keys are either read from disk if they exist and are valid (see below) or // are generated and written to disk. - // - // In addition, the KeyManager detects when the keys obsolete (e.g. as a - // result of a software upgrade) and backs up existing keys before writing - // out new ones. struct KeyManager { friend struct Router; + friend class handlers::SessionEndpoint; private: KeyManager(const Config& config, bool is_relay); @@ -37,12 +39,15 @@ namespace llarp static std::shared_ptr make(const Config& config, bool is_relay); Ed25519SecretKey identity_key; + Ed25519PrivateData identity_data; RouterID public_key; fs::path rc_path; void update_idkey(Ed25519SecretKey&& newkey); + Ed25519PrivateData derive_subkey(uint64_t domain = 1) const; + public: const RouterID& router_id() const { return public_key; } }; diff --git a/llarp/crypto/types.cpp b/llarp/crypto/types.cpp index 7f5f22e0f1..80cd8ab916 100644 --- a/llarp/crypto/types.cpp +++ b/llarp/crypto/types.cpp @@ -1,10 +1,13 @@ #include "types.hpp" -#include +#include +#include #include #include #include +#include +#include #include #include @@ -12,10 +15,7 @@ namespace llarp { static auto logcat = log::Cat("cryptoutils"); - PubKey Ed25519SecretKey::to_pubkey() const - { - return PubKey(data() + 32); - } + PubKey Ed25519SecretKey::to_pubkey() const { return PubKey(data() + 32); } bool Ed25519SecretKey::load_from_file(const fs::path& fname) { @@ -41,15 +41,15 @@ namespace llarp bool Ed25519SecretKey::recalculate() { log::trace(logcat, "{} called", __PRETTY_FUNCTION__); - Ed25519Hash key = to_edhash(); + Ed25519PrivateData key = to_eddata(); PubKey pubkey = key.to_pubkey(); std::memcpy(data() + 32, pubkey.data(), 32); return true; } - Ed25519Hash Ed25519SecretKey::to_edhash() const + Ed25519PrivateData Ed25519SecretKey::to_eddata() const { - Ed25519Hash k; + Ed25519PrivateData k; unsigned char h[crypto_hash_sha512_BYTES]; crypto_hash_sha512(h, data(), 32); h[0] &= 248; @@ -59,7 +59,27 @@ namespace llarp return k; } - PubKey Ed25519Hash::to_pubkey() const + Ed25519PrivateData Ed25519SecretKey::derive_private_subkey_data(uint64_t domain) const + { + Ed25519PrivateData ret{}; + + std::array h = crypto::make_scalar(to_pubkey(), domain); + + auto a = to_eddata(); + + // a' = ha + crypto_core_ed25519_scalar_mul(ret.data(), h.data(), a.data()); + + // s' = H(h || s) + std::array buf; + std::copy(h.begin(), h.end(), buf.begin()); + std::copy(a.signing_hash().begin(), a.signing_hash().end(), buf.begin() + 32); + if (crypto_generichash_blake2b(ret.signing_hash().data(), 32, buf.data(), buf.size(), nullptr, 0) == -1) + throw std::runtime_error{"Call to `crypto_generichash_blake2b` failed!"}; + return ret; + } + + PubKey Ed25519PrivateData::to_pubkey() const { PubKey p; crypto_scalarmult_ed25519_base_noclamp(p.data(), data()); @@ -97,4 +117,45 @@ namespace llarp return n; } + shared_kx_data::shared_kx_data(Ed25519SecretKey&& sk) : ephemeral_key{std::move(sk)} + { + pubkey = ephemeral_key.to_pubkey(); + } + + void shared_kx_data::generate_xor() + { + ShortHash xhash; + crypto::shorthash(xhash, shared_secret.data(), shared_secret.size()); + xor_nonce = xhash.data(); // truncate 32 -> 24 + } + + shared_kx_data shared_kx_data::generate() { return shared_kx_data{crypto::generate_identity()}; } + + // TESTNET: TODO: check if the libsodium functions ever actually fail... + + void shared_kx_data::client_dh(const RouterID& remote) + { + if (!crypto::dh_client(shared_secret, remote, ephemeral_key, nonce)) + throw std::runtime_error{"Client DH failed -- should this even ever happen?"}; + } + + void shared_kx_data::server_dh(const Ed25519SecretKey& local_sk) + { + if (!crypto::dh_server(shared_secret, pubkey, local_sk, nonce)) + throw std::runtime_error{"Server DH failed -- should this even ever happen?"}; + } + + void shared_kx_data::encrypt(std::span data) + { + if (!crypto::xchacha20(data.data(), data.size(), shared_secret, nonce)) + throw std::runtime_error{"xchacha20 encryption failed -- should this even ever happen?"}; + } + + // identical methods, separated for clarity of use/logging for now + void shared_kx_data::decrypt(std::span data) + { + if (!crypto::xchacha20(data.data(), data.size(), shared_secret, nonce)) + throw std::runtime_error{"xchacha20 decryption failed -- should this even ever happen?"}; + } + } // namespace llarp diff --git a/llarp/crypto/types.hpp b/llarp/crypto/types.hpp index 03671feded..382c891af9 100644 --- a/llarp/crypto/types.hpp +++ b/llarp/crypto/types.hpp @@ -3,21 +3,21 @@ #include "constants.hpp" #include +#include #include #include #include -#include namespace llarp { using SharedSecret = AlignedBuffer; - using KeyExchangeNonce = AlignedBuffer<32>; + struct RouterID; struct PubKey; - struct Ed25519Hash; + struct Ed25519PrivateData; - /// Stores a sodium "secret key" value, which is actually the seed + /// Stores a sodium "secret key" value, which is actually the Ed25519 seed /// concatenated with the public key. Note that the seed is *not* the private /// key value itself, but rather the seed from which it can be calculated. struct Ed25519SecretKey final : public AlignedBuffer @@ -39,52 +39,53 @@ namespace llarp /// recalculate public component bool recalculate(); - std::string_view to_string() const { return "[secretkey]"; } - PubKey to_pubkey() const; - Ed25519Hash to_edhash() const; + Ed25519PrivateData to_eddata() const; + + Ed25519PrivateData derive_private_subkey_data(uint64_t domain = 1) const; bool load_from_file(const fs::path& fname); bool write_to_file(const fs::path& fname) const; + + std::string_view to_string() const { return "[secretkey]"; } + static constexpr bool to_string_formattable{true}; }; - /// PrivateKey is similar to SecretKey except that it only stores the private - /// key value and a hash, unlike SecretKey which stores the seed from which - /// the private key and hash value are generated. This is primarily intended - /// for use with derived keys, where we can derive the private key but not the - /// seed. - struct Ed25519Hash final : public AlignedBuffer<64> + /// Ed25519PrivateData is similar to Ed25519SecretKey except that it only stores the + /// private scalar and a hash, unlike SecretKey which stores the seed from which + /// the private key and hash value are generated. + struct Ed25519PrivateData final : public AlignedBuffer<64> { - Ed25519Hash() = default; + friend struct Ed25519SecretKey; - explicit Ed25519Hash(const uint8_t* ptr) : AlignedBuffer<64>(ptr) {} + Ed25519PrivateData() = default; - explicit Ed25519Hash(const AlignedBuffer<64>& key_and_hash) : AlignedBuffer<64>(key_and_hash) {} + explicit Ed25519PrivateData(const uint8_t* ptr) : AlignedBuffer<64>(ptr) {} + + explicit Ed25519PrivateData(const AlignedBuffer<64>& key_and_hash) : AlignedBuffer<64>(key_and_hash) {} // Returns writeable access to the 32-byte Ed25519 Private Scalar - uspan scalar() { return {data(), 32}; } + std::span scalar() { return {data(), 32}; } // Returns readable access to the 32-byte Ed25519 Private Scalar - ustring_view scalar() const { return {data(), 32}; } + uspan scalar() const { return {data(), 32}; } // Returns writeable access to the 32-byte Ed25519 Signing Hash - uspan signing_hash() { return {data() + 32, 32}; } + std::span signing_hash() { return {data() + 32, 32}; } // Returns readable access to the 32-byte Ed25519 Signing Hash - ustring_view signing_hash() const { return {data() + 32, 32}; } - - std::string_view to_string() const { return "[privatekey]"; } + uspan signing_hash() const { return {data() + 32, 32}; } PubKey to_pubkey() const; + + std::string_view to_string() const { return "[privatekey]"; } + static constexpr bool to_string_formattable{true}; }; using ShortHash = AlignedBuffer; - using LongHash = AlignedBuffer; struct Signature final : public AlignedBuffer {}; - // using SymmNonce = AlignedBuffer; - struct SymmNonce final : public AlignedBuffer { using AlignedBuffer::AlignedBuffer; @@ -101,13 +102,32 @@ namespace llarp static SymmNonce make_random(); }; - using TunnelNonce = AlignedBuffer; - using SymmKey = AlignedBuffer<32>; // not used + /// Holds all the data used for symmetric DH key-exchange (ex: path-build, session-init, etc) + struct shared_kx_data + { + shared_kx_data() = default; + + private: + shared_kx_data(Ed25519SecretKey&& sk); + + public: + Ed25519SecretKey ephemeral_key; + PubKey pubkey{}; + SharedSecret shared_secret{}; + SymmNonce nonce{SymmNonce::make_random()}; + SymmNonce xor_nonce{SymmNonce::make_random()}; + + void generate_xor(); - using PQCipherBlock = AlignedBuffer; - using PQPubKey = AlignedBuffer; - using PQKeyPair = AlignedBuffer; + static shared_kx_data generate(); + + void client_dh(const RouterID& remote); + + void server_dh(const Ed25519SecretKey& local_sk); + + void encrypt(std::span data); + + void decrypt(std::span enc); + }; - /// PKE(result, publickey, secretkey, nonce) - using path_dh_func = bool (*)(SharedSecret&, const PubKey&, const Ed25519SecretKey&, const TunnelNonce&); } // namespace llarp diff --git a/llarp/dht/bucket.hpp b/llarp/dht/bucket.hpp index 133e86fad9..b31117500d 100644 --- a/llarp/dht/bucket.hpp +++ b/llarp/dht/bucket.hpp @@ -1,18 +1,46 @@ #pragma once -#include "kademlia.hpp" #include "key.hpp" +#include #include #include #include #include +namespace llarp::concepts +{ + template > + concept XOR_comparable = U::SIZE == PUBKEYSIZE && (std::same_as || std::same_as); +} + namespace llarp::dht { static auto logcat = log::Cat("dht.bucket"); + struct XorMetric + { + const Key_t us; + + XorMetric(Key_t ourKey) : us{std::move(ourKey)} {} + + bool operator()(const Key_t& left, const Key_t& right) const { return (us ^ left) < (us ^ right); } + + bool operator()(const RemoteRC& left, const RemoteRC& right) const + { + return (left.router_id() ^ us) < (right.router_id() ^ us); + } + + template + bool operator()(const T& left, const U& right) const + { + return (left ^ us) < (right < us); + } + }; + + using rc_set = std::set; + template struct Bucket { @@ -46,7 +74,7 @@ namespace llarp::dht } }; - bool GetRandomNodeExcluding(Key_t& result, const std::set& exclude) const + bool get_random_node_excluding(Key_t& result, const std::set& exclude) const { std::vector candidates; std::set_difference( @@ -65,7 +93,7 @@ namespace llarp::dht return true; } - bool FindClosest(const Key_t& target, Key_t& result) const + bool find_closest(const Key_t& target, Key_t& result) const { Key_t mindist; mindist.Fill(0xff); @@ -81,7 +109,7 @@ namespace llarp::dht return nodes.size() > 0; } - bool GetManyRandom(std::set& result, size_t N) const + bool get_n_random(std::set& result, size_t N) const { if (nodes.size() < N || nodes.empty()) { @@ -110,7 +138,7 @@ namespace llarp::dht return result.size() == expecting; } - bool FindCloseExcluding(const Key_t& target, Key_t& result, const std::set& exclude) const + bool get_nearest_excluding(const Key_t& target, Key_t& result, const std::set& exclude) const { Key_t maxdist; maxdist.Fill(0xff); @@ -133,7 +161,7 @@ namespace llarp::dht return mindist < maxdist; } - bool GetManyNearExcluding( + bool get_n_nearest_excluding( const Key_t& target, std::set& result, size_t N, const std::set& exclude) const { std::set s(exclude.begin(), exclude.end()); @@ -141,7 +169,7 @@ namespace llarp::dht Key_t peer; while (N--) { - if (!FindCloseExcluding(target, peer, s)) + if (!get_nearest_excluding(target, peer, s)) { return false; } @@ -151,7 +179,7 @@ namespace llarp::dht return true; } - void PutNode(const Val_t& val) + void put_node(const Val_t& val) { auto itr = nodes.find(val.ID); if (itr == nodes.end() || itr->second < val) @@ -160,7 +188,7 @@ namespace llarp::dht } } - void DelNode(const Key_t& key) + void delete_node(const Key_t& key) { auto itr = nodes.find(key); if (itr != nodes.end()) @@ -169,11 +197,11 @@ namespace llarp::dht } } - bool HasNode(const Key_t& key) const { return nodes.find(key) != nodes.end(); } + bool has_node(const Key_t& key) const { return nodes.find(key) != nodes.end(); } // remove all nodes who's key matches a predicate template - void RemoveIf(Predicate pred) + void delete_node_if(Predicate pred) { auto itr = nodes.begin(); while (itr != nodes.end()) @@ -186,7 +214,7 @@ namespace llarp::dht } template - void ForEachNode(Visit_t visit) + void for_each_node(Visit_t visit) { for (const auto& item : nodes) { @@ -194,7 +222,7 @@ namespace llarp::dht } } - void Clear() { nodes.clear(); } + void clear() { nodes.clear(); } BucketStorage_t nodes; Random_t random; diff --git a/llarp/dht/kademlia.hpp b/llarp/dht/kademlia.hpp deleted file mode 100644 index 8ce8d3e9de..0000000000 --- a/llarp/dht/kademlia.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "key.hpp" - -#include - -namespace llarp::dht -{ - struct XorMetric - { - const Key_t us; - - XorMetric(const Key_t& ourKey) : us(ourKey) {} - - bool operator()(const Key_t& left, const Key_t& right) const { return (us ^ left) < (us ^ right); } - - bool operator()(const RouterContact& left, const RouterContact& right) const - { - return (left.router_id() ^ us) < (right.router_id() ^ us); - } - }; -} // namespace llarp::dht diff --git a/llarp/dht/key.hpp b/llarp/dht/key.hpp index 53a12583d5..ec7056df5e 100644 --- a/llarp/dht/key.hpp +++ b/llarp/dht/key.hpp @@ -1,7 +1,7 @@ #pragma once +#include #include -#include #include #include @@ -25,11 +25,11 @@ namespace llarp::dht std::string to_string() const { return oxenc::to_base32z(begin(), end()); } - static Key_t derive_from_rid(RouterID rid) + static Key_t derive_from_rid(PubKey root) { - PubKey pk; - crypto::derive_subkey(pk, PubKey{rid.data()}, 1); - return Key_t{pk.as_array()}; + Key_t derived; + crypto::derive_subkey(derived.data(), derived.size(), root, 1); + return derived; } Key_t operator^(const Key_t& other) const diff --git a/llarp/dht/node.hpp b/llarp/dht/node.hpp index 986a3e66c9..441b50385a 100644 --- a/llarp/dht/node.hpp +++ b/llarp/dht/node.hpp @@ -2,8 +2,8 @@ #include "key.hpp" -#include -#include +#include +#include #include @@ -11,33 +11,29 @@ namespace llarp::dht { struct RCNode { - RouterContact rc; + RelayContact rc; Key_t ID; RCNode() { ID.zero(); } - RCNode(const RouterContact& other) : rc(other), ID(other.router_id()) {} + RCNode(const RelayContact& other) : rc(other), ID(other.router_id()) {} nlohmann::json ExtractStatus() const { return rc.extract_status(); } bool operator<(const RCNode& other) const { return rc.timestamp() < other.rc.timestamp(); } }; - struct ISNode + struct CCNode { - service::EncryptedIntroSet introset; - + EncryptedClientContact ecc; Key_t ID; - ISNode() { ID.zero(); } + CCNode() { ID.zero(); } - ISNode(service::EncryptedIntroSet other) : introset(std::move(other)) - { - ID = Key_t(introset.derived_signing_key.as_array()); - } + CCNode(EncryptedClientContact other) : ecc{std::move(other)}, ID{ecc.blinded_pubkey} {} - nlohmann::json ExtractStatus() const { return introset.ExtractStatus(); } + nlohmann::json ExtractStatus() const { return nlohmann::json{{"key", ecc.key().to_string()}}; } - bool operator<(const ISNode& other) const { return introset.signed_at < other.introset.signed_at; } + bool operator<(const CCNode& other) const { return ecc.signed_at < other.ecc.signed_at; } }; } // namespace llarp::dht diff --git a/llarp/dns/message.cpp b/llarp/dns/message.cpp index e9b21dd299..2f48e0937f 100644 --- a/llarp/dns/message.cpp +++ b/llarp/dns/message.cpp @@ -3,8 +3,7 @@ #include "dns.hpp" #include "srv_data.hpp" -#include -#include +#include #include #include @@ -47,7 +46,7 @@ namespace llarp::dns return true; } - bool MessageHeader::decode(std::span b) + bool MessageHeader::decode(std::span b) { std::memcpy(_data.data(), b.data(), sizeof(_data)); for (auto& d : _data) @@ -55,10 +54,7 @@ namespace llarp::dns return true; } - nlohmann::json MessageHeader::ToJSON() const - { - return nlohmann::json{}; - } + nlohmann::json MessageHeader::ToJSON() const { return nlohmann::json{}; } Message::Message(Message&& other) : hdr_id(std::move(other.hdr_id)), @@ -86,10 +82,7 @@ namespace llarp::dns additional.resize(size_t(hdr._ar_count)); } - Message::Message(const Question& question) : hdr_id{0}, hdr_fields{} - { - questions.emplace_back(question); - } + Message::Message(const Question& question) : hdr_id{0}, hdr_fields{} { questions.emplace_back(question); } bool Message::Encode(llarp_buffer_t* buf) const { @@ -173,35 +166,7 @@ namespace llarp::dns } } - static constexpr uint16_t reply_flags(uint16_t setbits) - { - return setbits | flags_QR | flags_AA | flags_RA; - } - - void Message::add_IN_reply(llarp::huint128_t ip, bool isV6, RR_TTL_t ttl) - { - if (questions.size()) - { - hdr_fields = reply_flags(hdr_fields); - ResourceRecord rec; - rec.rr_name = questions[0].qname; - rec.rr_class = qClassIN; - rec.ttl = ttl; - if (isV6) - { - rec.rr_type = qTypeAAAA; - ip.ToV6(rec.rData); - } - else - { - const auto addr = net::TruncateV6(ip); - rec.rr_type = qTypeA; - rec.rData.resize(4); - oxenc::write_host_as_big(addr.h, rec.rData.data()); - } - answers.emplace_back(std::move(rec)); - } - } + static constexpr uint16_t reply_flags(uint16_t setbits) { return setbits | flags_QR | flags_AA | flags_RA; } void Message::add_reply(std::string name, RR_TTL_t ttl) { diff --git a/llarp/dns/message.hpp b/llarp/dns/message.hpp index 6e02538bdc..7492d8836b 100644 --- a/llarp/dns/message.hpp +++ b/llarp/dns/message.hpp @@ -39,7 +39,7 @@ namespace llarp bool Decode(llarp_buffer_t* buf) override; - bool decode(std::span b) override; + bool decode(std::span b) override; nlohmann::json ToJSON() const override; @@ -68,8 +68,6 @@ namespace llarp void add_CNAME_reply(std::string name, RR_TTL_t ttl = 1); - void add_IN_reply(llarp::huint128_t addr, bool isV6, RR_TTL_t ttl = 1); - void add_reply(std::string name, RR_TTL_t ttl = 1); void add_srv_reply(std::vector records, RR_TTL_t ttl = 1); @@ -82,7 +80,7 @@ namespace llarp bool Decode(llarp_buffer_t* buf) override; - bool decode(std::span /* b */) override { return {}; }; // TODO: + bool decode(std::span /* b */) override { return {}; }; // TODO: // Wrapper around Encode that encodes into a new buffer and returns it std::vector to_buffer() const; diff --git a/llarp/dns/platform.cpp b/llarp/dns/platform.cpp index 4ecc1c916f..bc16de54f0 100644 --- a/llarp/dns/platform.cpp +++ b/llarp/dns/platform.cpp @@ -2,10 +2,7 @@ namespace llarp::dns { - void Multi_Platform::add_impl(std::unique_ptr impl) - { - m_Impls.emplace_back(std::move(impl)); - } + void Multi_Platform::add_impl(std::unique_ptr impl) { m_Impls.emplace_back(std::move(impl)); } void Multi_Platform::set_resolver(unsigned int index, oxen::quic::Address dns, bool global) { diff --git a/llarp/dns/question.cpp b/llarp/dns/question.cpp index 6c7e4ea195..b04e14201f 100644 --- a/llarp/dns/question.cpp +++ b/llarp/dns/question.cpp @@ -93,10 +93,7 @@ namespace llarp::dns return qname.substr(0, pos); } - std::string Question::Name() const - { - return qname.substr(0, qname.find_last_of('.')); - } + std::string Question::Name() const { return qname.substr(0, qname.find_last_of('.')); } bool Question::HasTLD(const std::string& tld) const { diff --git a/llarp/dns/question.hpp b/llarp/dns/question.hpp index dbfa6f13ed..43bb674efd 100644 --- a/llarp/dns/question.hpp +++ b/llarp/dns/question.hpp @@ -3,8 +3,6 @@ #include "name.hpp" #include "serialize.hpp" -#include - namespace llarp::dns { using QType_t = uint16_t; @@ -23,7 +21,7 @@ namespace llarp::dns bool Decode(llarp_buffer_t* buf) override; - bool decode(std::span /* b */) override { return {}; } + bool decode(std::span /* b */) override { return {}; } std::string to_string() const; diff --git a/llarp/dns/rr.hpp b/llarp/dns/rr.hpp index 662500703f..e2b3e2e8af 100644 --- a/llarp/dns/rr.hpp +++ b/llarp/dns/rr.hpp @@ -3,8 +3,6 @@ #include "name.hpp" #include "serialize.hpp" -#include - #include #include @@ -27,7 +25,7 @@ namespace llarp::dns bool Decode(llarp_buffer_t* buf) override; - bool decode(std::span /* b */) override { return {}; }; + bool decode(std::span /* b */) override { return {}; }; nlohmann::json ToJSON() const override; diff --git a/llarp/dns/serialize.hpp b/llarp/dns/serialize.hpp index 5020d2b2bf..d6505b0115 100644 --- a/llarp/dns/serialize.hpp +++ b/llarp/dns/serialize.hpp @@ -19,7 +19,7 @@ namespace llarp::dns /// decode entity from buffer virtual bool Decode(llarp_buffer_t* buf) = 0; - virtual bool decode(std::span b) = 0; + virtual bool decode(std::span b) = 0; /// convert this whatever into json virtual nlohmann::json ToJSON() const = 0; diff --git a/llarp/dns/server.cpp b/llarp/dns/server.cpp index 5d31e199c1..c43a93148c 100644 --- a/llarp/dns/server.cpp +++ b/llarp/dns/server.cpp @@ -501,7 +501,7 @@ namespace llarp::dns if (parent_ptr) { parent_ptr->call( - [self = shared_from_this(), parent_ptr = std::move(parent_ptr), buf = std::move(data)] { + [self = shared_from_this(), parent_ptr = std::move(parent_ptr), buf = std::move(data)]() mutable { log::trace( logcat, "forwarding dns response from libunbound to userland (resolverAddr: {}, " @@ -597,10 +597,7 @@ namespace llarp::dns return std::nullopt; } - void Server::add_resolver(std::weak_ptr resolver) - { - _resolvers.insert(resolver); - } + void Server::add_resolver(std::weak_ptr resolver) { _resolvers.insert(resolver); } void Server::add_resolver(std::shared_ptr resolver) { @@ -608,10 +605,7 @@ namespace llarp::dns add_resolver(std::weak_ptr{resolver}); } - void Server::add_packet_source(std::weak_ptr pkt) - { - _packet_sources.push_back(pkt); - } + void Server::add_packet_source(std::weak_ptr pkt) { _packet_sources.push_back(pkt); } void Server::add_packet_source(std::shared_ptr pkt) { diff --git a/llarp/dns/server.hpp b/llarp/dns/server.hpp index 830a433f86..f06d68b16d 100644 --- a/llarp/dns/server.hpp +++ b/llarp/dns/server.hpp @@ -3,10 +3,9 @@ #include "message.hpp" #include "platform.hpp" -#include #include #include -#include +#include #include #include @@ -57,7 +56,7 @@ namespace llarp::dns virtual void send_to( const oxen::quic::Address& to, const oxen::quic::Address& from, std::vector data) const { - send_to(to, from, IPPacket{data.data(), data.size()}); + send_to(to, from, IPPacket{std::move(data)}); } /// stop reading packets and end operation @@ -136,7 +135,7 @@ namespace llarp::dns : QueryJob_Base{query}, src{std::move(source)}, resolver{to_}, asker{from_} {} - void send_reply(std::vector buf) override { src->send_to(asker, resolver, IPPacket{buf}); } + void send_reply(std::vector buf) override { src->send_to(asker, resolver, IPPacket{std::move(buf)}); } }; /// handler of dns query hooking diff --git a/llarp/dns/srv_data.cpp b/llarp/dns/srv_data.cpp index b714edc7f4..f8db510b58 100644 --- a/llarp/dns/srv_data.cpp +++ b/llarp/dns/srv_data.cpp @@ -19,12 +19,11 @@ namespace llarp::dns throw std::invalid_argument{"Invalid SRVData!"}; } - SRVData::SRVData(std::string bt) + SRVData::SRVData(oxenc::bt_dict_consumer&& btdc) { try { - oxenc::bt_dict_consumer btdc{bt}; - bt_decode(btdc); + bt_decode(std::move(btdc)); } catch (const std::exception& e) { @@ -107,16 +106,19 @@ namespace llarp::dns return is_valid(); } - std::string SRVData::bt_encode() const + void SRVData::bt_encode(oxenc::bt_dict_producer&& btdp) const { - oxenc::bt_dict_producer btdp; - btdp.append("p", port); btdp.append("s", service_proto); btdp.append("t", target); btdp.append("u", priority); btdp.append("w", weight); + } + std::string SRVData::bt_encode() const + { + oxenc::bt_dict_producer btdp; + bt_encode(std::move(btdp)); return std::move(btdp).str(); } @@ -124,8 +126,7 @@ namespace llarp::dns { try { - oxenc::bt_dict_consumer btdc{buf}; - return bt_decode(btdc); + return bt_decode(oxenc::bt_dict_consumer{buf}); } catch (const std::exception& e) { @@ -134,7 +135,7 @@ namespace llarp::dns } } - bool SRVData::bt_decode(oxenc::bt_dict_consumer& btdc) + bool SRVData::bt_decode(oxenc::bt_dict_consumer&& btdc) { try { diff --git a/llarp/dns/srv_data.hpp b/llarp/dns/srv_data.hpp index 8091e583f8..27bcc557c0 100644 --- a/llarp/dns/srv_data.hpp +++ b/llarp/dns/srv_data.hpp @@ -26,7 +26,7 @@ namespace llarp::dns { SRVData() = default; // SRVData constructor expecting a bt-encoded dictionary - explicit SRVData(std::string bt); + SRVData(oxenc::bt_dict_consumer&& btdc); SRVData(std::string _proto, uint16_t _priority, uint16_t _weight, uint16_t _port, std::string _target); /* bind-like formatted string for SRV records in config file @@ -65,21 +65,24 @@ namespace llarp::dns // but rather some sanity/safety checks bool is_valid() const; - /// so we can put SRVData in a std::set - bool operator<(const SRVData& other) const + auto operator<=>(const SRVData& other) const { return std::tie(service_proto, priority, weight, port, target) - < std::tie(other.service_proto, other.priority, other.weight, other.port, other.target); + <=> std::tie(other.service_proto, other.priority, other.weight, other.port, other.target); } - bool operator==(const SRVData& other) const + bool operator==(const SRVData& other) const { return (*this <=> other) == 0; } + + /// so we can put SRVData in a std::set + bool operator<(const SRVData& other) const { return std::tie(service_proto, priority, weight, port, target) - == std::tie(other.service_proto, other.priority, other.weight, other.port, other.target); + < std::tie(other.service_proto, other.priority, other.weight, other.port, other.target); } - bool operator!=(const SRVData& other) const { return !(*this == other); } + void bt_encode(oxenc::bt_dict_producer&& btdp) const; + // TESTNET: TODO: remove this after refactoring IntroSet -> ClientContact std::string bt_encode() const; bool bt_decode(std::string buf); @@ -87,7 +90,7 @@ namespace llarp::dns nlohmann::json ExtractStatus() const; private: - bool bt_decode(oxenc::bt_dict_consumer& btdc); + bool bt_decode(oxenc::bt_dict_consumer&& btdc); bool from_string(std::string_view srvString); }; @@ -98,7 +101,7 @@ namespace std template <> struct hash { - size_t operator()(const llarp::dns::SRVData& data) const + size_t operator()(const llarp::dns::SRVData& data) const noexcept { const std::hash h_str{}; const std::hash h_port{}; diff --git a/llarp/endpoint_base.hpp b/llarp/endpoint_base.hpp deleted file mode 100644 index 48780e56f2..0000000000 --- a/llarp/endpoint_base.hpp +++ /dev/null @@ -1,186 +0,0 @@ -#pragma once - -#include "router_id.hpp" - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace llarp -{ - namespace dns - { - class Server; - } - - /** TODO: - - add protected QUICTunnel accessor - */ - - template - class EndpointBase - { - protected: - std::unordered_set _srv_records; - - session_map _sessions; - - public: - bool should_publish_introset{true}; - - virtual ~EndpointBase() = default; - - std::shared_ptr get_session(const service::SessionTag& tag) const - { - return _sessions.get_session(tag); - } - - std::shared_ptr get_session(const net_addr_t& remote) const { return _sessions.get_session(remote); } - - /// add an srv record to this endpoint's descriptor - virtual void put_srv_record(dns::SRVData srv) - { - if (auto result = _srv_records.insert(std::move(srv)); result.second) - { - srv_records_changed(); - } - } - - /// get dns server if we have on on this endpoint - virtual std::shared_ptr DNS() const { return nullptr; }; - - /// called when srv data changes in some way - virtual void srv_records_changed() = 0; - - /// Removes one SRV record that returns true given a filter function. Returns true if one - /// SRV record was removed, false otherwise - bool delete_srv_record_conditional(std::function filter) - { - for (auto itr = _srv_records.begin(); itr != _srv_records.end(); ++itr) - { - if (filter(*itr)) - { - itr = _srv_records.erase(itr); - srv_records_changed(); - return true; - } - } - - return false; - } - - /// Removes up to `n` (or exactly `n` if the optional third parameter is passed true) SRV - /// records that return true given a filter function. Returns true if up to/exactly `n` were - /// removed (depending on the third parameter), false otherwise - bool delete_n_srv_records_conditional( - size_t n, std::function filter, bool exact = false) - - { - // `n` cannot be 0, or we have an insufficient amount of SRV records to return exactly `n` - if (n == 0 or (exact and _srv_records.size() < n)) - return false; - - bool notify{false}; - size_t num_deleted{0}; - std::unordered_set _copy{_srv_records}; - - for (auto itr = _copy.begin(); itr != _copy.end(); ++itr) - { - // `notify` - // - if (notify |= filter(*itr); notify) - { - num_deleted += 1; - itr = _copy.erase(itr); - - // We return early from the for-loop in one of two cases: - // 1) exact = true, num_deleted = n - // - Return true always - // 2) exact = false, num_deleted = n - // - Return true always - if (num_deleted == n) - { - _srv_records.swap(_copy); - srv_records_changed(); - return notify; - } - - continue; - } - } - - /** We only exit the for-loop in one of two cases: - 1) exact = true, num_deleted < n - - In this case, we return false always - 2) exact = false - - In this case, we return true if num_deleted > 0 - - (num_deleted > 0) iff (notify == true), so we can treat them as identical - - exact notify num_deleted < n num_deleted > 0 return - T T T T F - T F T F F - F T T T T - F F T F F - */ - - // Handles the first two rows of the above truth table - if (exact) - return false; - - // Handles the last two rows of the above truth table - if (notify ^= exact; notify) - { - _srv_records.swap(_copy); - srv_records_changed(); - } - - return notify; - } - - /// Removes all SRV records that return true given a filter function, indiscriminate of - /// number - bool delete_all_srv_records_conditional(std::function filter) - { - bool notify{false}; - - for (auto itr = _srv_records.begin(); itr != _srv_records.end(); ++itr) - { - if (notify |= filter(*itr); notify) - { - itr = _srv_records.erase(itr); - continue; - } - } - - if (notify) - srv_records_changed(); - - return notify; - } - - /// get copy of all srv records - std::set srv_records() const { return {_srv_records.begin(), _srv_records.end()}; } - - /// Gets the local address for the given endpoint, service or exit node - virtual oxen::quic::Address local_address() const = 0; - - virtual const std::shared_ptr& loop() = 0; - - // virtual void send_to(service::SessionTag tag, std::string payload) = 0; - }; - -} // namespace llarp diff --git a/llarp/ev/loop.cpp b/llarp/ev/loop.cpp index 723387c039..839deba878 100644 --- a/llarp/ev/loop.cpp +++ b/llarp/ev/loop.cpp @@ -6,10 +6,7 @@ namespace llarp { static auto logcat = log::Cat("EventLoop"); - std::shared_ptr EventLoop::make() - { - return std::shared_ptr{new EventLoop{}}; - } + std::shared_ptr EventLoop::make() { return std::shared_ptr{new EventLoop{}}; } EventLoop::EventLoop() : _loop{std::make_shared()} {} diff --git a/llarp/ev/loop.hpp b/llarp/ev/loop.hpp index c9b1212acb..153b3efe5d 100644 --- a/llarp/ev/loop.hpp +++ b/llarp/ev/loop.hpp @@ -2,8 +2,7 @@ #include "types.hpp" -#include -#include +#include #include #include #include diff --git a/llarp/ev/tcp.cpp b/llarp/ev/tcp.cpp index 31c818c15e..25279e1ed3 100644 --- a/llarp/ev/tcp.cpp +++ b/llarp/ev/tcp.cpp @@ -1,6 +1,6 @@ #include "tcp.hpp" -#include +#include #include namespace llarp diff --git a/llarp/ev/types.cpp b/llarp/ev/types.cpp index af5ff1be02..cd3b807115 100644 --- a/llarp/ev/types.cpp +++ b/llarp/ev/types.cpp @@ -92,7 +92,7 @@ namespace llarp } log::info(logcat, "EventTrigger resuming callback iteration..."); - self->begin(); + self->start(); } catch (const std::exception& e) { @@ -103,7 +103,7 @@ namespace llarp if (start_immediately) { - auto rv = begin(); + auto rv = start(); log::debug(logcat, "EventTrigger started {}successfully!", rv ? "" : "un"); } } @@ -134,7 +134,7 @@ namespace llarp event_add(ev.get(), &_null_tv); } - bool EventTrigger::halt() + bool EventTrigger::stop() { _is_cooling_down = false; _is_iterating = false; @@ -147,7 +147,7 @@ namespace llarp return ret; } - bool EventTrigger::begin() + bool EventTrigger::start() { _is_cooling_down = false; _is_iterating = true; @@ -197,20 +197,20 @@ namespace llarp }, this)); - log::debug(logcat, "Linux poller configured to watch FD: {}", fd); + log::debug(logcat, "Linux poller configured to watch FD {}", fd); } bool LinuxPoller::start() { auto rv = event_add(ev.get(), nullptr) == 0; - log::info(logcat, "Linux poller {} watching FD: {}", rv ? "successfully began" : "failed to start", fd); + log::info(logcat, "Linux poller {} watching FD {}", rv ? "successfully began" : "failed to start", fd); return rv; } bool LinuxPoller::stop() { auto rv = event_del(ev.get()); - log::info(logcat, "Linux poller {} watching FD: {}", rv ? "successfully stopped" : "failed to stop", fd); + log::info(logcat, "Linux poller {} watching FD {}", rv ? "successfully stopped" : "failed to stop", fd); return rv; } } // namespace llarp diff --git a/llarp/ev/types.hpp b/llarp/ev/types.hpp index 6638a063ef..558678086b 100644 --- a/llarp/ev/types.hpp +++ b/llarp/ev/types.hpp @@ -62,10 +62,10 @@ namespace llarp ~EventTrigger(); // Resumes iterative execution after successfully cooling down or being signalled to stop by the callback - bool begin(); + bool start(); // Called by the passed callback to signal that the iterative invocation should STOP - bool halt(); + bool stop(); private: // Invokes the function `f`, incrementing `::current` up to `n` before cooling down diff --git a/llarp/ev/udp.cpp b/llarp/ev/udp.cpp index 182450cb9d..413d4433e3 100644 --- a/llarp/ev/udp.cpp +++ b/llarp/ev/udp.cpp @@ -18,10 +18,7 @@ namespace llarp _local = socket->address(); } - UDPHandle::~UDPHandle() - { - socket.reset(); - } + UDPHandle::~UDPHandle() { socket.reset(); } io_result UDPHandle::_send_impl( const oxen::quic::Path& path, std::byte* buf, size_t size, uint8_t ecn, size_t& n_pkts) diff --git a/llarp/handlers/session.cpp b/llarp/handlers/session.cpp index fe925a6f54..2df55bd8ec 100644 --- a/llarp/handlers/session.cpp +++ b/llarp/handlers/session.cpp @@ -1,8 +1,10 @@ #include "session.hpp" -#include +#include +#include #include #include +#include #include namespace llarp::handlers @@ -15,10 +17,7 @@ namespace llarp::handlers _is_snode_service{_router.is_service_node()} {} - const std::shared_ptr& SessionEndpoint::loop() - { - return _router.loop(); - } + const std::shared_ptr& SessionEndpoint::loop() { return _router.loop(); } void SessionEndpoint::tick(std::chrono::milliseconds now) { @@ -36,6 +35,12 @@ namespace llarp::handlers _running = false; + if (_cc_publisher) + { + log::debug(logcat, "ClientContact publish ticker stopped!"); + _cc_publisher->stop(); + } + Lock_t l{paths_mutex}; _sessions.stop_sessions(send_close); @@ -47,9 +52,6 @@ namespace llarp::handlers { auto net_config = _router.config()->network; - if (net_config.is_reachable) - should_publish_introset = true; - _is_exit_node = _router.is_exit_node(); _is_snode_service = _router.is_service_node(); @@ -57,18 +59,15 @@ namespace llarp::handlers { assert(not _is_snode_service); - if (not net_config._routed_ranges.empty()) - { - _routed_ranges.merge(net_config._routed_ranges); - _local_introset._routed_ranges = _routed_ranges; - } - _exit_policy = net_config.traffic_policy; - _local_introset.exit_policy = _exit_policy; + client_contact.exit_policy = _exit_policy; } if (not net_config.srv_records.empty()) - _local_introset.SRVs = std::move(net_config.srv_records); + { + _srv_records.merge(net_config.srv_records); + client_contact.SRVs = _srv_records; + } if (use_tokens = not net_config.auth_static_tokens.empty(); use_tokens) _static_auth_tokens.merge(net_config.auth_static_tokens); @@ -93,18 +92,41 @@ namespace llarp::handlers { _auth_tokens.merge(net_config.exit_auths); } + + // always accept ipv4 (currently) + uint8_t protoflags = meta::to_underlying(protocol_flag::IPV4); + if (!_is_v4) + protoflags |= meta::to_underlying(protocol_flag::IPV6); + // if we are a full client, we accept standard and tunneled (tcp2quic) traffic + if (_router.using_tun_if()) + protoflags |= meta::to_underlying(protocol_flag::TCP2QUIC); + + if (_is_exit_node) + protoflags |= meta::to_underlying(protocol_flag::EXIT); + + auto& key_manager = _router.key_manager(); + + client_contact = ClientContact::generate( + key_manager->derive_subkey(), + key_manager->identity_data.to_pubkey(), + _srv_records, + protoflags, + _exit_policy); + + should_publish_cc = net_config.is_reachable; } void SessionEndpoint::build_more(size_t n) { size_t count{0}; - log::debug( - logcat, "SessionEndpoint building {} paths to random remotes (needed: {})", n, path::DEFAULT_PATHS_HELD); + log::debug(logcat, "SessionEndpoint building {} paths to random remotes (needed: {})", n, num_paths_desired); - for (size_t i = 0; i < n; ++i) - { + // TESTNET: ensure one path is built to pivot + RouterID pivot{oxenc::from_base32z("55fxrybf3jtausbnmxpgwcsz9t8qkf5pr8t5f4xyto4omjrkorpy")}; + count += build_path_aligned_to_remote(pivot); + + while (count < n) count += build_path_to_random(); - } if (count == n) log::debug(logcat, "SessionEndpoint successfully initiated {} path-builds", n); @@ -114,16 +136,47 @@ namespace llarp::handlers void SessionEndpoint::srv_records_changed() { - // TODO: Investigate the usage or the term exit RE: service nodes acting as exits - // ^^ lol - _local_introset.SRVs.clear(); + log::debug(logcat, "{} called", __PRETTY_FUNCTION__); + update_and_publish_localcc(get_current_client_intros(), _srv_records); + } + + // static std::atomic testnet_trigger = false; - for (const auto& srv : srv_records()) + void SessionEndpoint::start_tickers() + { + if (should_publish_cc) { - _local_introset.SRVs.emplace_back(srv); + log::critical(logcat, "Starting ClientContact publish ticker..."); + _cc_publisher = _router.loop()->call_every( + CC_PUBLISH_INTERVAL, + [this]() { + log::critical(logcat, "Updating and publishing ClientContact..."); + update_and_publish_localcc(get_current_client_intros()); + }, + true); + + // if (not testnet_trigger) + // { + // testnet_trigger = true; + + // _router.loop()->call_later(5s, [this]() { + // try + // { + // RouterID cpk{oxenc::from_base32z("xr8qpu9pu4qp4nhooiktfuyrwmgkut4ud3kqsf9tscwa8rtzuzhy")}; + // log::info(logcat, "Beginning session init to client: {}", cpk.to_network_address(false)); + // _initiate_session( + // NetworkAddress::from_pubkey(cpk, true), [](ip_v) { log::critical(logcat, "FUCK YEAH"); + // }); + // } + // catch (const std::exception& e) + // { + // log::critical(logcat, "Failed to parse client netaddr: {}", e.what()); + // } + // }); + // } } - - regen_and_publish_introset(); + else + log::info(logcat, "SessionEndpoint configured to NOT publish ClientContact..."); } void SessionEndpoint::resolve_ons_mappings() @@ -133,7 +186,7 @@ namespace llarp::handlers if (auto n_ons_ranges = ons_ranges.size(); n_ons_ranges > 0) { - log::info(logcat, "SessionEndpoint resolving {} ONS addresses mapped to IP ranges", n_ons_ranges); + log::info(logcat, "SessionEndpoint resolving {} SNS addresses mapped to IP ranges", n_ons_ranges); for (auto itr = ons_ranges.begin(); itr != ons_ranges.end();) { @@ -144,7 +197,7 @@ namespace llarp::handlers { log::debug( logcat, - "Successfully resolved ONS lookup for {} mapped to IPRange:{}", + "Successfully resolved SNS lookup for {} mapped to IPRange:{}", *maybe_addr, ip_range); _range_map.insert_or_assign(std::move(ip_range), std::move(*maybe_addr)); @@ -171,7 +224,7 @@ namespace llarp::handlers { log::debug( logcat, - "Successfully resolved ONS lookup for {} mapped to static auth token", + "Successfully resolved SNS lookup for {} mapped to static auth token", *maybe_addr); _auth_tokens.emplace(std::move(*maybe_addr), std::move(auth_token)); } @@ -183,169 +236,183 @@ namespace llarp::handlers } } - void SessionEndpoint::resolve_ons(std::string ons, std::function)> func) + void SessionEndpoint::resolve_ons(std::string sns, std::function)> func) { - if (not service::is_valid_ons(ons)) + if (not is_valid_sns(sns)) { - log::debug(logcat, "Invalid ONS name ({}) queried for lookup", ons); + log::debug(logcat, "Invalid SNS name ({}) queried for lookup", sns); return func(std::nullopt); } - log::debug(logcat, "Looking up ONS name {}", ons); + log::debug(logcat, "Looking up SNS name {}", sns); - auto response_handler = [ons_name = ons, hook = std::move(func)](std::string response) { - if (auto record = service::EncryptedONSRecord::construct(response); - auto client_addr = record->decrypt(ons_name)) + auto response_handler = [sns_name = sns, hook = std::move(func)](oxen::quic::message m) mutable { + try { - return hook(std::move(client_addr)); - } + if (m) + { + log::critical(logcat, "Call to ResolveSNS succeeded!"); - std::optional status = std::nullopt; + auto enc = ResolveSNS::deserialize_response(oxenc::bt_dict_consumer{m.body()}); - try - { - oxenc::bt_dict_consumer btdc{response}; + if (auto client_addr = enc.decrypt(sns_name)) + { + log::info( + logcat, + "Successfully decrypted SNS record (name: {}, address: {})", + sns_name, + client_addr->to_string()); + return hook(std::move(client_addr)); + } - if (auto s = btdc.maybe(messages::STATUS_KEY)) - status = s; + log::warning(logcat, "Failed to decrypt SNS record (name: {})", sns_name); + } } - catch (...) + catch (const std::exception& e) { - log::warning(logcat, "Exception caught parsing 'find_name' response!"); + log::warning(logcat, "Exception: {}", e.what()); } - log::warning(logcat, "Call to endpoint 'lookup_name' failed -- status:{}", status.value_or("")); hook(std::nullopt); }; { Lock_t l{paths_mutex}; - for (const auto& [rid, path] : _paths) + for (const auto& [_, path] : _paths) { - log::info(logcat, "Querying pivot:{} for name lookup (target: {})", path->pivot_rid(), ons); + log::info(logcat, "Querying pivot:{} for name lookup (target: {})", path->pivot_rid(), sns); - path->resolve_ons(ons, response_handler); + path->resolve_sns(sns, response_handler); } } } - void SessionEndpoint::lookup_intro( - RouterID remote, bool is_relayed, uint64_t order, std::function)> func) + void SessionEndpoint::lookup_client_intro(RouterID remote, std::function)> func) { - if (auto maybe_intro = _router.contacts().get_decrypted_introset(remote)) + auto remote_key = dht::Key_t::derive_from_rid(remote); + + if (auto maybe_intro = _router.contact_db().get_decrypted_cc(remote)) { - log::debug(logcat, "Decrypted introset for remote (rid:{}) found locally~", remote); + log::info(logcat, "Decrypted ClientContact for remote (rid: {}) found locally!", remote); return func(std::move(maybe_intro)); } - log::debug(logcat, "Looking up introset for remote (rid:{})", remote); - auto remote_key = dht::Key_t::derive_from_rid(remote); + log::info( + logcat, + "Looking up ClientContact (key: {}) for remote (rid:{})", + remote_key, + remote.to_network_address(false)); - auto response_handler = [this, remote, hook = std::move(func)](std::string response) { - if (auto encrypted = service::EncryptedIntroSet::construct(response); - auto intro = encrypted->decrypt(remote)) - { - log::debug(logcat, "Storing introset for remote (rid:{})", remote); - _router.contacts().put_intro(std::move(*encrypted)); - return hook(std::move(intro)); - } + auto ignore_remaining = std::make_shared(false); - std::optional status = std::nullopt; + auto response_handler = + [this, remote, hook = std::move(func), ignore_remaining](oxen::quic::message m) mutable { + if (ignore_remaining->load()) + { + log::trace(logcat, "Dropping subsequent `find_cc` response (success: {})...", not m.is_error()); + return; + } + try + { + if (m) + { + log::critical(logcat, "Call to FindClientContact succeeded!"); - try - { - oxenc::bt_dict_consumer btdc{response}; + auto enc = FindClientContact::deserialize_response(oxenc::bt_dict_consumer{m.body()}); - if (auto s = btdc.maybe(messages::STATUS_KEY)) - status = s; - } - catch (...) - { - log::warning(logcat, "Exception caught parsing 'find_intro' response!"); - } + if (auto intro = enc.decrypt(remote)) + { + log::info(logcat, "Storing ClientContact for remote rid:{}", remote); + _router.contact_db().put_cc(std::move(enc)); + ignore_remaining->store(true); + return hook(std::move(intro)); + } - log::warning(logcat, "Call to endpoint 'find_intro' failed -- status:{}", status.value_or("")); - hook(std::nullopt); - }; + log::warning(logcat, "Failed to decrypt returned EncryptedClientContact!"); + } + else + { + std::optional status = std::nullopt; + oxenc::bt_dict_consumer btdc{m.body()}; + + if (auto s = btdc.maybe(messages::STATUS_KEY)) + status = s; + + log::warning( + logcat, "Call to FindClientContact FAILED; reason: {}", status.value_or("")); + } + } + catch (const std::exception& e) + { + log::warning(logcat, "Exception: {}", e.what()); + } + + hook(std::nullopt); + }; { Lock_t l{paths_mutex}; - for (const auto& [rid, path] : _paths) + for (const auto& [_, path] : _paths) { - log::info( - logcat, "Querying pivot (rid:{}) for introset lookup target (rid:{})", path->pivot_rid(), remote); + if (not path or not path->is_ready()) + continue; - path->find_intro(remote_key, is_relayed, order, response_handler); + log::debug( + logcat, + "Querying pivot (rid:{}) for ClientContact lookup target (rid:{})", + path->pivot_rid(), + remote); + + path->find_client_contact(remote_key, response_handler); } } } - /** Introset publishing: - - When a local service or exit node publishes an introset, it is also sent along the path currently used - for that session - */ - // TODO: this - void SessionEndpoint::regen_and_publish_introset() + void SessionEndpoint::_localcc_update_fail() { - const auto now = llarp::time_now_ms(); - _last_introset_regen_attempt = now; + log::warning( + logcat, + "Failed to query enough client introductions from current paths! Building more paths to publish " + "introset"); + return build_more(1); + } - std::set path_intros; + void SessionEndpoint::update_and_publish_localcc(intro_set intros) + { + if (intros.empty()) + return _localcc_update_fail(); + client_contact.regenerate(std::move(intros)); + _update_and_publish_localcc(); + } - if (auto maybe_intros = get_path_intros_conditional([now](const service::Introduction& intro) -> bool { - return not intro.expires_soon(now, path::INTRO_STALE_THRESHOLD); - })) - { - path_intros.merge(*maybe_intros); - } - else + void SessionEndpoint::_update_and_publish_localcc() + { + try { - log::warning(logcat, "Failed to get enough valid path introductions to publish introset!"); - return build_more(1); - } - - auto& intro_protos = _local_introset.supported_protocols; - intro_protos.clear(); + auto enc = client_contact.encrypt_and_sign(); - if (_router.using_tun_if()) - { - intro_protos.push_back(_is_v4 ? service::ProtocolType::TrafficV4 : service::ProtocolType::TrafficV6); + if (not enc.verify()) + log::critical(logcat, "COULD NOT VERIFY ENCRYPTEDCLIENTCONTACT"); - if (_is_exit_node) + if (auto decrypt = enc.decrypt(_router.local_rid())) { - intro_protos.push_back(service::ProtocolType::Exit); - _local_introset.exit_policy = _exit_policy; - _local_introset._routed_ranges = _routed_ranges; + auto is_equal = client_contact == *decrypt; + log::critical(logcat, "Decrypted ClientContact is {}EQUAL to the original!", is_equal ? "" : "NOT "); } - } - - intro_protos.push_back(service::ProtocolType::TCP2QUIC); - - auto& intros = _local_introset.intros; - intros.clear(); + else + log::critical(logcat, "COULD NOT DECRYPT ENCRYPTEDCLIENTCONTACT"); - for (auto& intro : path_intros) - { - if (intros.size() < num_paths_desired) - intros.emplace(std::move(intro)); + if (publish_client_contact(enc)) + log::info(logcat, "Successfully republished updated EncryptedClientContact!"); + else + log::warning(logcat, "Failed to republish updated EncryptedClientContact!"); } - - // We already check that path_intros is not empty, so we can assert here - assert(not intros.empty()); - - if (auto maybe_encrypted = _identity.encrypt_and_sign_introset(_local_introset, now)) + catch (const std::exception& e) { - if (publish_introset(*maybe_encrypted)) - { - log::debug(logcat, "Successfully republished encrypted introset"); - } - else - log::warning(logcat, "Failed to republish encrypted introset!"); + log::warning(logcat, "ClientContact encryption/signing exception: {}", e.what()); } - else - log::warning(logcat, "Failed to encrypt and sign introset!"); } bool SessionEndpoint::validate(const NetworkAddress& remote, std::optional maybe_auth) @@ -362,13 +429,23 @@ namespace llarp::handlers } bool SessionEndpoint::prefigure_session( - NetworkAddress initiator, service::SessionTag tag, std::shared_ptr path, bool use_tun) + NetworkAddress initiator, + session_tag tag, + HopID remote_pivot_txid, + std::shared_ptr path, + shared_kx_data kx_data, + bool use_tun) { bool ret = true; - assert(path->is_client_path()); - auto inbound = - std::make_shared(initiator, std::move(path), *this, std::move(tag), use_tun); + auto inbound = std::make_shared( + initiator, + std::move(path), + *this, + std::move(remote_pivot_txid), + std::move(tag), + use_tun, + std::move(kx_data)); auto [session, _] = _sessions.insert_or_assign(std::move(initiator), std::move(inbound)); @@ -392,7 +469,7 @@ namespace llarp::handlers else { // TODO: if this fails, we should close the session - log::warning(logcat, "TUN devcice failed to route session (remote: {}) to local ip", session->remote()); + log::warning(logcat, "TUN device failed to route session (remote: {}) to local ip", session->remote()); ret = false; } } @@ -405,18 +482,47 @@ namespace llarp::handlers return ret; } - bool SessionEndpoint::publish_introset(const service::EncryptedIntroSet& introset) + bool SessionEndpoint::publish_client_contact(const EncryptedClientContact& ecc) { bool ret{true}; + log::trace(logcat, "Publishing new EncryptedClientContact: {}", ecc.bt_payload()); + { Lock_t l{paths_mutex}; - for (const auto& [rid, path] : _paths) + for (const auto& [_, path] : _paths) { - log::debug(logcat, "Publishing introset to pivot {}", path->pivot_rid()); + // If path-build is underway, don't use it + if (not path or not path->is_ready()) + continue; + + log::debug(logcat, "Publishing ClientContact on {}", path->hop_string()); - ret += path->publish_intro(introset, true); + ret &= path->publish_client_contact(ecc, [](oxen::quic::message m) { + if (m) + { + log::critical(logcat, "Call to PublishClientContact succeeded!"); + } + else + { + std::optional status = std::nullopt; + try + { + oxenc::bt_dict_consumer btdc{m.body()}; + + if (auto s = btdc.maybe(messages::STATUS_KEY)) + status = s; + } + catch (const std::exception& e) + { + log::warning(logcat, "Exception: {}", e.what()); + } + + log::critical( + logcat, "Call to PublishClientContact FAILED; reason: {}", status.value_or("")); + } + }); } } @@ -433,58 +539,147 @@ namespace llarp::handlers return ret; } + /** + + - 'k' : next HopID + - 'n' : symmetric nonce + - 'x' : encrypted payload + PATH MESSAGE ONION LAYER ('outer payload') + - 'e' : request endpoint ('path_control') + - 'p' : request payload + - 'k' : next HopID + - 'n' : symmetric nonce + - 'x' : encrypted payload + PIVOT RELAY LAYER ('intermediate payload') + - 'e' : request endpoint ('path_control') + - 'p' : request payload + - 'k' : remote client intro pivot txid, (NOT rx) + - 'n' : symmetric nonce + - 'x' : encrypted payload + REMOTE CLIENT LAYER ('inner payload') + - 'e' : request endpoint ('session_init') + - 'p' : request payload + - 'k' : shared pubkey used to derive symmetric key + - 'n' : symmetric nonce + - 'x' : encrypted payload + - 'i' : RouterID of initiator + - 'p' : HopID at the pivot taken from remote ClientIntro + - 's' : session_tag for current session + - 't' : Use Tun interface (bool) + - 'u' : Authentication field + - bt-encoded dict, values TBD + */ void SessionEndpoint::_make_session( - NetworkAddress remote, std::shared_ptr path, on_session_init_hook cb, bool is_exit) + NetworkAddress remote, + ClientIntro remote_intro, + std::shared_ptr path, + on_session_init_hook cb, + bool /* is_exit */) { - auto tag = service::SessionTag::make_random(); + auto tag = client_contact.generate_session_tag(); + + std::string inner_payload; + shared_kx_data kx_data; + + // internal payload for remote client + std::tie(inner_payload, kx_data) = InitiateSession::serialize_encrypt( + _router.local_rid(), + remote.router_id(), + path->pivot_txid(), + tag, + remote_intro.pivot_txid, + fetch_auth_token(remote), + _router.using_tun_if()); + + log::trace(logcat, "inner payload: {}", buffer_printer{inner_payload}); + + auto pivot_payload = + ONION::serialize_hop(remote_intro.pivot_txid.to_view(), SymmNonce::make_random(), inner_payload); + log::trace(logcat, "pivot payload: {}", buffer_printer{pivot_payload}); + + auto intermediate_payload = PATH::CONTROL::serialize("path_control", std::move(pivot_payload)); + log::trace(logcat, "intermediate payload: {}", buffer_printer{intermediate_payload}); path->send_path_control_message( - "session_init", - InitiateSession::serialize_encrypt( - _router.local_rid(), - remote.router_id(), - tag, - path->pivot_txid(), - fetch_auth_token(remote), - _router.using_tun_if()), - [this, remote, tag, path, hook = std::move(cb), is_exit](std::string response) mutable { - if (response == messages::OK_RESPONSE) + "path_control", + std::move(intermediate_payload), + [this, + remote, + tag = std::move(tag), + path, + remote_pivot_txid = remote_intro.pivot_txid, + hook = std::move(cb), + session_keys = std::move(kx_data)](oxen::quic::message m) mutable { + if (m) { + log::critical(logcat, "Call to InitiateSession succeeded!"); + auto outbound = std::make_shared( - remote, *this, std::move(path), std::move(tag), is_exit); + remote, + *this, + std::move(path), + std::move(remote_pivot_txid), + std::move(tag), + std::move(session_keys)); auto [session, _] = _sessions.insert_or_assign(std::move(remote), std::move(outbound)); - auto msg = "SessionEndpoint successfully created and mapped InboundSession object!"; + log::info(logcat, "Outbound session to {} successfully created...", session->remote()); // TESTNET: if (session->using_tun()) { - log::info(logcat, "{} Instructing lokinet TUN device to create mapped route...", msg); + log::info(logcat, "Instructing lokinet TUN device to create mapped route..."); if (auto maybe_ip = _router.tun_endpoint()->map_session_to_local_ip(session->remote())) { log::info( - logcat, "TUN device successfully routing session to remote: {}", session->remote()); + logcat, + "TUN device successfully routing session (remote: {}) via local ip: {}", + session->remote(), + std::holds_alternative(*maybe_ip) ? std::get(*maybe_ip).to_string() + : std::get(*maybe_ip).to_string()); - hook(*maybe_ip); - } - else - { - // TODO: if this fails, we should close the session + return hook(*maybe_ip); } + + log::critical( + logcat, + "Lokinet TUN failed to map route for session traffic to remote: {}", + session->remote()); + // TESTNET: TODO: CLOSE THIS BISH HERE } else { - log::info(logcat, "{} Starting TCP listener to route session traffic to backend...", msg); + log::info(logcat, "Starting TCP listener to route session traffic to backend..."); session->tcp_backend_listen(std::move(hook)); } } + else + { + std::optional status = std::nullopt; + try + { + oxenc::bt_dict_consumer btdc{m.body()}; + + if (auto s = btdc.maybe(messages::STATUS_KEY)) + status = s; + } + catch (const std::exception& e) + { + log::warning(logcat, "Exception: {}", e.what()); + } + + log::critical( + logcat, "Call to InitiateSession FAILED; reason: {}", status.value_or("")); + } }); + log::info(logcat, "mesage sent..."); } void SessionEndpoint::_make_session_path( - service::IntroductionSet intros, NetworkAddress remote, on_session_init_hook cb, bool is_exit) + intro_set intros, NetworkAddress remote, on_session_init_hook cb, bool is_exit) { + log::debug(logcat, "{} called", __PRETTY_FUNCTION__); // we can recurse through this function as we remove the first pivot of the set of introductions every // invocation if (intros.empty()) @@ -494,17 +689,30 @@ namespace llarp::handlers return; } - auto intro = intros.extract(intros.begin()).value(); - auto pivot = intro.pivot_router; + // TESTNET: + RouterID edge{oxenc::from_base32z("55fxrybf3jtausbnmxpgwcsz9t8qkf5pr8t5f4xyto4omjrkorpy")}; + bool using_hacky_bullshit{false}; - // DISCUSS: we don't share paths, but if every successful path-build is logged in PathContext, we are - // effectively sharing across all path-building objects...? - if (auto path_ptr = _router.path_context()->get_path(intro.pivot_hop_id)) + ClientIntro intro; + + for (auto itr = intros.begin(); itr != intros.end(); ++itr) { - log::info(logcat, "Found path to pivot (hopid: {}); initiating session!", intro.pivot_hop_id); - return _make_session(std::move(remote), std::move(path_ptr), std::move(cb), is_exit); + log::trace(logcat, "itr->pivot_rid: {}", itr->pivot_rid); + if (itr->pivot_rid == edge) + { + using_hacky_bullshit = true; + intro = intros.extract(itr).value(); + break; + } } + if (not using_hacky_bullshit) + { + intro = intros.extract(intros.begin()).value(); + } + + auto& pivot = intro.pivot_rid; + log::info(logcat, "Initiating session path-build to remote:{} via pivot:{}", remote, pivot); auto maybe_hops = aligned_hops_to_remote(pivot); @@ -512,7 +720,7 @@ namespace llarp::handlers if (not maybe_hops) { log::error(logcat, "Failed to get hops for path-build to pivot:{}", pivot); - return; + return _make_session_path(std::move(intros), std::move(remote), std::move(cb), is_exit); } auto& hops = *maybe_hops; @@ -520,20 +728,29 @@ namespace llarp::handlers auto path = std::make_shared(_router, std::move(hops), get_weak(), true, remote.is_client()); - log::info(logcat, "Building path -> {} : {}", path->to_string(), path->HopsString()); + log::info(logcat, "Building path -> {} : {}", path->to_string(), path->hop_string()); auto payload = build2(path); + auto upstream = path->upstream_rid(); if (not build3( - path->upstream_rid(), + std::move(upstream), std::move(payload), - [this, path, intros, remote, hook = std::move(cb), is_exit](oxen::quic::message m) mutable { + [this, + path = std::move(path), + remote_intro = std::move(intro), + intros = std::move(intros), + remote, + hook = std::move(cb), + is_exit](oxen::quic::message m) mutable { if (m) { // Do not call ::add_path() or ::path_build_succeeded() here; OutboundSession constructor will // take care of both path storage and logging in PathContext + log::critical(logcat, "PATH ESTABLISHED: {}", path->hop_string()); log::info(logcat, "Path build to remote:{} succeeded, initiating session!", remote); - return _make_session(std::move(remote), std::move(path), std::move(hook), is_exit); + return _make_session( + std::move(remote), std::move(remote_intro), std::move(path), std::move(hook), is_exit); } try @@ -561,7 +778,7 @@ namespace llarp::handlers _make_session_path(std::move(intros), std::move(remote), std::move(hook), is_exit); })) { - log::critical(logcat, "Error sending path_build control message for session initiation!"); + log::critical(logcat, "Error sending `path_build` control message for session initiation!"); } } @@ -570,29 +787,23 @@ namespace llarp::handlers if (is_exit and not remote.is_client()) throw std::runtime_error{"Cannot initiate exit session to remote service node!"}; - auto counter = std::make_shared(path::DEFAULT_PATHS_HELD); + auto counter = std::make_shared(num_paths_desired); _router.loop()->call([this, remote, handler = std::move(cb), is_exit, counter]() mutable { - lookup_intro( + lookup_client_intro( remote.router_id(), - false, - 0, - [this, remote, hook = std::move(handler), is_exit, counter]( - std::optional intro) mutable { - // already have a successful return + [this, remote, hook = std::move(handler), is_exit, counter](std::optional cc) mutable { if (*counter == 0) return; - if (intro) + if (cc) { *counter = 0; - log::info(logcat, "Session initiation returned successful 'lookup_intro'..."); - _make_session_path(std::move(intro->intros), remote, std::move(hook), is_exit); + log::info(logcat, "Session initiation returned client contact: {}", cc->to_string()); + _make_session_path(std::move(cc->intros), remote, std::move(hook), is_exit); } else if (--*counter == 0) - { - log::warning(logcat, "Failed to initiate session at 'lookup_intro' (target:{})", remote); - } + log::warning(logcat, "Failed to initiate session at 'find_cc' (target:{})", remote.router_id()); }); }); @@ -604,29 +815,17 @@ namespace llarp::handlers _address_map.insert_or_assign(std::move(local), std::move(remote)); } - void SessionEndpoint::unmap_local_addr_by_remote(const NetworkAddress& remote) - { - _address_map.unmap(remote); - } + void SessionEndpoint::unmap_local_addr_by_remote(const NetworkAddress& remote) { _address_map.unmap(remote); } - void SessionEndpoint::unmap_remote_by_name(const std::string& name) - { - _address_map.unmap(name); - } + void SessionEndpoint::unmap_remote_by_name(const std::string& name) { _address_map.unmap(name); } void SessionEndpoint::map_remote_to_local_range(NetworkAddress remote, IPRange range) { _range_map.insert_or_assign(std::move(range), std::move(remote)); } - void SessionEndpoint::unmap_local_range_by_remote(const NetworkAddress& remote) - { - _range_map.unmap(remote); - } + void SessionEndpoint::unmap_local_range_by_remote(const NetworkAddress& remote) { _range_map.unmap(remote); } - void SessionEndpoint::unmap_range_by_name(const std::string& name) - { - _range_map.unmap(name); - } + void SessionEndpoint::unmap_range_by_name(const std::string& name) { _range_map.unmap(name); } } // namespace llarp::handlers diff --git a/llarp/handlers/session.hpp b/llarp/handlers/session.hpp index 013dea25dd..d5e697a630 100644 --- a/llarp/handlers/session.hpp +++ b/llarp/handlers/session.hpp @@ -2,8 +2,8 @@ #include #include +#include #include -#include #include namespace llarp @@ -21,7 +21,7 @@ namespace llarp std::unordered_set _srv_records; - bool should_publish_introset{true}; + bool should_publish_cc{false}; session_map _sessions; @@ -31,10 +31,9 @@ namespace llarp // - Directly pre-loaded from config address_map _range_map; - service::Identity _identity; // TODO: TESTNET: move responsibilities to KeyManager - service::IntroSet _local_introset; + ClientContact client_contact; - std::chrono::milliseconds _last_introset_regen_attempt{0s}; + std::shared_ptr _cc_publisher; // auth tokens for making outbound sessions std::unordered_map _auth_tokens; @@ -61,7 +60,7 @@ namespace llarp std::set _routed_ranges; // formerly from LocalEndpoint // policies about traffic that we are willing to carry -- Exit mode only! - std::optional _exit_policy = std::nullopt; + std::optional _exit_policy = std::nullopt; public: SessionEndpoint(Router& r); @@ -84,13 +83,11 @@ namespace llarp oxen::quic::Address local_address() const { return _local_addr; } - const service::IntroSet& intro_set() const { return _local_introset; } - // get copy of all srv records std::set srv_records() const { return {_srv_records.begin(), _srv_records.end()}; } template - std::shared_ptr get_session(const service::SessionTag& tag) const + std::shared_ptr get_session(const session_tag& tag) const { return std::static_pointer_cast(_sessions.get_session(tag)); } @@ -103,28 +100,42 @@ namespace llarp void srv_records_changed(); - void regen_and_publish_introset(); + // This function can be called with the fields to be updated. ClientIntros are always passed, so there + // is no need to pass them to this function + template + void update_and_publish_localcc(intro_set intros, Opt&&... args) + { + if (intros.empty()) + return _localcc_update_fail(); + client_contact.regenerate(std::move(intros), std::forward(args)...); + _update_and_publish_localcc(); + } + + void update_and_publish_localcc(intro_set intros); - bool publish_introset(const service::EncryptedIntroSet& introset); + void start_tickers(); + + bool publish_client_contact(const EncryptedClientContact& ecc); // SessionEndpoint can use either a whitelist or a static auth token list to validate incomininbg requests // to initiate a session bool validate(const NetworkAddress& remote, std::optional maybe_auth = std::nullopt); bool prefigure_session( - NetworkAddress initiator, service::SessionTag tag, std::shared_ptr path, bool use_tun); - - // lookup ONS address to return "{pubkey}.loki" hidden service or exit node operated on a remote client + NetworkAddress initiator, + session_tag tag, + HopID remote_pivot_txid, + std::shared_ptr path, + shared_kx_data kx_data, + bool use_tun); + + // lookup SNS address to return "{pubkey}.loki" hidden service or exit node operated on a remote client void resolve_ons(std::string name, std::function)> func = nullptr); void lookup_remote_srv( std::string name, std::string service, std::function)> handler); - void lookup_intro( - RouterID remote, - bool is_relayed, - uint64_t order, - std::function)> func); + void lookup_client_intro(RouterID remote, std::function)> func); // resolves any config mappings that parsed ONS addresses to their pubkey network address void resolve_ons_mappings(); @@ -157,13 +168,20 @@ namespace llarp void unmap_range_by_name(const std::string& name); private: + void _localcc_update_fail(); + + void _update_and_publish_localcc(); + bool _initiate_session(NetworkAddress remote, on_session_init_hook cb, bool is_exit = false); - void _make_session_path( - service::IntroductionSet intros, NetworkAddress remote, on_session_init_hook cb, bool is_exit); + void _make_session_path(intro_set intros, NetworkAddress remote, on_session_init_hook cb, bool is_exit); void _make_session( - NetworkAddress remote, std::shared_ptr path, on_session_init_hook cb, bool is_exit); + NetworkAddress remote, + ClientIntro remote_intro, + std::shared_ptr path, + on_session_init_hook cb, + bool is_exit); }; } // namespace handlers diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index e4b8a34ae9..adef0537cf 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -9,13 +9,12 @@ #include #include +#include #include -#include #include #include #include -#include -#include +#include #include namespace llarp::handlers @@ -268,153 +267,133 @@ namespace llarp::handlers } } + /** DISCUSS: Can the auth objects be further simplified? + - In the original implementation, the AuthPolicy async logic was for the instance receiving the connection + request to execute its aynchronous logic and queue the authentication job + + Static Token Auth: + - In the re-designed auth paradigm, static tokens are either independantly coordinated with the exit/service + operator + - The session initiator will automatically include any static tokens that are either (A) loaded into the + config mapping or (B) passed to the lokinet-vpn cli utility + - As a result, the session initiator doesn't necessarily need an AuthPolicy object + + RPC Auth: + - Why can't the functionality of this be entirely subsumed by the RPCClient? + - If the config specifies the auth_type as RPC plus + */ void TunEndpoint::configure() { - log::debug(logcat, "{} called", __PRETTY_FUNCTION__); - auto& net_conf = _router.config()->network; - - /** DISCUSS: Can the auth objects be further simplified? - - In the original implementation, the AuthPolicy async logic was for the instance receiving the connection - request to execute its aynchronous logic and queue the authentication job - - Static Token Auth: - - In the re-designed auth paradigm, static tokens are either independantly coordinated with the exit/service - operator - - The session initiator will automatically include any static tokens that are either (A) loaded into the - config mapping or (B) passed to the lokinet-vpn cli utility - - As a result, the session initiator doesn't necessarily need an AuthPolicy object - - RPC Auth: - - Why can't the functionality of this be entirely subsumed by the RPCClient? - - If the config specifies the auth_type as RPC plus - - */ - // switch (net_conf.auth_type) - // { - // case auth::AuthType::WHITELIST: - // case auth::AuthType::OMQ: - // // The RPCAuthPolicy constructor will throw if auth_{endpoint,method} are empty - // _auth_policy = auth::make_auth_policy( - // router(), *net_conf.auth_endpoint, *net_conf.auth_method, router().lmq(), shared_from_this()); - - // std::static_pointer_cast(_auth_policy)->start(); - // break; - - // case auth::AuthType::FILE: - // _auth_policy = auth::make_auth_policy( - // router(), net_conf.auth_files, net_conf.auth_file_type); - // break; - - // case auth::AuthType::NONE: - // default: - // break; - // } + return _router.loop()->call_get([&]() { + log::trace(logcat, "{} called", __PRETTY_FUNCTION__); - _traffic_policy = net_conf.traffic_policy; - _base_ipv6_range = net_conf._base_ipv6_range; + auto& net_conf = _router.config()->network; - if (net_conf.path_alignment_timeout) - { - if (is_service_node()) - throw std::runtime_error{"Service nodes cannot specify path alignment timeout!"}; + _exit_policy = net_conf.traffic_policy; + _base_ipv6_range = net_conf._base_ipv6_range; - _path_alignment_timeout = *net_conf.path_alignment_timeout; - } + if (net_conf.path_alignment_timeout) + { + if (is_service_node()) + throw std::runtime_error{"Service nodes cannot specify path alignment timeout!"}; - _if_name = *net_conf._if_name; - _local_range = *net_conf._local_ip_range; - _local_addr = *net_conf._local_addr; - _local_base_ip = *net_conf._local_base_ip; + _path_alignment_timeout = *net_conf.path_alignment_timeout; + } - ipv6_enabled = not _local_range.is_ipv4(); - if (ipv6_enabled and not net_conf.enable_ipv6) - throw std::runtime_error{"Config must explicitly enable IPv6 to use local range: {}"_format(_local_range)}; + _if_name = *net_conf._if_name; + _local_range = *net_conf._local_ip_range; + _local_addr = *net_conf._local_addr; + _local_base_ip = *net_conf._local_base_ip; - _persisting_addr_file = net_conf.addr_map_persist_file; + ipv6_enabled = not _local_range.is_ipv4(); + if (ipv6_enabled and not net_conf.enable_ipv6) + throw std::runtime_error{ + "Config must explicitly enable IPv6 to use local range: {}"_format(_local_range)}; - if (not net_conf._reserved_local_ips.empty()) - { - for (auto& [remote, local] : net_conf._reserved_local_ips) + if (net_conf.addr_map_persist_file) { - _local_ip_mapping.insert_or_assign(local, remote); + _persisting_addr_file = net_conf.addr_map_persist_file; + persist_addrs = true; } - } - log::debug(logcat, "Tun constructing IPRange iterator on local range: {}", _local_range); - _local_range_iterator = IPRangeIterator(_local_range); + if (not net_conf._reserved_local_ips.empty()) + { + for (auto& [remote, local] : net_conf._reserved_local_ips) + { + _local_ip_mapping.insert_or_assign(local, remote); + } + } - _local_netaddr = NetworkAddress::from_pubkey(_router.local_rid(), not _router.is_service_node()); - _local_ip_mapping.insert_or_assign(_local_base_ip, std::move(_local_netaddr)); + log::debug(logcat, "Tun constructing IPRange iterator on local range: {}", _local_range); + _local_range_iterator = IPRangeIterator(_local_range); - vpn::InterfaceInfo info; - info.ifname = _if_name; - info.if_info = net_conf._if_info; - info.addrs.emplace_back(_local_range); + _local_netaddr = NetworkAddress::from_pubkey(_router.local_rid(), not _router.is_service_node()); + _local_ip_mapping.insert_or_assign(_local_range.net_ip(), std::move(_local_netaddr)); - if (net_conf.enable_ipv6 and _base_ipv6_range) - { - log::info(logcat, "{} using ipv6 range:{}", name(), *_base_ipv6_range); - info.addrs.emplace_back(*_base_ipv6_range); - } + vpn::InterfaceInfo info; + info.ifname = _if_name; + info.if_info = net_conf._if_info; + info.addrs.emplace_back(_local_range); - log::debug(logcat, "{} setting up network...", name()); + if (net_conf.enable_ipv6 and _base_ipv6_range) + { + log::info(logcat, "{} using ipv6 range:{}", name(), *_base_ipv6_range); + info.addrs.emplace_back(*_base_ipv6_range); + } - _local_ipv6 = ipv6_enabled ? _local_addr : _local_addr.mapped_ipv4_as_ipv6(); + log::debug(logcat, "{} setting up network...", name()); - if (ipv6_enabled) - { - if constexpr (not llarp::platform::is_apple) + _local_ipv6 = ipv6_enabled ? _local_addr : _local_addr.mapped_ipv4_as_ipv6(); + + if (ipv6_enabled) { - if (auto maybe = router().net().get_interface_ipv6_addr(_if_name)) + if constexpr (not llarp::platform::is_apple) { - _local_ipv6 = *maybe; + if (auto maybe = router().net().get_interface_ipv6_addr(_if_name)) + { + _local_ipv6 = *maybe; + } } } - } - log::info( - logcat, "{} has interface ipv4 address ({}) with ipv6 address ({})", name(), _local_addr, _local_ipv6); + log::info( + logcat, "{} has interface ipv4 address ({}) with ipv6 address ({})", name(), _local_addr, _local_ipv6); + + _net_if = router().vpn_platform()->create_interface(std::move(info), &_router); + _if_name = _net_if->interface_info().ifname; - _net_if = router().vpn_platform()->create_interface(std::move(info), &_router); - _if_name = _net_if->interface_info().ifname; + log::info(logcat, "{} got network interface:{}", name(), _if_name); - log::info(logcat, "{} got network interface:{}", name(), _if_name); + auto pkt_hook = [this]() mutable { + for (auto pkt = _net_if->read_next_packet(); not pkt.empty(); pkt = _net_if->read_next_packet()) + { + log::trace(logcat, "packet router receiving {}", pkt.info_line()); + _packet_router->handle_ip_packet(std::move(pkt)); + } + }; - auto pkt_hook = [this]() { - for (auto pkt = _net_if->read_next_packet(); not pkt.empty(); pkt = _net_if->read_next_packet()) + if (_poller = router().loop()->add_network_interface(_net_if, std::move(pkt_hook)); not _poller) { - log::debug(logcat, "packet router receiving {}", pkt.info_line()); - _packet_router->handle_ip_packet(std::move(pkt)); + auto err = "{} failed to add network interface!"_format(name()); + log::error(logcat, "{}", err); + throw std::runtime_error{std::move(err)}; } - }; - if (_poller = router().loop()->add_network_interface(_net_if, std::move(pkt_hook)); not _poller) - { - auto err = "{} failed to add network interface!"_format(name()); - log::error(logcat, "{}", err); - throw std::runtime_error{std::move(err)}; - } - - // if (auto* quic = GetQUICTunnel()) - // { - // TODO: - // quic->listen([this](std::string_view, uint16_t port) { - // return llarp::SockAddr{net::TruncateV6(GetIfAddr()), huint16_t{port}}; - // }); - // } + // if (auto* quic = GetQUICTunnel()) + // { + // TODO: + // quic->listen([this](std::string_view, uint16_t port) { + // return llarp::SockAddr{net::TruncateV6(GetIfAddr()), huint16_t{port}}; + // }); + // } - setup_dns(); + setup_dns(); + }); } - static bool is_random_snode(const dns::Message& msg) - { - return msg.questions[0].IsName("random.snode"); - } + static bool is_random_snode(const dns::Message& msg) { return msg.questions[0].IsName("random.snode"); } - static bool is_localhost_loki(const dns::Message& msg) - { - return msg.questions[0].IsLocalhost(); - } + static bool is_localhost_loki(const dns::Message& msg) { return msg.questions[0].IsLocalhost(); } static dns::Message& clear_dns_message(dns::Message& msg) { @@ -436,7 +415,7 @@ namespace llarp::handlers // [this, snode, msg, reply, isV6]( // const RouterID&, // std::shared_ptr s, - // [[maybe_unused]] service::SessionTag tag) { + // [[maybe_unused]] session_tag tag) { // SendDNSReply(snode, s, msg, reply, isV6); // }); // }; @@ -568,7 +547,7 @@ namespace llarp::handlers // } // else if (subdomain == "netid") // { - // msg.AddTXTReply(fmt::format("netid={};", RouterContact::ACTIVE_NETID)); + // msg.AddTXTReply(fmt::format("netid={};", RelayContact::ACTIVE_NETID)); // } // else // { @@ -807,10 +786,7 @@ namespace llarp::handlers return true; } - bool TunEndpoint::supports_ipv6() const - { - return ipv6_enabled; - } + bool TunEndpoint::supports_ipv6() const { return ipv6_enabled; } // FIXME: pass in which question it should be addressing bool TunEndpoint::should_hook_dns_message(const dns::Message& msg) const @@ -842,20 +818,11 @@ namespace llarp::handlers return false; } - std::string TunEndpoint::get_if_name() const - { - return _if_name; - } + std::string TunEndpoint::get_if_name() const { return _if_name; } - bool TunEndpoint::is_service_node() const - { - return _router.is_service_node(); - } + bool TunEndpoint::is_service_node() const { return _router.is_service_node(); } - bool TunEndpoint::is_exit_node() const - { - return _router.is_exit_node(); - } + bool TunEndpoint::is_exit_node() const { return _router.is_exit_node(); } bool TunEndpoint::stop() { @@ -974,12 +941,14 @@ namespace llarp::handlers } } + // handles an outbound packet going OUT from user -> network void TunEndpoint::handle_outbound_packet(IPPacket pkt) { ip_v src, dest; - auto pkt_is_ipv4 = pkt.is_ipv4(); + log::debug(logcat, "outbound packet: {}: {}", pkt.info_line(), buffer_printer{pkt.uview()}); + if (pkt_is_ipv4) { src = pkt.source_ipv4(); @@ -991,13 +960,12 @@ namespace llarp::handlers dest = pkt.dest_ipv6(); } + log::trace(logcat, "src:{}, dest:{}", src, dest); + if constexpr (llarp::platform::is_apple) { if (ip_equals_address(dest, _local_addr, pkt_is_ipv4)) - { - rewrite_and_send_packet(std::move(pkt), src, dest); - return; - } + return rewrite_and_send_packet(std::move(pkt), std::move(src), std::move(dest)); } // we pass `dest` because that is our local private IP on the outgoing IPPacket @@ -1008,54 +976,50 @@ namespace llarp::handlers if (auto session = _router.session_endpoint()->get_session(remote)) { - log::debug(logcat, "Dispatching outbound packet for session (remote: {})", remote); + log::info( + logcat, + "Dispatching outbound {}B packet for session (remote: {}): {}", + pkt.size(), + remote, + pkt.info_line()); session->send_path_data_message(std::move(pkt).steal_payload()); } else - log::warning(logcat, "Could not find session (remote: {}) for outbound packet!", remote); + log::info(logcat, "Could not find session (remote: {}) for outbound packet!", remote); } else - log::debug(logcat, "Could not find remote for route {}", pkt.info_line()); + { + log::critical(logcat, "Could not find remote for route {}", pkt.info_line()); + + // make ICMP unreachable + if (auto icmp = pkt.make_icmp_unreachable()) + rewrite_and_send_packet(std::move(*icmp), std::move(src), std::move(dest)); + } } - bool TunEndpoint::obtain_src_for_remote(const NetworkAddress& remote, ip_v& src, bool use_ipv4) + std::optional TunEndpoint::obtain_src_for_remote(const NetworkAddress& remote, bool use_ipv4) { - // we are receiving traffic from a session to a local exit node if (auto maybe_src = _local_ip_mapping.get_local_from_remote(remote)) { if (std::holds_alternative(*maybe_src)) { if (use_ipv4) - src = *maybe_src; - else - { - auto quicaddr = oxen::quic::Address{std::get(*maybe_src)}; - src = quicaddr.to_ipv6(); - } - } - else - { - if (use_ipv4) - { - auto quicaddr = oxen::quic::Address{std::get(*maybe_src)}; - src = quicaddr.to_ipv4(); - } - else - src = *maybe_src; + return *maybe_src; + return oxen::quic::Address{std::get(*maybe_src)}.to_ipv6(); } - } - else - { - log::critical(logcat, "Unable to find local IP for inbound packet from remote: {}", remote); - return false; + + if (use_ipv4) + return oxen::quic::Address{std::get(*maybe_src)}.to_ipv4(); + return *maybe_src; } - return true; + log::warning(logcat, "Unable to find src IP for inbound packet from remote: {}", remote); + return std::nullopt; } - void TunEndpoint::send_packet_to_net_if(IPPacket&& pkt) + void TunEndpoint::send_packet_to_net_if(IPPacket pkt) { - _router.loop()->call([this, pkt = std::move(pkt)]() { _net_if->write_packet(std::move(pkt)); }); + _router.loop()->call([this, pkt = std::move(pkt)]() mutable { _net_if->write_packet(std::move(pkt)); }); } void TunEndpoint::rewrite_and_send_packet(IPPacket&& pkt, ip_v src, ip_v dest) @@ -1065,82 +1029,159 @@ namespace llarp::handlers else pkt.update_ipv6_address(std::get(src), std::get(dest)); + log::trace(logcat, "Rewritten packet: {}: {}", pkt.info_line(), buffer_printer{pkt.uview()}); send_packet_to_net_if(std::move(pkt)); } - bool TunEndpoint::handle_inbound_packet( - IPPacket pkt, NetworkAddress remote, bool is_exit_session, bool is_outbound_session) + void TunEndpoint::handle_inbound_packet(IPPacket pkt, session_tag tag, NetworkAddress remote) { ip_v src, dest; - auto pkt_is_ipv4 = pkt.is_ipv4(); - if (is_exit_session and is_outbound_session) - { - // we are receiving traffic from a session to a remote exit node - if (pkt_is_ipv4) - { - src = pkt.source_ipv4(); - dest = _local_addr.to_ipv4(); - } - else - { - src = pkt.source_ipv6(); - dest = _local_ipv6.to_ipv6(); - } + auto [is_exit_pkt, is_tunneled_pkt] = tag.proto_bits(); - assert(remote.is_client()); - - auto maybe_remote = _local_ip_mapping.get_remote_from_local(src); + if (is_tunneled_pkt) + { + log::critical(logcat, "Dropping tcp2quic pkt"); + // TODO: pass to tunnel + // TODO: also finish quic tunnel + // TODO: route this even earlier - if (not maybe_remote) - { - log::critical( - logcat, "Could not find mapping of local IP (ip:{}) for session to remote: {}", src, remote); - return false; - } - if (*maybe_remote != remote) - { - log::critical( - logcat, - "Internal mapping of local IP (ip:{}, remote:{}) did not match inbound packet from remote: {}", - src, - *maybe_remote, - remote); - return false; - } + return; } - else + + if (is_exit_pkt) { - if (is_exit_session and not is_outbound_session) + if (is_exit_node()) // traffic to local exit node { - // we are receiving traffic from a session to a local exit node - if (not is_allowing_traffic(pkt)) - return false; + log::info(logcat, "inbound exit pkt for local exit node: {}", pkt.info_line()); + + if (not _exit_policy->allow_ip_traffic(pkt)) + { + log::warning(logcat, "Invalid pkt proto ({}) for local exit", pkt.protocol()); + return; + } if (pkt_is_ipv4) dest = pkt.dest_ipv4(); else dest = pkt.dest_ipv6(); } - else + else // traffic to remote exit node { - // we are receiving hidden service traffic + log::info(logcat, "inbound exit pkt from remote exit node: {}", pkt.info_line()); + if (pkt_is_ipv4) + { + src = pkt.source_ipv4(); dest = _local_addr.to_ipv4(); + } else - dest = _local_ipv6.to_ipv6(); + { + src = pkt.source_ipv6(); + dest = _local_addr.to_ipv6(); + } } + } + else + { + log::info(logcat, "inbound session pkt: {}", pkt.info_line()); - if (not obtain_src_for_remote(remote, src, pkt_is_ipv4)) - return false; + if (pkt_is_ipv4) + dest = _local_addr.to_ipv4(); + else + dest = _local_addr.to_ipv6(); } - rewrite_and_send_packet(std::move(pkt), src, dest); + if (auto maybe_src = obtain_src_for_remote(remote, pkt_is_ipv4)) + src = std::move(*maybe_src); + else + return; - return true; + log::trace(logcat, "src:{}, dest:{}", src, dest); + rewrite_and_send_packet(std::move(pkt), src, dest); } + // handles an inbound packet coming IN from network -> user + // bool TunEndpoint::handle_inbound_packet( + // IPPacket pkt, NetworkAddress remote, bool is_exit_session, bool is_outbound_session) + // { + // ip_v src, dest; + + // auto pkt_is_ipv4 = pkt.is_ipv4(); + + // if (is_exit_session and is_outbound_session) + // { + // log::info(logcat, "inbound exit session pkt: {}", pkt.info_line()); + // // we are receiving traffic from a session to a remote exit node + // if (pkt_is_ipv4) + // { + // src = pkt.source_ipv4(); + // dest = _local_addr.to_ipv4(); + // } + // else + // { + // src = pkt.source_ipv6(); + // dest = _local_ipv6.to_ipv6(); + // } + + // assert(remote.is_client()); + + // auto maybe_remote = _local_ip_mapping.get_remote_from_local(src); + + // if (not maybe_remote) + // { + // log::info(logcat, "Could not find mapping of local IP (ip:{}) for session to remote: {}", src, + // remote); return false; + // } + // if (*maybe_remote != remote) + // { + // log::info( + // logcat, + // "Internal mapping of local IP (ip:{}, remote:{}) did not match inbound packet from remote: {}", + // src, + // *maybe_remote, + // remote); + // return false; + // } + // } + // else + // { + // if (is_exit_session and not is_outbound_session) + // { + // log::info(logcat, "inbound exit session pkt: {}", pkt.info_line()); + // // we are receiving traffic from a session to a local exit node + // if (not _exit_policy->allow_ip_traffic(pkt)) + // return false; + + // if (pkt_is_ipv4) + // dest = pkt.dest_ipv4(); + // else + // dest = pkt.dest_ipv6(); + // } + // else + // { + // log::info(logcat, "inbound service session pkt: {}", pkt.info_line()); + // // we are receiving hidden service traffic + // if (pkt_is_ipv4) + // dest = _local_addr.to_ipv4(); + // else + // dest = _local_ipv6.to_ipv6(); + // } + + // if (auto maybe_src = obtain_src_for_remote(remote, pkt_is_ipv4)) + // src = std::move(*maybe_src); + // else + // return false; + // } + + // log::trace(logcat, "src:{}, dest:{}", src, dest); + + // rewrite_and_send_packet(std::move(pkt), src, dest); + + // return true; + // } + void TunEndpoint::start_poller() { if (not _poller->start()) @@ -1150,10 +1191,7 @@ namespace llarp::handlers bool TunEndpoint::is_allowing_traffic(const IPPacket& pkt) const { - if (auto exitPolicy = get_traffic_policy()) - return exitPolicy->allow_ip_traffic(pkt); - - return true; + return _exit_policy ? _exit_policy->allow_ip_traffic(pkt) : true; } bool TunEndpoint::has_mapping_to_remote(const NetworkAddress& addr) const @@ -1166,10 +1204,7 @@ namespace llarp::handlers return _local_ip_mapping.get_local_from_remote(addr); } - oxen::quic::Address TunEndpoint::get_if_addr() const - { - return _local_addr; - } + oxen::quic::Address TunEndpoint::get_if_addr() const { return _local_addr; } TunEndpoint::~TunEndpoint() = default; diff --git a/llarp/handlers/tun.hpp b/llarp/handlers/tun.hpp index e64256bae5..951078f31c 100644 --- a/llarp/handlers/tun.hpp +++ b/llarp/handlers/tun.hpp @@ -1,12 +1,8 @@ #pragma once -#include #include #include -#include -#include -#include -#include +#include #include #include #include @@ -47,7 +43,7 @@ namespace llarp::handlers /// list of strict connect addresses for hooks // std::vector _strict_connect_addrs; /// use v6? - bool ipv6_enabled; + bool ipv6_enabled{}; std::string _if_name; @@ -58,10 +54,11 @@ namespace llarp::handlers std::shared_ptr _packet_router; - std::optional _traffic_policy = std::nullopt; + std::optional _exit_policy = std::nullopt; /// a file to load / store the ephemeral address map to std::optional _persisting_addr_file = std::nullopt; + bool persist_addrs{false}; /// how long to wait for path alignment std::chrono::milliseconds _path_alignment_timeout{30s}; @@ -110,13 +107,17 @@ namespace llarp::handlers void setup_dns(); // INPROGRESS: new API - // Handles an outbound packet going out INTO the network + // Handles an outbound packet going OUT to the network void handle_outbound_packet(IPPacket pkt); void rewrite_and_send_packet(IPPacket&& pkt, ip_v src, ip_v dest); - // Handle an inbound packet coming in FROM the network - bool handle_inbound_packet(IPPacket pkt, NetworkAddress remote, bool is_exit_session, bool is_outbound_session); + // TESTNET: TODO: new inbound packet handling logic + void handle_inbound_packet(IPPacket pkt, session_tag tag, NetworkAddress remote); + + // Handles an inbound packet coming IN from the network + // bool handle_inbound_packet(IPPacket pkt, NetworkAddress remote, bool is_exit_session, bool + // is_outbound_session); // Upon session creation, SessionHandler will instruct TunEndpoint to requisition a private IP through which to // route session traffic @@ -128,7 +129,7 @@ namespace llarp::handlers bool has_if_addr() const { return true; } - std::optional get_traffic_policy() const { return _traffic_policy; } + std::optional get_exit_policy() const { return _exit_policy; } std::chrono::milliseconds get_path_alignment_timeout() const { return _path_alignment_timeout; } @@ -147,45 +148,17 @@ namespace llarp::handlers void start_poller(); - // protected: - struct WritePacket - { - uint64_t seqno; - IPPacket pkt; - - bool operator>(const WritePacket& other) const { return seqno > other.seqno; } - }; - // Stores assigned IP's for each session in/out of this lokinet instance - // - Reserved local addresses is directly pre-loaded from config + // - Reserved local addresses are directly pre-loaded from config // - Persisting address map is directly pre-loaded from config address_map _local_ip_mapping; private: std::optional get_next_local_ip(); - bool obtain_src_for_remote(const NetworkAddress& remote, ip_v& src, bool use_ipv4); - - void send_packet_to_net_if(IPPacket&& pkt); - - template - void send_dns_reply( - Addr_t addr, - Endpoint_t ctx, - std::shared_ptr query, - std::function reply, - bool sendIPv6) - { - if (ctx) - { - huint128_t ip = get_ip_for_addr(addr); - query->answers.clear(); - query->add_IN_reply(ip, sendIPv6); - } - else - query->add_nx_reply(); - reply(*query); - } + std::optional obtain_src_for_remote(const NetworkAddress& remote, bool use_ipv4); + + void send_packet_to_net_if(IPPacket pkt); }; } // namespace llarp::handlers diff --git a/llarp/link/connection.cpp b/llarp/link/connection.cpp index 56fa54e8c8..b8aafa2f52 100644 --- a/llarp/link/connection.cpp +++ b/llarp/link/connection.cpp @@ -7,8 +7,9 @@ namespace llarp::link Connection::Connection( std::shared_ptr c, std::shared_ptr s, - bool is_relay) - : conn{std::move(c)}, control_stream{std::move(s)}, remote_is_relay{is_relay} + bool _is_relay, + bool _is_active) + : conn{std::move(c)}, control_stream{std::move(s)}, is_active{_is_active}, remote_is_relay{_is_relay} {} void Connection::close_quietly() diff --git a/llarp/link/connection.hpp b/llarp/link/connection.hpp index 7ff4678570..8e3acbea3a 100644 --- a/llarp/link/connection.hpp +++ b/llarp/link/connection.hpp @@ -1,7 +1,7 @@ #pragma once -#include -#include +#include +#include #include @@ -9,13 +9,16 @@ namespace llarp::link { struct Connection { - std::shared_ptr conn; - std::shared_ptr control_stream; - Connection( std::shared_ptr c, std::shared_ptr s, - bool is_relay = true); + bool _is_relay = true, + bool _is_active = false); + + std::shared_ptr conn; + std::shared_ptr control_stream; + + std::atomic is_active{false}; bool remote_is_relay{true}; @@ -27,8 +30,6 @@ namespace llarp::link /** TODO: - - add a boolean in this connection object - - do not continue to try to send things to the bootstarp until the connection - is actually established! - + - add a boolean in this connection object + - do not continue to try to send things to the bootstrap until the connection is actually established! */ diff --git a/llarp/link/contacts.cpp b/llarp/link/contacts.cpp deleted file mode 100644 index e3311fddde..0000000000 --- a/llarp/link/contacts.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "contacts.hpp" - -#include -#include - -namespace llarp -{ - Contacts::Contacts(Router& r) : _router{r}, _local_key{dht::Key_t::derive_from_rid(r.local_rid())} - { - timer_keepalive = std::make_shared(0); - _introset_nodes = std::make_unique>(_local_key, llarp::randint); - } - - std::optional Contacts::get_decrypted_introset(RouterID remote) const - { - std::optional ret = std::nullopt; - - if (auto encrypted = get_encrypted_introset(dht::Key_t::derive_from_rid(remote)); - auto intro = encrypted->decrypt(remote)) - ret = *intro; - - return ret; - } - - std::optional Contacts::get_encrypted_introset(const dht::Key_t& key) const - { - std::optional enc = std::nullopt; - - auto& introsets = _introset_nodes->nodes; - - if (auto itr = introsets.find(key); itr != introsets.end() && not itr->second.introset.is_expired()) - enc = itr->second.introset; - - return enc; - } - - nlohmann::json Contacts::ExtractStatus() const - { - nlohmann::json obj{{"services", _introset_nodes->ExtractStatus()}, {"local_key", _local_key.ToHex()}}; - return obj; - } - - void Contacts::put_intro(service::EncryptedIntroSet enc) - { - _introset_nodes->PutNode(std::move(enc)); - } - -} // namespace llarp diff --git a/llarp/link/contacts.hpp b/llarp/link/contacts.hpp deleted file mode 100644 index 999302377f..0000000000 --- a/llarp/link/contacts.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include -#include - -namespace llarp -{ - struct Router; - - /// This class mediates storage, retrieval, and functionality for the various types - /// of contact information that needs to be stored locally by the link manager and - /// router, like RouterContacts and introsets for example - struct Contacts - { - private: - // TODO: why was this a shared ptr in the original implementation? revisit this - std::shared_ptr timer_keepalive; - Router& _router; - const dht::Key_t _local_key; - - // holds introsets for remote services - std::unique_ptr> _introset_nodes; - - public: - explicit Contacts(Router& r); - - std::optional get_decrypted_introset(RouterID remote) const; - - std::optional get_encrypted_introset(const dht::Key_t& key) const; - - nlohmann::json ExtractStatus() const; - - void put_intro(service::EncryptedIntroSet enc); - - dht::Bucket* services() const { return _introset_nodes.get(); } - - Router* router() const { return &_router; } - }; - -} // namespace llarp diff --git a/llarp/link/link_manager.cpp b/llarp/link/link_manager.cpp index fd21b9904d..99170d0f71 100644 --- a/llarp/link/link_manager.cpp +++ b/llarp/link/link_manager.cpp @@ -1,8 +1,9 @@ #include "link_manager.hpp" #include "connection.hpp" -#include "contacts.hpp" +#include +#include #include #include #include @@ -11,7 +12,6 @@ #include #include #include -#include #include #include @@ -24,7 +24,7 @@ namespace llarp { static auto logcat = llarp::log::Cat("lquic"); - static constexpr auto static_shared_key = "Lokinet static shared secret key"_usv; + static constexpr auto static_shared_key = "Lokinet static shared secret key"_usp; static static_secret make_static_secret(const Ed25519SecretKey& sk) { @@ -57,18 +57,14 @@ namespace llarp std::shared_ptr Endpoint::get_conn(const RouterID& remote) const { - return link_manager.router().loop()->call_get([this, rid = remote]() -> std::shared_ptr { - if (auto itr = service_conns.find(rid); itr != service_conns.end()) - return itr->second; - - if (_is_service_node) - { - if (auto itr = client_conns.find(rid); itr != client_conns.end()) - return itr->second; - } + if (auto itr = service_conns.find(remote); itr != service_conns.end()) + return itr->second; - return nullptr; - }); + if (_is_service_node) + { + if (auto itr = client_conns.find(remote); itr != client_conns.end()) + return itr->second; + } return nullptr; } @@ -88,16 +84,18 @@ namespace llarp return link_manager.router().loop()->call_get([this, remote]() { return service_conns.count(remote); }); } - void Endpoint::for_each_connection(std::function hook) + void Endpoint::for_each_connection(std::function func) { - link_manager.router().loop()->call([this, func = std::move(hook)]() { - for (const auto& [rid, conn] : service_conns) - func(*conn); + link_manager.router().loop()->call([this, func = std::move(func)]() mutable { + for (auto& [rid, conn] : service_conns) + if (conn) + func(rid, *conn); if (_is_service_node) { - for (const auto& [rid, conn] : client_conns) - func(*conn); + for (auto& [rid, conn] : client_conns) + if (conn) + func(rid, *conn); } }); } @@ -143,17 +141,23 @@ namespace llarp return link_manager.router().loop()->call_get([this]() -> std::tuple { size_t in{0}, out{0}; - for (const auto& c : service_conns) + for (const auto& [_, c] : service_conns) { - if (c.second->is_inbound()) + if (not c) + continue; + + if (c->is_inbound()) ++in; else ++out; } - for (const auto& c : client_conns) + for (const auto& [_, c] : client_conns) { - if (c.second->is_inbound()) + if (not c) + continue; + + if (c->is_inbound()) ++in; else ++out; @@ -168,35 +172,46 @@ namespace llarp return link_manager.router().loop()->call_get([this]() { return client_conns.size(); }); } - size_t Endpoint::num_router_conns() const + size_t Endpoint::num_router_conns(bool active_only) const { - return link_manager.router().loop()->call_get([this]() { return service_conns.size(); }); + return link_manager.router().loop()->call_get([&]() { + size_t n{}; + + for (const auto& [_, c] : service_conns) + if (c and (active_only ? c->is_active.load() : true)) + ++n; + + return n; + }); } } // namespace link - std::tuple LinkManager::connection_stats() const - { - return ep->connection_stats(); - } + std::tuple LinkManager::connection_stats() const { return ep->connection_stats(); } - size_t LinkManager::get_num_connected_routers() const - { - return ep->num_router_conns(); - } + size_t LinkManager::get_num_connected_routers(bool active_only) const { return ep->num_router_conns(active_only); } - size_t LinkManager::get_num_connected_clients() const - { - return ep->num_client_conns(); - } + size_t LinkManager::get_num_connected_clients() const { return ep->num_client_conns(); } using messages::serialize_response; - void LinkManager::for_each_connection(std::function func) + std::set LinkManager::get_current_remotes() const + { + // invoke using Router method to wrap in call_get + std::set ret{}; + + for (auto& [rid, conn] : ep->service_conns) + if (conn and conn->is_active) + ret.insert(rid); + + return ret; + } + + void LinkManager::for_each_connection(std::function func) { if (is_stopping) return; - return ep->for_each_connection(func); + return ep->for_each_connection(std::move(func)); } void LinkManager::register_commands( @@ -204,13 +219,13 @@ namespace llarp { log::debug(logcat, "{} called", __PRETTY_FUNCTION__); - s->register_handler("path_control"s, [this, rid = remote_rid](oxen::quic::message m) mutable { - _router.loop()->call([&, msg = std::move(m)]() mutable { handle_path_control(std::move(msg), rid); }); + s->register_handler("path_control"s, [this](oxen::quic::message m) mutable { + _router.loop()->call([&, msg = std::move(m)]() mutable { handle_path_control(std::move(msg)); }); }); if (client_only) { - s->register_handler("session_init", [this](oxen::quic::message m) mutable { + s->register_handler("session_init"s, [this](oxen::quic::message m) mutable { _router.loop()->call([&, msg = std::move(m)]() mutable { handle_initiate_session(std::move(msg)); }); }); log::debug(logcat, "Registered all client-only BTStream commands!"); @@ -237,19 +252,17 @@ namespace llarp _router.loop()->call([&, msg = std::move(m)]() mutable { handle_gossip_rc(std::move(msg)); }); }); - for (auto& method : path_requests) - { - s->register_handler( - std::string{method.first}, [this, func = std::move(method.second)](oxen::quic::message m) mutable { - _router.loop()->call([&, msg = std::move(m), func = std::move(func)]() mutable { - auto body = msg.body_str(); - auto respond = [&, m = std::move(msg)](std::string response) mutable { - m.respond(std::move(response), m.is_error()); - }; - std::invoke(func, this, body, std::move(respond)); - }); - }); - } + s->register_handler("publish_cc"s, [this](oxen::quic::message m) mutable { + _router.loop()->call([&, msg = std::move(m)]() mutable { handle_publish_cc(std::move(msg)); }); + }); + + s->register_handler("find_cc"s, [this](oxen::quic::message m) mutable { + _router.loop()->call([&, msg = std::move(m)]() mutable { handle_find_cc(std::move(msg)); }); + }); + + s->register_handler("resolve_sns"s, [this](oxen::quic::message m) mutable { + _router.loop()->call([&, msg = std::move(m)]() mutable { handle_resolve_sns(std::move(msg)); }); + }); log::debug(logcat, "Registered all commands for connection to remote RID:{}", remote_rid); } @@ -257,15 +270,7 @@ namespace llarp void LinkManager::start_tickers() { log::debug(logcat, "Starting gossip ticker..."); - _gossip_ticker = _router.loop()->call_every( - _router._gossip_interval, - [this]() { - log::critical(logcat, "Regenerating and gossiping RC..."); - _router.router_contact.resign(); - _router.save_rc(); - gossip_rc(_router.local_rid(), _router.router_contact.to_remote()); - }, - true); + _gossip_ticker = _router.loop()->call_every(_router._gossip_interval, [this]() { regenerate_and_gossip_rc(); }); } LinkManager::LinkManager(Router& r) @@ -275,7 +280,7 @@ namespace llarp quic{std::make_unique()}, tls_creds{oxen::quic::GNUTLSCreds::make_from_ed_keys( {reinterpret_cast(_router.identity().data()), 32}, - {reinterpret_cast(_router.router_id().data()), 32})}, + {reinterpret_cast(_router.local_rid().data()), 32})}, ep{_router.loop()->template make_shared(startup_endpoint(), *this)}, is_stopping{false} {} @@ -295,17 +300,17 @@ namespace llarp - stream constructor callback - will return a BTRequestStream on the first call to get_new_stream - bt stream construction contains a stream close callback that shuts down the - connection if the btstream closes unexpectedly + connection if the btstream closes unexpectedly */ auto e = quic->endpoint( _router.listen_addr(), make_static_secret(_router.identity()), [this](oxen::quic::connection_interface& ci) { return on_conn_open(ci); }, [this](oxen::quic::connection_interface& ci, uint64_t ec) { return on_conn_closed(ci, ec); }, - [this](oxen::quic::dgram_interface&, bstring dgram) { handle_path_data_message(std::move(dgram)); }, + [this](oxen::quic::dgram_interface&, bstring dgram) { return handle_path_data_message(std::move(dgram)); }, is_service_node() ? alpns::SERVICE_INBOUND : alpns::CLIENT_INBOUND, is_service_node() ? alpns::SERVICE_OUTBOUND : alpns::CLIENT_OUTBOUND, - oxen::quic::opt::disable_stateless_reset{}); + oxen::quic::opt::enable_datagrams{oxen::quic::Splitting::ACTIVE}); // While only service nodes accept inbound connections, clients must have this key verify // callback set. It will reject any attempted inbound connection to a lokinet client prior @@ -320,7 +325,7 @@ namespace llarp { if (alpn == alpns::C_ALPNS) { - log::critical(logcat, "{} node accepting client connection (remote ID:{})!", us, other); + log::critical(logcat, "{} accepting client connection (remote ID:{})!", us, other); ep->client_conns.emplace(other, nullptr); return true; } @@ -329,7 +334,6 @@ namespace llarp { // verify as service node! bool result = node_db->registered_routers().count(other); - // result = true; // TESTNET: turn this off for non-local testing if (result) { @@ -350,9 +354,9 @@ namespace llarp itr->second = nullptr; } - log::critical( + log::debug( logcat, - "{} node received inbound with ongoing outbound to remote " + "{} received inbound with ongoing outbound to remote " "(RID:{}); {}!", us, other, @@ -361,13 +365,12 @@ namespace llarp return defer_to_incoming; } - log::critical( - logcat, "{} node accepting inbound from registered remote (RID:{})", us, other); + log::trace(logcat, "{} accepting inbound from registered remote (RID:{})", us, other); } else - log::critical( + log::info( logcat, - "{} node was unable to confirm remote (RID:{}) is registered; " + "{} was unable to confirm remote (RID:{}) is registered; " "rejecting " "connection!", us, @@ -376,14 +379,12 @@ namespace llarp return result; } - log::critical(logcat, "{} node received unknown ALPN; rejecting connection!", us); - return false; + log::critical(logcat, "{} received unknown ALPN; rejecting connection!", us); } + else + log::critical(logcat, "Clients should not be validating inbound connections!"); - // TESTNET: change this to an error message later; just because someone tries to - // erroneously connect to a local lokinet client doesn't mean we should kill the - // program? - throw std::runtime_error{"Clients should not be validating inbound connections!"}; + return false; }); }); @@ -394,9 +395,9 @@ namespace llarp } std::shared_ptr LinkManager::make_control( - oxen::quic::connection_interface& ci, const RouterID& remote) + const std::shared_ptr& ci, const RouterID& remote) { - auto control_stream = ci.template queue_incoming_stream( + auto control_stream = ci->template queue_incoming_stream( [](oxen::quic::Stream&, uint64_t error_code) { log::warning(logcat, "BTRequestStream closed unexpectedly (ec:{})", error_code); }); @@ -408,44 +409,44 @@ namespace llarp return control_stream; } - void LinkManager::on_inbound_conn(oxen::quic::connection_interface& ci) + void LinkManager::on_inbound_conn(std::shared_ptr ci) { assert(_is_service_node); - RouterID rid{ci.remote_key()}; + RouterID rid{ci->remote_key()}; auto control = make_control(ci, rid); + bool is_client_conn = false; - _router.loop()->call([&, ci_ptr = ci.shared_from_this(), bstream = std::move(control), rid]() { - bool is_client_conn = false; - if (auto it = ep->service_conns.find(rid); it != ep->service_conns.end()) - { - log::debug(logcat, "Configuring inbound connection from relay RID:{}", rid); - it->second = std::make_shared(std::move(ci_ptr), std::move(bstream)); - } - else if (auto it = ep->client_conns.find(rid); it != ep->client_conns.end()) - { - is_client_conn = true; - log::debug(logcat, "Configuring inbound connection from client RID:{}", rid.to_network_address(false)); - it->second = std::make_shared(std::move(ci_ptr), std::move(bstream), false); - } + if (auto it = ep->service_conns.find(rid); it != ep->service_conns.end()) + { + log::debug(logcat, "Configuring inbound connection from relay RID:{}", rid); + it->second = std::make_shared(std::move(ci), std::move(control), false, true); + } + else if (auto it = ep->client_conns.find(rid); it != ep->client_conns.end()) + { + is_client_conn = true; + log::debug(logcat, "Configuring inbound connection from client RID:{}", rid.to_network_address(false)); + it->second = std::make_shared(std::move(ci), std::move(control), false, true); + } - log::critical( - logcat, - "SERVICE NODE (RID:{}) ESTABLISHED CONNECTION TO RID:{}", - _router.local_rid(), - rid.to_network_address(!is_client_conn)); - }); + log::critical( + logcat, + "SERVICE NODE (RID:{}) ESTABLISHED CONNECTION TO RID:{}", + _router.local_rid(), + rid.to_network_address(!is_client_conn)); } - void LinkManager::on_outbound_conn(oxen::quic::connection_interface& ci) + void LinkManager::on_outbound_conn(RouterID rid) { - RouterID rid{ci.remote_key()}; log::trace(logcat, "Outbound connection to {}", rid); - if (ep->have_service_conn(rid)) + if (auto conn = ep->get_service_conn(rid)) { - log::debug(logcat, "Fetched configured outbound connection to relay RID:{}", rid); + conn->is_active = true; + log::trace(logcat, "Fetched configured outbound connection to relay RID: {}", rid); } + else + log::warning(logcat, "Could not find outbound connection corresponding to RID: {}", rid); log::critical( logcat, @@ -455,16 +456,24 @@ namespace llarp rid); } - void LinkManager::on_conn_open(oxen::quic::connection_interface& ci) + void LinkManager::on_conn_open(oxen::quic::connection_interface& _ci) { - if (ci.is_inbound()) - { - on_inbound_conn(ci); - } - else - { - on_outbound_conn(ci); - } + log::debug(logcat, "{} called", __PRETTY_FUNCTION__); + + _router.loop()->call([this, wci = _ci.weak_from_this()]() { + auto ci = wci.lock(); + + if (not ci) + { + log::warning(logcat, "Connection died before connection open callback execution!"); + return; + } + + if (ci->is_inbound()) + on_inbound_conn(std::move(ci)); + else + on_outbound_conn(RouterID{ci->remote_key()}); + }); } void LinkManager::on_conn_closed(oxen::quic::connection_interface& ci, uint64_t ec) @@ -475,16 +484,16 @@ namespace llarp if (auto s_itr = ep->service_conns.find(rid); s_itr != ep->service_conns.end()) { - log::critical(logcat, "Quic connection to relay RID:{} purged successfully", rid); + log::debug(logcat, "Quic connection to relay RID:{} purged successfully", rid); ep->service_conns.erase(s_itr); } else if (auto c_itr = ep->client_conns.find(rid); c_itr != ep->client_conns.end()) { - log::critical(logcat, "Quic connection to client RID:{} purged successfully", rid); + log::debug(logcat, "Quic connection to client RID:{} purged successfully", rid); ep->client_conns.erase(c_itr); } else - log::critical(logcat, "Nothing to purge for quic connection {}", ref_id); + log::trace(logcat, "Nothing to purge for quic connection {}", ref_id); }); } @@ -537,10 +546,7 @@ namespace llarp return false; } - void LinkManager::close_connection(RouterID rid) - { - return ep->close_connection(rid); - } + void LinkManager::close_connection(RouterID rid) { return ep->close_connection(rid); } void LinkManager::test_reachability(const RouterID& rid, conn_open_hook on_open, conn_closed_hook on_close) { @@ -549,7 +555,7 @@ namespace llarp connect_to(*rc, std::move(on_open), std::move(on_close)); } else - log::warning(logcat, "Could not find RouterContact for connection to rid:{}", rid); + log::warning(logcat, "Could not find RelayContact for connection to rid:{}", rid); } void LinkManager::connect_and_send( @@ -566,7 +572,7 @@ namespace llarp if (auto rv = ep->establish_and_send( KeyedAddress{router.to_view(), remote_addr}, - *rc, + router, std::move(endpoint), std::move(body), std::move(func)); @@ -597,7 +603,7 @@ namespace llarp auto remote_addr = rc.addr(); if (auto rv = ep->establish_connection( - KeyedAddress{rid.to_view(), remote_addr}, rc, std::move(on_open), std::move(on_close)); + KeyedAddress{rid.to_view(), remote_addr}, rid, std::move(on_open), std::move(on_close)); rv) { log::info(logcat, "Begun establishing connection to {}", remote_addr); @@ -606,20 +612,11 @@ namespace llarp log::warning(logcat, "Failed to begin establishing connection to {}", remote_addr); } - bool LinkManager::have_connection_to(const RouterID& remote) const - { - return ep->have_conn(remote); - } + bool LinkManager::have_connection_to(const RouterID& remote) const { return ep->have_conn(remote); } - bool LinkManager::have_service_connection_to(const RouterID& remote) const - { - return ep->have_service_conn(remote); - } + bool LinkManager::have_service_connection_to(const RouterID& remote) const { return ep->have_service_conn(remote); } - bool LinkManager::have_client_connection_to(const RouterID& remote) const - { - return ep->have_client_conn(remote); - } + bool LinkManager::have_client_connection_to(const RouterID& remote) const { return ep->have_client_conn(remote); } void LinkManager::close_all_links() { @@ -668,10 +665,7 @@ namespace llarp } } - bool LinkManager::is_service_node() const - { - return _is_service_node; - } + bool LinkManager::is_service_node() const { return _is_service_node; } // TODO: this? perhaps no longer necessary in the same way? void LinkManager::check_persisting_conns(std::chrono::milliseconds) @@ -681,12 +675,9 @@ namespace llarp } // TODO: this - nlohmann::json LinkManager::extract_status() const - { - return {}; - } + nlohmann::json LinkManager::extract_status() const { return {}; } - void LinkManager::connect_to_random(size_t num_conns) + void LinkManager::connect_to_keep_alive(size_t num_conns) { auto filter = [this](const RemoteRC& rc) -> bool { const auto& rid = rc.router_id(); @@ -697,6 +688,16 @@ namespace llarp return res; }; + std::optional> rcs = std::nullopt; + + if (node_db->strict_connect_enabled()) + { + assert(not _is_service_node); + + // TESTNET: TODO: if given strict-connects, fetch their RCs SPECIFICALLY in bootstrapping + log::warning(logcat, "FINISH STRICT CONNECT (SEE COMMENT)"); + } + if (auto maybe = node_db->get_n_random_rcs_conditional(num_conns, filter)) { std::vector& rcs = *maybe; @@ -708,96 +709,38 @@ namespace llarp log::warning(logcat, "NodeDB query for {} random RCs for connection returned none", num_conns); } - void LinkManager::handle_path_data_message(bstring message) + void LinkManager::regenerate_and_gossip_rc() { - ustring nonce, hop_id_str, payload; - - try - { - oxenc::bt_dict_consumer btdc{message}; - std::tie(hop_id_str, nonce, payload) = Onion::deserialize(btdc); - } - catch (const std::exception& e) - { - log::warning(logcat, "Exception: {}", e.what()); - return; - } - - auto symmnonce = SymmNonce{nonce.data()}; - HopID hopid{hop_id_str.data()}; - auto hop = _router.path_context()->get_transit_hop(hopid); - - if (not hop) - return; - - symmnonce = crypto::onion(payload.data(), payload.size(), hop->shared, symmnonce, hop->nonceXOR); - - // if terminal hop, pass to the correct path expecting to receive this message - if (hop->terminal_hop) - { - NetworkAddress sender; - bstring data; + log::info(logcat, "Regenerating and gossiping RC..."); + gossip_rc(_router.local_rid(), _router.relay_contact.to_remote()); + _router.save_rc(); + } - try - { - oxenc::bt_dict_consumer btdc{payload}; - std::tie(sender, data) = PathData::deserialize(btdc); + void LinkManager::gossip_rc(const RouterID& last_sender, const RemoteRC& rc) + { + int count{}; + const auto& gossip_src = rc.router_id(); - if (auto session = _router.session_endpoint()->get_session(sender)) - { - session->recv_path_data_message(std::move(data)); - } - else - { - log::warning(logcat, "Could not find session (remote:{}) to relay path data message!", sender); - } - } - catch (const std::exception& e) - { - log::warning(logcat, "Exception: {}", e.what()); - } - } - else + for (auto& [rid, conn] : ep->service_conns) { - // if not terminal hop, relay datagram onwards - auto hop_is_rx = hop->rxid() == hopid; + if (not conn or not conn->is_active) + continue; - const auto& next_id = hop_is_rx ? hop->txid() : hop->rxid(); - const auto& next_router = hop_is_rx ? hop->upstream() : hop->downstream(); + // don't send back to the gossip source or the last sender + if (rid == gossip_src or rid == last_sender) + continue; - std::string new_payload = Onion::serialize(symmnonce, next_id, payload); - - send_data_message(next_router, std::move(new_payload)); + count += send_control_message( + rid, "gossip_rc"s, GossipRCMessage::serialize(last_sender, rc), [](oxen::quic::message) { + log::trace(logcat, "PLACEHOLDER FOR GOSSIP RC RESPONSE HANDLER"); + }); } - } - - void LinkManager::gossip_rc(const RouterID& last_sender, const RemoteRC& rc) - { - _router.loop()->call([this, last_sender, rc]() { - int count = 0; - const auto& gossip_src = rc.router_id(); - - for (auto& [rid, conn] : ep->service_conns) - { - // don't send back to the gossip source or the last sender - if (rid == gossip_src or rid == last_sender) - continue; - - send_control_message( - rid, "gossip_rc"s, GossipRCMessage::serialize(last_sender, rc), [](oxen::quic::message) { - log::trace(logcat, "PLACEHOLDER FOR GOSSIP RC RESPONSE HANDLER"); - }); - ++count; - } - log::critical(logcat, "Dispatched {} GossipRC requests!", count); - }); + log::critical(logcat, "Dispatched {} GossipRC requests!", count); } void LinkManager::handle_gossip_rc(oxen::quic::message m) { - log::debug(logcat, "Handling GossipRC request..."); - // RemoteRC constructor wraps deserialization in a try/catch RemoteRC rc; RouterID src; @@ -816,13 +759,15 @@ namespace llarp return; } + log::trace(logcat, "Handling GossipRC request (sender:{}, rc:{})...", src, rc); + if (node_db->verify_store_gossip_rc(rc)) { - log::critical(logcat, "Received updated RC, forwarding to relay peers."); + log::info(logcat, "Received updated RC (rid:{}), forwarding to peers", rc.router_id()); gossip_rc(_router.local_rid(), rc); } else - log::debug(logcat, "Received known or old RC, not storing or forwarding."); + log::trace(logcat, "Received known or old RC, not storing or forwarding."); } // TODO: can probably use ::send_control_message instead. Need to discuss the potential @@ -867,7 +812,7 @@ namespace llarp } catch (const std::exception& e) { - log::critical(logcat, "Exception handling bootstarp RC Fetch request (body:{}): {}", m.body(), e.what()); + log::critical(logcat, "Exception handling bootstrap RC Fetch request (body:{}): {}", m.body(), e.what()); m.respond(messages::ERROR_RESPONSE, true); return; } @@ -1058,452 +1003,712 @@ namespace llarp m.respond(std::move(btdp).str()); } - void LinkManager::handle_resolve_ons(std::string_view body, std::function respond) + void LinkManager::_handle_resolve_sns(oxen::quic::message m, std::optional inner_body) { + log::trace(logcat, "Received request to publish client contact!"); + std::string name_hash; try { - oxenc::bt_dict_consumer btdp{body}; - - name_hash = btdp.require("H"); + if (inner_body) + name_hash = ResolveSNS::deserialize(oxenc::bt_dict_consumer{*inner_body}); + else + name_hash = ResolveSNS::deserialize(oxenc::bt_dict_consumer{m.body()}); } catch (const std::exception& e) { log::warning(logcat, "Exception: {}", e.what()); - respond(messages::ERROR_RESPONSE); - return; + return m.respond(messages::ERROR_RESPONSE, true); } _router.rpc_client()->lookup_ons_hash( - name_hash, - [respond = - std::move(respond)]([[maybe_unused]] std::optional maybe_enc) mutable { + name_hash, [prev_msg = std::move(m)](std::optional maybe_enc) mutable { if (maybe_enc) - respond(maybe_enc->bt_encode()); + { + log::info(logcat, "RPC lookup successfully returned encrypted SNS record!"); + prev_msg.respond(ResolveSNS::serialize_response(*maybe_enc)); + } else - respond(serialize_response({{messages::STATUS_KEY, FindNameMessage::NOT_FOUND}})); + { + log::warning(logcat, "RPC lookup could not find SNS registry!"); + prev_msg.respond(ResolveSNS::NOT_FOUND, true); + } }); } - void LinkManager::handle_resolve_ons_response(oxen::quic::message m) + void LinkManager::handle_resolve_sns(oxen::quic::message m) { return _handle_resolve_sns(std::move(m)); } + + void LinkManager::_handle_publish_cc(oxen::quic::message m, std::optional inner_body) { - if (m.timed_out) - { - log::info(logcat, "FindNameMessage request timed out!"); - return; - } + log::trace(logcat, "Received request to publish client contact!"); - std::string payload; + EncryptedClientContact enc; try { - oxenc::bt_dict_consumer btdc{m.body()}; - payload = btdc.require(m ? "E" : messages::STATUS_KEY); + if (inner_body) + enc = PublishClientContact::deserialize(oxenc::bt_dict_consumer{*inner_body}); + else + enc = PublishClientContact::deserialize(oxenc::bt_dict_consumer{m.body()}); } catch (const std::exception& e) { - log::warning(logcat, "Exception: {}", e.what()); - return; + log::warning(logcat, "Exception: {}: payload: {}", e.what(), buffer_printer{m.body()}); + return m.respond(messages::ERROR_RESPONSE, true); } - if (m) + if (enc.is_expired()) { - // TODO: wtf + log::warning(logcat, "Received expired EncryptedClientContact!"); + return m.respond(PublishClientContact::EXPIRED, true); } - else + + if (not enc.verify()) { - if (payload == "ERROR") - { - log::info(logcat, "FindNameMessage failed with unkown error!"); + log::warning(logcat, "Received invalid EncryptedClientContact!"); + return m.respond(PublishClientContact::INVALID, true); + } - // resend? - } - else if (payload == FindNameMessage::NOT_FOUND) + // If the optional was nullopt, then this was a relay <-> relay request. As a result, we should NOT + // allow it to continue propagating + if (not inner_body) + { + log::critical(logcat, "Received relayed PublishClientContact request (key: {}); accepting...", enc.key()); + _router.contact_db().put_cc(std::move(enc)); + return m.respond(messages::OK_RESPONSE); + } + + auto dht_key = enc.key(); + auto local_rid = _router.local_rid(); + + auto closest_rcs = _router.node_db()->find_many_closest_to(dht_key, path::DEFAULT_PATHS_HELD); + const auto& closest_peer = closest_rcs.begin()->router_id(); + + // TESTNET: testing this method + auto other_closest = _router.node_db()->find_closest_to(dht_key).router_id(); + log::info(logcat, "First-closest and closest are {}EQUAL", closest_peer == other_closest ? "" : "NOT "); + + for (const auto& rc : closest_rcs) + { + auto& _rid = rc.router_id(); + + log::debug(logcat, "Closest RCs to received ClientContact: {}", _rid); + if (_rid == local_rid) { - log::info(logcat, "FindNameMessage failed with unkown error!"); - // what to do here? + log::info( + logcat, + "Received PublishClientContact (key: {}) for which we are a candidate; accepting...", + dht_key); + _router.contact_db().put_cc(std::move(enc)); + return m.respond(messages::OK_RESPONSE); } - else - log::info(logcat, "FindNameMessage failed with unkown error!"); } + + log::info( + logcat, + "Received PublishClientContact (key: {}); propagating to closest peer (rid: {})...", + enc.key(), + closest_peer); + + send_control_message( + closest_peer, + "publish_cc", + PublishClientContact::serialize(std::move(enc)), + [prev_msg = std::move(m)](oxen::quic::message msg) mutable { + log::info( + logcat, + "Relayed PublishClientContact {}! Relaying response...", + msg ? "SUCCEEDED" + : msg.timed_out ? "timed out" + : "failed"); + log::info(logcat, "Relayed PublishClientContact response: {}", buffer_printer{msg.body()}); + prev_msg.respond(msg.body_str(), msg.is_error()); + }); } - void LinkManager::handle_publish_intro(std::string_view body, std::function respond) + void LinkManager::handle_publish_cc(oxen::quic::message m) { return _handle_publish_cc(std::move(m)); } + + void LinkManager::_handle_find_cc(oxen::quic::message m, std::optional inner_body) { - service::EncryptedIntroSet enc; - std::string introset; - uint64_t is_relayed, relay_order; + log::trace(logcat, "Received request to find client contact!"); + + dht::Key_t dht_key; try { - oxenc::bt_dict_consumer btdc_a{body}; - - introset = btdc_a.require("I"); - relay_order = btdc_a.require("O"); - is_relayed = btdc_a.require("R"); - - enc = *service::EncryptedIntroSet::construct(std::move(introset)); + if (inner_body) + dht_key = FindClientContact::deserialize(oxenc::bt_dict_consumer{*inner_body}); + else + dht_key = FindClientContact::deserialize(oxenc::bt_dict_consumer{m.body()}); } catch (const std::exception& e) { log::warning(logcat, "Exception: {}", e.what()); - respond(messages::ERROR_RESPONSE); - return; + return m.respond(messages::ERROR_RESPONSE, true); } - const auto addr = dht::Key_t{reinterpret_cast(enc.derived_signing_key.data())}; - const auto local_key = _router.rc().router_id(); - - if (not enc.verify()) + if (auto maybe_cc = _router.contact_db().get_encrypted_cc(dht_key)) { - log::error(logcat, "Received PublishIntroMessage with invalid introset: {}", introset); - respond(serialize_response({{messages::STATUS_KEY, PublishIntroMessage::INVALID_INTROSET}})); - return; + log::info( + logcat, + "Received FindClientContact request (key: {}); returning local EncryptedClientContact...", + dht_key); + return m.respond(FindClientContact::serialize_response(std::move(*maybe_cc))); } - auto closest_rcs = _router.node_db()->find_many_closest_to(addr, path::DEFAULT_PATHS_HELD); - - if (closest_rcs.size() != path::DEFAULT_PATHS_HELD) + // If the optional was nullopt, then this was a relay <-> relay request. As a result, we should NOT + // allow it to continue propagating + if (not inner_body) { - log::error(logcat, "Received PublishIntroMessage but only know {} nodes", closest_rcs.size()); - respond(serialize_response({{messages::STATUS_KEY, PublishIntroMessage::INSUFFICIENT}})); - return; + log::critical( + logcat, + "Received relayed FindClientContact request (key: {}); could not find locally, relaying error...", + dht_key); + return m.respond(FindClientContact::NOT_FOUND, true); } - if (is_relayed) + auto local_rid = _router.local_rid(); + + auto closest_rcs = _router.node_db()->find_many_closest_to(dht_key, path::DEFAULT_PATHS_HELD); + auto n_closest = closest_rcs.size(); + + for (const auto& rc : closest_rcs) { - if (relay_order >= path::DEFAULT_PATHS_HELD) + auto& _rid = rc.router_id(); + + if (_rid == local_rid) { - log::error(logcat, "Received PublishIntroMessage with invalide relay order: {}", relay_order); - respond(serialize_response({{messages::STATUS_KEY, PublishIntroMessage::INVALID_ORDER}})); - return; + log::warning( + logcat, + "We are closest peer for FindClientContact request (key: {}); no EncryptedClientContact found" + "locally!", + dht_key); + return m.respond(FindClientContact::NOT_FOUND, true); } + } - log::info(logcat, "Relaying PublishIntroMessage for {}", addr); + auto counter = std::make_shared(n_closest); - const auto& peer_rc = closest_rcs[relay_order]; - const auto& peer_key = peer_rc.router_id(); + auto hook = [prev_msg = std::move(m), counter](oxen::quic::message msg) mutable { + if (*counter == 0) + return; - if (peer_key == local_key) + if (msg) { - log::info( - logcat, - "Received PublishIntroMessage in which we are peer index {}.. storing introset", - relay_order); - - _router.contacts().put_intro(std::move(enc)); - respond(messages::OK_RESPONSE); + *counter = 0; + log::info(logcat, "Relayed FindClientContact request SUCCEEDED! Relaying response..."); + log::info(logcat, "Relayed FindClientContact response: {}", buffer_printer{msg.body()}); } - else + else if (--*counter == 0) { - log::info(logcat, "Received PublishIntroMessage; propagating to peer index {}", relay_order); - - send_control_message( - peer_key, - "publish_intro", - PublishIntroMessage::serialize(enc, relay_order, is_relayed), - [respond = std::move(respond)](oxen::quic::message m) mutable { - if (m.timed_out) - return; // drop if timed out; requester will have timed out as well - respond(m.body_str()); - }); + log::warning(logcat, "All FindClientContact requests FAILED! Relaying response..."); } + else + return; - return; - } + prev_msg.respond(msg.body_str(), msg.is_error()); + }; - int rc_index = -1, index = 0; + log::info(logcat, "Relaying FindClientContactMessage (key: {}) to {} peers", dht_key, n_closest); for (const auto& rc : closest_rcs) { - if (rc.router_id() == local_key) - { - rc_index = index; - break; - } - ++index; - } - - if (rc_index >= 0) - { - log::info(logcat, "Received PublishIntroMessage for {}; we are candidate {}", addr, relay_order); - - _router.contacts().put_intro(std::move(enc)); - respond(messages::OK_RESPONSE); + send_control_message(rc.router_id(), "find_cc", FindClientContact::serialize(dht_key), hook); } - else - log::warning(logcat, "Received non-relayed PublishIntroMessage from {}; we are not the candidate", addr); } - // DISCUSS: I feel like ::handle_publish_intro_response should be the callback that handles the - // response to a relayed publish_intro (above line 1131-ish) + void LinkManager::handle_find_cc(oxen::quic::message m) { return _handle_find_cc(std::move(m)); } - void LinkManager::handle_publish_intro_response(oxen::quic::message m) + void LinkManager::handle_path_build(oxen::quic::message m, const RouterID& from) { - if (m.timed_out) + if (!_router.path_context()->is_transit_allowed()) { - log::info(logcat, "PublishIntroMessage timed out!"); - return; + log::warning(logcat, "got path build request when not permitting transit"); + return m.respond(PATH::BUILD::NO_TRANSIT, true); } - std::string payload; - try { - oxenc::bt_dict_consumer btdc{m.body()}; - payload = btdc.require(messages::STATUS_KEY); - } - catch (const std::exception& e) - { - log::warning(logcat, "Exception: {}", e.what()); - return; - } + auto frames = ONION::deserialize_frames(m.body()); + auto n_frames = frames.size(); - if (m) - { - // DISCUSS: not sure what to do on success of a publish intro command? - } - else - { - if (payload == "ERROR") + if (n_frames != path::MAX_LEN) { - log::info(logcat, "PublishIntroMessage failed with remote exception!"); - // Do something smart here probably - return; + log::info(logcat, "Path build message with wrong number of frames: {}", frames.size()); + return m.respond(PATH::BUILD::BAD_FRAMES, true); } - log::info(logcat, "PublishIntroMessage failed with error code: {}", payload); + log::trace(logcat, "Deserializing frame: {}", buffer_printer{frames.front()}); - if (payload == PublishIntroMessage::INVALID_INTROSET) - { - } - else if (payload == PublishIntroMessage::EXPIRED) - { - } - else if (payload == PublishIntroMessage::INSUFFICIENT) - { - } - else if (payload == PublishIntroMessage::INVALID_ORDER) + auto hop = PATH::BUILD::deserialize_hop(oxenc::bt_dict_consumer{frames.front()}, _router, from); + + // we are terminal hop and everything is okay + if (hop->upstream() == _router.local_rid()) { + log::info(logcat, "We are the terminal hop; path build succeeded"); + if (not hop->terminal_hop) + { + // TESTNET: remove this eventually + log::critical( + logcat, "DANIEL FIX THIS: Hop is terminal hop; constructor should have flipped this boolean"); + hop->terminal_hop = true; + } + + _router.path_context()->put_transit_hop(std::move(hop)); + return m.respond(messages::OK_RESPONSE, false); } - } - } - void LinkManager::handle_find_intro(std::string_view body, std::function respond) - { - ustring location; - uint64_t relay_order, is_relayed; - - try - { - oxenc::bt_dict_consumer btdc{body}; - - relay_order = btdc.require("O"); - is_relayed = btdc.require("R"); - location = btdc.require("S"); - } - catch (const std::exception& e) - { - log::warning(logcat, "Exception: {}", e.what()); - respond(messages::ERROR_RESPONSE); - return; - } - - const auto addr = dht::Key_t{location.data()}; + // rotate our frame to the back + std::ranges::rotate(frames, frames.begin() + 1); - if (is_relayed) - { - if (relay_order >= path::DEFAULT_PATHS_HELD) - { - log::warning(logcat, "Received FindIntroMessage with invalid relay order: {}", relay_order); - respond(serialize_response({{messages::STATUS_KEY, FindIntroMessage::INVALID_ORDER}})); - return; - } + // clear our frame, to be randomized after onion step and appended + frames.back().clear(); - auto closest_rcs = _router.node_db()->find_many_closest_to(addr, path::DEFAULT_PATHS_HELD); + auto onion_nonce = hop->kx.nonce ^ hop->kx.xor_nonce; - if (closest_rcs.size() != path::DEFAULT_PATHS_HELD) + // (de-)onion each further frame using the established shared secret and + // onion_nonce = nonce ^ nonceXOR + // Note: final value passed to crypto::onion is xor factor, but that's for *after* the + // onion round to compute the return value, so we don't care about it. + // for (auto& element : frames) + for (size_t i = 0; i < n_frames - 1; ++i) { - log::error(logcat, "Received FindIntroMessage but only know {} nodes", closest_rcs.size()); - respond(serialize_response({{messages::STATUS_KEY, FindIntroMessage::INSUFFICIENT_NODES}})); - return; + crypto::onion( + reinterpret_cast(frames[i].data()), + frames[i].size(), + hop->kx.shared_secret, + onion_nonce, + onion_nonce); } - log::info(logcat, "Relaying FindIntroMessage for {}", addr); + // randomize final frame + randombytes(reinterpret_cast(frames.back().data()), frames.back().size()); - const auto& peer_rc = closest_rcs[relay_order]; - const auto& peer_key = peer_rc.router_id(); + auto upstream = hop->upstream(); send_control_message( - peer_key, - "find_intro", - FindIntroMessage::serialize(addr, is_relayed, relay_order), - [respond = std::move(respond)](oxen::quic::message relay_response) mutable { - if (relay_response) + std::move(upstream), + "path_build", + ONION::serialize_frames(std::move(frames)), + [this, transit_hop = std::move(hop), prev_message = std::move(m)](oxen::quic::message m) mutable { + if (m) + { log::info( logcat, - "Relayed FindIntroMessage returned successful response; transmitting " - "to initial " - "requester"); - else if (relay_response.timed_out) - log::critical(logcat, "Relayed FindIntroMessage timed out! Notifying initial requester"); - else - log::critical(logcat, "Relayed FindIntroMessage failed! Notifying initial requester"); + "Upstream returned successful path build response; locally storing Hop ({}) and relaying", + transit_hop->to_string()); + _router.path_context()->put_transit_hop(std::move(transit_hop)); + return prev_message.respond(messages::OK_RESPONSE, false); + } + + log::info( + logcat, + "Upstream ({}) returned path build {}; relaying...", + transit_hop->upstream(), + m.timed_out ? "time out" : "failure"); - respond(relay_response.body_str()); + return prev_message.respond(m.body_str(), m.is_error()); }); } - else + catch (const std::exception& e) { - if (auto maybe_intro = _router.contacts().get_encrypted_introset(addr)) - respond(serialize_response({{"INTROSET", maybe_intro->bt_encode()}})); - else - { - log::warning(logcat, "Received FindIntroMessage with relayed == false and no local introset entry"); - respond(serialize_response({{messages::STATUS_KEY, FindIntroMessage::NOT_FOUND}})); - } + log::warning(logcat, "Exception: {}: input: {}", e.what(), m.body()); + // We can respond with the exception string, as all exceptions thrown in the parsing functions + // (ex: `TransitHop::deserialize_hop(...)`) contain the correct response bodies + return m.respond(e.what(), true); } } - void LinkManager::handle_find_intro_response(oxen::quic::message m) + void LinkManager::_handle_path_control(oxen::quic::message m, std::optional inner_body) { - if (m.timed_out) - { - log::info(logcat, "FindIntroMessage timed out!"); - return; - } + log::trace(logcat, "{} called", __PRETTY_FUNCTION__); + HopID hop_id; std::string payload; + SymmNonce nonce; try { - oxenc::bt_dict_consumer btdc{m.body()}; - payload = btdc.require((m) ? "INTROSET" : messages::STATUS_KEY); + if (inner_body) + std::tie(hop_id, nonce, payload) = ONION::deserialize_hop(oxenc::bt_dict_consumer{*inner_body}); + else + std::tie(hop_id, nonce, payload) = ONION::deserialize_hop(oxenc::bt_dict_consumer{m.body()}); } catch (const std::exception& e) { log::warning(logcat, "Exception: {}", e.what()); - return; + log::warning(logcat, "Payload: {}", inner_body ? buffer_printer{*inner_body} : buffer_printer{m.body()}); + return m.respond(messages::ERROR_RESPONSE, true); } - // success case, neither timed out nor errored - if (m) + if (!_is_service_node) { - if (auto enc = service::EncryptedIntroSet::construct(payload)) + auto path = _router.path_context()->get_path(hop_id); + + if (not path) + { + log::warning(logcat, "Client received path control with unknown rxID: {}", hop_id); + return m.respond(messages::ERROR_RESPONSE, true); + } + + log::info(logcat, "Received path control for local client: {}", buffer_printer{payload}); + + for (auto& hop : path->hops) { - _router.contacts().put_intro(std::move(*enc)); + nonce = crypto::onion( + reinterpret_cast(payload.data()), + payload.size(), + hop.kx.shared_secret, + nonce, + hop.kx.xor_nonce); + + log::trace(logcat, "xchacha20 -> {}", buffer_printer{payload}); } + + return handle_path_request(std::move(m), std::move(payload)); } - else + + auto hop = _router.path_context()->get_transit_hop(hop_id); + + if (not hop) { - log::info(logcat, "FindIntroMessage failed with error: {}", payload); - // Do something smart here probably + log::warning(logcat, "Received path control with unknown next hop (ID: {})", hop_id); + return m.respond(messages::ERROR_RESPONSE, true); } - } - void LinkManager::handle_path_build(oxen::quic::message m, const RouterID& from) - { - if (!_router.path_context()->is_transit_allowed()) + auto onion_nonce = nonce ^ hop->kx.xor_nonce; + + crypto::onion( + reinterpret_cast(payload.data()), + payload.size(), + hop->kx.shared_secret, + onion_nonce, + hop->kx.xor_nonce); + + if (not inner_body.has_value()) { - log::warning(logcat, "got path build request when not permitting transit"); - m.respond(PathBuildMessage::NO_TRANSIT, true); - return; + // if terminal hop, payload should contain a request (e.g. "ons_resolve"); handle and respond. + if (hop->terminal_hop) + { + log::debug(logcat, "We are terminal hop for path request: {}", hop->to_string()); + return handle_path_request(std::move(m), std::move(payload)); + } + + log::debug(logcat, "We are intermediate hop for path request: {}", hop->to_string()); } + else + log::info( + logcat, + "We are bridge node for aligned path request ({})! Forwarding downstream: {}", + hop->to_string(), + buffer_printer{*inner_body}); - try + auto next_ids = hop->next_id(hop_id); + + if (not next_ids) { - auto frames = Frames::deserialize(m.body()); - auto n_frames = frames.size(); + log::error(logcat, "Failed to query hop ({}) for next ids (input: {})", hop->to_string(), hop_id); + return m.respond(messages::ERROR_RESPONSE, true); + } - if (n_frames != path::MAX_LEN) + std::string new_payload = ONION::serialize_hop(next_ids->second.to_view(), onion_nonce, std::move(payload)); + + send_control_message( + next_ids->first, + "path_control", + std::move(new_payload), + [hop_weak = hop->weak_from_this(), hop_id, prev_message = std::move(m)]( + oxen::quic::message response) mutable { + auto hop = hop_weak.lock(); + + if (not hop) + { + log::warning(logcat, "Received response to path control message with non-existent TransitHop!"); + return prev_message.respond(messages::ERROR_RESPONSE, true); + } + + if (response) + log::info(logcat, "Path control message returned successfully!"); + else if (response.timed_out) + log::warning(logcat, "Path control message returned as time out!"); + else + log::warning(logcat, "Path control message returned as error!"); + + prev_message.respond(response.body_str(), response.is_error()); + + // TODO: onion encrypt path message responses + // HopID hop_id; + // SymmNonce nonce; + // std::string payload; + + // try + // { + // std::tie(hop_id, nonce, payload) = + // ONION::deserialize_hop(oxenc::bt_dict_consumer{response.body()}); + // } + // catch (const std::exception& e) + // { + // log::warning(logcat, "Exception: {}; payload: {}", e.what(), + // buffer_printer{response.body()}); return prev_message.respond(messages::ERROR_RESPONSE, + // true); + // } + + // auto resp_payload = ONION::serialize_hop(hop_id.to_view(), nonce, std::move(payload)); + // prev_message.respond(std::move(resp_payload), false); + }); + } + + void LinkManager::handle_path_control(oxen::quic::message m) { return _handle_path_control(std::move(m)); } + + void LinkManager::handle_path_data_message(bstring data) + { + _router.loop()->call([this, message = std::move(data)]() mutable { + HopID hop_id; + std::string payload; + SymmNonce nonce; + + try { - log::info(logcat, "Path build message with wrong number of frames: {}", frames.size()); - return m.respond(PathBuildMessage::BAD_FRAMES, true); + std::tie(hop_id, nonce, payload) = + ONION::deserialize_hop(oxenc::bt_dict_consumer{bstring_view{message}}); + } + catch (const std::exception& e) + { + log::warning(logcat, "Exception: {}", e.what()); + return; } - log::trace(logcat, "Deserializing frame: {}", buffer_printer{frames.front()}); + if (!_is_service_node) + { + auto path = _router.path_context()->get_path(hop_id); - SymmNonce nonce; - PubKey remote_pk; - ustring hop_payload; + if (not path) + { + log::warning(logcat, "Client received path data with unknown rxID: {}", hop_id); + return; + } - std::tie(nonce, remote_pk, hop_payload) = - PathBuildMessage::deserialize_hop(oxenc::bt_dict_consumer{frames.front()}, _router.identity()); + for (auto& hop : path->hops) + { + nonce = crypto::onion( + reinterpret_cast(payload.data()), + payload.size(), + hop.kx.shared_secret, + nonce, + hop.kx.xor_nonce); + } - log::trace(logcat, "Deserializing hop payload: {}", buffer_printer{hop_payload}); + log::trace(logcat, "Received path data for local client: {}", buffer_printer{payload}); - auto hop = path::TransitHop::deserialize_hop( - oxenc::bt_dict_consumer{hop_payload}, from, _router, remote_pk, nonce); + try + { + auto [tag, data] = PATH::DATA::deserialize_inner(std::move(payload)); - hop->started = _router.now(); - set_conn_persist(hop->downstream(), hop->expiry_time() + 10s); + if (auto session = _router.session_endpoint()->get_session(tag)) + { + session->recv_path_data_message(std::move(data)); + } + else + { + log::warning(logcat, "Could not find session (tag:{}) to relay path data message!", tag); + } + } + catch (const std::exception& e) + { + log::warning(logcat, "Exception: {}: {}", e.what(), buffer_printer{payload}); + } + return; + } - // we are terminal hop and everything is okay - if (hop->upstream() == _router.local_rid()) + auto hop = _router.path_context()->get_transit_hop(hop_id); + + if (not hop) { - log::info(logcat, "We are the terminal hop; path build succeeded"); - hop->terminal_hop = true; - _router.path_context()->put_transit_hop(std::move(hop)); - return m.respond(messages::OK_RESPONSE, false); + log::warning(logcat, "Received path data with unknown next hop (ID: {})", hop_id); + return; } - // rotate our frame to the back - std::ranges::rotate(frames, frames.begin() + 1); + auto onion_nonce = nonce ^ hop->kx.xor_nonce; - // clear our frame, to be randomized after onion step and appended - frames.back().clear(); + crypto::onion( + reinterpret_cast(payload.data()), + payload.size(), + hop->kx.shared_secret, + onion_nonce, + hop->kx.xor_nonce); - auto onion_nonce = nonce ^ hop->nonceXOR; + std::optional> next_ids = std::nullopt; + std::string next_payload; - // (de-)onion each further frame using the established shared secret and - // onion_nonce = nonce ^ nonceXOR - // Note: final value passed to crypto::onion is xor factor, but that's for *after* the - // onion round to compute the return value, so we don't care about it. - // for (auto& element : frames) - for (size_t i = 0; i < n_frames - 1; ++i) + log::trace( + logcat, + "We are {} hop for path data: {}: {}", + hop->terminal_hop ? "terminal" : "intermediate", + hop->to_string(), + buffer_printer{payload}); + + // if terminal hop, pass to the correct path expecting to receive this message + if (hop->terminal_hop) { + HopID ihid; + std::string intermediate; + + try + { + std::tie(ihid, intermediate) = + PATH::DATA::deserialize_intermediate(oxenc::bt_dict_consumer{payload}); + } + catch (const std::exception& e) + { + log::warning( + logcat, "Path data intermediate payload exception: {}: {}", e.what(), buffer_printer{payload}); + return; + } + + log::trace(logcat, "Inbound path rxid:{}, outbound path txid:{}", hop_id, ihid); + + auto next_hop = _router.path_context()->get_transit_hop(ihid); + + if (not next_hop) + { + log::warning(logcat, "We are bridge node for path data message with unknown txID: {}", ihid); + return; + } + + log::trace(logcat, "Bridging path data message to hop: {}", next_hop->to_string()); + + next_ids = next_hop->next_id(ihid); + + onion_nonce ^= next_hop->kx.xor_nonce; + crypto::onion( - reinterpret_cast(frames[i].data()), - frames[i].size(), - hop->shared, + reinterpret_cast(intermediate.data()), + intermediate.size(), + next_hop->kx.shared_secret, onion_nonce, - onion_nonce); + next_hop->kx.xor_nonce); + + if (not next_ids) + { + log::error( + logcat, "Failed to query hop ({}) for next ids (input: {})", next_hop->to_string(), hop_id); + return; + } + + next_payload = ONION::serialize_hop(next_ids->second.to_view(), onion_nonce, std::move(intermediate)); } + else + { + next_ids = hop->next_id(hop_id); - // randomize final frame - randombytes(reinterpret_cast(frames.back().data()), frames.back().size()); + if (not next_ids) + { + log::error(logcat, "Failed to query hop ({}) for next ids (input: {})", hop->to_string(), hop_id); + return; + } - auto upstream = hop->upstream(); + next_payload = ONION::serialize_hop(next_ids->second.to_view(), onion_nonce, std::move(payload)); + } - send_control_message( - std::move(upstream), - "path_build", - Frames::serialize(std::move(frames)), - [this, transit_hop = std::move(hop), prev_message = std::move(m)](oxen::quic::message m) mutable { - if (m) - { - log::info( - logcat, - "Upstream returned successful path build response; locally storing Hop and relaying"); - _router.path_context()->put_transit_hop(std::move(transit_hop)); - return prev_message.respond(messages::OK_RESPONSE, false); - } - if (m.timed_out) - log::info(logcat, "Upstream timed out on path build; relaying timeout"); - else - log::info(logcat, "Upstream returned path build failure; relaying response"); + send_data_message(next_ids->first, std::move(next_payload)); + }); + } - return prev_message.respond(m.body(), m.is_error()); - }); + void LinkManager::handle_path_request(oxen::quic::message m, std::string payload) + { + std::string endpoint, body; + + try + { + std::tie(endpoint, body) = PATH::CONTROL::deserialize(oxenc::bt_dict_consumer{payload}); + + if (_is_service_node and endpoint == "path_control") + { + log::info(logcat, "Received path control relay request; deserializing intermediate payload..."); + auto [_, i_body] = PATH::CONTROL::deserialize(oxenc::bt_dict_consumer{std::move(body)}); + return _handle_path_control(std::move(m), std::move(i_body)); + } } catch (const std::exception& e) { - log::warning(logcat, "Exception: {}: input: {}", e.what(), m.body()); - // We can respond with the exception string, as all exceptions thrown in the parsing functions - // (ex: `TransitHop::deserialize_hop(...)`) contain the correct response bodies - return m.respond(e.what(), true); + log::warning(logcat, "Exception: {}; Payload: {}", e.what(), buffer_printer{payload}); + return m.respond(messages::serialize_response({{messages::STATUS_KEY, e.what()}}), true); + } + + if (auto it = path_requests.find(endpoint); it != path_requests.end()) + { + log::debug(logcat, "Received path control request (`{}`); invoking endpoint...", endpoint); + std::invoke(it->second, this, std::move(m), std::move(body)); } + else + log::warning(logcat, "Received path control request (`{}`), which has no local handler!", endpoint); + } + + void LinkManager::_handle_initiate_session(oxen::quic::message m, std::optional inner_body) + { + log::trace(logcat, "{} called", __PRETTY_FUNCTION__); + + NetworkAddress initiator; + session_tag tag; + HopID remote_pivot_txid; + HopID local_pivot_txid; + bool use_tun; + shared_kx_data kx_data; + std::optional maybe_auth = std::nullopt; + std::shared_ptr path_ptr; + + try + { + if (inner_body) + std::tie(kx_data, initiator, local_pivot_txid, tag, remote_pivot_txid, use_tun, maybe_auth) = + InitiateSession::decrypt_deserialize(oxenc::bt_dict_consumer{*inner_body}, _router.identity()); + else + std::tie(kx_data, initiator, local_pivot_txid, tag, remote_pivot_txid, use_tun, maybe_auth) = + InitiateSession::decrypt_deserialize(oxenc::bt_dict_consumer{m.body()}, _router.identity()); + + if (maybe_auth and not _router.session_endpoint()->validate(initiator, maybe_auth)) + { + log::warning(logcat, "Failed to authenticate session initiation request from remote:{}", initiator); + return m.respond(InitiateSession::AUTH_ERROR, true); + } + + path_ptr = _router.path_context()->get_path(local_pivot_txid); + + if (not path_ptr) + { + log::warning( + logcat, "Failed to find local path for new inbound session over pivot: {}", local_pivot_txid); + return m.respond(InitiateSession::BAD_PATH, true); + } + + if (_router.session_endpoint()->prefigure_session( + std::move(initiator), + std::move(tag), + std::move(remote_pivot_txid), + std::move(path_ptr), + std::move(kx_data), + use_tun)) + { + log::debug(logcat, "InboundSession configured successfully!"); + return m.respond(messages::OK_RESPONSE); + } + + log::warning(logcat, "Failed to configure InboundSession!"); + } + catch (const std::exception& e) + { + log::warning(logcat, "Exception: {}", e.what()); + } + + m.respond(messages::ERROR_RESPONSE, true); + } + + // TESTNET: pretty sure this isn't called ever -> do not register it + void LinkManager::handle_initiate_session(oxen::quic::message m) + { + log::critical(logcat, "{} called, LOOK AT WHY", __PRETTY_FUNCTION__); + return _handle_initiate_session(std::move(m)); } void LinkManager::handle_path_latency(oxen::quic::message m) @@ -1839,170 +2044,6 @@ namespace llarp // path_ptr->mark_exit_closed(); } - void LinkManager::handle_path_control(oxen::quic::message m, const RouterID& /* from */) - { - ustring nonce, hop_id_str, payload; - - try - { - oxenc::bt_dict_consumer btdc{m.body()}; - std::tie(hop_id_str, nonce, payload) = Onion::deserialize(btdc); - } - catch (const std::exception& e) - { - log::warning(logcat, "Exception: {}", e.what()); - return; - } - - auto symmnonce = SymmNonce{nonce.data()}; - HopID hopid{hop_id_str.data()}; - auto hop = _router.path_context()->get_transit_hop(hopid); - - // TODO: use "path_control" for both directions? If not, drop message on - // floor if we don't have the path_id in question; if we decide to make this - // bidirectional, will need to check if we have a Path with path_id. - if (not hop) - return; - - symmnonce = crypto::onion(payload.data(), payload.size(), hop->shared, symmnonce, hop->nonceXOR); - - // if terminal hop, payload should contain a request (e.g. "ons_resolve"); handle and respond. - if (hop->terminal_hop) - { - handle_inner_request( - std::move(m), - std::string{reinterpret_cast(payload.data()), payload.size()}, - std::move(hop)); - return; - } - - auto hop_is_rx = hop->rxid() == hopid; - - const auto& next_id = hop_is_rx ? hop->txid() : hop->rxid(); - const auto& next_router = hop_is_rx ? hop->upstream() : hop->downstream(); - - std::string new_payload = Onion::serialize(symmnonce, next_id, payload); - - send_control_message( - next_router, - "path_control"s, - std::move(new_payload), - [hop_weak = hop->weak_from_this(), hopid, prev_message = std::move(m)]( - oxen::quic::message response) mutable { - auto hop = hop_weak.lock(); - - if (not hop) - return; - - ustring hop_id, nonce, payload; - - try - { - oxenc::bt_dict_consumer btdc{response.body()}; - std::tie(hop_id, nonce, payload) = Onion::deserialize(btdc); - } - catch (const std::exception& e) - { - log::warning(logcat, "Exception: {}", e.what()); - return; - } - - auto symmnonce = SymmNonce{nonce.data()}; - auto resp_payload = Onion::serialize(symmnonce, HopID{hop_id.data()}, payload); - prev_message.respond(std::move(resp_payload), false); - }); - } - - void LinkManager::handle_inner_request( - oxen::quic::message m, std::string payload, std::shared_ptr hop) - { - std::string endpoint, body; - - try - { - oxenc::bt_dict_consumer btdc{payload}; - std::tie(endpoint, body) = PathControl::deserialize(btdc); - } - catch (const std::exception& e) - { - log::warning(logcat, "Exception: {}", e.what()); - return; - } - - // If a handler exists for "method", call it; else drop request on the floor. - auto itr = path_requests.find(endpoint); - - if (itr == path_requests.end()) - { - log::info(logcat, "Received path control request \"{}\", which has no handler.", endpoint); - return; - } - - auto respond = [m = std::move(m), hop_weak = hop->weak_from_this()](std::string response) mutable { - auto hop = hop_weak.lock(); - if (not hop) - return; // transit hop gone, drop response - - auto n = SymmNonce::make_random(); - m.respond(Onion::serialize(n, hop->rxid(), response), false); - }; - - std::invoke(itr->second, this, std::move(body), std::move(respond)); - } - - void LinkManager::handle_initiate_session(oxen::quic::message m) - { - if (not m) - { - log::info(logcat, "Initiate session message timed out!"); - return; - } - - NetworkAddress initiator; - service::SessionTag tag; - HopID pivot_txid; - bool use_tun; - std::optional maybe_auth = std::nullopt; - std::shared_ptr path_ptr; - - try - { - oxenc::bt_dict_consumer btdc{m.body()}; - - std::tie(initiator, pivot_txid, tag, use_tun, maybe_auth) = - InitiateSession::decrypt_deserialize(btdc, _router.identity()); - - if (not _router.session_endpoint()->validate(initiator, maybe_auth)) - { - log::warning(logcat, "Failed to authenticate session initiation request from remote:{}", initiator); - return m.respond(InitiateSession::AUTH_DENIED, true); - } - - path_ptr = _router.path_context()->get_path(pivot_txid); - - if (not path_ptr) - { - log::warning(logcat, "Failed to find local path corresponding to session over pivot: {}", pivot_txid); - return m.respond(messages::ERROR_RESPONSE, true); - } - - if (_router.session_endpoint()->prefigure_session( - std::move(initiator), std::move(tag), std::move(path_ptr), use_tun)) - { - return m.respond(messages::OK_RESPONSE); - } - - log::warning(logcat, "Failed to configure InboundSession!"); - } - catch (const std::exception& e) - { - log::warning(logcat, "Exception: {}", e.what()); - } - - _router.path_context()->drop_path(path_ptr); - m.respond(messages::ERROR_RESPONSE, true); - } - void LinkManager::handle_convo_intro(oxen::quic::message m) { if (m.timed_out) diff --git a/llarp/link/link_manager.hpp b/llarp/link/link_manager.hpp index dacbe34768..4656a792ab 100644 --- a/llarp/link/link_manager.hpp +++ b/llarp/link/link_manager.hpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include @@ -39,9 +38,6 @@ namespace llarp inline const keep_alive RELAY_KEEP_ALIVE{10s}; inline const keep_alive CLIENT_KEEP_ALIVE{10s}; - inline constexpr int MIN_CLIENT_ROUTER_CONNS{8}; - inline constexpr int MAX_CLIENT_ROUTER_CONNS{10}; - namespace alpns { inline const auto SN_ALPNS = "SERVICE_NODE"_us; @@ -90,21 +86,21 @@ namespace llarp size_t num_client_conns() const; - size_t num_router_conns() const; + size_t num_router_conns(bool active_only = true) const; template - bool establish_connection(KeyedAddress remote, RemoteRC rc, Opt&&... opts); + bool establish_connection(KeyedAddress remote, RouterID rid, Opt&&... opts); template bool establish_and_send( KeyedAddress remote, - RemoteRC rc, + RouterID rid, std::optional endpoint, std::string body, std::function func = nullptr, Opt&&... opts); - void for_each_connection(std::function func); + void for_each_connection(std::function func); void close_connection(RouterID rid); @@ -162,14 +158,12 @@ namespace llarp std::atomic is_stopping; - void handle_path_data_message(bstring dgram); - std::shared_ptr make_control( - oxen::quic::connection_interface& ci, const RouterID& rid); + const std::shared_ptr& ci, const RouterID& rid); - void on_inbound_conn(oxen::quic::connection_interface& ci); + void on_inbound_conn(std::shared_ptr ci); - void on_outbound_conn(oxen::quic::connection_interface& ci); + void on_outbound_conn(RouterID id); void on_conn_open(oxen::quic::connection_interface& ci); @@ -185,6 +179,8 @@ namespace llarp const oxen::quic::Address& local() { return addr; } + void regenerate_and_gossip_rc(); + void gossip_rc(const RouterID& last_sender, const RemoteRC& rc); void handle_gossip_rc(oxen::quic::message m); @@ -229,7 +225,7 @@ namespace llarp std::tuple connection_stats() const; - size_t get_num_connected_routers() const; + size_t get_num_connected_routers(bool active_only = true) const; size_t get_num_connected_clients() const; @@ -239,23 +235,33 @@ namespace llarp nlohmann::json extract_status() const; - void for_each_connection(std::function func); + std::set get_current_remotes() const; + + void for_each_connection(std::function func); // Attempts to connect to a number of random routers. // // This will try to connect to *up to* num_conns routers, but will not // check if we already have a connection to any of the random set, as making // that thread safe would be slow...I think. - void connect_to_random(size_t num_conns); + void connect_to_keep_alive(size_t num_conns); /// always maintain this many client connections to other routers int client_router_connections = 4; private: // DHT messages - void handle_resolve_ons(std::string_view body, std::function respond); // relay - void handle_find_intro(std::string_view body, std::function respond); // relay - void handle_publish_intro(std::string_view body, std::function respond); // relay + // TESTNET: // NEW CLIENT_CONTACT HANDLERS + void handle_publish_cc(oxen::quic::message); + void handle_find_cc(oxen::quic::message); + void handle_resolve_sns(oxen::quic::message); + + // Inner handlers for relayed requests + void _handle_path_control(oxen::quic::message, std::optional = std::nullopt); + void _handle_publish_cc(oxen::quic::message, std::optional = std::nullopt); + void _handle_find_cc(oxen::quic::message, std::optional = std::nullopt); + void _handle_resolve_sns(oxen::quic::message, std::optional = std::nullopt); + void _handle_initiate_session(oxen::quic::message, std::optional = std::nullopt); // Path messages void handle_path_build(oxen::quic::message, const RouterID& from); // relay @@ -276,25 +282,23 @@ namespace llarp void handle_convo_intro(oxen::quic::message); // These requests come over a path (as a "path_control" request), - // may or may not need to make a request to another relay, + // we may or may not need to make a request to another relay, // then respond (onioned) back along the path. - std::unordered_map< - std::string_view, - void (LinkManager::*)(std::string_view body, std::function respond)> + std::unordered_map)> path_requests = { - {"resolve_ons"sv, &LinkManager::handle_resolve_ons}, - {"publish_intro"sv, &LinkManager::handle_publish_intro}, - {"find_intro"sv, &LinkManager::handle_find_intro}}; + {"path_control"sv, &LinkManager::_handle_path_control}, + {"publish_cc"sv, &LinkManager::_handle_publish_cc}, + {"find_cc"sv, &LinkManager::_handle_find_cc}, + {"resolve_sns"sv, &LinkManager::_handle_resolve_sns}, + {"session_init"sv, &LinkManager::_handle_initiate_session}}; // Path relaying - void handle_path_control(oxen::quic::message, const RouterID& from); + void handle_path_data_message(bstring dgram); + void handle_path_control(oxen::quic::message); - void handle_inner_request(oxen::quic::message m, std::string payload, std::shared_ptr hop); + void relay_path_request(oxen::quic::message m, std::string payload); - // DHT responses - void handle_resolve_ons_response(oxen::quic::message); - void handle_find_intro_response(oxen::quic::message); - void handle_publish_intro_response(oxen::quic::message); + void handle_path_request(oxen::quic::message m, std::string payload); // Path responses void handle_path_latency_response(oxen::quic::message); @@ -313,7 +317,7 @@ namespace llarp template bool Endpoint::establish_and_send( KeyedAddress remote, - RemoteRC rc, + RouterID rid, std::optional ep, std::string body, std::function func, @@ -322,7 +326,6 @@ namespace llarp return link_manager.router().loop()->call_get([&]() { try { - const auto& rid = rc.router_id(); const auto& is_control = ep.has_value(); const auto us = _is_service_node ? "Relay"s : "Client"s; @@ -345,7 +348,6 @@ namespace llarp _is_service_node ? RELAY_KEEP_ALIVE : CLIENT_KEEP_ALIVE, std::forward(opts)...); - // auto auto control_stream = conn_interface->template open_stream( [](oxen::quic::Stream&, uint64_t error_code) { log::warning(logcat, "BTRequestStream closed unexpectedly (ec:{})", error_code); @@ -364,7 +366,7 @@ namespace llarp : conn_interface->send_datagram(std::move(body)); itr->second = - std::make_shared(std::move(conn_interface), std::move(control_stream), true); + std::make_shared(std::move(conn_interface), std::move(control_stream)); log::info(logcat, "Outbound connection to RID:{} added to service conns...", rid); return true; @@ -378,13 +380,11 @@ namespace llarp } template - bool Endpoint::establish_connection(KeyedAddress remote, RemoteRC rc, Opt&&... opts) + bool Endpoint::establish_connection(KeyedAddress remote, RouterID rid, Opt&&... opts) { return link_manager.router().loop()->call_get([&]() { try { - const auto& rid = rc.router_id(); - log::debug(logcat, "Establishing connection to RID:{}", rid); // add to service conns auto [itr, b] = service_conns.try_emplace(rid, nullptr); @@ -401,7 +401,7 @@ namespace llarp _is_service_node ? RELAY_KEEP_ALIVE : CLIENT_KEEP_ALIVE, std::forward(opts)...); - log::critical(logcat, "Created outbound connection with path: {}", conn_interface->path()); + log::trace(logcat, "Created outbound connection with path: {}", conn_interface->path()); auto control_stream = conn_interface->template open_stream( [](oxen::quic::Stream&, uint64_t error_code) { @@ -411,7 +411,7 @@ namespace llarp link_manager.register_commands(control_stream, rid, not _is_service_node); itr->second = - std::make_shared(std::move(conn_interface), std::move(control_stream), true); + std::make_shared(std::move(conn_interface), std::move(control_stream)); log::info(logcat, "Outbound connection to RID:{} added to service conns...", rid); return true; diff --git a/llarp/link/tunnel.cpp b/llarp/link/tunnel.cpp index 913005385d..2b97ec11ee 100644 --- a/llarp/link/tunnel.cpp +++ b/llarp/link/tunnel.cpp @@ -12,14 +12,8 @@ namespace llarp _tls_creds{oxen::quic::GNUTLSCreds::make_from_ed_keys(TUNNEL_SEED, TUNNEL_PUBKEY)} {} - std::shared_ptr QUICTunnel::make(Router& r) - { - return r.loop()->template make_shared(r); - } + std::shared_ptr QUICTunnel::make(Router& r) { return r.loop()->template make_shared(r); } - uint16_t QUICTunnel::listen() - { - return {}; - } + uint16_t QUICTunnel::listen() { return {}; } } // namespace llarp diff --git a/llarp/lokinet_shared.cpp b/llarp/lokinet_shared.cpp index d4f3dcd9ad..2a1d41b96c 100644 --- a/llarp/lokinet_shared.cpp +++ b/llarp/lokinet_shared.cpp @@ -446,7 +446,7 @@ extern "C" } else { - llarp::RouterContact rc{}; + llarp::RelayContact rc{}; if (not rc.BDecode(&buf)) { oxen::log::error(logcat, "failed to decode signle RC: {}", llarp::buffer_printer{buf}); @@ -459,10 +459,7 @@ extern "C" return 0; } - struct lokinet_context* EXPORT lokinet_context_new() - { - return new lokinet_context{}; - } + struct lokinet_context* EXPORT lokinet_context_new() { return new lokinet_context{}; } void EXPORT lokinet_context_free(struct lokinet_context* ctx) { diff --git a/llarp/messages/common.hpp b/llarp/messages/common.hpp index 9682a54b50..9426909a5f 100644 --- a/llarp/messages/common.hpp +++ b/llarp/messages/common.hpp @@ -1,77 +1,24 @@ #pragma once +#include +#include #include #include -#include -#include -#include +#include #include #include #include -namespace llarp +namespace llarp::messages { - namespace messages - { - static auto logcat = log::Cat("messages"); + static auto logcat = log::Cat("messages"); - inline std::string serialize_response(oxenc::bt_dict supplement = {}) - { - return oxenc::bt_serialize(supplement); - } + inline std::string serialize_response(oxenc::bt_dict supplement = {}) { return oxenc::bt_serialize(supplement); } - // ideally STATUS is the first key in a bt-dict, so use a single, early ascii char - inline const auto STATUS_KEY = "!"s; - inline const auto TIMEOUT_RESPONSE = serialize_response({{STATUS_KEY, "TIMEOUT"}}); - inline const auto ERROR_RESPONSE = serialize_response({{STATUS_KEY, "ERROR"}}); - inline const auto OK_RESPONSE = serialize_response({{STATUS_KEY, "OK"}}); - } // namespace messages - - namespace Onion - { - static auto logcat = llarp::log::Cat("onion"); - - /** Bt-encoded contents: - - 'h' : HopID of the next layer of the onion - - 'n' : Symmetric nonce used to encrypt the layer - - 'x' : Encrypted payload transmitted to next recipient - */ - inline static std::string serialize( - const SymmNonce& nonce, const HopID& hop_id, const std::string_view& payload) - { - oxenc::bt_dict_producer btdp; - btdp.append("h", hop_id.to_view()); - btdp.append("n", nonce.to_view()); - btdp.append("x", payload); - - return std::move(btdp).str(); - } - - inline static std::string serialize(const SymmNonce& nonce, const HopID& hop_id, const ustring_view& payload) - { - return serialize( - nonce, hop_id, std::string_view{reinterpret_cast(payload.data()), payload.size()}); - } - - inline static std::tuple deserialize(oxenc::bt_dict_consumer& btdc) - { - ustring hopid, nonce, payload; - - try - { - hopid = btdc.require("h"); - nonce = btdc.require("n"); - payload = btdc.require("x"); - } - catch (const std::exception& e) - { - log::warning(logcat, "Exception caught deserializing onion data:{}", e.what()); - throw; - } - - return {std::move(hopid), std::move(nonce), std::move(payload)}; - } - } // namespace Onion - -} // namespace llarp + // ideally STATUS is the first key in a bt-dict, so use a single, early ascii char + inline const auto STATUS_KEY = "!"s; + inline const auto TIMEOUT_RESPONSE = serialize_response({{STATUS_KEY, "TIMEOUT"}}); + inline const auto ERROR_RESPONSE = serialize_response({{STATUS_KEY, "ERROR"}}); + inline const auto OK_RESPONSE = serialize_response({{STATUS_KEY, "OK"}}); +} // namespace llarp::messages diff --git a/llarp/messages/dht.hpp b/llarp/messages/dht.hpp index 4fa6a916b5..97ad64bb73 100644 --- a/llarp/messages/dht.hpp +++ b/llarp/messages/dht.hpp @@ -2,96 +2,176 @@ #include "common.hpp" -#include +#include +#include namespace llarp { - namespace FindIntroMessage + namespace PublishClientContact { - inline constexpr auto NOT_FOUND = "NOT FOUND"sv; - inline constexpr auto INVALID_ORDER = "INVALID ORDER"sv; - inline constexpr auto INSUFFICIENT_NODES = "INSUFFICIENT NODES"sv; + inline const auto INVALID = messages::serialize_response({{messages::STATUS_KEY, "INVALID CC"}}); + inline const auto EXPIRED = messages::serialize_response({{messages::STATUS_KEY, "EXPIRED CC"}}); - inline static std::string serialize(const dht::Key_t& location, bool is_relayed, uint64_t order) + /** Bt-encoded contents: + - 'x' : EncryptedClientContact + + Note: we are bt-encoding to leave space for future fields (ex: version) + */ + inline static std::string serialize(const EncryptedClientContact& ecc) { oxenc::bt_dict_producer btdp; + btdp.append("x", ecc.bt_payload()); + + return std::move(btdp).str(); + } + + inline static EncryptedClientContact deserialize(oxenc::bt_dict_consumer&& btdc) + { + EncryptedClientContact ecc; + try { - btdp.append("O", order); - btdp.append("R", is_relayed ? 1 : 0); - btdp.append("S", location.to_view()); + ecc = EncryptedClientContact::deserialize(btdc.require("x")); } - catch (...) + catch (const std::exception& e) { - log::error(messages::logcat, "Error: FindIntroMessage failed to bt encode contents!"); + throw std::runtime_error{"Exception caught deserializing EncryptedClientContact: {}"_format(e.what())}; } - return std::move(btdp).str(); + return ecc; } - } // namespace FindIntroMessage + } // namespace PublishClientContact - namespace FindNameMessage + namespace FindClientContact { - inline constexpr auto NOT_FOUND = "NOT FOUND"sv; + inline const auto NOT_FOUND = messages::serialize_response({{messages::STATUS_KEY, "NOT FOUND"}}); + inline const auto INSUFFICIENT = messages::serialize_response({{messages::STATUS_KEY, "INSUFFICIENT NODES"}}); + inline const auto INVALID_ORDER = messages::serialize_response({{messages::STATUS_KEY, "INVALID ORDER"}}); + + /** Bt-encoded contents: + - 'k' : DHT key corresponding to client contact - inline static std::string serialize(std::string name_hash) + Note: we are bt-encoding to leave space for future fields (ex: version) + */ + inline static std::string serialize(const dht::Key_t& location) { oxenc::bt_dict_producer btdp; + btdp.append("k", location.to_view()); + + return std::move(btdp).str(); + } + + inline static dht::Key_t deserialize(oxenc::bt_dict_consumer&& btdc) + { + dht::Key_t key; + try { - btdp.append("H", std::move(name_hash)); + key.from_string(btdc.require("k")); } - catch (...) + catch (const std::exception& e) { - log::error(messages::logcat, "Error: FindNameMessage failed to bt encode contents!"); + log::error(messages::logcat, "Error: failed to deserialize FindClientContact contents: {}", e.what()); + throw; } - return std::move(btdp).str(); + return key; } - inline static std::string serialize_response(std::string encrypted_name) + /** Bt-encoded contents: + - 'x' : EncryptedClientContact + + Note: we are bt-encoding to leave space for future fields (ex: version) + */ + inline static std::string serialize_response(EncryptedClientContact ecc) { oxenc::bt_dict_producer btdp; + btdp.append("x", ecc.bt_payload()); + + return std::move(btdp).str(); + } + + inline static EncryptedClientContact deserialize_response(oxenc::bt_dict_consumer&& btdc) + { + EncryptedClientContact ecc; + try { - btdp.append("E", std::move(encrypted_name)); + ecc = EncryptedClientContact::deserialize(btdc.require("x")); } - catch (...) + catch (const std::exception& e) { - log::error(messages::logcat, "Error: FindNameMessage failed to bt encode contents!"); + throw std::runtime_error{"Exception caught deserializing EncryptedClientContact: {}"_format(e.what())}; } - return std::move(btdp).str(); + return ecc; } - } // namespace FindNameMessage + } // namespace FindClientContact - namespace PublishIntroMessage + namespace ResolveSNS { - inline constexpr auto INVALID_INTROSET = "INVALID INTROSET"sv; - inline constexpr auto EXPIRED = "EXPIRED INTROSET"sv; - inline constexpr auto INSUFFICIENT = "INSUFFICIENT NODES"sv; - inline constexpr auto INVALID_ORDER = "INVALID ORDER"sv; + inline const auto NOT_FOUND = messages::serialize_response({{messages::STATUS_KEY, "NOT FOUND"}}); - inline static std::string serialize( - const service::EncryptedIntroSet& introset, uint64_t relay_order, uint64_t is_relayed) + /** Bt-encoded contents: + - 's' : SNS name + + Note: we are bt-encoding to leave space for future fields (ex: version) + */ + inline static std::string serialize(std::string_view name_hash) { oxenc::bt_dict_producer btdp; + btdp.append("s", name_hash); + + return std::move(btdp).str(); + } + + inline static std::string deserialize(oxenc::bt_dict_consumer&& btdc) + { try { - btdp.append("I", introset.bt_encode()); - btdp.append("O", relay_order); - btdp.append("R", is_relayed); + return btdc.require("s"); } - catch (...) + catch (const std::exception& e) { - log::error(messages::logcat, "Error: FindNameMessage failed to bt encode contents!"); + log::error(messages::logcat, "Error: failed to deserialize ResolveSNS contents: {}", e.what()); + throw; } + } + + /** Bt-encoded contents: + - 'x' : EncryptedSNSRecord + + Note: we are bt-encoding to leave space for future fields (ex: version) + */ + inline static std::string serialize_response(const EncryptedSNSRecord& enc) + { + oxenc::bt_dict_producer btdp; + + btdp.append("x", enc.bt_payload()); return std::move(btdp).str(); } - } // namespace PublishIntroMessage + + inline static EncryptedSNSRecord deserialize_response(oxenc::bt_dict_consumer&& btdc) + { + EncryptedSNSRecord enc{}; + + try + { + enc = EncryptedSNSRecord::deserialize(btdc.require("x")); + } + catch (const std::exception& e) + { + log::error(messages::logcat, "Error: failed to deserialize ResolveSNS contents: {}", e.what()); + throw; + } + + return enc; + } + } // namespace ResolveSNS + } // namespace llarp diff --git a/llarp/messages/exit.hpp b/llarp/messages/exit.hpp index bfa5ce443b..f911b501f3 100644 --- a/llarp/messages/exit.hpp +++ b/llarp/messages/exit.hpp @@ -2,13 +2,14 @@ #include "common.hpp" +#include +#include + namespace llarp { /* TODO: - - ADD PUBKEY FIELD OR AT LEAST SEE WHY LINKMANAGER::HANDLE_OBTAIN_EXIT() LOOKS FOR ONE - - - change these parameters to ustringviews and ustrings where needed after bumping oxenc + - change these parameters to uspans where needed after bumping oxenc - change std::string sig(64, '\0') --> std::array sig */ diff --git a/llarp/messages/path.hpp b/llarp/messages/path.hpp index 1b5efa2435..6c1b37ccf4 100644 --- a/llarp/messages/path.hpp +++ b/llarp/messages/path.hpp @@ -3,226 +3,355 @@ #include "common.hpp" #include +#include #include namespace llarp { using namespace oxenc::literals; - namespace Frames + namespace ONION { - static auto logcat = llarp::log::Cat("path-build-frames"); + static auto logcat = llarp::log::Cat("onion"); - inline static std::string serialize(std::vector&& frames) + inline static std::string serialize_frames(std::vector&& frames) { return oxenc::bt_serialize(std::move(frames)); } - inline static std::vector deserialize(std::string_view&& buf) + inline static std::vector deserialize_frames(std::string_view&& buf) { return oxenc::bt_deserialize>(buf); } - } // namespace Frames - namespace PathData - { - static auto logcat = llarp::log::Cat("path-data"); - - /** Fields for transmitting Path Data: - - 'b' : request/command body - - 's' : RouterID of sender - NOTE: more fields may be added later as needed, hence the namespacing + /** Bt-encoded contents: + - 'k' : Next upstream HopID (path messages) OR shared pubkey (path builds) + - 'n' : Symmetric nonce used to encrypt the layer + - 'x' : Encrypted payload transmitted to next recipient */ - inline static std::string serialize(std::string body, const RouterID& local) + // template + // inline static std::string serialize_hop(K key, const SymmNonce& nonce, T encrypted) + inline static std::string serialize_hop( + std::string_view key, const SymmNonce& nonce, std::string_view encrypted) { oxenc::bt_dict_producer btdp; - btdp.append("b", body); - btdp.append("s", local.to_view()); + btdp.append("k", key); + btdp.append("n", nonce.to_view()); + btdp.append("x", encrypted); + return std::move(btdp).str(); } - inline static std::tuple deserialize(oxenc::bt_dict_consumer& btdc) + inline static std::tuple deserialize_decrypt( + oxenc::bt_dict_consumer&& btdc, const Ed25519SecretKey& local_sk) { - RouterID remote; - bstring body; + std::string payload; + shared_kx_data kx_data{}; try { - body = btdc.require("b"); - remote.from_string(btdc.require("s")); - auto sender = NetworkAddress::from_pubkey(remote, true); - - return {std::move(sender), std::move(body)}; + kx_data.pubkey.from_string(btdc.require("k")); + kx_data.nonce.from_string(btdc.require("n")); + payload = btdc.require("x"); } catch (const std::exception& e) { - log::warning(logcat, "Exception caught deserializing path data:{}", e.what()); - throw; + log::warning(logcat, "Exception caught deserializing onion data: {}", e.what()); + throw std::runtime_error{messages::ERROR_RESPONSE}; } - } - } // namespace PathData - namespace PathControl - { - static auto logcat = llarp::log::Cat("path-control"); + log::trace(logcat, "payload: {}", buffer_printer{payload}); - /** Fields for transmitting Path Control: - - 'e' : request endpoint being invoked - - 'r' : request body - */ - inline static std::string serialize(std::string endpoint, std::string body) - { - oxenc::bt_dict_producer btdp; - btdp.append("e", endpoint); - btdp.append("r", body); - return std::move(btdp).str(); + try + { + kx_data.server_dh(local_sk); + kx_data.decrypt(to_uspan(payload)); + + log::trace(logcat, "xchacha -> payload: {}", buffer_printer{payload}); + + kx_data.generate_xor(); + } + catch (const std::exception& e) + { + log::warning(logcat, "Failed to derive and decrypt outer wrapping!"); + throw std::runtime_error{messages::ERROR_RESPONSE}; + } + + return {std::move(payload), std::move(kx_data)}; } - inline static std::tuple deserialize(oxenc::bt_dict_consumer& btdc) + inline static std::tuple deserialize(oxenc::bt_dict_consumer&& btdc) { - std::string endpoint, body; + RouterID rid; + std::string payload; + SymmNonce nonce; try { - endpoint = btdc.require("e"); - body = btdc.require("r"); + rid.from_string(btdc.require("k")); + nonce.from_string(btdc.require("n")); + payload = btdc.require("x"); } catch (const std::exception& e) { - log::warning(logcat, "Exception caught deserializing path control:{}", e.what()); - throw; + throw std::runtime_error{"Exception caught deserializing onion data: {}"_format(e.what())}; } - return {std::move(endpoint), std::move(body)}; + return {std::move(rid), std::move(nonce), std::move(payload)}; } - } // namespace PathControl - namespace PathBuildMessage - { - static auto logcat = llarp::log::Cat("path-build"); - - inline constexpr auto bad_frames = "BAD_FRAMES"sv; - inline constexpr auto bad_crypto = "BAD_CRYPTO"sv; - inline constexpr auto no_transit = "NOT ALLOWING TRANSIT"sv; - inline constexpr auto bad_pathid = "BAD PATH ID"sv; - inline constexpr auto bad_lifetime = "BAD PATH LIFETIME (TOO LONG)"sv; - - inline const auto NO_TRANSIT = messages::serialize_response({{messages::STATUS_KEY, no_transit}}); - inline const auto BAD_LIFETIME = messages::serialize_response({{messages::STATUS_KEY, bad_lifetime}}); - inline const auto BAD_FRAMES = messages::serialize_response({{messages::STATUS_KEY, bad_frames}}); - inline const auto BAD_PATHID = messages::serialize_response({{messages::STATUS_KEY, bad_pathid}}); - inline const auto BAD_CRYPTO = messages::serialize_response({{messages::STATUS_KEY, bad_crypto}}); - - /** For each hop: - - Generate an Ed keypair for the hop (`shared_key`) - - Generate a symmetric nonce for subsequent DH - - Derive the shared secret (`hop.shared`) for DH key-exchange using the ED keypair, hop pubkey, and - symmetric nonce - - Encrypt the hop info in-place using `hop.shared` and the generated symmetric nonce from DH - - Generate the XOR nonce by hashing the symmetric key from DH (`hop.shared`) and truncating - - Bt-encoded contents: - - 'n' : symmetric nonce used for DH key-exchange - - 's' : shared pubkey used to derive symmetric key - - 'x' : encrypted payload - - 'l' : path lifetime - - 'r' : rxID (the path ID for messages going *to* the hop) - - 't' : txID (the path ID for messages coming *from* the client/path origin) - - 'u' : upstream hop RouterID - - All of these 'frames' are inserted sequentially into the list and padded with any needed dummy frames - */ - inline static std::string serialize_hop(path::PathHopConfig& hop) + inline static std::tuple deserialize_hop(oxenc::bt_dict_consumer&& btdc) { - std::string hop_payload; + HopID hop_id; + std::string payload; + SymmNonce nonce; + try { - oxenc::bt_dict_producer btdp; - - btdp.append("l", path::DEFAULT_LIFETIME.count()); - btdp.append("r", hop.rxID.to_view()); - btdp.append("t", hop.txID.to_view()); - btdp.append("u", hop.upstream.to_view()); - - hop_payload = std::move(btdp).str(); + hop_id.from_string(btdc.require("k")); + nonce.from_string(btdc.require("n")); + payload = btdc.require("x"); + } + catch (const std::exception& e) + { + throw std::runtime_error{"Exception caught deserializing onion data: {}"_format(e.what())}; } - Ed25519SecretKey ephemeral_key; - crypto::identity_keygen(ephemeral_key); - - hop.nonce = SymmNonce::make_random(); - - crypto::derive_encrypt_outer_wrapping( - ephemeral_key, hop.shared, hop.nonce, hop.rc.router_id(), to_uspan(hop_payload)); - - // generate nonceXOR value self->hop->pathKey - ShortHash xor_hash; - crypto::shorthash(xor_hash, hop.shared.data(), hop.shared.size()); - - hop.nonceXOR = xor_hash.data(); // nonceXOR is 24 bytes, ShortHash is 32; this will truncate - - log::trace( - logcat, - "Hop serialized; nonce: {}, remote router_id: {}, shared pk: {}, shared secret: {}, payload: {}", - hop.nonce.to_string(), - hop.rc.router_id().to_string(), - ephemeral_key.to_pubkey().to_string(), - hop.shared.to_string(), - buffer_printer{hop_payload}); + return {std::move(hop_id), std::move(nonce), std::move(payload)}; + } - oxenc::bt_dict_producer btdp; + } // namespace ONION - btdp.append("n", hop.nonce.to_view()); - btdp.append("s", ephemeral_key.to_pubkey().to_view()); - btdp.append("x", hop_payload); + namespace PATH + { + namespace BUILD + { + static auto logcat = llarp::log::Cat("path-build"); + + inline constexpr auto bad_frames = "BAD_FRAMES"sv; + inline constexpr auto bad_crypto = "BAD_CRYPTO"sv; + inline constexpr auto no_transit = "NOT ALLOWING TRANSIT"sv; + inline constexpr auto bad_pathid = "BAD PATH ID"sv; + inline constexpr auto bad_lifetime = "BAD PATH LIFETIME (TOO LONG)"sv; + + inline const auto NO_TRANSIT = messages::serialize_response({{messages::STATUS_KEY, no_transit}}); + inline const auto BAD_LIFETIME = messages::serialize_response({{messages::STATUS_KEY, bad_lifetime}}); + inline const auto BAD_FRAMES = messages::serialize_response({{messages::STATUS_KEY, bad_frames}}); + inline const auto BAD_PATHID = messages::serialize_response({{messages::STATUS_KEY, bad_pathid}}); + inline const auto BAD_CRYPTO = messages::serialize_response({{messages::STATUS_KEY, bad_crypto}}); + + /** For each hop: + - Generate an Ed keypair for the hop (`shared_key`) + - Generate a symmetric nonce for subsequent DH + - Derive the shared secret (`hop.shared`) for DH key-exchange using the Ed keypair, hop pubkey, and + symmetric nonce + - Encrypt the hop info in-place using `hop.shared` and the generated symmetric nonce from DH + - Generate the XOR nonce by hashing the symmetric key from DH (`hop.shared`) and truncating + + Bt-encoded contents: + - 'k' : shared pubkey used to derive symmetric key + - 'n' : symmetric nonce used for DH key-exchange + - 'x' : encrypted payload + - 'r' : rxID (the path ID for messages going *to* the hop) + - 't' : txID (the path ID for messages coming *from* the client/path origin) + - 'u' : upstream hop RouterID + + All of these 'frames' are inserted sequentially into the list and padded with any needed dummy frames + */ + inline static std::string serialize_hop(path::TransitHop& hop) + { + std::string hop_payload = hop.bt_encode(); + + // client dh key derivation + hop.kx.client_dh(hop.router_id()); + // encrypt payload + hop.kx.encrypt(to_uspan(hop_payload)); + // generate nonceXOR value + hop.kx.generate_xor(); + + log::trace( + logcat, + "Hop serialized; nonce: {}, remote router_id: {}, shared pk: {}, shared secret: {}, payload: {}", + hop.kx.nonce.to_string(), + hop.router_id().to_string(), + hop.kx.pubkey.to_string(), + hop.kx.shared_secret.to_string(), + buffer_printer{hop_payload}); + + return ONION::serialize_hop(hop.kx.pubkey.to_view(), hop.kx.nonce, hop_payload); + } - return std::move(btdp).str(); - } + inline static std::shared_ptr deserialize_hop( + oxenc::bt_dict_consumer&& btdc, Router& r, const RouterID& src) + { + std::string payload; + auto hop = std::make_shared(); + + try + { + hop->kx.pubkey.from_string(btdc.require("k")); + hop->kx.nonce.from_string(btdc.require("n")); + payload = btdc.require("x"); + } + catch (const std::exception& e) + { + log::warning(logcat, "Exception caught deserializing hop dict: {}", e.what()); + throw; + } + + log::trace( + logcat, + "Hop deserialized; nonce: {}, remote pk: {}, payload: {}", + hop->kx.nonce.to_string(), + hop->kx.pubkey.to_string(), + buffer_printer{payload}); + + try + { + hop->kx.server_dh(r.identity()); + hop->kx.decrypt(to_uspan(payload)); + hop->kx.generate_xor(); + + log::trace( + logcat, + "Hop decrypted; nonce: {}, remote pk: {}, payload: {}", + hop->kx.nonce.to_string(), + hop->kx.pubkey.to_string(), + buffer_printer{payload}); + + hop->deserialize(oxenc::bt_dict_consumer{std::move(payload)}, src, r); + } + catch (...) + { + log::info(logcat, "Failed to derive and decrypt outer wrapping!"); + throw std::runtime_error{BAD_CRYPTO}; + } + + log::trace(logcat, "TransitHop data successfully deserialized: {}", hop->to_string()); + return hop; + } + } // namespace BUILD - inline static std::tuple deserialize_hop( - oxenc::bt_dict_consumer&& btdc, const Ed25519SecretKey& local_sk) + namespace CONTROL { - SymmNonce nonce; - PubKey remote_pk; - ustring hop_payload; + /** Fields for transmitting Path Control: + - 'e' : request endpoint being invoked + - 'p' : request payload + */ + inline static std::string serialize(std::string endpoint, std::string payload) + { + oxenc::bt_dict_producer btdp; + btdp.append("e", endpoint); + btdp.append("p", payload); + return std::move(btdp).str(); + } - try + inline static std::tuple deserialize(oxenc::bt_dict_consumer&& btdc) { - nonce.from_string(btdc.require("n")); - remote_pk.from_string(btdc.require("s")); - hop_payload = btdc.require("x"); + std::string endpoint, payload; + + try + { + endpoint = btdc.require("e"); + payload = btdc.require("p"); + } + catch (const std::exception& e) + { + throw std::runtime_error{"Exception caught deserializing path control: {}"_format(e.what())}; + } + + return {std::move(endpoint), std::move(payload)}; } - catch (const std::exception& e) + } // namespace CONTROL + + namespace DATA + { + /** Fields for transmitting Path Data: + - 'i' : RouterID of sender + - 'p' : messages payload + NOTE: more fields may be added later as needed, hence the namespacing + */ + inline static std::string serialize(std::string payload, const RouterID& local) { - log::warning(logcat, "Exception caught deserializing hop dict:{}", e.what()); - throw; + oxenc::bt_dict_producer btdp; + btdp.append("i", local.to_view()); + btdp.append("p", payload); + return std::move(btdp).str(); } - log::trace( - logcat, - "Hop deserialized; nonce: {}, remote pk: {}, payload: {}", - nonce.to_string(), - remote_pk.to_string(), - buffer_printer{hop_payload}); + inline static std::string serialize_intermediate(std::string payload, const HopID& pivot_txid) + { + oxenc::bt_dict_producer btdp; + btdp.append("i", pivot_txid.to_view()); + btdp.append("p", payload); + return std::move(btdp).str(); + } - try + inline static std::string serialize_inner(std::string body, session_tag tag) { - crypto::derive_decrypt_outer_wrapping(local_sk, remote_pk, nonce, to_uspan(hop_payload)); + std::string payload{tag.view()}; + payload.append(body); + return payload; } - catch (...) + + inline static std::tuple deserialize(oxenc::bt_dict_consumer&& btdc) { - log::info(logcat, "Failed to derive and decrypt outer wrapping!"); - throw std::runtime_error{BAD_CRYPTO}; + RouterID remote; + bstring payload; + + try + { + remote.from_string(btdc.require("i")); + payload = btdc.require("p"); + auto sender = NetworkAddress::from_pubkey(remote, true); + + return {std::move(sender), std::move(payload)}; + } + catch (const std::exception& e) + { + throw std::runtime_error{ + "Exception caught deserializing outer datagram payload: {}"_format(e.what())}; + } } - log::trace( - logcat, - "Hop decrypted; nonce: {}, remote pk: {}, payload: {}", - nonce.to_string(), - remote_pk.to_string(), - buffer_printer{hop_payload}); + inline static std::tuple deserialize_intermediate(oxenc::bt_dict_consumer&& btdc) + { + HopID hop_id; + std::string payload; + + try + { + hop_id.from_string(btdc.require("i")); + payload = btdc.require("p"); + + return {std::move(hop_id), std::move(payload)}; + } + catch (const std::exception& e) + { + throw std::runtime_error{ + "Exception caught deserializing intermediate datagram payload: {}"_format(e.what())}; + } + } - return {std::move(nonce), std::move(remote_pk), std::move(hop_payload)}; - } - } // namespace PathBuildMessage + inline static std::tuple> deserialize_inner(std::string&& payload) + { + session_tag t{}; + std::vector body{}; + + try + { + t.read({payload.data(), session_tag::SIZE}); + body.resize(payload.size() - session_tag::SIZE); + std::memmove(body.data(), payload.data() + session_tag::SIZE, payload.size() - session_tag::SIZE); + + return {std::move(t), std::move(body)}; + } + catch (const std::exception& e) + { + throw std::runtime_error{ + "Exception caught deserializing inner datagram payload: {}"_format(e.what())}; + } + } + } // namespace DATA + } // namespace PATH } // namespace llarp diff --git a/llarp/messages/session.hpp b/llarp/messages/session.hpp index 60db08131f..cedcca9409 100644 --- a/llarp/messages/session.hpp +++ b/llarp/messages/session.hpp @@ -1,6 +1,6 @@ #pragma once -#include "common.hpp" +#include "path.hpp" #include #include @@ -9,12 +9,13 @@ namespace llarp { /** Fields for initiating sessions: + - 'k' : shared pubkey used to derive symmetric key - 'n' : symmetric nonce - - 's' : shared pubkey used to derive symmetric key - 'x' : encrypted payload - 'i' : RouterID of initiator - - 'p' : HopID at the pivot node of the newly constructed path - - 's' : SessionTag for current session + - 'p' : HopID at the pivot taken from local ClientIntro + - 'r' : HopID at the pivot taken from remote's ClientIntro + - 's' : session_tag for current session - 't' : Use Tun interface (bool) - 'u' : Authentication field - bt-encoded dict, values TBD @@ -23,15 +24,15 @@ namespace llarp { static auto logcat = llarp::log::Cat("session-init"); - inline constexpr auto auth_denied = "AUTH_DENIED"sv; + inline const auto AUTH_ERROR = messages::serialize_response({{messages::STATUS_KEY, "AUTH ERROR"}}); + inline const auto BAD_PATH = messages::serialize_response({{messages::STATUS_KEY, "BAD PATH"}}); - inline const auto AUTH_DENIED = messages::serialize_response({{messages::STATUS_KEY, auth_denied}}); - - inline static std::string serialize_encrypt( + inline static std::tuple serialize_encrypt( const RouterID& local, const RouterID& remote, - service::SessionTag& tag, - HopID pivot_txid, + HopID local_pivot_txid, + session_tag& tag, + HopID remote_pivot_txid, std::optional auth_token, bool use_tun) { @@ -43,71 +44,82 @@ namespace llarp oxenc::bt_dict_producer btdp; btdp.append("i", local.to_view()); - btdp.append("p", pivot_txid.to_view()); - btdp.append("s", tag.to_view()); + btdp.append("p", local_pivot_txid.to_view()); + btdp.append("r", remote_pivot_txid.to_view()); + btdp.append("s", tag.view()); btdp.append("t", use_tun); - // DISCUSS: this auth field + // TOTHINK: this auth field if (auth_token) btdp.append("u", *auth_token); payload = std::move(btdp).str(); } - Ed25519SecretKey shared_key; - crypto::encryption_keygen(shared_key); - - SharedSecret shared; - auto nonce = SymmNonce::make_random(); + auto kx_data = shared_kx_data::generate(); - crypto::derive_encrypt_outer_wrapping(shared_key, shared, nonce, remote, to_uspan(payload)); + kx_data.client_dh(remote); + kx_data.encrypt(to_uspan(payload)); + kx_data.generate_xor(); - oxenc::bt_dict_producer btdp; + auto new_payload = ONION::serialize_hop(kx_data.pubkey.to_view(), kx_data.nonce, std::move(payload)); - btdp.append("n", nonce.to_view()); - btdp.append("s", shared_key.to_pubkey().to_view()); - btdp.append("x", payload); - - return std::move(btdp).str(); + return {PATH::CONTROL::serialize("session_init", std::move(new_payload)), std::move(kx_data)}; } - catch (...) + catch (const std::exception& e) { - log::error(messages::logcat, "Error: InitiateSessionMessage failed to bt encode contents"); + log::error(messages::logcat, "Exception caught encrypting session initiation message: {}", e.what()); throw; } }; - inline static std::tuple> - decrypt_deserialize(oxenc::bt_dict_consumer& btdc, const Ed25519SecretKey& local) + inline static std:: + tuple> + decrypt_deserialize(oxenc::bt_dict_consumer&& outer_btdc, const Ed25519SecretKey& local) { SymmNonce nonce; - RouterID shared_pubkey; - ustring payload; + PubKey shared_pubkey; + std::string payload; + SharedSecret shared; + shared_kx_data kx_data{}; try { - nonce = SymmNonce::make(btdc.require("n")); - shared_pubkey = RouterID{btdc.require("s")}; - payload = btdc.require("x"); - - crypto::derive_decrypt_outer_wrapping(local, shared_pubkey, nonce, to_uspan(payload)); + std::tie(payload, kx_data) = ONION::deserialize_decrypt(std::move(outer_btdc), local); + } + catch (const std::exception& e) + { + log::warning(logcat, "Exception caught deserializing/decrypting hop dict: {}", e.what()); + throw; + } - { - RouterID remote; - service::SessionTag tag; - HopID pivot_txid; - bool use_tun; - std::optional maybe_auth = std::nullopt; - - remote.from_string(btdc.require("i")); - auto initiator = NetworkAddress::from_pubkey(remote, true); - pivot_txid.from_string(btdc.require("p")); - tag.from_string(btdc.require("s")); - use_tun = btdc.require("t"); - maybe_auth = btdc.maybe("u"); - - return { - std::move(initiator), std::move(pivot_txid), std::move(tag), use_tun, std::move(maybe_auth)}; - } + try + { + oxenc::bt_dict_consumer btdc{payload}; + + NetworkAddress initiator; + RouterID init_rid; + session_tag tag; + HopID remote_pivot_txid; + HopID local_pivot_txid; + bool use_tun; + std::optional maybe_auth = std::nullopt; + + init_rid.from_string(btdc.require("i")); + initiator = NetworkAddress::from_pubkey(init_rid, true); + remote_pivot_txid.from_string(btdc.require("p")); + local_pivot_txid.from_string(btdc.require("r")); + tag.read(btdc.require("s")); + use_tun = btdc.require("t"); + maybe_auth = btdc.maybe("u"); + + return { + std::move(kx_data), + std::move(initiator), + std::move(local_pivot_txid), + std::move(tag), + std::move(remote_pivot_txid), + use_tun, + std::move(maybe_auth)}; } catch (const std::exception& e) { diff --git a/llarp/net/interface_info.cpp b/llarp/net/interface_info.cpp deleted file mode 100644 index 542851deb1..0000000000 --- a/llarp/net/interface_info.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "interface_info.hpp" - -namespace llarp::net -{ - std::string InterfaceInfo::to_string() const - { - return fmt::format("{}[i={}; addrs={}]", name, index, fmt::join(addrs, ",")); - } -} // namespace llarp::net diff --git a/llarp/net/interface_info.hpp b/llarp/net/interface_info.hpp deleted file mode 100644 index b9d5488560..0000000000 --- a/llarp/net/interface_info.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include - -namespace llarp::net -{ - /// info about a network interface lokinet does not own - struct InterfaceInfo - { - private: - public: - // TODO: is this needed? - /// a gateway we can use if it exists - std::optional _gateway; - - /// human readable name of interface - std::string name; - /// interface's index - int index; - /// the addresses owned by this interface - std::vector addrs; - - std::string to_string() const; - }; -} // namespace llarp::net diff --git a/llarp/net/ip.cpp b/llarp/net/ip.cpp deleted file mode 100644 index bd3609cd54..0000000000 --- a/llarp/net/ip.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "ip.hpp" - -#include - -namespace llarp::net -{ - huint128_t In6ToHUInt(in6_addr addr) - { - uint8_t* ptr = reinterpret_cast(addr.s6_addr); - uint128_t x{0}; - for (int i = 0; i < 16; i++) - { - x <<= 8; - x |= ptr[i]; - } - return huint128_t{x}; - } - - in6_addr HUIntToIn6(huint128_t x) - { - in6_addr addr; - auto i = ntoh128(x.h); - std::memcpy(&addr, &i, 16); - return addr; - } - -} // namespace llarp::net diff --git a/llarp/net/ip.hpp b/llarp/net/ip.hpp deleted file mode 100644 index 0638fd51fc..0000000000 --- a/llarp/net/ip.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "net_int.hpp" - -#include - -namespace llarp::net -{ - huint128_t In6ToHUInt(in6_addr addr); - - in6_addr HUIntToIn6(huint128_t x); - - constexpr huint128_t ExpandV4(huint32_t x) - { - return huint128_t{0x0000'ffff'0000'0000UL} | huint128_t{x.h}; - } - - constexpr huint128_t ExpandV4Lan(huint32_t x) - { - return huint128_t{uint128_t{0xfd00'0000'0000'0000UL, 0UL}} | huint128_t{x.h}; - } - - constexpr huint32_t TruncateV6(huint128_t x) - { - huint32_t ret = {0}; - ret.h = (uint32_t)(x.h & 0x0000'0000'ffff'ffffUL); - return ret; - } - -} // namespace llarp::net diff --git a/llarp/net/ip_headers.hpp b/llarp/net/ip_headers.hpp new file mode 100644 index 0000000000..3e77adc906 --- /dev/null +++ b/llarp/net/ip_headers.hpp @@ -0,0 +1,133 @@ +#pragma once + +#include "utils.hpp" + +#include + +namespace llarp +{ + struct ip_header + { +#if __BYTE_ORDER == __LITTLE_ENDIAN + uint8_t header_len : 4; + uint8_t version : 4; +#else + uint8_t version : 4; + uint8_t header_len : 4; +#endif + uint8_t service_type; + uint16_t total_len; // entire packet size + uint16_t id; + uint16_t frag_off; // fragmentation offset + uint8_t ttl; + uint8_t protocol; + uint16_t checksum; + uint32_t src; + uint32_t dest; + }; + + static_assert(sizeof(ip_header) == 20); + + // TODO: WIP + // struct ipv6_header2 + // { + // private: + // std::array preamble; + + // // #if __BYTE_ORDER == __LITTLE_ENDIAN + // // uint32_t flow_label : 20; + // // uint8_t traffic_class; + // // uint8_t version : 4; + // // #else + // // uint8_t version : 4; + // // uint8_t traffic_class : 8; + // // uint32_t flow_label : 20; + // // #endif + // uint16_t pload_len; // payload length + // uint8_t nxt_hdr; // next header (protocol) + // uint8_t hop_limit; + // in6_addr src; + // in6_addr dest; + + // public: + + // }; + + // static_assert(sizeof(ipv6_header2) == 40); + + struct ipv6_header + { + union + { +#if __BYTE_ORDER == __LITTLE_ENDIAN + unsigned char pad_small : 4; + unsigned char version : 4; +#else + unsigned char version : 4; + unsigned char pad_small : 4; +#endif + uint8_t pad[3]; + uint32_t flowlabel; + } preamble; + + uint16_t payload_len; + uint8_t protocol; + uint8_t hoplimit; + in6_addr src; + in6_addr dest; + + /// Returns the flowlabel (stored in network order) in HOST ORDER + uint32_t get_flowlabel() const { return oxenc::big_to_host(preamble.flowlabel & htonl(ipv6_flowlabel_mask)); } + + /// Sets a flowlabel in network order. Takes in a label in HOST ORDER + void set_flowlabel(uint32_t label) + { + // the ipv6 flow label is the last 20 bits in the first 32 bits of the header + preamble.flowlabel = + (htonl(ipv6_flowlabel_mask) & htonl(label)) | (preamble.flowlabel & htonl(~ipv6_flowlabel_mask)); + } + }; + + static_assert(sizeof(ipv6_header) == 40); + + /** TODO: for mobile ipv6, implement ipv6 routing headers + */ + + enum class TCPFLAG : uint8_t + { + FIN = 0x01, + SYN = 0x02, + RST = 0x04, + PUSH = 0x08, + ACK = 0x10, + URG = 0x20 + }; + + struct tcp_header + { + uint16_t src; // src addr or port + uint16_t dest; // dst addr or port + uint32_t seqno; // sequence number + uint32_t ack; // ack number +#if __BYTE_ORDER == __LITTLE_ENDIAN + uint8_t xx : 4; // unused/reserved + uint8_t data_off : 4; // data offset +#else + uint8_t data_off : 4; // data offset + uint8_t xx : 4; // unused/reserved +#endif + uint8_t flags; + uint16_t window; + uint16_t checksum; + uint16_t urg_ptr; // urgent ptr + }; + + struct udp_header + { + uint16_t src; + uint16_t dest; + uint16_t len; // datagram length + uint16_t checksum; + }; + +} // namespace llarp diff --git a/llarp/address/ip_packet.cpp b/llarp/net/ip_packet.cpp similarity index 52% rename from llarp/address/ip_packet.cpp rename to llarp/net/ip_packet.cpp index 3d1ebbccb6..e323190ad8 100644 --- a/llarp/address/ip_packet.cpp +++ b/llarp/net/ip_packet.cpp @@ -1,8 +1,9 @@ #include "ip_packet.hpp" -#include "utils.hpp" +#include "net.hpp" #include +#include #include #include @@ -14,6 +15,16 @@ namespace llarp { static auto logcat = log::Cat("ip_packet"); + // constexpr auto IP_CSUM_OFF = offsetof(struct ip_header, checksum); + // constexpr auto IP_DST_OFF = offsetof(struct ip_header, dest); + // constexpr auto IP_SRC_OFF = offsetof(struct ip_header, src); + // constexpr auto IP_PROTO_OFF = offsetof(struct ip_header, protocol); + // constexpr auto TCP_DATA_OFF = oxenc::host_to_big(0xF0000000); + constexpr auto TCP_CSUM_OFF = offsetof(struct tcp_header, checksum); + constexpr auto UDP_CSUM_OFF = offsetof(struct udp_header, checksum); + // auto TCP_DATA_OFFSET = htonl(0xF0000000); + // constexpr auto IS_PSEUDO = 0x10; + IPPacket::IPPacket(size_t sz) { if (sz and sz < MIN_PACKET_SIZE) @@ -25,20 +36,14 @@ namespace llarp IPPacket::IPPacket(bstring_view data) : IPPacket{reinterpret_cast(data.data()), data.size()} {} - IPPacket::IPPacket(ustring_view data) : IPPacket{data.data(), data.size()} {} - - IPPacket::IPPacket(std::vector data) : IPPacket{data.data(), data.size()} {} + IPPacket::IPPacket(std::vector&& data) : IPPacket{data.data(), data.size()} {} IPPacket::IPPacket(const uint8_t* buf, size_t len) { - if (len < MIN_PACKET_SIZE) - { - _buf.resize(0); - } - else + if (len >= MIN_PACKET_SIZE) { _buf.resize(len); - std::copy_n(buf, len, _buf.data()); + std::memcpy(_buf.data(), buf, len); } _init_internals(); @@ -60,36 +65,47 @@ namespace llarp return ret; } + static const auto v4_header_version = oxenc::host_to_big(4); + void IPPacket::_init_internals() { _header = reinterpret_cast(data()); _v6_header = reinterpret_cast(data()); - _is_v4 = _header->protocol == uint8_t{4}; - _is_udp = _header->protocol == uint8_t{17}; + _proto = net::IPProtocol{_header->protocol}; + + if (_buf.empty()) + return; + + _is_v4 = _header->version == v4_header_version; + auto keep_port = _proto == net::IPProtocol::UDP || _proto == net::IPProtocol::TCP; uint16_t src_port = - (_is_udp) ? *reinterpret_cast(data() + (static_cast(_header->header_len) * 4)) : 0; - uint16_t dest_port = (_is_udp) + (keep_port) ? *reinterpret_cast(data() + (static_cast(_header->header_len) * 4)) : 0; + uint16_t dest_port = (keep_port) ? *reinterpret_cast(data() + (static_cast(_header->header_len) * 4) + 2) : 0; if (_is_v4) { - auto src = in_addr{_header->src}; - auto dest = in_addr{_header->dest}; + auto srcv4 = ipv4{oxenc::big_to_host(_header->src)}; + auto dstv4 = ipv4{oxenc::big_to_host(_header->dest)}; + + log::trace(logcat, "srcv4={}:{}, dstv4={}:{}", srcv4, src_port, dstv4, dest_port); - _src_addr.set_addr(&src); - _dst_addr.set_addr(&dest); + _src_addr = oxen::quic::Address{srcv4, src_port}; + _dst_addr = oxen::quic::Address{dstv4, dest_port}; } else { - _src_addr.set_addr(&_v6_header->srcaddr); - _dst_addr.set_addr(&_v6_header->dstaddr); - } + auto srcv6 = ipv6{&_v6_header->src}; + auto dstv6 = ipv6{&_v6_header->dest}; + + log::trace(logcat, "srcv6={}:{}, dstv6={}:{}", srcv6, src_port, dstv6, dest_port); - _src_addr.set_port(src_port); - _dst_addr.set_port(dest_port); + _src_addr = oxen::quic::Address{srcv6, src_port}; + _dst_addr = oxen::quic::Address{dstv6, dest_port}; + } } std::optional> IPPacket::l4_data() const @@ -97,9 +113,7 @@ namespace llarp size_t hdr_sz = 0; if (_header->protocol == 0x11) - { hdr_sz = 8; - } else return std::nullopt; @@ -114,32 +128,55 @@ namespace llarp void IPPacket::update_ipv4_address(ipv4 src, ipv4 dst) { - log::debug(logcat, "Setting new source ({}) and destination ({}) IPs", src, dst); - - std::basic_string_view head_u16s{reinterpret_cast(_header), sizeof(ip_header)}; - // set new IP addresses - _header->src = src.addr; - _header->dest = dst.addr; + log::trace(logcat, "Setting new source ({}) and destination ({}) IPs", src, dst); - switch (_header->protocol) + if (auto ihs = size_t(_header->header_len * 4); ihs <= size()) { - case 6: // TCP - case 17: // UDP - case 136: // UDP-Lite - same checksum place, same 0->0xFFff condition - case 33: // DCCP - _header->checksum = tcpudp_checksum_ipv4( - _header->src, _header->dest, _header->header_len, _header->protocol, _header->checksum); - break; - default: - // do nothing - break; + auto* payload = data() + ihs; + auto payload_size = size() - ihs; + auto frag_off = size_t(oxenc::big_to_host(_header->frag_off) & 0x1Fff) * 8; + + switch (_header->protocol) + { + case 6: // TCP + if (frag_off <= TCP_CSUM_OFF || payload_size >= TCP_CSUM_OFF - frag_off + 2) + { + auto* tcp_hdr = reinterpret_cast(payload); + tcp_hdr->checksum = + utils::ipv4_tcp_checksum_diff(tcp_hdr->checksum, _header->src, _header->dest, src, dst); + } + break; + case 17: // UDP + case 136: // UDP-Lite - same checksum place, same 0->0xFFff condition + if (frag_off <= UDP_CSUM_OFF || payload_size >= UDP_CSUM_OFF + 2) + { + auto* udp_hdr = reinterpret_cast(payload); + udp_hdr->checksum = + utils::ipv4_udp_checksum_diff(udp_hdr->checksum, _header->src, _header->dest, src, dst); + } + break; + case 33: // DCCP + if (frag_off <= UDP_CSUM_OFF || payload_size >= UDP_CSUM_OFF - frag_off + 2) + { + auto* tcp_hdr = reinterpret_cast(payload); + tcp_hdr->checksum = + utils::ipv4_tcp_checksum_diff(tcp_hdr->checksum, _header->src, _header->dest, src, dst); + } + break; + default: + // do nothing + break; + } } - // IPv4 checksum - auto v4chk = (uint16_t*)&(_header->checksum); - *v4chk = checksum_ipv4(_header, _header->header_len); + _header->checksum = utils::ipv4_checksum_diff(_header->checksum, _header->src, _header->dest, src, dst); - _init_internals(); + // set new IP addresses + _header->src = oxenc::host_to_big(src.addr); + _header->dest = oxenc::host_to_big(dst.addr); + + _src_addr.set_addr(reinterpret_cast(&_header->src)); + _dst_addr.set_addr(reinterpret_cast(&_header->dest)); } void IPPacket::update_ipv6_address(ipv6 src, ipv6 dst, std::optional flowlabel) @@ -158,8 +195,8 @@ namespace llarp } // IPv6 address - hdr->srcaddr = src.to_in6(); - hdr->dstaddr = dst.to_in6(); + hdr->src = src.to_in6(); + hdr->dest = dst.to_in6(); // TODO IPv6 header options auto* pld = data() + ihs; @@ -213,59 +250,60 @@ namespace llarp uint16_t chksumoff{0}; uint16_t chksum{0}; + bool is_udp = false; + switch (nextproto) { case 6: // TCP chksumoff = 16; [[fallthrough]]; case 33: // DCCP - chksum = tcp_checksum_ipv6(&hdr->srcaddr, &hdr->dstaddr, hdr->payload_len, 0); - + chksum = tcp_checksum_ipv6(&hdr->src, &hdr->dest, hdr->payload_len, 0); // ones-complement addition fo 0xFFff is 0; this is verboten if (chksum == 0xFFff) chksum = 0x0000; chksumoff = chksumoff == 16 ? 16 : 6; - _is_udp = false; break; case 17: // UDP case 136: // UDP-Lite - same checksum place, same 0->0xFFff condition - chksum = udp_checksum_ipv6(&hdr->srcaddr, &hdr->dstaddr, hdr->payload_len, 0); - _is_udp = true; + chksum = udp_checksum_ipv6(&hdr->src, &hdr->dest, hdr->payload_len, 0); + is_udp = true; break; default: // do nothing break; } - auto check = _is_udp ? (uint16_t*)(pld + 6) : (uint16_t*)(pld + chksumoff - fragoff); + auto check = is_udp ? (uint16_t*)(pld + 6) : (uint16_t*)(pld + chksumoff - fragoff); *check = chksum; _init_internals(); } + // TODO: make a compile-time ICMP template with configurable fields std::optional IPPacket::make_icmp_unreachable() const { if (is_ipv4()) { auto ip_hdr_sz = _header->header_len * 4; - auto pkt_size = (ICMP_HEADER_SIZE + ip_hdr_sz) * 2; - - IPPacket pkt{static_cast(pkt_size)}; - - _header->version = 4; - _header->header_len = 0x05; - _header->service_type = 0; - _header->checksum = 0; - _header->total_len = ntohs(pkt_size); - _header->src = _header->dest; - _header->dest = _header->src; - _header->protocol = 1; // ICMP - _header->ttl = _header->ttl; - _header->frag_off = htons(0b0100000000000000); - - uint16_t* checksum; + size_t pkt_size = (ICMP_HEADER_SIZE + ip_hdr_sz) * 2; + + IPPacket pkt{pkt_size}; + auto* pkt_header = pkt.header(); + + pkt_header->version = 4; + pkt_header->header_len = 0x05; + pkt_header->service_type = 0; + pkt_header->checksum = 0; + pkt_header->total_len = ntohs(pkt_size); + pkt_header->src = _header->dest; + pkt_header->dest = _header->src; + pkt_header->protocol = 1; // ICMP + pkt_header->ttl = pkt_header->ttl; + pkt_header->frag_off = oxenc::host_to_big(0b0100000000000000); + uint8_t* itr = pkt.data() + ip_hdr_sz; uint8_t* icmp_begin = itr; // type 'destination unreachable' *itr++ = 3; @@ -275,46 +313,36 @@ namespace llarp // checksum + unused oxenc::write_host_as_big(0, itr); - checksum = (uint16_t*)itr; + auto* checksum = (uint16_t*)itr; itr += 4; // next hop mtu is ignored but let's put something here anyways just in case tm oxenc::write_host_as_big(1500, itr); itr += 2; - // copy ip header and first 8 bytes of datagram for icmp rject - std::copy_n(data(), ip_hdr_sz + ICMP_HEADER_SIZE, itr); + // copy ip header and first 8 bytes of datagram for icmp reject + std::memcpy(itr, _buf.data(), ip_hdr_sz + ICMP_HEADER_SIZE); itr += ip_hdr_sz + ICMP_HEADER_SIZE; // calculate checksum of ip header - _header->checksum = checksum_ipv4(_header, _header->header_len); - const auto icmp_size = std::distance(icmp_begin, itr); + pkt_header->checksum = utils::ip_checksum(_buf.data(), ip_hdr_sz); // calculate icmp checksum - *checksum = checksum_ipv4(icmp_begin, icmp_size); + *checksum = utils::ip_checksum(icmp_begin, std::distance(icmp_begin, itr)); + + log::debug(logcat, "ICMP unreachable pkt configured"); return pkt; } - return std::nullopt; - } - NetworkPacket IPPacket::make_netpkt() - { - return NetworkPacket{oxen::quic::Path{_src_addr, _dst_addr}, bview()}; - } - - bool IPPacket::load(ustring_view data) - { - return load(data.data(), data.size()); - } - - bool IPPacket::load(std::string_view data) - { - return load(reinterpret_cast(data.data()), data.size()); + return std::nullopt; } - bool IPPacket::load(std::vector data) + NetworkPacket IPPacket::make_netpkt() && { - return load(data.data(), data.size()); + bstring data{}; + data.reserve(_buf.size()); + std::memmove(data.data(), _buf.data(), _buf.size()); + return NetworkPacket{oxen::quic::Path{_src_addr, _dst_addr}, std::move(data)}; } bool IPPacket::load(const uint8_t* buf, size_t len) @@ -324,7 +352,7 @@ namespace llarp _buf.clear(); _buf.resize(len); - std::copy_n(buf, len, _buf.data()); + std::memcpy(_buf.data(), buf, len); _init_internals(); @@ -346,13 +374,7 @@ namespace llarp return true; } - std::vector IPPacket::steal_buffer() && - { - std::vector b; - b.resize(size()); - _buf.swap(b); - return b; - } + std::vector IPPacket::steal_buffer() && { return std::move(_buf); } std::string IPPacket::steal_payload() && { @@ -361,22 +383,14 @@ namespace llarp return ret; } - std::vector IPPacket::give_buffer() - { - std::vector b; - b.resize(size()); - std::memcpy(b.data(), data(), size()); - return b; - } + std::vector IPPacket::give_buffer() { return {_buf}; } - std::string IPPacket::to_string() - { - return {reinterpret_cast(data()), size()}; - } + std::string IPPacket::to_string() const { return {reinterpret_cast(data()), size()}; } std::string IPPacket::info_line() const { - return "IPPacket (src:{}, dest:{}, size:{})"_format(_src_addr, _dst_addr, size()); + return "IPPacket:[type={} | src={} | dest={} | size={}]"_format( + ip_protocol_name(_proto), _src_addr, _dst_addr, size()); } } // namespace llarp diff --git a/llarp/address/ip_packet.hpp b/llarp/net/ip_packet.hpp similarity index 87% rename from llarp/address/ip_packet.hpp rename to llarp/net/ip_packet.hpp index 3c49ef8819..da146d2472 100644 --- a/llarp/address/ip_packet.hpp +++ b/llarp/net/ip_packet.hpp @@ -1,6 +1,7 @@ #pragma once -#include "types.hpp" +#include "ip_headers.hpp" +#include "policy.hpp" #include #include @@ -32,7 +33,7 @@ namespace llarp struct IPPacket { private: - std::vector _buf; + std::vector _buf{}; ip_header* _header{}; ipv6_header* _v6_header{}; @@ -40,38 +41,45 @@ namespace llarp oxen::quic::Address _src_addr{}; oxen::quic::Address _dst_addr{}; - bool _is_v4{false}; - bool _is_udp{false}; + bool _is_v4{true}; + + net::IPProtocol _proto{}; void _init_internals(); public: + // TESTNET: TODO: after merging libquic retyping for libc++19, revise these constructors + IPPacket() : IPPacket{size_t{0}} {} explicit IPPacket(size_t sz); explicit IPPacket(bstring_view data); - explicit IPPacket(ustring_view data); - explicit IPPacket(std::vector data); + explicit IPPacket(std::vector&& data); explicit IPPacket(const uint8_t* buf, size_t len); static IPPacket from_netpkt(NetworkPacket pkt); static std::optional from_buffer(const uint8_t* buf, size_t len); - NetworkPacket make_netpkt(); + NetworkPacket make_netpkt() &&; + + // TESTNET: debug methods + // uint16_t checksum() const { return _is_v4 ? header()->checksum : 0; } bool is_ipv4() const { return _is_v4; } + net::IPProtocol protocol() const { return _proto; } + const oxen::quic::Address& source() const { return _src_addr; } uint16_t source_port() { return source().port(); } - const oxen::quic::Address& destination() const { return _dst_addr; } - - uint16_t dest_port() { return destination().port(); } - ipv4 source_ipv4() { return _src_addr.to_ipv4(); } ipv6 source_ipv6() { return _src_addr.to_ipv6(); } + const oxen::quic::Address& destination() const { return _dst_addr; } + + uint16_t dest_port() { return destination().port(); } + ipv4 dest_ipv4() const { return _dst_addr.to_ipv4(); } ipv6 dest_ipv6() const { return _dst_addr.to_ipv6(); } @@ -103,18 +111,10 @@ namespace llarp const uint8_t* data() const { return _buf.data(); } - const uint8_t* protocol() const { return _is_v4 ? &header()->protocol : &v6_header()->protocol; } - size_t size() const { return _buf.size(); } bool empty() const { return _buf.empty(); } - bool load(ustring_view data); - - bool load(std::string_view data); - - bool load(std::vector data); - bool load(const uint8_t* buf, size_t len); // takes posession of the data @@ -134,7 +134,7 @@ namespace llarp ustring_view uview() const { return {data(), size()}; } - std::string to_string(); + std::string to_string() const; std::string info_line() const; }; diff --git a/llarp/net/net.hpp b/llarp/net/net.hpp index 763d0c4ade..64c886a972 100644 --- a/llarp/net/net.hpp +++ b/llarp/net/net.hpp @@ -1,9 +1,6 @@ #pragma once -#include "interface_info.hpp" #include "net.h" -#include "net_int.hpp" -#include "uint128.hpp" #include #include @@ -32,18 +29,32 @@ namespace llarp { - inline int cmp(const in_addr& a, const in_addr& b) - { - return memcmp(&a, &b, sizeof(in_addr)); - } + inline int cmp(const in_addr& a, const in_addr& b) { return memcmp(&a, &b, sizeof(in_addr)); } - inline int cmp(const in6_addr& a, const in6_addr& b) - { - return memcmp(&a, &b, sizeof(in6_addr)); - } + inline int cmp(const in6_addr& a, const in6_addr& b) { return memcmp(&a, &b, sizeof(in6_addr)); } namespace net { + /// info about a network interface lokinet does not own + struct InterfaceInfo + { + // TODO: is this needed? + /// a gateway we can use if it exists + std::optional _gateway; + + /// human readable name of interface + std::string name; + /// interface's index + int index; + /// the addresses owned by this interface + std::vector addrs; + + std::string to_string() const + { + return "{} [ idx={}, addrs={}]"_format(name, index, fmt::join(addrs, ",")); + } + }; + struct if_info { explicit if_info(int _af = AF_INET) : af{_af} {} @@ -52,9 +63,9 @@ namespace llarp std::optional if_name = std::nullopt; std::optional if_addr = std::nullopt; std::optional if_netmask = std::nullopt; - std::optional if_index = std::nullopt; + std::optional if_index = std::nullopt; - operator bool() const { return if_name and if_addr /* and if_netmask */ and if_index; } + operator bool() const { return if_name and if_addr; } }; /// network platform (all methods virtual so it can be mocked by unit tests) @@ -127,15 +138,9 @@ namespace llarp } // namespace llarp -inline bool operator==(const in_addr& a, const in_addr& b) -{ - return llarp::cmp(a, b) == 0; -} +inline bool operator==(const in_addr& a, const in_addr& b) { return llarp::cmp(a, b) == 0; } -inline bool operator==(const in6_addr& a, const in6_addr& b) -{ - return llarp::cmp(a, b) == 0; -} +inline bool operator==(const in6_addr& a, const in6_addr& b) { return llarp::cmp(a, b) == 0; } inline bool operator==(const sockaddr_in& a, const sockaddr_in& b) { @@ -162,15 +167,9 @@ inline bool operator==(const sockaddr& a, const sockaddr& b) } } -inline bool operator<(const in_addr& a, const in_addr& b) -{ - return llarp::cmp(a, b) < 0; -} +inline bool operator<(const in_addr& a, const in_addr& b) { return llarp::cmp(a, b) < 0; } -inline bool operator<(const in6_addr& a, const in6_addr& b) -{ - return llarp::cmp(a, b) < 0; -} +inline bool operator<(const in6_addr& a, const in6_addr& b) { return llarp::cmp(a, b) < 0; } inline bool operator<(const sockaddr_in6& a, const sockaddr_in6& b) { diff --git a/llarp/net/net_int.cpp b/llarp/net/net_int.cpp deleted file mode 100644 index 07b37ef6ea..0000000000 --- a/llarp/net/net_int.cpp +++ /dev/null @@ -1,150 +0,0 @@ -#include "net_int.hpp" - -#include "ip.hpp" - -#include - -#include -#include - -namespace llarp -{ - namespace net - { - huint16_t ToHost(port_t x) - { - return huint16_t{oxenc::big_to_host(x.n)}; - } - - huint32_t ToHost(ipv4addr_t x) - { - return huint32_t{oxenc::big_to_host(x.n)}; - } - - huint128_t ToHost(ipv6addr_t x) - { - return {ntoh128(x.n)}; - } - - port_t ToNet(huint16_t x) - { - return port_t{oxenc::host_to_big(x.h)}; - } - - ipv4addr_t ToNet(huint32_t x) - { - return ipv4addr_t{oxenc::host_to_big(x.h)}; - } - - ipv6addr_t ToNet(huint128_t x) - { - return ipv6addr_t{hton128(x.h)}; - } - } // namespace net - - template <> - void huint32_t::ToV6(V6Container& c) - { - c.resize(16); - std::fill(c.begin(), c.end(), 0); - oxenc::write_host_as_big(h, c.data() + 12); - c[11] = 0xff; - c[10] = 0xff; - } - - template <> - void huint128_t::ToV6(V6Container& c) - { - c.resize(16); - const in6_addr addr = net::HUIntToIn6(*this); - std::copy_n(addr.s6_addr, 16, c.begin()); - } - - template <> - std::string huint32_t::to_string() const - { - uint32_t n = htonl(h); - char tmp[INET_ADDRSTRLEN] = {0}; - if (!inet_ntop(AF_INET, (void*)&n, tmp, sizeof(tmp))) - return ""; - return tmp; - } - - template <> - std::string huint128_t::to_string() const - { - auto addr = ntoh128(h); - char tmp[INET6_ADDRSTRLEN] = {0}; - if (!inet_ntop(AF_INET6, (void*)&addr, tmp, sizeof(tmp))) - return ""; - return tmp; - } - - template <> - bool huint16_t::FromString(const std::string& str) - { - if (auto val = std::atoi(str.c_str()); val >= 0) - { - h = val; - return true; - } - else - return false; - } - - template <> - bool huint32_t::FromString(const std::string& str) - { - uint32_t n; - if (!inet_pton(AF_INET, str.c_str(), &n)) - return false; - h = ntohl(n); - return true; - } - - template <> - bool huint128_t::FromString(const std::string& str) - { - llarp::uint128_t i; - if (!inet_pton(AF_INET6, str.c_str(), &i)) - return false; - h = ntoh128(i); - return true; - } - - template <> - std::string nuint32_t::to_string() const - { - char tmp[INET_ADDRSTRLEN] = {0}; - if (!inet_ntop(AF_INET, (void*)&n, tmp, sizeof(tmp))) - return ""; - return tmp; - } - - template <> - std::string nuint128_t::to_string() const - { - char tmp[INET6_ADDRSTRLEN] = {0}; - if (!inet_ntop(AF_INET6, (void*)&n, tmp, sizeof(tmp))) - return ""; - return tmp; - } - - template <> - std::string huint16_t::to_string() const - { - return std::to_string(h); - } - - template <> - std::string nuint16_t::to_string() const - { - return std::to_string(ntohs(n)); - } - - std::string net::to_string(const ipaddr_t& ipaddr) - { - return std::visit([](const auto& ip) { return ip.to_string(); }, ipaddr); - } - -} // namespace llarp diff --git a/llarp/net/net_int.hpp b/llarp/net/net_int.hpp deleted file mode 100644 index 8170b890a0..0000000000 --- a/llarp/net/net_int.hpp +++ /dev/null @@ -1,207 +0,0 @@ -#pragma once - -// for addrinfo -#ifndef _WIN32 -#include -#include -#include -#else -#include - -#include - -#define inet_aton(x, y) inet_pton(AF_INET, x, y) -#endif - -#include "net.h" -#include "uint128.hpp" - -#include - -#include - -#include // for itoa -#include -#include - -namespace llarp -{ - template - struct huint_t - { - UInt_t h; - - constexpr huint_t operator&(huint_t x) const { return huint_t{UInt_t{h & x.h}}; } - - constexpr huint_t operator|(huint_t x) const { return huint_t{UInt_t{h | x.h}}; } - - constexpr huint_t operator-(huint_t x) const { return huint_t{UInt_t{h - x.h}}; } - - constexpr huint_t operator+(huint_t x) const { return huint_t{UInt_t{h + x.h}}; } - - constexpr huint_t operator^(huint_t x) const { return huint_t{UInt_t{h ^ x.h}}; } - - constexpr huint_t operator~() const { return huint_t{UInt_t{~h}}; } - - constexpr huint_t operator<<(int n) const - { - UInt_t v{h}; - v <<= n; - return huint_t{v}; - } - - inline huint_t operator++() - { - ++h; - return *this; - } - - inline huint_t operator--() - { - --h; - return *this; - } - - constexpr bool operator<(huint_t x) const { return h < x.h; } - - constexpr bool operator!=(huint_t x) const { return h != x.h; } - - constexpr bool operator==(huint_t x) const { return h == x.h; } - - using V6Container = std::vector; - [[deprecated]] void ToV6(V6Container& c); - - std::string to_string() const; - - bool FromString(const std::string&); - }; - - using huint32_t = huint_t; - using huint16_t = huint_t; - using huint128_t = huint_t; - - template - struct nuint_t - { - UInt_t n = 0; - - constexpr nuint_t operator&(nuint_t x) const { return nuint_t{UInt_t(n & x.n)}; } - - constexpr nuint_t operator|(nuint_t x) const { return nuint_t{UInt_t(n | x.n)}; } - - constexpr nuint_t operator^(nuint_t x) const { return nuint_t{UInt_t(n ^ x.n)}; } - - constexpr nuint_t operator~() const { return nuint_t{UInt_t(~n)}; } - - inline nuint_t operator++() - { - ++n; - return *this; - } - inline nuint_t operator--() - { - --n; - return *this; - } - - constexpr bool operator<(nuint_t x) const { return n < x.n; } - - constexpr bool operator!=(nuint_t x) const { return n != x.n; } - - constexpr bool operator==(nuint_t x) const { return n == x.n; } - - using V6Container = std::vector; - [[deprecated]] void ToV6(V6Container& c); - - std::string to_string() const; - - bool FromString(const std::string& data) - { - huint_t x; - if (not x.FromString(data)) - return false; - *this = ToNet(x); - return true; - } - - inline static nuint_t from_string(const std::string& str) - { - nuint_t x{}; - if (not x.FromString(str)) - throw std::invalid_argument{fmt::format("{} is not a valid value", str)}; - return x; - } - - template - inline static nuint_t from_host(Args_t&&... args) - { - return ToNet(huint_t{std::forward(args)...}); - } - }; - - namespace net - { - /// hides the nuint types used with net_port_t / net_ipv4addr_t / net_ipv6addr_t - namespace - { - using n_uint16_t = llarp::nuint_t; - using n_uint32_t = llarp::nuint_t; - using n_uint128_t = llarp::nuint_t; - } // namespace - - using port_t = n_uint16_t; - using ipv4addr_t = n_uint32_t; - using flowlabel_t = n_uint32_t; - using ipv6addr_t = n_uint128_t; - using ipaddr_t = std::variant; - - std::string to_string(const ipaddr_t& ip); - - huint16_t ToHost(port_t); - huint32_t ToHost(ipv4addr_t); - huint128_t ToHost(ipv6addr_t); - - port_t ToNet(huint16_t); - ipv4addr_t ToNet(huint32_t); - ipv6addr_t ToNet(huint128_t); - - } // namespace net - - using nuint16_t /* [[deprecated("use llarp::net::port_t instead")]] */ = llarp::net::port_t; - using nuint32_t /* [[deprecated("use llarp::net::ipv4addr_t instead")]] */ = llarp::net::ipv4addr_t; - using nuint128_t /* [[deprecated("use llarp::net::ipv6addr_t instead")]] */ = llarp::net::ipv6addr_t; - - template - /* [[deprecated("use llarp::net::ToNet instead")]] */ inline llarp::nuint_t ToNet( - llarp::huint_t x) - { - return llarp::net::ToNet(x); - } - - template - /* [[deprecated("use llarp::net::ToHost instead")]] */ inline llarp::huint_t ToHost( - llarp::nuint_t x) - { - return llarp::net::ToHost(x); - } - - /* [[deprecated("use llarp::net::ToHost instead")]] */ inline net::ipv4addr_t xhtonl(huint32_t x) - { - return ToNet(x); - } -} // namespace llarp - -namespace std -{ - template - struct hash> - { - size_t operator()(const llarp::nuint_t& x) const { return std::hash{}(x.n); } - }; - - template - struct hash> - { - size_t operator()(const llarp::huint_t& x) const { return std::hash{}(x.h); } - }; -} // namespace std diff --git a/llarp/net/traffic_policy.cpp b/llarp/net/policy.cpp similarity index 54% rename from llarp/net/traffic_policy.cpp rename to llarp/net/policy.cpp index 4ef4405986..59514a92c6 100644 --- a/llarp/net/traffic_policy.cpp +++ b/llarp/net/policy.cpp @@ -1,4 +1,6 @@ -#include "traffic_policy.hpp" +#include "policy.hpp" + +#include "ip_packet.hpp" #include #include @@ -8,67 +10,66 @@ namespace llarp::net static auto logcat = log::Cat("TrafficPolicy"); // Two functions copied over from llarp/net/ip_packet_old.hpp - std::string IPProtocolName(IPProtocol proto) + static std::string ip_proto_str(IPProtocol proto) { - if (const auto* ent = ::getprotobynumber(static_cast(proto))) + if (const auto* ent = ::getprotobynumber(meta::to_underlying(proto))) { return ent->p_name; } - throw std::invalid_argument{ - "cannot determine protocol name for ip proto '" + std::to_string(static_cast(proto)) + "'"}; + throw std::invalid_argument{"Cannot determine protocol name for IP Protocol: {}"_format(proto)}; } - IPProtocol ParseIPProtocol(std::string data) + static IPProtocol parse_ip_proto(std::string data) { if (const auto* ent = ::getprotobyname(data.c_str())) { return static_cast(ent->p_proto); } - if (starts_with(data, "0x")) + if (data.starts_with("0x")) { if (const int intVal = std::stoi(data.substr(2), nullptr, 16); intVal > 0) return static_cast(intVal); } - throw std::invalid_argument{"no such ip protocol: '" + data + "'"}; + throw std::invalid_argument{"Call to ::getprotobyname failed for input: {}"_format(data)}; } - ProtocolInfo::ProtocolInfo(std::string_view data) + // ProtocolInfo::ProtocolInfo(std::string_view data) + // { + // const auto parts = split(data, "/"); + // proto = parse_ip_proto(std::string{parts[0]}); + // if (parts.size() == 2) + // { + // uint16_t port_host{}; + + // std::string portStr{parts[1]}; + // std::string protoName = ip_proto_str(proto); + + // if (const auto* serv = ::getservbyname(portStr.c_str(), protoName.c_str())) + // { + // port_host = serv->s_port; + // } + // else if (const auto port_int = std::stoi(portStr); port_int > 0) + // { + // port_host = port_int; + // } + // else + // throw std::invalid_argument{"Invalid port in protocol info: {}"_format(portStr)}; + + // port = port_host; + // } + // else + // port = std::nullopt; + // } + + bool ProtocolInfo::matches_packet_proto(const IPPacket& pkt) const { return pkt.protocol() == proto; } + + bool ExitPolicy::allow_ip_traffic(const IPPacket& pkt) const { - const auto parts = split(data, "/"); - protocol = ParseIPProtocol(std::string{parts[0]}); - if (parts.size() == 2) - { - uint16_t port_host{}; - - std::string portStr{parts[1]}; - std::string protoName = IPProtocolName(protocol); + log::trace(logcat, "{} called", __PRETTY_FUNCTION__); - if (const auto* serv = ::getservbyname(portStr.c_str(), protoName.c_str())) - { - port_host = serv->s_port; - } - else if (const auto port_int = std::stoi(portStr); port_int > 0) - { - port_host = port_int; - } - else - throw std::invalid_argument{"invalid port in protocol info: " + portStr}; - port = port_host; - } - else - port = std::nullopt; - } - - bool ProtocolInfo::matches_packet_proto(const IPPacket& pkt) const - { - return pkt.header()->protocol == static_cast>(protocol); - } - - bool TrafficPolicy::allow_ip_traffic(const IPPacket& pkt) - { if (protocols.empty() and ranges.empty()) return true; @@ -78,22 +79,18 @@ namespace llarp::net return true; } - ipv4 v4 = pkt.dest_ipv4(); - ipv6 v6 = pkt.dest_ipv6(); auto is_ipv4 = pkt.is_ipv4(); + ip_v pkt_ip; + + if (is_ipv4) + pkt_ip = pkt.dest_ipv4(); + else + pkt_ip = pkt.dest_ipv6(); for (const auto& range : ranges) { - if (is_ipv4) - { - if (range.contains(v4)) - return true; - } - else - { - if (range.contains(v6)) - return true; - } + if (range.contains(pkt_ip)) + return true; } return false; @@ -103,7 +100,7 @@ namespace llarp::net { try { - protocol = IPProtocol{btlc.consume_integer()}; + proto = IPProtocol{btlc.consume_integer()}; if (not btlc.is_finished()) port = btlc.consume_integer(); @@ -119,8 +116,6 @@ namespace llarp::net { port = std::nullopt; - std::vector vals; - try { oxenc::bt_list_consumer btlc{buf}; @@ -142,8 +137,10 @@ namespace llarp::net { try { - btlp.append(static_cast(protocol)); - btlp.append(port.value_or(0)); + btlp.append(meta::to_underlying(proto)); + + if (port.has_value()) + btlp.append(*port); } catch (...) { @@ -151,13 +148,15 @@ namespace llarp::net } } - ProtocolInfo::ProtocolInfo(std::string buf) + ProtocolInfo::ProtocolInfo(std::string_view buf) { try { - oxenc::bt_list_consumer btlc{std::move(buf)}; - protocol = static_cast(btlc.consume_integer()); - port = btlc.consume_integer(); + oxenc::bt_list_consumer btlc{buf}; + proto = IPProtocol{btlc.consume_integer()}; + + if (not btlc.is_finished()) + port = btlc.consume_integer(); } catch (...) { @@ -165,7 +164,7 @@ namespace llarp::net } } - void TrafficPolicy::bt_decode(oxenc::bt_dict_consumer& btdc) + void ExitPolicy::bt_decode(oxenc::bt_dict_consumer&& btdc) { try { @@ -177,7 +176,7 @@ namespace llarp::net while (not sublist.is_finished()) { - protocols.emplace(sublist.consume_string()); + protocols.emplace(sublist.consume_string_view()); } } @@ -200,7 +199,7 @@ namespace llarp::net } } - void TrafficPolicy::bt_encode(oxenc::bt_dict_producer& btdp) const + void ExitPolicy::bt_encode(oxenc::bt_dict_producer&& btdp) const { try { @@ -222,13 +221,11 @@ namespace llarp::net } } - bool TrafficPolicy::bt_decode(std::string_view buf) + bool ExitPolicy::bt_decode(std::string_view buf) { try { - oxenc::bt_dict_consumer btdc{buf}; - - bt_decode(btdc); + bt_decode(oxenc::bt_dict_consumer{buf}); } catch (const std::exception& e) { @@ -240,30 +237,4 @@ namespace llarp::net return true; } - - nlohmann::json ProtocolInfo::ExtractStatus() const - { - nlohmann::json status{ - {"protocol", static_cast(protocol)}, - }; - if (port) - status["port"] = *port; - return status; - } - - nlohmann::json TrafficPolicy::ExtractStatus() const - { - std::vector rangesStatus; - std::transform(ranges.begin(), ranges.end(), std::back_inserter(rangesStatus), [](const auto& range) { - return range.to_string(); - }); - - std::vector protosStatus; - std::transform(protocols.begin(), protocols.end(), std::back_inserter(protosStatus), [](const auto& proto) { - return proto.ExtractStatus(); - }); - - return nlohmann::json{{"ranges", rangesStatus}, {"protocols", protosStatus}}; - } - } // namespace llarp::net diff --git a/llarp/net/policy.hpp b/llarp/net/policy.hpp new file mode 100644 index 0000000000..818c3c0089 --- /dev/null +++ b/llarp/net/policy.hpp @@ -0,0 +1,156 @@ +#pragma once + +#include "net.hpp" + +#include + +#include + +#include + +namespace llarp +{ + struct IPPacket; + + inline constexpr uint8_t proto_mask{0b0000'0011}; + + /** protocol_flag + - When negotiating sessions and advertising client contacts, all flags can be used + - When prepended to a datagram, only 2 bits are needed for 4 configurations + - host, standard = 00 + - host, tcp2quic = 01 + - exit, standard = 10 + - exit, tcp2quic = 11 + */ + enum class protocol_flag : uint8_t + { + EXIT = 1 << 0, + TCP2QUIC = 1 << 1, + IPV4 = 1 << 2, + IPV6 = 1 << 3, + }; + + // TODO: WIP implementation + struct ip_protocol + { + enum class type : uint8_t + { + ICMP = 0x01, + IGMP = 0x02, + IPIP = 0x04, + TCP = 0x06, + UDP = 0x11, + GRE = 0x2F, + ICMP6 = 0x3A, + OSPF = 0x59, + PGM = 0x71, + }; + }; + + namespace net + { + enum class IPProtocol : uint8_t + { + ICMP = 0x01, + IGMP = 0x02, + IPIP = 0x04, + TCP = 0x06, + UDP = 0x11, + GRE = 0x2F, + ICMP6 = 0x3A, + OSPF = 0x59, + PGM = 0x71, + }; + + inline constexpr auto ip_protocol_name(IPProtocol p) + { + switch (p) + { + case IPProtocol::ICMP: + return "ICMP"sv; + case IPProtocol::IGMP: + return "IGMP"sv; + case IPProtocol::IPIP: + return "IPIP"sv; + case IPProtocol::TCP: + return "TCP"sv; + case IPProtocol::UDP: + return "UDP"sv; + case IPProtocol::GRE: + return "GRE"sv; + case IPProtocol::ICMP6: + return "ICMP6"sv; + case IPProtocol::OSPF: + return "OSPF"sv; + case IPProtocol::PGM: + return "PGM"sv; + default: + return ""sv; + } + } + + /// information about an IP protocol + struct ProtocolInfo + { + /// ip protocol byte of this protocol + IPProtocol proto; + + /// the layer 3 port IN HOST ORDER FFS + std::optional port{std::nullopt}; + + ProtocolInfo() = default; + ProtocolInfo(std::string_view buf); + + void bt_encode(oxenc::bt_list_producer& btlp) const; + + void bt_decode(oxenc::bt_list_consumer& btlc); + + bool bt_decode(std::string_view buf); + + // Compares packet protocol with protocol info + bool matches_packet_proto(const IPPacket& pkt) const; + + auto operator<=>(const ProtocolInfo& other) const + { + return std::tie(proto, port) <=> std::tie(other.proto, other.port); + } + + bool operator==(const ProtocolInfo& other) const { return (*this <=> other) == 0; } + + bool operator<(const ProtocolInfo& other) const + { + return std::tie(proto, port) < std::tie(other.proto, other.port); + } + + // explicit ProtocolInfo(std::string_view spec); + }; + + /// information about what exit traffic an endpoint will carry + struct ExitPolicy + { + /// ranges that are explicitly allowed + std::set ranges; + + /// protocols that are explicity allowed + std::set protocols; + + bool empty() const { return ranges.empty() and protocols.empty(); } + + void bt_encode(oxenc::bt_dict_producer&& btdp) const; + + void bt_decode(oxenc::bt_dict_consumer&& btdc); + + bool bt_decode(std::string_view buf); + + auto operator<=>(const ExitPolicy& other) const + { + return std::tie(ranges, protocols) <=> std::tie(other.ranges, other.protocols); + } + + bool operator==(const ExitPolicy& other) const { return (*this <=> other) == 0; } + + // Verifies if IPPacket traffic is allowed; return true/false + bool allow_ip_traffic(const IPPacket& pkt) const; + }; + } // namespace net +} // namespace llarp diff --git a/llarp/net/posix.cpp b/llarp/net/posix.cpp index 0e341e1931..e491688e50 100644 --- a/llarp/net/posix.cpp +++ b/llarp/net/posix.cpp @@ -92,7 +92,7 @@ namespace llarp::net oxen::quic::Address addr{i->ifa_addr}; auto nma = reinterpret_cast(i->ifa_netmask)->sin_addr.s_addr; auto m = std::popcount(nma); - log::trace( + log::debug( logcat, "Adding {} {} (mask={}) to current ranges", addr.is_ipv4() ? "ipv4" : "ipv6", addr, m); current_ranges.emplace_back(std::move(addr), std::move(m)); } @@ -287,8 +287,5 @@ namespace llarp::net const Platform_Impl g_plat{}; - const Platform* Platform::Default_ptr() - { - return &g_plat; - } + const Platform* Platform::Default_ptr() { return &g_plat; } } // namespace llarp::net diff --git a/llarp/net/traffic_policy.hpp b/llarp/net/traffic_policy.hpp deleted file mode 100644 index 10ae4fae3c..0000000000 --- a/llarp/net/traffic_policy.hpp +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include -#include - -#include - -#include - -namespace llarp::net -{ - // Copied over from llarp/net/ip_packet_old.hpp... - // TODO: do we fucking need this? - enum class IPProtocol : uint8_t - { - ICMP = 0x01, - IGMP = 0x02, - IPIP = 0x04, - TCP = 0x06, - UDP = 0x11, - GRE = 0x2F, - ICMP6 = 0x3A, - OSFP = 0x59, - PGM = 0x71, - }; - - /// information about an IP protocol - struct ProtocolInfo - { - /// ip protocol byte of this protocol - IPProtocol protocol; - - /// the layer 3 port IN HOST ORDER FFS - std::optional port; - - ProtocolInfo(std::string buf); - - void bt_encode(oxenc::bt_list_producer& btlp) const; - - void bt_decode(oxenc::bt_list_consumer& btlc); - - bool bt_decode(std::string_view buf); - - nlohmann::json ExtractStatus() const; - - /// returns true if an ip packet looks like it matches this protocol info - /// returns false otherwise - bool matches_packet_proto(const IPPacket& pkt) const; - - bool operator<(const ProtocolInfo& other) const - { - return std::tie(protocol, port) < std::tie(other.protocol, other.port); - } - - ProtocolInfo() = default; - - explicit ProtocolInfo(std::string_view spec); - }; - - /// information about what traffic an endpoint will carry - struct TrafficPolicy - { - /// ranges that are explicitly allowed - std::set ranges; - - /// protocols that are explicity allowed - std::set protocols; - - void bt_encode(oxenc::bt_dict_producer& btdp) const; - - void bt_decode(oxenc::bt_dict_consumer& btdc); - - bool bt_decode(std::string_view buf); - nlohmann::json ExtractStatus() const; - - /// returns true if we allow the traffic in this ip packet - /// returns false otherwise - bool allow_ip_traffic(const IPPacket& pkt); - }; -} // namespace llarp::net diff --git a/llarp/net/uint128.hpp b/llarp/net/uint128.hpp deleted file mode 100644 index 7c94aa0cc4..0000000000 --- a/llarp/net/uint128.hpp +++ /dev/null @@ -1,269 +0,0 @@ -#pragma once -#include - -#include -#include -#include -#include - -namespace llarp -{ - /// 128-bit unsigned integer. Does *not* support - /// multiplication/division/modulus. - struct uint128_t - { - // Swap order on little/big endian so that the first byte of the struct is - // always most significant on big endian and least significant on little - // endian. -#ifdef __BIG_ENDIAN__ - uint64_t upper, lower; -#else - uint64_t lower, upper; -#endif - - // Initializes with 0s - constexpr uint128_t() : uint128_t{0, 0} {} - - // Initializes with least-significant value - constexpr uint128_t(uint64_t lower) : uint128_t{0, lower} {} - - // Initializes with upper and lower values - constexpr uint128_t(uint64_t upper, uint64_t lower) - // clang-format off -#ifdef __BIG_ENDIAN__ - : upper{upper}, lower{lower} -#else - : lower{lower}, upper{upper} -#endif - // clang-format on - {} - - constexpr uint128_t(const uint128_t&) = default; - constexpr uint128_t(uint128_t&&) = default; - constexpr uint128_t& operator=(const uint128_t&) = default; - constexpr uint128_t& operator=(uint128_t&&) = default; - - // bitwise and - constexpr uint128_t& operator&=(const uint128_t& o) - { - upper &= o.upper; - lower &= o.lower; - return *this; - } - constexpr uint128_t operator&(const uint128_t& o) const - { - uint128_t result = *this; - result &= o; - return result; - } - - // bitwise or - constexpr uint128_t& operator|=(const uint128_t& o) - { - upper |= o.upper; - lower |= o.lower; - return *this; - } - constexpr uint128_t operator|(const uint128_t& o) const - { - uint128_t result = *this; - result |= o; - return result; - } - - // bitwise xor - constexpr uint128_t& operator^=(const uint128_t& o) - { - upper ^= o.upper; - lower ^= o.lower; - return *this; - } - constexpr uint128_t operator^(const uint128_t& o) const - { - uint128_t result = *this; - result ^= o; - return result; - } - - // bitwise not - constexpr uint128_t operator~() const { return {~upper, ~lower}; } - - // bool: true if any bit set - explicit constexpr operator bool() const { return static_cast(lower) || static_cast(upper); } - - // Casting to basic unsigned int types: casts away upper bits - explicit constexpr operator uint8_t() const { return static_cast(lower); } - explicit constexpr operator uint16_t() const { return static_cast(lower); } - explicit constexpr operator uint32_t() const { return static_cast(lower); } - explicit constexpr operator uint64_t() const { return lower; } - - constexpr bool operator==(const uint128_t& b) const { return lower == b.lower && upper == b.upper; } - - constexpr bool operator!=(const uint128_t& b) const { return lower != b.lower || upper != b.upper; } - - constexpr bool operator<(const uint128_t& b) const - { - return std::tie(upper, lower) < std::tie(b.upper, b.lower); - } - - constexpr bool operator<=(const uint128_t& b) const - { - return std::tie(upper, lower) <= std::tie(b.upper, b.lower); - } - - constexpr bool operator>(const uint128_t& b) const - { - return std::tie(upper, lower) > std::tie(b.upper, b.lower); - } - - constexpr bool operator>=(const uint128_t& b) const - { - return std::tie(upper, lower) >= std::tie(b.upper, b.lower); - } - - constexpr uint128_t& operator++() - { - if (++lower == 0) - ++upper; - return *this; - } - - constexpr uint128_t operator++(int) - { - auto copy = *this; - ++*this; - return copy; - } - - constexpr uint128_t& operator+=(const uint128_t& b) - { - lower += b.lower; - if (lower < b.lower) - ++upper; - upper += b.upper; - return *this; - } - constexpr uint128_t operator+(const uint128_t& b) const - { - uint128_t result = *this; - result += b; - return result; - } - - constexpr uint128_t& operator-=(const uint128_t& b) - { - if (b.lower > lower) - --upper; - lower -= b.lower; - upper -= b.upper; - return *this; - } - constexpr uint128_t operator-(const uint128_t& b) const - { - uint128_t result = *this; - result -= b; - return result; - } - - constexpr uint128_t& operator<<=(uint64_t shift) - { - if (shift == 0) - { - } - else if (shift < 64) - { - upper = upper << shift | (lower >> (64 - shift)); - lower <<= shift; - } - else if (shift == 64) - { - upper = lower; - lower = 0; - } - else if (shift < 128) - { - upper = lower << (shift - 64); - lower = 0; - } - else - { - upper = lower = 0; - } - return *this; - } - constexpr uint128_t operator<<(uint64_t shift) const - { - uint128_t result = *this; - result <<= shift; - return result; - } - - constexpr uint128_t& operator>>=(uint64_t shift) - { - if (shift == 0) - { - } - else if (shift < 64) - { - lower = lower >> shift | upper << (64 - shift); - upper >>= shift; - } - else if (shift == 64) - { - lower = upper; - upper = 0; - } - else if (shift < 128) - { - lower = upper >> (shift - 64); - upper = 0; - } - else - { - upper = lower = 0; - } - return *this; - } - - constexpr uint128_t operator>>(uint64_t shift) const - { - uint128_t result = *this; - result >>= shift; - return result; - } - }; - - static_assert(sizeof(uint128_t) == 16, "uint128_t has unexpected size (padding?)"); - -} // namespace llarp - -namespace std -{ - // Hash function for uint128_t - template <> - struct hash - { - size_t operator()(const llarp::uint128_t& i) const - { - size_t h = std::hash()(i.lower); - h ^= std::hash()(i.upper) + 0x9e3779b9 + (h << 6) + (h >> 2); - return h; - } - }; -} // namespace std - -inline llarp::uint128_t ntoh128(llarp::uint128_t i) -{ -#ifdef __BIG_ENDIAN__ - return i; -#else - const auto loSwapped = oxenc::big_to_host(i.lower); - const auto hiSwapped = oxenc::big_to_host(i.upper); - return {/*upper=*/loSwapped, /*lower=*/hiSwapped}; -#endif -} - -inline llarp::uint128_t hton128(llarp::uint128_t i) -{ - return ntoh128(i); // Same bit flipping as n-to-h -} diff --git a/llarp/net/utils.cpp b/llarp/net/utils.cpp new file mode 100644 index 0000000000..b7bed3c244 --- /dev/null +++ b/llarp/net/utils.cpp @@ -0,0 +1,157 @@ +#include "utils.hpp" + +#include + +namespace llarp +{ + static auto logcat = log::Cat("net-utils"); + + bool ip_equals_address(const ip_v &ip, const oxen::quic::Address &addr, bool compare_v4) + { + if (compare_v4 and std::holds_alternative(ip)) + return std::get(ip) == addr.to_ipv4(); + + if (not compare_v4 and std::holds_alternative(ip)) + return std::get(ip) == addr.to_ipv6(); + + log::warning(logcat, "Failed to compare ip variant in desired ipv{} scheme!", compare_v4 ? "4" : "6"); + return false; + } + + namespace utils + { + static constexpr uint32_t add_u32(uint32_t x) { return uint32_t{x & 0xFFff} + uint32_t{x >> 16}; } + // static constexpr uint32_t add_u32(ipv4 x) { return add_u32(oxenc::host_to_big(x.addr)); } + static uint32_t add_u32(ipv4 x) { return add_u32(oxenc::host_to_big(x.addr)); } + + static constexpr uint32_t sub_u32(uint32_t x) { return add_u32(~x); } + // static constexpr uint32_t sub_u32(ipv4 x) { return sub_u32(oxenc::host_to_big(x.addr)); } + static uint32_t sub_u32(ipv4 x) { return sub_u32(oxenc::host_to_big(x.addr)); } + + uint16_t ip_checksum(const uint8_t *buf, size_t sz) + { + uint32_t sum = 0; + + while (sz > 1) + { + sum += *(uint16_t *)(buf); + sz -= sizeof(uint16_t); + buf += sizeof(uint16_t); + } + + if (sz != 0) + { + uint16_t x = 0; + *(uint8_t *)&x = *buf; + sum += x; + } + + sum = (sum & 0xFFff) + (sum >> 16); + sum += sum >> 16; + + return uint16_t((~sum) & 0xFFff); + } + + uint16_t ipv4_checksum_diff(uint16_t old_sum, uint32_t old_src, uint32_t old_dest, ipv4 new_src, ipv4 new_dest) + { + uint32_t sum = old_sum + add_u32(old_src) + add_u32(old_dest) + sub_u32(new_src) + sub_u32(new_dest); + + sum = (sum & 0xFFff) + (sum >> 16); + sum += sum >> 16; + + return uint16_t(sum & 0xFFff); + } + + uint16_t ipv4_tcp_checksum_diff( + uint16_t old_sum, uint32_t old_src, uint32_t old_dest, ipv4 new_src, ipv4 new_dest) + { + auto new_sum = ipv4_checksum_diff(old_sum, old_src, old_dest, new_src, new_dest); + return new_sum == 0xFFff ? 0x0000 : new_sum; + } + + uint16_t ipv4_udp_checksum_diff( + uint16_t old_sum, uint32_t old_src, uint32_t old_dest, ipv4 new_src, ipv4 new_dest) + { + if (old_sum == 0x0000) + return old_sum; + + return ipv4_checksum_diff(old_sum, old_src, old_dest, new_src, new_dest); + } + } // namespace utils + + uint16_t csum_add(uint16_t csum, uint16_t rhs) + { + uint32_t res = csum, other = rhs; + res += other; + return static_cast(res + (res < other)); + } + + uint16_t csum_sub(uint16_t csum, uint16_t rhs) { return csum_add(csum, ~rhs); } + + uint16_t from_32_to_16(uint32_t x) + { + /* add up 16-bit and 16-bit for 16+c bit */ + x = (x & 0xffff) + (x >> 16); + /* add up carry.. */ + x = (x & 0xffff) + (x >> 16); + return x; + } + + uint32_t from_64_to_32(uint64_t x) + { + /* add up 32-bit and 32-bit for 32+c bit */ + x = (x & 0xffffffff) + (x >> 32); + /* add up carry.. */ + x = (x & 0xffffffff) + (x >> 32); + return x; + } + + uint16_t fold_csum(uint32_t csum) + { + auto sum = csum; + sum = (sum & 0xffff) + (sum >> 16); + sum = (sum & 0xffff) + (sum >> 16); + return static_cast(~sum); + } + + uint16_t ipv6_checksum_magic( + const struct in6_addr *saddr, const struct in6_addr *daddr, uint32_t len, uint8_t proto, uint32_t sum) + { + uint32_t csum = sum; + + for (size_t i = 0; i < 4; ++i) + { + auto val = static_cast(saddr->s6_addr32[i]); + csum += val; + csum += (csum < val); + } + + for (size_t i = 0; i < 4; ++i) + { + auto val = static_cast(daddr->s6_addr32[i]); + csum += val; + csum += (csum < val); + } + + uint32_t ulen = htonl(len); + uint32_t uproto = htonl(proto); + + csum += ulen; + csum += (csum < ulen); + + csum += uproto; + csum += (csum < uproto); + + return fold_csum(csum); + } + + uint32_t tcp_checksum_ipv6(const struct in6_addr *saddr, const struct in6_addr *daddr, uint32_t len, uint32_t csum) + { + return ~ipv6_checksum_magic(saddr, daddr, len, IPPROTO_TCP, csum); + } + + uint32_t udp_checksum_ipv6(const struct in6_addr *saddr, const struct in6_addr *daddr, uint32_t len, uint32_t csum) + { + return ~ipv6_checksum_magic(saddr, daddr, len, IPPROTO_UDP, csum); + } +} // namespace llarp diff --git a/llarp/net/utils.hpp b/llarp/net/utils.hpp new file mode 100644 index 0000000000..c98c60aaab --- /dev/null +++ b/llarp/net/utils.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include + +namespace llarp +{ + inline constexpr uint32_t ipv6_flowlabel_mask = 0b0000'0000'0000'1111'1111'1111'1111'1111; + + inline constexpr size_t ICMP_HEADER_SIZE{8}; + + // Compares the given ip variant against a quic address + // Returns: + // - true : ip == address + // - false : + // - ip != address + // - ip and address are mismatched ipv4 vs ipv6 + bool ip_equals_address(const ip_v &ip, const oxen::quic::Address &addr, bool compare_v4); + + namespace utils + { + uint16_t ip_checksum(const uint8_t *buf, size_t sz); + + // Parameters: + // - old_sum : old checksum (NETWORK order!) + // - old_{src,dest} : old src and dest IP's (stored internally in HOST order!) + // - new_{src,dest} : new src and dest IP's (stored internally in HOST order!) + // + // Returns: + // - uint16_t : new checksum (NETWORK order!) + uint16_t ipv4_checksum_diff(uint16_t old_sum, uint32_t old_src, uint32_t old_dest, ipv4 new_src, ipv4 new_dest); + + uint16_t ipv4_tcp_checksum_diff( + uint16_t old_sum, uint32_t old_src, uint32_t old_dest, ipv4 new_src, ipv4 new_dest); + + uint16_t ipv4_udp_checksum_diff( + uint16_t old_sum, uint32_t old_src, uint32_t old_dest, ipv4 new_src, ipv4 new_dest); + + uint16_t ipv6_checksum_diff(); + } // namespace utils + + uint32_t tcp_checksum_ipv6(const struct in6_addr *saddr, const struct in6_addr *daddr, uint32_t len, uint32_t csum); + + uint32_t udp_checksum_ipv6(const struct in6_addr *saddr, const struct in6_addr *daddr, uint32_t len, uint32_t csum); + +} // namespace llarp diff --git a/llarp/net/win32.cpp b/llarp/net/win32.cpp index b670058f24..b11b73fdf0 100644 --- a/llarp/net/win32.cpp +++ b/llarp/net/win32.cpp @@ -1,5 +1,3 @@ -#include "ip.hpp" -#include "ip_range.hpp" #include "net.hpp" #include "net_if.hpp" @@ -196,8 +194,5 @@ namespace llarp::net const Platform_Impl g_plat{}; - const Platform* Platform::Default_ptr() - { - return &g_plat; - } + const Platform* Platform::Default_ptr() { return &g_plat; } } // namespace llarp::net diff --git a/llarp/nodedb.cpp b/llarp/nodedb.cpp index 99729713a3..ecc4cb2dac 100644 --- a/llarp/nodedb.cpp +++ b/llarp/nodedb.cpp @@ -1,10 +1,9 @@ #include "nodedb.hpp" #include "crypto/types.hpp" -#include "dht/kademlia.hpp" #include "link/link_manager.hpp" #include "messages/fetch.hpp" -#include "router_contact.hpp" +#include "util/meta.hpp" #include "util/time.hpp" #include @@ -34,10 +33,7 @@ namespace llarp throw std::runtime_error{fmt::format("nodedb {} is not a directory", nodedbDir)}; } - std::tuple NodeDB::db_stats() const - { - return {num_rcs(), num_rids(), num_bootstraps()}; - } + std::tuple NodeDB::db_stats() const { return {num_rcs(), num_rids(), num_bootstraps()}; } std::optional NodeDB::get_rc_by_rid(const RouterID& rid) { @@ -76,61 +72,16 @@ namespace llarp return rc; } - size_t i = 0; - std::optional res = std::nullopt; - - for (const auto& rc : known_rcs) - { - if (not hook(rc)) - continue; - - if (++i <= 1) - { - res = rc; - continue; - } - - size_t x = csrng() % (i + 1); - if (x <= 1) - res = rc; - } - - return res; + return meta::sample(known_rcs, std::move(hook)); } std::optional> NodeDB::get_n_random_rcs_conditional( - size_t n, std::function hook, bool exact) const + size_t n, std::function hook, bool exact, bool /* use_strict_connect */) const { - auto selected = std::make_optional>(); - selected->reserve(n); - - size_t i = 0; - - for (const auto& rc : known_rcs) - { - // ignore any RC's that do not pass the condition - if (not hook(rc)) - continue; - - // load the first n RC's that pass the condition into selected - if (++i <= n) - { - selected->push_back(rc); - continue; - } - - // replace selections with decreasing probability per iteration - size_t x = csrng() % (i + 1); - if (x < n) - (*selected)[x] = rc; - } - - if (selected->size() < (exact ? n : 1)) - selected.reset(); - return selected; + return meta::sample_n(known_rcs, std::move(hook), n, exact); } - bool NodeDB::tick(std::chrono::milliseconds now) + bool NodeDB::tick([[maybe_unused]] std::chrono::milliseconds now) { if (_is_bootstrapping or _is_connecting_bstrap) { @@ -184,21 +135,26 @@ namespace llarp _is_service_node ? "Relay" : "Client", num_rcs(), MIN_ACTIVE_RCS); - _bootstrap_handler->begin(); + _bootstrap_handler->start(); } return false; } - purge_rcs(now); return true; } void NodeDB::purge_rcs(std::chrono::milliseconds now) { + if (_router.is_stopping() || not _router.is_running()) + { + log::debug(logcat, "NodeDB unable to continue NodeDB purge -- router is stopped!"); + return; + } + remove_if([&](const RemoteRC& rc) -> bool { // don't purge bootstrap nodes from nodedb - if (is_bootstrap_node(rc.router_id())) + if (is_bootstrap_node(rc)) { log::trace(logcat, "Not removing {}: is bootstrap node", rc.router_id()); return false; @@ -229,11 +185,12 @@ namespace llarp } // if we don't have the whitelist yet don't remove the entry - if (not _router.whitelist_received) + if (not _router.has_whitelist()) { log::debug(logcat, "Skipping check on {}: don't have whitelist yet", rc.router_id()); return false; } + // if we have no whitelist enabled or we have // the whitelist enabled and we got the whitelist // check against the whitelist and remove if it's not @@ -243,6 +200,7 @@ namespace llarp log::debug(logcat, "Removing {}: not a valid router", rc.router_id()); return true; } + return false; }); @@ -254,13 +212,7 @@ namespace llarp return "{}/{}{}"_format(_root.c_str(), pubkey.to_string(), RC_FILE_EXT); } - bool NodeDB::want_rc(const RouterID& rid) const - { - if (not _is_service_node) - return true; - - return known_rids.count(rid); - } + bool NodeDB::want_rc(const RouterID& rid) const { return known_rids.count(rid) and not rc_lookup.contains(rid); } void NodeDB::set_bootstrap_routers(BootstrapList& from_router) { @@ -343,7 +295,7 @@ namespace llarp for (const auto& [rid, count] : rid_result_counters) { - log::info(logcat, "RID: {}, Freq: {}", rid.ShortString(), count); + log::trace(logcat, "RID: {}, Freq: {}", rid.ShortString(), count); if (count >= MIN_RID_FETCH_FREQ) union_set.insert(rid); else @@ -407,11 +359,10 @@ namespace llarp { return _router.loop()->call_get([this]() { std::vector needed; - const auto now = time_point_now(); for (const auto& [rid, rc] : rc_lookup) { - if (now - rc.timestamp() > RouterContact::OUTDATED_AGE) + if (rc.is_outdated()) needed.push_back(rid); } @@ -421,7 +372,7 @@ namespace llarp void NodeDB::fetch_rcs() { - if (_router._is_stopping || not _router._is_running) + if (_router.is_stopping() || not _router.is_running()) { log::info(logcat, "NodeDB unable to continue RC fetch -- router is stopped!"); return stop_rc_fetch(false); @@ -478,7 +429,7 @@ namespace llarp if (process_fetched_rcs(*result)) { - log::info(logcat, "Accumulated RID's accepted by trust model"); + log::info(logcat, "Accumulated RC's accepted by trust model"); return stop_rc_fetch(true); } @@ -494,7 +445,7 @@ namespace llarp void NodeDB::fetch_rids() { - if (_router._is_stopping || not _router._is_running) + if (_router.is_stopping() || not _router.is_running()) { log::info(logcat, "NodeDB unable to continue RouterID fetch -- router is stopped!"); return stop_rid_fetch(false); @@ -515,10 +466,6 @@ namespace llarp auto& src = fetch_source; - // TESTNET: - // rid_sources.emplace(oxenc::from_base32z("55fxrrdt9ggkra9yoi58gbespa13is1sqqrykdzjamgkxrq91tto")); - // auto& src = _bootstraps.current().router_id(); - for (const auto& target : rid_sources) { if (target == src) @@ -614,9 +561,9 @@ namespace llarp reselect_router_id_sources(fail_sources); } - bool NodeDB::is_bootstrap_node(RouterID rid) const + bool NodeDB::is_bootstrap_node(const RemoteRC& rc) const { - return has_bootstraps() ? _bootstraps.contains(rid) : false; + return has_bootstraps() ? _bootstraps.contains(rc) : false; } void NodeDB::start_tickers() @@ -628,6 +575,9 @@ namespace llarp save_to_disk(); }); + _purge_ticker = _router.loop()->call_every( + PURGE_INTERVAL, [this]() mutable { purge_rcs(); }, not _needs_bootstrap); + if (not _is_service_node) { // start these immediately if we do not need to bootstrap @@ -640,7 +590,7 @@ namespace llarp void NodeDB::configure() { - _is_service_node = _router._is_service_node; + _is_service_node = _router.is_service_node(); bootstrap_init(); load_from_disk(); @@ -655,9 +605,9 @@ namespace llarp _rc_fetch_ticker->stop(); if (success) - log::info(logcat, "Client successfully completed RouterContact fetch!"); + log::info(logcat, "Client successfully completed RelayContact fetch!"); else - log::warning(logcat, "Client stopped RouterContact fetch without a sucessful response!"); + log::warning(logcat, "Client stopped RelayContact fetch without a sucessful response!"); } void NodeDB::stop_rid_fetch(bool success) @@ -680,12 +630,18 @@ namespace llarp _is_bootstrapping = false; // this function is only called in success or lokinet shutdown, so we will never need bootstrapping _needs_bootstrap = false; - _bootstrap_handler->halt(); + _bootstrap_handler->stop(); if (success) { log::info(logcat, "{} completed processing BootstrapRC fetch!", _is_service_node ? "Relay" : "Client"); + if (not _purge_ticker->is_running()) + { + log::debug(logcat, "{} activating NodeDB purge ticker", _is_service_node ? "Relay" : "Client"); + _purge_ticker->start(); + } + if (not _is_service_node) { if (not _rid_fetch_ticker->is_running()) @@ -710,7 +666,7 @@ namespace llarp { log::debug(logcat, "{} called", __PRETTY_FUNCTION__); - if (_router._is_stopping || not _router._is_running) + if (_router.is_stopping() || not _router.is_running()) { log::info(logcat, "NodeDB unable to continue bootstrap fetch -- router is stopped!"); return stop_bootstrap(false); @@ -726,7 +682,7 @@ namespace llarp _router.link_manager()->fetch_bootstrap_rcs( rc, BootstrapFetchMessage::serialize( - _is_service_node ? std::make_optional(_router.router_contact) : std::nullopt, num_needed), + _is_service_node ? std::make_optional(_router.rc()) : std::nullopt, num_needed), [this, src = source](oxen::quic::message m) mutable { log::info(logcat, "Received response to BootstrapRC fetch request..."); @@ -793,46 +749,24 @@ namespace llarp }); } - void NodeDB::set_router_whitelist( - const std::vector& whitelist, - const std::vector& greylist, - const std::vector& greenlist) + void NodeDB::set_router_whitelist(const std::vector& whitelist) { - log::critical( - logcat, - "Oxend provided {}/{}/{} (white/gray/green) routers", - whitelist.size(), - greylist.size(), - greenlist.size()); + log::critical(logcat, "Oxend provided {} whitelisted routers", whitelist.size()); if (whitelist.empty()) return; _registered_routers.clear(); _registered_routers.insert(whitelist.begin(), whitelist.end()); - _registered_routers.insert(greylist.begin(), greylist.end()); - _registered_routers.insert(greenlist.begin(), greenlist.end()); - - _router_whitelist.clear(); - _router_whitelist.insert(whitelist.begin(), whitelist.end()); - _router_greylist.clear(); - _router_greylist.insert(greylist.begin(), greylist.end()); - _router_greenlist.clear(); - _router_greenlist.insert(greenlist.begin(), greenlist.end()); log::critical( - logcat, - "Service node holding {}:{} (whitelist:registered) after oxend integration", - _router_whitelist.size(), - _registered_routers.size()); + logcat, "Service node holding {} registered relays after oxend integration", _registered_routers.size()); } - std::optional NodeDB::get_random_whitelist_router() const + std::optional NodeDB::get_random_registered_router() const { - std::optional rand = std::nullopt; - - std::sample(_router_whitelist.begin(), _router_whitelist.end(), &*rand, 1, csrng); - return rand; + std::function hook = [](const auto&) -> bool { return true; }; + return meta::sample(_registered_routers, hook); } bool NodeDB::is_connection_allowed(const RouterID& remote) const @@ -841,10 +775,11 @@ namespace llarp { if (_pinned_edges.size() && _pinned_edges.count(remote) == 0 && not _bootstraps.contains(remote)) return false; + + return known_rids.count(remote); } - // TESTNET: make this check an updated registry - return known_rids.count(remote) or _registered_routers.count(remote); + return known_rids.count(remote) and _registered_routers.empty() ? true : _registered_routers.count(remote); } bool NodeDB::is_first_hop_allowed(const RouterID& remote) const @@ -943,7 +878,7 @@ namespace llarp if (_bootstrap_handler) { log::debug(logcat, "NodeDB clearing bootstrap handler..."); - _bootstrap_handler->halt(); + _bootstrap_handler->stop(); _bootstrap_handler.reset(); } @@ -956,11 +891,18 @@ namespace llarp if (_rc_fetch_ticker) { - log::debug(logcat, "NodeDB clearing rc fetch ticker..."); + log::debug(logcat, "NodeDB clearing RC fetch ticker..."); _rc_fetch_ticker->stop(); _rc_fetch_ticker.reset(); } + if (_purge_ticker) + { + log::debug(logcat, "NodeDB clearing purge ticker..."); + _purge_ticker->stop(); + _purge_ticker.reset(); + } + if (_flush_ticker) { log::debug(logcat, "NodeDB clearing flush ticker..."); @@ -969,15 +911,9 @@ namespace llarp } } - bool NodeDB::has_rc(const RemoteRC& rc) const - { - return known_rcs.count(rc); - } + bool NodeDB::has_rc(const RemoteRC& rc) const { return known_rcs.count(rc); } - bool NodeDB::has_rc(const RouterID& pk) const - { - return rc_lookup.count(pk); - } + bool NodeDB::has_rc(const RouterID& pk) const { return rc_lookup.count(pk); } std::optional NodeDB::get_rc(const RouterID& pk) const { @@ -987,25 +923,6 @@ namespace llarp return std::nullopt; } - void NodeDB::remove_stale_rcs() - { - auto cutoff_time = time_point_now(); - - cutoff_time -= _is_service_node ? RouterContact::OUTDATED_AGE : RouterContact::LIFETIME; - - for (auto itr = rc_lookup.begin(); itr != rc_lookup.end();) - { - if (cutoff_time > itr->second.timestamp()) - { - log::info(logcat, "Pruning RC for {}, as it is too old to keep.", itr->first); - known_rcs.erase(itr->second); - itr = rc_lookup.erase(itr); - continue; - } - itr++; - } - } - bool NodeDB::put_rc(RemoteRC rc) { Lock_t l{nodedb_mutex}; @@ -1016,8 +933,18 @@ namespace llarp if (rid == _router.local_rid()) return false; - known_rcs.erase(rc); - rc_lookup.erase(rid); + // Use the rc_lookup RemoteRC to delete from known_rcs, as the differing timestamp between the old and new will + // result in set::insert not matching to the previous value + if (auto it = rc_lookup.find(rid); it != rc_lookup.end()) + { + known_rcs.erase(it->second); + rc_lookup.erase(it); + } + else + { + known_rcs.erase(rc); + rc_lookup.erase(rid); + } auto [itr, b] = known_rcs.insert(std::move(rc)); ret &= b; @@ -1027,15 +954,9 @@ namespace llarp return ret; } - size_t NodeDB::num_rcs() const - { - return known_rcs.size(); - } + size_t NodeDB::num_rcs() const { return known_rcs.size(); } - size_t NodeDB::num_rids() const - { - return known_rids.size(); - } + size_t NodeDB::num_rids() const { return known_rids.size(); } bool NodeDB::verify_store_gossip_rc(const RemoteRC& rc) { @@ -1053,7 +974,8 @@ namespace llarp return false; } - return put_rc(rc); + put_rc(std::move(rc)); + return true; } void NodeDB::remove_many_from_disk_async(std::unordered_set remove) const @@ -1076,47 +998,18 @@ namespace llarp RemoteRC NodeDB::find_closest_to(llarp::dht::Key_t location) const { - return _router.loop()->call_get([this, location]() -> RemoteRC { - RemoteRC rc{}; - const llarp::dht::XorMetric compare(location); - - visit_all([&rc, compare](const auto& otherRC) { - const auto& rid = rc.router_id(); - - if (rid.is_zero() || compare(dht::Key_t{otherRC.router_id()}, dht::Key_t{rid})) - { - rc = otherRC; - return; - } - }); - return rc; + return _router.loop()->call_get([this, compare = dht::XorMetric{location}]() -> RemoteRC { + return *std::ranges::min_element(known_rcs, compare); }); } - std::vector NodeDB::find_many_closest_to(llarp::dht::Key_t location, uint32_t numRouters) const + dht::rc_set NodeDB::find_many_closest_to(llarp::dht::Key_t location, uint32_t num_routers) const { - return _router.loop()->call_get([this, location, numRouters]() -> std::vector { - std::vector all; - - all.reserve(known_rcs.size()); - - for (auto& entry : rc_lookup) - { - all.push_back(&entry.second); - } - - auto it_mid = numRouters < all.size() ? all.begin() + numRouters : all.end(); - - std::partial_sort(all.begin(), it_mid, all.end(), [compare = dht::XorMetric{location}](auto* a, auto* b) { - return compare(*a, *b); - }); - - std::vector closest; - closest.reserve(numRouters); - for (auto it = all.begin(); it != it_mid; ++it) - closest.push_back(**it); - - return closest; + return _router.loop()->call_get([this, compare = dht::XorMetric{location}, num_routers]() -> dht::rc_set { + dht::rc_set ret{known_rcs.begin(), known_rcs.end(), compare}; + if (num_routers) + ret.erase(std::next(ret.begin(), num_routers), ret.end()); + return ret; }); } } // namespace llarp diff --git a/llarp/nodedb.hpp b/llarp/nodedb.hpp index 66f5560ad1..d2849325df 100644 --- a/llarp/nodedb.hpp +++ b/llarp/nodedb.hpp @@ -1,10 +1,10 @@ #pragma once +#include "contact/relay_contact.hpp" +#include "contact/router_id.hpp" #include "crypto/crypto.hpp" -#include "dht/key.hpp" +#include "dht/bucket.hpp" #include "router/router.hpp" -#include "router_contact.hpp" -#include "router_id.hpp" #include "util/common.hpp" #include "util/thread/threading.hpp" @@ -23,6 +23,8 @@ namespace llarp // TESTNET: the following constants have been shortened for testing purposes inline constexpr auto FETCH_INTERVAL{10min}; + inline constexpr auto PURGE_INTERVAL{5min}; + inline constexpr auto FLUSH_INTERVAL{15min}; /* RC Fetch Constants */ // fallback to bootstrap if we have less than this many RCs @@ -62,8 +64,6 @@ namespace llarp // threshold amount of verifications to promote an unconfirmed rc/rid inline constexpr int CONFIRMATION_THRESHOLD{3}; - inline constexpr auto FLUSH_INTERVAL{15min}; - template < typename ID_t, std::enable_if_t || std::is_same_v, int> = 0> @@ -88,15 +88,14 @@ namespace llarp class NodeDB { + friend struct Router; + Router& _router; const fs::path _root; - // const std::function)> _disk_hook; - bool _is_service_node; + bool _is_service_node{false}; - std::chrono::milliseconds _next_flush_time; - - /******** RouterID/RouterContacts ********/ + /******** RouterID/RelayContacts ********/ using Lock_t = util::NullLock; mutable util::NullMutex nodedb_mutex; @@ -129,20 +128,15 @@ namespace llarp BootstrapList _bootstraps{}; - /** RouterID lists // TODO: get rid of all these, replace with better decom/not staked - sets - - white: active routers - - gray: fully funded, but decommissioned routers - - green: registered, but not fully-staked routers - */ - std::set _router_whitelist{}; - std::set _router_greylist{}; - std::set _router_greenlist{}; - // All registered relays (service nodes) std::set _registered_routers; + // if populated from a config file, lists specific exclusively used as path first-hops std::set _pinned_edges; + + // if true, ONLY use pinned edges for first hop + bool _strict_connect{false}; + // source of "truth" for RC updating. This relay will also mediate requests to the // 8 selected active RID's for RID fetching RouterID fetch_source; @@ -161,6 +155,7 @@ namespace llarp _router.queue_disk_io(std::forward(f)); } + // Returns true iff the RID is known, but not the RC bool want_rc(const RouterID& rid) const; /// asynchronously remove the files for a set of rcs on disk given their public ident key @@ -180,6 +175,7 @@ namespace llarp std::shared_ptr _rid_fetch_ticker; std::shared_ptr _rc_fetch_ticker; + std::shared_ptr _purge_ticker; std::shared_ptr _flush_ticker; public: @@ -188,13 +184,14 @@ namespace llarp return std::make_shared(std::move(rootdir), r); } - explicit NodeDB(fs::path rootdir, Router* r) - : _router{*r}, _root{std::move(rootdir)}, _next_flush_time{time_now_ms() + FLUSH_INTERVAL} + explicit NodeDB(fs::path rootdir, Router* r) : _router{*r}, _root{std::move(rootdir)} { _ensure_skiplist(_root); rid_result_counters.clear(); } + bool strict_connect_enabled() const { return _strict_connect; } + void start_tickers(); void configure(); @@ -219,19 +216,19 @@ namespace llarp // TESTNET: new bootstrap/initial fetch functions void fetch_rcs(); void fetch_rids(); - void bootstrap(); // private + void bootstrap(); void stop_rid_fetch(bool success = true); void stop_rc_fetch(bool success = true); void rid_fetch_result(const RouterID& via); void rc_fetch_result(std::optional> result = std::nullopt); - void stop_bootstrap(bool success = true); // private + void stop_bootstrap(bool success = true); bool is_bootstrapping() const { return _is_bootstrapping; } bool needs_bootstrap() const { return _needs_bootstrap; } bool bootstrap_completed() const { return not(_is_bootstrapping or _needs_bootstrap); } - bool is_bootstrap_node(RouterID rid) const; - void purge_rcs(std::chrono::milliseconds now); + bool is_bootstrap_node(const RemoteRC& rc) const; + void purge_rcs(std::chrono::milliseconds now = llarp::time_now_ms()); // Bootstrap fallback fetching // void fallback_to_bootstrap(); @@ -243,12 +240,9 @@ namespace llarp // variable ::known_rids bool reselect_router_id_sources(std::set specific); - void set_router_whitelist( - const std::vector& whitelist, - const std::vector& greylist, - const std::vector& greenlist); + void set_router_whitelist(const std::vector& whitelist); - std::optional get_random_whitelist_router() const; + std::optional get_random_registered_router() const; // client: // if pinned edges were specified, connections are allowed only to those and @@ -285,10 +279,6 @@ namespace llarp void set_bootstrap_routers(BootstrapList& from_router); - const std::set& whitelist() const { return _router_whitelist; } - - const std::set& greylist() const { return _router_greylist; } - std::set& registered_routers() { return _registered_routers; } const std::set& registered_routers() const { return _registered_routers; } @@ -315,8 +305,8 @@ namespace llarp /// find the absolute closets router to a dht location RemoteRC find_closest_to(dht::Key_t location) const; - /// find many routers closest to dht key - std::vector find_many_closest_to(dht::Key_t location, uint32_t numRouters) const; + /// find many routers closest to dht key; if num_routers = 0, return ALL routers + dht::rc_set find_many_closest_to(dht::Key_t location, uint32_t num_routers = 0) const; /// return true if we have an rc by its ident pubkey bool has_rc(const RouterID& pk) const; @@ -335,7 +325,7 @@ namespace llarp /** The following random conditional functions utilize a simple implementation of reservoir sampling to return either 1 or n random RC's using only one pass through the set of - RC's. + RC's. Pseudocode: - begin iterating through the set @@ -347,7 +337,7 @@ namespace llarp std::optional get_random_rc_conditional(std::function hook) const; std::optional> get_n_random_rcs_conditional( - size_t n, std::function hook, bool exact = false) const; + size_t n, std::function hook, bool exact = false, bool use_strict_connect = false) const; // Updates `current` to not contain any of the elements of `replace` and resamples (up to // `target_size`) from population to refill it. @@ -394,6 +384,7 @@ namespace llarp if (visit(itr->second)) { removed.insert(itr->first); + known_rids.erase(itr->first); known_rcs.erase(itr->second); itr = rc_lookup.erase(itr); } @@ -451,12 +442,6 @@ namespace llarp } } - /// remove rcs that are older than we want to keep. For relays, this is when - /// they become "outdated" (i.e. 12hrs). Clients will hang on to them until - /// they are fully "expired" (i.e. 30 days), as the client may go offline for - /// some time and can still try to use those RCs to re-learn the network. - void remove_stale_rcs(); - /// put (or replace) the RC if we consider it valid (want_rc). returns true if put. bool put_rc(RemoteRC rc); diff --git a/llarp/path/path.cpp b/llarp/path/path.cpp index ccae861410..31cf9f4559 100644 --- a/llarp/path/path.cpp +++ b/llarp/path/path.cpp @@ -5,81 +5,116 @@ #include #include #include -#include #include +#include + namespace llarp::path { static auto logcat = log::Cat("path"); Path::Path( Router& rtr, - const std::vector& _hops, + const std::vector& hop_rcs, std::weak_ptr _handler, bool is_session, bool is_client) - : handler{std::move(_handler)}, _router{rtr}, _is_session_path{is_session}, _is_client{is_client} + : handler{std::move(_handler)}, + _router{rtr}, + _is_session_path{is_session}, + _is_client{is_client}, + num_hops{hop_rcs.size()} + { + populate_internals(hop_rcs); + log::info(logcat, "Path successfully constructed: {}", to_string()); + } + + void Path::populate_internals(const std::vector& hop_rcs) { - hops.resize(_hops.size()); - size_t hsz = _hops.size(); + hops.resize(num_hops); - for (size_t idx = 0; idx < hsz; ++idx) + for (size_t i = 0; i < num_hops; ++i) { - hops[idx].rc = _hops[idx]; - do + /** Conditions: + - First hop RXID is unique, the rest are the previous hop TXID + - Last hop upstream is it's own RID, the rest are the next hop RID + - First hop downstream is client's RID, the rest are the previous hop RID + - Local hop RXID is random, TXID is first hop RXID + - Local hop upstream is first hop RID, downstream is local instance RID + */ + + hops[i]._rid = hop_rcs[i].router_id(); + hops[i]._txid = HopID::make_random(); + + if (i == 0) { - hops[idx].txID.Randomize(); - } while (hops[idx].txID.is_zero()); - - do + hops[i]._rxid = HopID::make_random(); + hops[i]._upstream = hop_rcs[i + 1].router_id(); + hops[i]._downstream = _router.local_rid(); + } + else if (i == num_hops - 1) + { + hops[i]._rxid = hops[i - 1]._txid; + hops[i]._upstream = hops[i]._rid; + hops[i]._downstream = hops[i - 1]._rid; + } + else { - hops[idx].rxID.Randomize(); - } while (hops[idx].rxID.is_zero()); + hops[i]._rxid = hops[i - 1]._txid; + hops[i]._upstream = hop_rcs[i + 1].router_id(); + hops[i]._downstream = hops[i - 1]._rid; + } + + // generate dh kx components + hops[i].kx = shared_kx_data::generate(); + + // Conditions written as ternaries + // hops[i]._rxid = i ? hops[i - 1]._txid : HopID::make_random(); + // hops[i]._upstream = i == num_hops - 1 ? hops[i]._rid : hop_rcs[i + 1].router_id(); + // hops[i]._downstream = i ? hops[i - 1]._rid : _router.local_rid(); } - for (size_t idx = 0; idx < hsz - 1; ++idx) - { - hops[idx].txID = hops[idx + 1].rxID; - } + hops.back().terminal_hop = true; + + log::trace(logcat, "Path populated with hops: {}", hop_string()); - // initialize parts of the introduction - intro.pivot_router = hops[hsz - 1].rc.router_id(); - intro.pivot_hop_id = hops[hsz - 1].txID; + // initialize parts of the clientintro + intro.pivot_rid = hops.back().router_id(); + intro.pivot_txid = hops.back()._txid; + + log::debug( + logcat, "Path client intro holding pivot_rid ({}) and pivot_txid ({})", intro.pivot_rid, intro.pivot_txid); } - void Path::link_session(recv_session_dgram_cb cb) + void Path::link_session(session_tag t) { - _recv_dgram = std::move(cb); + _linked_sessions.insert(t); + log::critical(logcat, "Current path has {} linked sessions!", _linked_sessions.size()); _is_session_path = true; } - bool Path::unlink_session() + bool Path::unlink_session(session_tag t) { - if (_is_linked) - { - _is_linked = false; - _recv_dgram = nullptr; - return true; - } - - log::warning(logcat, "Path is not currently linked to an ongoing session!"); - return false; + auto n = _linked_sessions.erase(t); + _is_session_path = not _linked_sessions.empty(); + log::critical(logcat, "Current path has {} linked sessions!", _linked_sessions.size()); + return n != 0; } - void Path::recv_path_data_message(bstring data) - { - if (_recv_dgram) - _recv_dgram(std::move(data)); - else - throw std::runtime_error{"Path does not have hook to receive datagrams!"}; - } + // void Path::recv_path_data_message(bstring data) + // { + // if (_recv_dgram) + // _recv_dgram(std::move(data)); + // else + // throw std::runtime_error{"Path does not have hook to receive datagrams!"}; + // } bool Path::operator<(const Path& other) const { - auto& first_hop = hops[0]; - auto& other_first = other.hops[0]; - return std::tie(first_hop.txID, first_hop.rxID, first_hop.upstream) - < std::tie(other_first.txID, other_first.rxID, other_first.upstream); + auto& first_hop = hops.front(); + auto& other_first = other.hops.front(); + return std::tie(first_hop._txid, first_hop._rxid, first_hop._upstream) + < std::tie(other_first._txid, other_first._rxid, other_first._upstream); } bool Path::operator==(const Path& other) const @@ -96,215 +131,115 @@ namespace llarp::path return ret; } - bool Path::operator!=(const Path& other) const - { - return not(*this == other); - } + bool Path::operator!=(const Path& other) const { return not(*this == other); } bool Path::obtain_exit( - const Ed25519SecretKey& sk, uint64_t flag, std::string tx_id, std::function func) + const Ed25519SecretKey& sk, uint64_t flag, std::string tx_id, std::function func) { return send_path_control_message( "obtain_exit", ObtainExitMessage::sign_and_serialize(sk, flag, std::move(tx_id)), std::move(func)); } - bool Path::close_exit(const Ed25519SecretKey& sk, std::string tx_id, std::function func) + bool Path::close_exit(const Ed25519SecretKey& sk, std::string tx_id, std::function func) { return send_path_control_message( "close_exit", CloseExitMessage::sign_and_serialize(sk, std::move(tx_id)), std::move(func)); } - bool Path::find_intro( - const dht::Key_t& location, bool is_relayed, uint64_t order, std::function func) + bool Path::find_client_contact(const dht::Key_t& location, std::function func) { - return send_path_control_message( - "find_intro", FindIntroMessage::serialize(location, is_relayed, order), std::move(func)); + return send_path_control_message("find_cc", FindClientContact::serialize(location), std::move(func)); } - bool Path::publish_intro( - const service::EncryptedIntroSet& introset, - bool is_relayed, - uint64_t order, - std::function func) + bool Path::publish_client_contact(const EncryptedClientContact& ecc, std::function func) { - return send_path_control_message( - "publish_intro", PublishIntroMessage::serialize(introset, is_relayed, order), std::move(func)); + return send_path_control_message("publish_cc", PublishClientContact::serialize(ecc), std::move(func)); } - bool Path::resolve_ons(std::string name, std::function func) + bool Path::resolve_sns(std::string_view name, std::function func) { - return send_path_control_message("resolve_ons", FindNameMessage::serialize(std::move(name)), std::move(func)); + return send_path_control_message("resolve_sns", ResolveSNS::serialize(name), std::move(func)); } - void Path::enable_exit_traffic() - { - log::info(logcat, "{} {} granted exit", name(), pivot_rid()); - // _role |= ePathRoleExit; - } + void Path::enable_exit_traffic() { log::info(logcat, "{} {} granted exit", name(), pivot_rid()); } - void Path::mark_exit_closed() - { - log::info(logcat, "{} hd its exit closed", name()); - // _role &= ePathRoleExit; - } + void Path::mark_exit_closed() { log::info(logcat, "{} hd its exit closed", name()); } - std::string Path::make_outer_payload(ustring_view payload, SymmNonce& nonce) + std::string Path::make_path_message(std::string inner_payload) { - // chacha and mutate nonce for each hop - for (const auto& hop : hops) + auto nonce = SymmNonce::make_random(); + + for (const auto& hop : std::ranges::reverse_view(hops)) { nonce = crypto::onion( - const_cast(payload.data()), payload.size(), hop.shared, nonce, hop.nonceXOR); + reinterpret_cast(inner_payload.data()), + inner_payload.size(), + hop.kx.shared_secret, + nonce, + hop.kx.xor_nonce); } - return Onion::serialize(nonce, upstream_txid(), payload); - } - - std::string Path::make_outer_payload(ustring_view payload) - { - auto nonce = SymmNonce::make_random(); - - return make_outer_payload(payload, nonce); + return ONION::serialize_hop(upstream_rxid().to_view(), nonce, std::move(inner_payload)); } bool Path::send_path_data_message(std::string data) { - auto payload = PathData::serialize(std::move(data), _router.local_rid()); - auto outer_payload = make_outer_payload(to_usv(payload)); - + auto inner_payload = PATH::DATA::serialize(std::move(data), _router.local_rid()); + auto outer_payload = make_path_message(std::move(inner_payload)); return _router.send_data_message(upstream_rid(), std::move(outer_payload)); } - bool Path::send_path_control_message(std::string endpoint, std::string body, std::function func) + bool Path::send_path_control_message( + std::string endpoint, std::string body, std::function func) { - auto inner_payload = PathControl::serialize(std::move(endpoint), std::move(body)); - auto outer_payload = make_outer_payload(to_usv(inner_payload)); - - return _router.send_control_message( - upstream_rid(), - "path_control", - std::move(outer_payload), - [response_cb = std::move(func), weak = weak_from_this()](oxen::quic::message m) mutable { - auto self = weak.lock(); - // TODO: do we want to allow empty callback here? - if ((not self) or (not response_cb)) - return; - - if (m.timed_out) - { - response_cb(messages::TIMEOUT_RESPONSE); - return; - } - - ustring hop_id_str, symmnonce, payload; - - try - { - oxenc::bt_dict_consumer btdc{m.body()}; - std::tie(hop_id_str, symmnonce, payload) = Onion::deserialize(btdc); - } - catch (const std::exception& e) - { - log::warning(logcat, "Error parsing path control message response: {}", e.what()); - response_cb(messages::ERROR_RESPONSE); - return; - } - - SymmNonce nonce{symmnonce.data()}; - - for (const auto& hop : self->hops) - { - nonce = crypto::onion( - reinterpret_cast(payload.data()), - payload.size(), - hop.shared, - nonce, - hop.nonceXOR); - } - - // TODO: should we do anything (even really simple) here to check if the decrypted - // response is sensible (e.g. is a bt dict)? Parsing and handling of the - // contents (errors or otherwise) is the currently responsibility of the - // callback. - response_cb(std::string{reinterpret_cast(payload.data()), payload.size()}); - }); + auto inner_payload = PATH::CONTROL::serialize(std::move(endpoint), std::move(body)); + auto outer_payload = make_path_message(std::move(inner_payload)); + return _router.send_control_message(upstream_rid(), "path_control", std::move(outer_payload), std::move(func)); } - bool Path::is_ready() const + bool Path::is_ready(std::chrono::milliseconds now) const { return _established ? !is_expired(now) : false; } + + std::shared_ptr Path::get_parent() { - if (is_expired(llarp::time_now_ms())) - return false; + if (auto parent = handler.lock()) + return parent; - return intro.latency > 0s && _established; + return nullptr; } - RouterID Path::upstream_rid() - { - return hops[0].rc.router_id(); - } + TransitHop Path::edge() const { return {hops.front()}; } - const RouterID& Path::upstream_rid() const - { - return hops[0].rc.router_id(); - } + RouterID Path::upstream_rid() { return hops.front().router_id(); } - HopID Path::upstream_txid() - { - return hops[0].txID; - } + const RouterID& Path::upstream_rid() const { return hops.front().router_id(); } - const HopID& Path::upstream_txid() const - { - return hops[0].txID; - } + HopID Path::upstream_txid() { return hops.front().txid(); } - HopID Path::upstream_rxid() - { - return hops[0].rxID; - } + const HopID& Path::upstream_txid() const { return hops.front().txid(); } - const HopID& Path::upstream_rxid() const - { - return hops[0].rxID; - } + HopID Path::upstream_rxid() { return hops.front().rxid(); } - RouterID Path::pivot_rid() - { - return hops.back().rc.router_id(); - } + const HopID& Path::upstream_rxid() const { return hops.front().rxid(); } - const RouterID& Path::pivot_rid() const - { - return hops.back().rc.router_id(); - } + RouterID Path::pivot_rid() { return hops.back().router_id(); } - HopID Path::pivot_txid() - { - return hops.back().txID; - } + const RouterID& Path::pivot_rid() const { return hops.back().router_id(); } - const HopID& Path::pivot_txid() const - { - return hops.back().txID; - } + HopID Path::pivot_txid() { return hops.back().txid(); } - HopID Path::pivot_rxid() - { - return hops.back().rxID; - } + const HopID& Path::pivot_txid() const { return hops.back().txid(); } - const HopID& Path::pivot_rxid() const - { - return hops.back().rxID; - } + HopID Path::pivot_rxid() { return hops.back().rxid(); } + + const HopID& Path::pivot_rxid() const { return hops.back().rxid(); } std::string Path::to_string() const { - return "RID:{} -- TX:{}/RX:{}"_format( + return "Path:[ Local RID:{} -- Edge TX:{}/RX:{} ]"_format( _router.local_rid().ShortString(), upstream_txid().to_string(), upstream_rxid().to_string()); } - std::string Path::HopsString() const + std::string Path::hop_string() const { std::string hops_str; hops_str.reserve(hops.size() * 62); // 52 for the pkey, 6 for .snode, 4 for the ' -> ' joiner @@ -312,38 +247,20 @@ namespace llarp::path { if (!hops.empty()) hops_str += " -> "; - hops_str += hop.rc.router_id().ShortString(); + hops_str += hop.router_id().ShortString(); } return hops_str; } - nlohmann::json PathHopConfig::ExtractStatus() const - { - nlohmann::json obj{ - {"ip", rc.addr().to_string()}, - {"lifetime", to_json(lifetime)}, - {"router", rc.router_id().ToHex()}, - {"txid", txID.ToHex()}, - {"rxid", rxID.ToHex()}}; - return obj; - } - nlohmann::json Path::ExtractStatus() const { auto now = llarp::time_now_ms(); nlohmann::json obj{ - {"intro", intro.ExtractStatus()}, {"lastRecvMsg", to_json(last_recv_msg)}, {"lastLatencyTest", to_json(last_latency_test)}, - {"buildStarted", to_json(buildStarted)}, {"expired", is_expired(now)}, - {"expiresSoon", ExpiresSoon(now)}, - {"expiresAt", to_json(ExpireTime())}, {"ready", is_ready()}, - // {"txRateCurrent", m_LastTXRate}, - // {"rxRateCurrent", m_LastRXRate}, - // {"hasExit", SupportsAnyRoles(ePathRoleExit)} }; std::vector hopsObj; @@ -352,63 +269,9 @@ namespace llarp::path }); obj["hops"] = hopsObj; - // switch (_status) - // { - // case PathStatus::BUILDING: - // obj["status"] = "building"; - // break; - // case PathStatus::ESTABLISHED: - // obj["status"] = "established"; - // break; - // case PathStatus::TIMEOUT: - // obj["status"] = "timeout"; - // break; - // case PathStatus::EXPIRED: - // obj["status"] = "expired"; - // break; - // case PathStatus::FAILED: - // obj["status"] = "failed"; - // break; - // case PathStatus::IGNORE: - // obj["status"] = "ignored"; - // break; - // default: - // obj["status"] = "unknown"; - // break; - // } return obj; } - void Path::rebuild() - { - if (auto parent = handler.lock()) - { - std::vector new_hops; - - for (const auto& hop : hops) - new_hops.emplace_back(hop.rc); - - log::info(logcat, "{} rebuilding on {}", name(), to_string()); - parent->build(new_hops); - } - } - - bool Path::SendLatencyMessage(Router*) - { - // const auto now = r->now(); - // // send path latency test - // routing::PathLatencyMessage latency{}; - // latency.sent_time = randint(); - // latency.sequence_number = NextSeqNo(); - // m_LastLatencyTestID = latency.sent_time; - // m_LastLatencyTestTime = now; - // LogDebug(name(), " send latency test id=", latency.sent_time); - // if (not SendRoutingMessage(latency, r)) - // return false; - // FlushUpstream(r); - return true; - } - bool Path::update_exit(uint64_t) { // TODO: do we still want this concept? @@ -417,116 +280,31 @@ namespace llarp::path void Path::Tick(std::chrono::milliseconds now) { + if (not is_ready()) + return; + if (is_expired(now)) return; if (_is_linked) { } - - // m_LastRXRate = m_RXRate; - // m_LastTXRate = m_TXRate; - - // m_RXRate = 0; - // m_TXRate = 0; - - // if (_status == PathStatus::BUILDING) - // { - // if (buildStarted == 0s) - // return; - // if (now >= buildStarted) - // { - // const auto dlt = now - buildStarted; - // if (dlt >= path::BUILD_TIMEOUT) - // { - // LogWarn(name(), " waited for ", to_string(dlt), " and no path was built"); - // r->router_profiling().MarkPathFail(this); - // EnterState(PathStatus::EXPIRED, now); - // return; - // } - // } - // } - // check to see if this path is dead - // if (_status == PathStatus::ESTABLISHED) - // { - // auto dlt = now - last_latency_test; - // if (dlt > path::LATENCY_INTERVAL && last_latency_test_id == 0) - // { - // SendLatencyMessage(r); - // // latency test FEC - // r->loop()->call_later(2s, [self = shared_from_this(), r]() { - // if (self->last_latency_test_id) - // self->SendLatencyMessage(r); - // }); - // return; - // } - // dlt = now - last_recv_msg; - // if (dlt >= path::ALIVE_TIMEOUT) - // { - // LogWarn(name(), " waited for ", to_string(dlt), " and path looks dead"); - // r->router_profiling().MarkPathFail(this); - // EnterState(PathStatus::TIMEOUT, now); - // } - // } - // if (_status == PathStatus::IGNORE and now - last_recv_msg >= path::ALIVE_TIMEOUT) - // { - // // clean up this path as we dont use it anymore - // EnterState(PathStatus::EXPIRED, now); - // } } - /// how long we wait for a path to become active again after it times out - // constexpr auto PathReanimationTimeout = 45s; - - bool Path::is_expired(std::chrono::milliseconds now) const + void Path::set_established() { - (void)now; - // if (_status == PathStatus::FAILED) - // return true; - // if (_status == PathStatus::BUILDING) - // return false; - // if (_status == PathStatus::TIMEOUT) - // { - // return now >= last_recv_msg + PathReanimationTimeout; - // } - // if (_status == PathStatus::ESTABLISHED or _status == PathStatus::IGNORE) - // { - // return now >= ExpireTime(); - // } - return true; + log::info(logcat, "Path marked as successfully established!"); + _established = true; + intro.expiry = llarp::time_now_ms() + path::DEFAULT_LIFETIME; } + bool Path::is_expired(std::chrono::milliseconds now) const { return intro.is_expired(now); } + std::string Path::name() const { return fmt::format("TX={} RX={}", upstream_txid().to_string(), upstream_rxid().to_string()); } - /* TODO: replace this with sending an onion-ed data message - bool Path::SendRoutingMessage(std::string payload, Router*) - { - std::string buf(MAX_LINK_MSG_SIZE / 2, '\0'); - buf.insert(0, payload); - - // make nonce - TunnelNonce N; - N.Randomize(); - - // pad smaller messages - if (payload.size() < PAD_SIZE) - { - // randomize padding - crypto::randbytes( - reinterpret_cast(buf.data()) + payload.size(), PAD_SIZE - - payload.size()); - } - log::debug(logcat, "Sending {}B routing message to {}", buf.size(), Endpoint()); - - // TODO: path relaying here - - return true; - } - */ - template static std::chrono::milliseconds computeLatency(const Samples_t& samps) { diff --git a/llarp/path/path.hpp b/llarp/path/path.hpp index f2b7ee84e5..605236338d 100644 --- a/llarp/path/path.hpp +++ b/llarp/path/path.hpp @@ -1,8 +1,11 @@ #pragma once #include "path_handler.hpp" +#include "transit_hop.hpp" #include +#include +#include #include #include #include @@ -21,6 +24,7 @@ namespace llarp { struct Router; + struct Profiling; namespace service { @@ -29,23 +33,13 @@ namespace llarp namespace path { - struct TransitHop; - struct PathHopConfig; - - using recv_session_dgram_cb = std::function; - - // TODO: replace vector of PathHopConfig with vector of TransitHops - /// A path we made struct Path : public std::enable_shared_from_this { - std::vector hops; - - std::weak_ptr handler; - - service::Introduction intro; - - std::chrono::milliseconds buildStarted = 0s; + friend struct PathHandler; + friend class handlers::SessionEndpoint; + friend struct llarp::Profiling; + friend struct LinkManager; Path( Router& rtr, @@ -54,34 +48,32 @@ namespace llarp bool is_session = false, bool is_client = false); + // hops on constructed path + std::vector hops; + // local hop info for onioned responses and session messages + // std::shared_ptr _local_hop{}; + std::weak_ptr handler; + ClientIntro intro{}; + std::shared_ptr get_self() { return shared_from_this(); } std::weak_ptr get_weak() { return weak_from_this(); } nlohmann::json ExtractStatus() const; - std::string to_string() const; - - std::string HopsString() const; + std::string hop_string() const; std::chrono::milliseconds LastRemoteActivityAt() const { return last_recv_msg; } - void set_established() { _established = true; } - - void recv_path_data_message(bstring data); + void set_established(); - void link_session(recv_session_dgram_cb cb); + // void recv_path_data_message(bstring data); - bool unlink_session(); + void link_session(session_tag t); - bool is_linked() const { return _is_linked; } + bool unlink_session(session_tag t); - std::chrono::milliseconds ExpireTime() const { return buildStarted + hops[0].lifetime; } - - bool ExpiresSoon(std::chrono::milliseconds now, std::chrono::milliseconds dlt = 5s) const - { - return now >= (ExpireTime() - dlt); - } + bool is_linked() const { return not _linked_sessions.empty(); } void enable_exit_traffic(); @@ -89,36 +81,25 @@ namespace llarp bool update_exit(uint64_t tx_id); - bool is_expired(std::chrono::milliseconds now) const; - - /// build a new path on the same set of hops as us - /// regenerates keys - void rebuild(); + bool is_expired(std::chrono::milliseconds now = llarp::time_now_ms()) const; void Tick(std::chrono::milliseconds now); - bool resolve_ons(std::string name, std::function func = nullptr); + bool resolve_sns(std::string_view name, std::function func); - bool find_intro( - const dht::Key_t& location, - bool is_relayed = false, - uint64_t order = 0, - std::function func = nullptr); + bool find_client_contact(const dht::Key_t& location, std::function func); - bool publish_intro( - const service::EncryptedIntroSet& introset, - bool is_relayed = false, - uint64_t order = 0, - std::function func = nullptr); + bool publish_client_contact( + const EncryptedClientContact& ecc, std::function func); bool close_exit( - const Ed25519SecretKey& sk, std::string tx_id, std::function func = nullptr); + const Ed25519SecretKey& sk, std::string tx_id, std::function = nullptr); bool obtain_exit( const Ed25519SecretKey& sk, uint64_t flag, std::string tx_id, - std::function func = nullptr); + std::function func); /// sends a control request along a path /// @@ -129,11 +110,19 @@ namespace llarp /// func is called with a bt-encoded response string (if applicable), and /// a timeout flag (if set, response string will be empty) bool send_path_control_message( - std::string method, std::string body, std::function func = nullptr); + std::string method, std::string body, std::function func); bool send_path_data_message(std::string body); - bool is_ready() const; + std::string make_path_message(std::string payload); + + bool is_established() const { return _established; } + + bool is_ready(std::chrono::milliseconds now = llarp::time_now_ms()) const; + + std::shared_ptr get_parent(); + + TransitHop edge() const; RouterID upstream_rid(); const RouterID& upstream_rid() const; @@ -155,8 +144,6 @@ namespace llarp std::string name() const; - bool is_session_path() const { return _is_session_path; } - bool is_client_path() const { return _is_client; } bool operator<(const Path& other) const; @@ -165,14 +152,11 @@ namespace llarp bool operator!=(const Path& other) const; + std::string to_string() const; static constexpr bool to_string_formattable = true; - private: - std::string make_outer_payload(ustring_view payload); - - std::string make_outer_payload(ustring_view payload, SymmNonce& nonce); - - bool SendLatencyMessage(Router* r); + protected: + void populate_internals(const std::vector& _hops); /// call obtained exit hooks bool InformExitResult(std::chrono::milliseconds b); @@ -185,11 +169,13 @@ namespace llarp bool _is_session_path{false}; bool _is_client{false}; - recv_session_dgram_cb _recv_dgram; + const size_t num_hops; + + std::unordered_set _linked_sessions; - std::chrono::milliseconds last_recv_msg = 0s; - std::chrono::milliseconds last_latency_test = 0s; - uint64_t last_latency_test_id = 0; + std::chrono::milliseconds last_recv_msg{0s}; + std::chrono::milliseconds last_latency_test{0s}; + uint64_t last_latency_test_id{}; }; } // namespace path } // namespace llarp @@ -199,15 +185,10 @@ namespace std template <> struct hash { - size_t operator()(const llarp::path::Path& p) const + size_t operator()(const llarp::path::Path& p) const noexcept { - auto& first_hop = p.hops[0]; - llarp::AlignedBuffer b; - std::memcpy(b.data(), first_hop.txID.data(), PATHIDSIZE); - std::memcpy(&b[PATHIDSIZE], first_hop.txID.data(), PATHIDSIZE); - - auto h = hash>{}(b); - return h ^ hash{}(first_hop.upstream); + auto h = hash{}(p.upstream_txid()); + return h ^ hash{}(p.upstream_rid()); } }; } // namespace std diff --git a/llarp/path/path_context.cpp b/llarp/path/path_context.cpp index 78dbf67680..de894892a4 100644 --- a/llarp/path/path_context.cpp +++ b/llarp/path/path_context.cpp @@ -6,98 +6,105 @@ namespace llarp::path { - PathContext::PathContext(RouterID local_rid) : _local_rid{std::move(local_rid)} {} + static auto logcat = log::Cat("pathctx"); - void PathContext::allow_transit() - { - _allow_transit = true; - } + PathContext::PathContext(Router& r) : _r{r} {} - bool PathContext::is_transit_allowed() const - { - return _allow_transit; - } + void PathContext::allow_transit() { _allow_transit = true; } - std::vector> PathContext::get_local_paths_to_remote(const RouterID& r) - { - Lock_t l{paths_mutex}; - - std::vector> found; - - for (const auto& [pathid, path] : _path_map) - { - if (path->upstream_txid() == pathid) - continue; - - if (path->pivot_rid() == r && path->is_ready()) - found.push_back(path); - } - return found; - } + bool PathContext::is_transit_allowed() const { return _allow_transit; } void PathContext::add_path(std::shared_ptr path) { - Lock_t l{paths_mutex}; - _path_map.emplace(path->upstream_rxid(), path); - _path_map.emplace(path->upstream_txid(), path); - - _path_map.emplace(path->pivot_rxid(), path); _path_map.emplace(path->pivot_txid(), path); } - void PathContext::drop_paths(std::vector> droplist) + void PathContext::drop_paths(std::vector droplist) { for (auto itr = droplist.begin(); itr != droplist.end();) { - drop_path(*itr); + _drop_path(*itr); itr = droplist.erase(itr); } } - void PathContext::drop_path(const std::shared_ptr& path) + void PathContext::expire_hops(std::chrono::milliseconds now) { - if (auto itr = _path_map.find(path->upstream_rxid()); itr != _path_map.end()) - _path_map.erase(itr); + size_t n = 0; - if (auto itr = _path_map.find(path->upstream_txid()); itr != _path_map.end()) - _path_map.erase(itr); + for (auto itr = _transit_hops.begin(); itr != _transit_hops.end();) + { + if (itr->second->is_expired(now)) + { + itr = _transit_hops.erase(itr); + n += 1; + } + else + ++itr; + } - if (auto itr = _path_map.find(path->pivot_rxid()); itr != _path_map.end()) - _path_map.erase(itr); + if (n) + log::info(logcat, "{} expired TransitHops purged!", n); + } - if (auto itr = _path_map.find(path->pivot_txid()); itr != _path_map.end()) - _path_map.erase(itr); + void PathContext::drop_path(const HopID& hop_id) + { + _r.loop()->call([&]() { _drop_path(hop_id); }); } - bool PathContext::has_transit_hop(const std::shared_ptr& hop) + void PathContext::drop_path(const std::shared_ptr& path) { - Lock_t l{paths_mutex}; + _drop_path(path->upstream_rxid()); + _drop_path(path->pivot_txid()); + } - return _transit_hops.count(hop->rxid()) or _transit_hops.count(hop->txid()); + std::tuple PathContext::path_ctx_stats() const + { + return _r.loop()->call_get([&]() -> std::tuple { + return {_path_map.size() / 2, _transit_hops.size() / 2}; + }); } - void PathContext::put_transit_hop(std::shared_ptr hop) + bool PathContext::has_transit_hop(const std::shared_ptr& hop) const { - Lock_t l{paths_mutex}; + assert(_r.loop()->in_event_loop()); + return has_transit_hop(hop->rxid()) or has_transit_hop(hop->txid()); + } + + bool PathContext::has_transit_hop(const HopID& hop_id) const + { + assert(_r.loop()->in_event_loop()); + return _transit_hops.count(hop_id); + } + void PathContext::put_transit_hop(std::shared_ptr hop) + { + assert(_r.loop()->in_event_loop()); _transit_hops.emplace(hop->rxid(), hop); _transit_hops.emplace(hop->txid(), hop); } - std::shared_ptr PathContext::get_transit_hop(const HopID& path_id) + std::shared_ptr PathContext::get_transit_hop(const HopID& path_id) const { - Lock_t l{paths_mutex}; - + assert(_r.loop()->in_event_loop()); if (auto itr = _transit_hops.find(path_id); itr != _transit_hops.end()) return itr->second; return nullptr; } - std::shared_ptr PathContext::get_path(const HopID& hop_id) + void PathContext::_drop_path(const HopID& hop_id) + { + assert(_r.loop()->in_event_loop()); + + if (auto itr = _path_map.find(hop_id); itr != _path_map.end()) + _path_map.erase(itr); + } + + std::shared_ptr PathContext::_get_path(const HopID& hop_id) const { - Lock_t l{paths_mutex}; + assert(_r.loop()->in_event_loop()); if (auto itr = _path_map.find(hop_id); itr != _path_map.end()) return itr->second; @@ -105,14 +112,25 @@ namespace llarp::path return nullptr; } - std::shared_ptr PathContext::get_path(const std::shared_ptr& hop) + std::shared_ptr PathContext::get_path(const HopID& hop_id) const { - Lock_t l{paths_mutex}; + assert(_r.loop()->in_event_loop()); + return _get_path(hop_id); + } + + bool PathContext::has_path(const HopID& hop_id) const + { + assert(_r.loop()->in_event_loop()); + return _path_map.contains(hop_id); + } - if (auto maybe_path = get_path(hop->rxid()); maybe_path) + std::shared_ptr PathContext::get_path(const std::shared_ptr& hop) const + { + assert(_r.loop()->in_event_loop()); + if (auto maybe_path = _get_path(hop->rxid())) return maybe_path; - if (auto maybe_path = get_path(hop->txid()); maybe_path) + if (auto maybe_path = _get_path(hop->txid())) return maybe_path; return nullptr; @@ -120,25 +138,22 @@ namespace llarp::path std::shared_ptr PathContext::get_path_handler(const HopID& id) { - Lock_t l{paths_mutex}; - - if (auto maybe_path = get_path(id); maybe_path) - { - if (auto parent = maybe_path->handler.lock()) - return parent; - } + return _r.loop()->call_get([&]() -> std::shared_ptr { + if (auto maybe_path = _get_path(id)) + return maybe_path->get_parent(); - return nullptr; + return nullptr; + }); } std::shared_ptr PathContext::get_path_for_transfer(const HopID& id) { - Lock_t l{paths_mutex}; + return _r.loop()->call_get([&]() -> std::shared_ptr { + if (auto itr = _transit_hops.find(id); itr != _transit_hops.end()) + return itr->second; - if (auto itr = _transit_hops.find(id); itr != _transit_hops.end()) - return itr->second; - - return nullptr; + return nullptr; + }); } } // namespace llarp::path diff --git a/llarp/path/path_context.hpp b/llarp/path/path_context.hpp index 43be06a690..48a3311303 100644 --- a/llarp/path/path_context.hpp +++ b/llarp/path/path_context.hpp @@ -4,6 +4,7 @@ #include "path_types.hpp" #include "transit_hop.hpp" +#include #include #include #include @@ -11,56 +12,70 @@ #include #include +namespace llarp +{ + struct Router; +} + namespace llarp::path { struct PathContext { - explicit PathContext(RouterID local_rid); + explicit PathContext(Router& r); - void allow_transit(); + private: + Router& _r; - void reject_transit(); + using Lock_t = util::NullLock; + mutable util::NullMutex paths_mutex; - bool is_transit_allowed() const; + // Paths are 1:1 with edge rxIDs + std::unordered_map> _path_map; + + std::unordered_map> _transit_hops; + + bool _allow_transit{false}; + + // internal unsafe methods + void _drop_path(const HopID& hop_id); + + std::shared_ptr _get_path(const HopID& hop_id) const; + + public: + std::tuple path_ctx_stats() const; - bool has_transit_hop(const std::shared_ptr& hop); + bool has_transit_hop(const std::shared_ptr& hop) const; + + bool has_transit_hop(const HopID& hop_id) const; void put_transit_hop(std::shared_ptr hop); - std::shared_ptr get_path(const std::shared_ptr& hop); + bool has_path(const HopID& hop_id) const; + + std::shared_ptr get_path(const std::shared_ptr& hop) const; - std::shared_ptr get_path(const HopID& hop_id); + std::shared_ptr get_path(const HopID& hop_id) const; std::shared_ptr get_path_for_transfer(const HopID& topath); - std::shared_ptr get_transit_hop(const HopID&); + std::shared_ptr get_transit_hop(const HopID&) const; std::shared_ptr get_path_handler(const HopID& id); - /// get a set of all paths that we own who's endpoint is r - std::vector> get_local_paths_to_remote(const RouterID& r); - void add_path(std::shared_ptr p); - void drop_path(const std::shared_ptr& p); + void drop_path(const HopID& hop_id); - void drop_paths(std::vector> droplist); + void drop_path(const std::shared_ptr& p); - private: - const RouterID _local_rid; + void drop_paths(std::vector droplist); - using Lock_t = util::NullLock; - mutable util::NullMutex paths_mutex; + void expire_hops(std::chrono::milliseconds now); - std::unordered_map> _transit_hops; - - /** TODO: - - paths are not 1:1 with upstream RID - - paths are 1:1 with txid's - */ + void allow_transit(); - std::unordered_map> _path_map; + void reject_transit(); - bool _allow_transit{false}; + bool is_transit_allowed() const; }; } // namespace llarp::path diff --git a/llarp/path/path_handler.cpp b/llarp/path/path_handler.cpp index b185f5f59e..710fa9c255 100644 --- a/llarp/path/path_handler.cpp +++ b/llarp/path/path_handler.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -17,20 +18,11 @@ namespace llarp::path { static auto logcat = log::Cat("pathhandler"); - bool BuildLimiter::Attempt(const RouterID& router) - { - return _edge_limiter.Insert(router); - } + bool BuildLimiter::Attempt(const RouterID& router) { return _edge_limiter.Insert(router); } - void BuildLimiter::Decay(std::chrono::milliseconds now) - { - _edge_limiter.Decay(now); - } + void BuildLimiter::Decay(std::chrono::milliseconds now) { _edge_limiter.Decay(now); } - bool BuildLimiter::Limited(const RouterID& router) const - { - return _edge_limiter.Contains(router); - } + bool BuildLimiter::Limited(const RouterID& router) const { return _edge_limiter.Contains(router); } nlohmann::json BuildStats::ExtractStatus() const { @@ -65,19 +57,28 @@ namespace llarp::path log::debug(logcat, "Adding path..."); Lock_t l(paths_mutex); - _paths.insert_or_assign(p->pivot_rid(), p); - associate_hop_ids(p); + _paths.insert_or_assign(p->upstream_rxid(), p); _router.path_context()->add_path(p); } + void PathHandler::drop_path(const std::shared_ptr& p) + { + Lock_t l{paths_mutex}; + + if (auto itr = _paths.find(p->upstream_rxid()); itr != _paths.end()) + _paths.erase(itr); + + _router.path_context()->drop_path(p); + } + std::optional> PathHandler::get_random_path() { - std::optional>> t = std::nullopt; + auto p = std::make_optional>>(); - std::sample(_paths.begin(), _paths.end(), &t, 1, csrng); // TOFIX: TESTNET: + std::sample(_paths.begin(), _paths.end(), &*p, 1, csrng); - return t.has_value() ? std::make_optional(t->second) : std::nullopt; + return p.has_value() ? std::make_optional(p->second) : std::nullopt; } std::optional> PathHandler::get_path_conditional( @@ -118,9 +119,9 @@ namespace llarp::path for (size_t i = 0; i < n; ++i) { - std::pair> t; + std::pair> t; - std::sample(_paths.begin(), _paths.end(), &t, 1, csrng); // TOFIX: TESTNET: + std::sample(_paths.begin(), _paths.end(), &t, 1, csrng); selected->insert(selected->end(), t.second); } @@ -166,76 +167,61 @@ namespace llarp::path return selected; } - size_t PathHandler::paths_at_time(std::chrono::milliseconds futureTime) const - { - size_t num = 0; - Lock_t l(paths_mutex); - - for (const auto& item : _paths) - { - if (item.second->is_ready() && !item.second->is_expired(futureTime)) - ++num; - } - return num; - } - void PathHandler::reset_path_state() { build_interval_limit = PATH_BUILD_RATE; - _last_build = 0s; + last_build = 0s; } // called within the scope of locked mutex void PathHandler::tick_paths() { + Lock_t l{paths_mutex}; + const auto now = llarp::time_now_ms(); - for (auto& item : _paths) + for (auto& [_, p] : _paths) { - item.second->Tick(now); + if (p) + p->Tick(now); } } - std::chrono::milliseconds PathHandler::now() const - { - return _router.now(); - } + std::chrono::milliseconds PathHandler::now() const { return _router.now(); } // called within the scope of locked mutex void PathHandler::expire_paths(std::chrono::milliseconds now) { + Lock_t lock{paths_mutex}; + if (_paths.size() == 0) return; - std::vector> droplist; + std::vector to_drop; for (auto itr = _paths.begin(); itr != _paths.end();) { - if (itr->second->is_expired(now)) + if (itr->second and itr->second->is_established() and itr->second->is_expired(now)) { - droplist.push_back(std::move(itr->second)); + to_drop.push_back(itr->second->upstream_rxid()); + to_drop.push_back(itr->second->pivot_txid()); itr = _paths.erase(itr); } else ++itr; } - _router.path_context()->drop_paths(std::move(droplist)); + if (not to_drop.empty()) + { + log::debug(logcat, "{} paths expired; giving path-ctx droplist", to_drop.size()); + _router.path_context()->drop_paths(std::move(to_drop)); + } } // called within the scope of locked mutex std::optional> PathHandler::get_path(HopID hid) const { - if (auto itr = _path_lookup.find(hid); itr != _path_lookup.end()) - return get_path(itr->second); - - return std::nullopt; - } - - // called within the scope of locked mutex - std::optional> PathHandler::get_path(const RouterID& rid) const - { - if (auto itr = _paths.find(rid); itr != _paths.end()) + if (auto itr = _paths.find(hid); itr != _paths.end()) return itr->second; return std::nullopt; @@ -251,23 +237,19 @@ namespace llarp::path } } - std::optional> PathHandler::get_path_intros_conditional( - std::function filter) const + intro_set PathHandler::get_current_client_intros() const { - std::set intros; - Lock_t l{paths_mutex}; + Lock_t lock{paths_mutex}; - for (const auto& p : _paths) + intro_set intros{}; + auto now = llarp::time_now_ms(); + + for (const auto& [_, p] : _paths) { - if (p.second->is_ready() and filter(p.second->intro)) - { - intros.insert(p.second->intro); - } + if (p and p->is_ready(now)) + intros.emplace(p->intro); } - if (intros.empty()) - return std::nullopt; - return intros; } @@ -280,7 +262,7 @@ namespace llarp::path now = llarp::time_now_ms(); _router.pathbuild_limiter().Decay(now); - // expire_paths(now); + expire_paths(now); if (auto n = should_build_more(); n > 0) build_more(n); @@ -289,7 +271,7 @@ namespace llarp::path if (_build_stats.attempts > 50) { - if (_build_stats.SuccessRatio() <= BuildStats::MinGoodRatio && now - last_warn_time > 5s) + if (_build_stats.SuccessRatio() <= BuildStats::THRESHOLD && now - last_warn_time > 5s) { log::warning(logcat, "Low path build success: {}", _build_stats); last_warn_time = now; @@ -312,26 +294,39 @@ namespace llarp::path std::optional PathHandler::select_first_hop(const std::set& exclude) const { - std::optional found = std::nullopt; - _router.for_each_connection([&](link::Connection& conn) { - RouterID rid{conn.conn->remote_key()}; + std::set current_remotes; -#ifndef TESTNET - if (_router.is_bootstrap_node(rid)) - return; -#endif - if (exclude.count(rid)) - return; + if (_router.node_db()->strict_connect_enabled()) + current_remotes = _router.node_db()->pinned_edges(); + else + current_remotes = _router.get_current_remotes(); + std::function hook = [&](const RouterID& rid) { + if (exclude.count(rid)) + return false; if (build_cooldown_hit(rid)) - return; - + return false; + // always returns false on testnet builds if (_router.router_profiling().is_bad_for_path(rid)) - return; + return false; + return true; + }; + + auto edge = meta::sample(current_remotes, hook); + + return edge ? _router.node_db()->get_rc(*edge) : std::nullopt; + } + + size_t PathHandler::num_active_paths() const + { + Lock_t l(paths_mutex); + + size_t n{}; + + for (const auto& [_, p] : _paths) + n += (p != nullptr); - found = _router.node_db()->get_rc(rid); - }); - return found; + return n; } size_t PathHandler::num_paths() const @@ -346,38 +341,18 @@ namespace llarp::path log::trace(logcat, "{} called", __PRETTY_FUNCTION__); _running = false; - - Lock_t l{paths_mutex}; - - for (auto& [_, p] : _paths) - { - dissociate_hop_ids(p); - } - _paths.clear(); return true; } - bool PathHandler::is_stopped() const - { - return !_running.load(); - } + bool PathHandler::is_stopped() const { return !_running.load(); } - bool PathHandler::should_remove() const - { - return is_stopped() and num_paths() == 0; - } + bool PathHandler::should_remove() const { return is_stopped() and num_active_paths() == 0; } - bool PathHandler::build_cooldown_hit(RouterID edge) const - { - return _router.pathbuild_limiter().Limited(edge); - } + bool PathHandler::build_cooldown_hit(RouterID edge) const { return _router.pathbuild_limiter().Limited(edge); } - bool PathHandler::build_cooldown() const - { - return llarp::time_now_ms() < _last_build + build_interval_limit; - } + bool PathHandler::build_cooldown() const { return llarp::time_now_ms() < last_build + build_interval_limit; } size_t PathHandler::should_build_more() const { @@ -387,7 +362,9 @@ namespace llarp::path if (build_cooldown()) return {}; - return num_paths_desired - num_paths(); + auto n_paths = num_paths(); + + return num_paths_desired >= n_paths ? num_paths_desired - n_paths : 0; } std::optional> PathHandler::get_hops_to_random() @@ -404,74 +381,191 @@ namespace llarp::path return std::nullopt; } - std::optional> PathHandler::aligned_hops_to_remote( - const RouterID& pivot, const std::set& exclude) + // std::optional> PathHandler::specific_hops_to_remote(std::vector specifgic) + // { + // log::trace(logcat, "{} called", __PRETTY_FUNCTION__); + + // assert(num_hops); + + // auto hops_needed = num_hops; + // std::vector hops{}; + + // } + + std::optional> PathHandler::aligned_hops_between(const RouterID& edge, const RouterID& pivot) { log::trace(logcat, "{} called", __PRETTY_FUNCTION__); - const auto& path_config = _router.config()->paths; + assert(num_hops); + auto hops_needed = num_hops; - // make a copy here to reference rather than creating one in the lambda every iteration - std::set to_exclude{exclude.begin(), exclude.end()}; - std::vector hops; + if (hops_needed == 1) + { + log::error(logcat, "Stop using debug methods for stupid path structures"); + return std::nullopt; + } - if (auto maybe = select_first_hop(exclude)) - hops.push_back(*maybe); + std::vector hops{}; + + RemoteRC pivot_rc{}; + + if (auto maybe = _router.node_db()->get_rc(pivot)) + { + // leave space to add the pivot last + --hops_needed; + pivot_rc = std::move(*maybe); + } else + return std::nullopt; + + if (auto maybe = _router.node_db()->get_rc(edge)) { - log::warning(logcat, "No first hop candidate for aligned hops!"); + // leave space to add the pivot last + --hops_needed; + hops.emplace_back(std::move(*maybe)); + } + else return std::nullopt; + + auto filter = [&](const RemoteRC& rc) -> bool { + const auto& rid = rc.router_id(); + + if (rid == edge || rid == pivot) + return false; + + return true; + }; + + if (auto maybe_rcs = _router.node_db()->get_n_random_rcs_conditional(hops_needed, filter)) + { + log::info(logcat, "Found {} RCs for aligned path (needed: {})", maybe_rcs->size(), hops_needed); + hops.insert(hops.end(), maybe_rcs->begin(), maybe_rcs->end()); + hops.emplace_back(std::move(pivot_rc)); + return hops; + } + + log::warning(logcat, "Failed to find RC for aligned path! (needed:{})", num_hops); + return std::nullopt; + } + + std::optional> PathHandler::aligned_hops_to_remote( + const RouterID& pivot, const std::set& exclude) + { + log::trace(logcat, "{} called", __PRETTY_FUNCTION__); + + assert(num_hops); + + auto hops_needed = num_hops; + + std::vector hops{}; + RemoteRC pivot_rc{}; + + if (auto maybe = _router.node_db()->get_rc(pivot)) + { + // if we only need one hop, return + if (hops_needed == 1) + { + hops.emplace_back(std::move(*maybe)); + return hops; + } + + // leave space to add the pivot last + --hops_needed; + pivot_rc = *maybe; } + else + return std::nullopt; + + auto netmask = _router.config()->paths.unique_hop_netmask; - RemoteRC remote_rc; - to_exclude.insert(remote_rc.router_id()); // we will manually add this last + // make a copy here to reference rather than creating one in the lambda every iteration + std::set to_exclude{exclude.begin(), exclude.end()}; + to_exclude.insert(pivot); + std::vector excluded_ranges{}; + // excluded_ranges.emplace_back(pivot_rc.addr().to_ipv4() / netmask); - if (const auto maybe = _router.node_db()->get_rc(pivot)) + if (auto maybe = select_first_hop(to_exclude)) { - remote_rc = *maybe; + hops.emplace_back(std::move(*maybe)); + // excluded_ranges.emplace_back(hops.back().addr().to_ipv4() / netmask); + --hops_needed; } else + { + log::warning(logcat, "No first hop candidate for aligned hops!"); return std::nullopt; + } - // leave one extra spot for the terminal node - auto hops_needed = num_hops - hops.size() - 1; + to_exclude.insert(hops.back().router_id()); - auto filter = [&r = _router, &to_exclude](const RemoteRC& rc) -> bool { + auto filter = [&](const RemoteRC& rc) -> bool { const auto& rid = rc.router_id(); + auto v4 = rc.addr().to_ipv4(); - if (r.router_profiling().is_bad_for_path(rid, 1)) - to_exclude.insert(rid); + for (auto& e : excluded_ranges) + { + if (e.contains(v4)) + return false; + } - if (to_exclude.count(rid)) + // if its already excluded, fail; (we want it added even on success) + if (not to_exclude.insert(rid).second) return false; - // add the rid on a success case so we don't select it again - to_exclude.insert(rid); + excluded_ranges.emplace_back(v4 % netmask); + + if (_router.router_profiling().is_bad_for_path(rid, 1)) + return false; return true; }; - auto maybe_rcs = _router.node_db()->get_n_random_rcs_conditional(hops_needed, filter, true); + log::trace(logcat, "First/last hop selected, {} hops remaining to select", hops_needed); - if (maybe_rcs) + if (auto maybe_hops = _router.node_db()->get_n_random_rcs_conditional(hops_needed, filter)) { - auto& rcs = *maybe_rcs; - hops.insert(hops.end(), rcs.begin(), rcs.end()); - hops.emplace_back(remote_rc); + log::trace(logcat, "Found {} RCs for aligned path (needed: {})", maybe_hops->size(), hops_needed); + hops.insert(hops.end(), maybe_hops->begin(), maybe_hops->end()); + hops.emplace_back(std::move(pivot_rc)); + return hops; + } + + log::warning(logcat, "Failed to find {} RCs for aligned path to pivot: {}", hops_needed, pivot); + return std::nullopt; -#ifndef TESTNET - if (not path_config.check_rcs({hops.begin(), hops.end()})) + while (hops_needed) + { + // do this 1 at a time so we can check for IP range overlap + if (auto maybe_rc = _router.node_db()->get_random_rc_conditional(filter)) + { + hops.emplace_back(std::move(*maybe_rc)); + } + else + { + log::warning( + logcat, "Failed to find RC for aligned path! (needed:{}, remaining:{})", num_hops, hops_needed); + + if (not hops.empty()) + { + for (auto& h : hops) + log::info(logcat, "{}", h); + } return std::nullopt; -#endif + } - return hops; + --hops_needed; } - return std::nullopt; + // add pivot rc last + hops.emplace_back(std::move(pivot_rc)); + + return hops; } bool PathHandler::build_path_to_random() { + Lock_t l(paths_mutex); + if (auto maybe_hops = get_hops_to_random()) { build(*maybe_hops); @@ -482,9 +576,11 @@ namespace llarp::path return false; } - bool PathHandler::build_path_aligned_to_remote(const NetworkAddress& remote) + bool PathHandler::build_path_aligned_to_remote(const RouterID& remote) { - if (auto maybe_hops = aligned_hops_to_remote(remote.router_id())) + Lock_t l(paths_mutex); + + if (auto maybe_hops = aligned_hops_to_remote(remote)) { build(*maybe_hops); return true; @@ -502,9 +598,8 @@ namespace llarp::path return false; } - _last_build = llarp::time_now_ms(); + last_build = llarp::time_now_ms(); const auto& edge = hops[0].router_id(); - const auto& terminus = hops.back().router_id(); if (not _router.pathbuild_limiter().Attempt(edge)) { @@ -512,16 +607,6 @@ namespace llarp::path return false; } - { - Lock_t l{paths_mutex}; - - if (auto [it, b] = _paths.try_emplace(terminus, nullptr); not b) - { - log::warning(logcat, "Pending build to {} already underway... aborting...", terminus); - return false; - } - } - return true; } @@ -529,16 +614,26 @@ namespace llarp::path { auto path = std::make_shared(_router, hops, get_weak()); - log::info(logcat, "Building path -> {} : {}", path->to_string(), path->HopsString()); + { + Lock_t l{paths_mutex}; + + if (auto [it, b] = _paths.try_emplace(path->upstream_rxid(), nullptr); not b) + { + log::warning(logcat, "Pending build to {} already underway... aborting...", path->upstream_rxid()); + return nullptr; + } + } + + log::info(logcat, "Building path -> {} : {}", path->to_string(), path->hop_string()); return path; } - std::string PathHandler::build2(std::shared_ptr& path) + std::string PathHandler::build2(const std::shared_ptr& path) { std::vector frames(path::MAX_LEN); auto& path_hops = path->hops; - int n_hops = static_cast(path_hops.size()); + int n_hops = static_cast(path->num_hops); size_t last_len{0}; // each hop will be able to read the outer part of its frame and decrypt @@ -552,13 +647,10 @@ namespace llarp::path // the same entity from knowing they are part of the same path // (unless they're adjacent in the path; nothing we can do about that obviously). - // i from n_hops downto 0 + // i from n_hops down to 0 for (int i = n_hops - 1; i >= 0; --i) { - const auto& next_rid = i == n_hops - 1 ? path_hops[i].rc.router_id() : path_hops[i + 1].rc.router_id(); - path_hops[i].upstream = next_rid; - - frames[i] = PathBuildMessage::serialize_hop(path_hops[i]); + frames[i] = PATH::BUILD::serialize_hop(path_hops[i]); if (last_len and frames[i].size() != last_len) { @@ -570,12 +662,12 @@ namespace llarp::path for (auto j = i + 1; j < n_hops; ++j) { - auto _onion_nonce = path_hops[i].nonce ^ path_hops[i].nonceXOR; + auto _onion_nonce = path_hops[i].kx.nonce ^ path_hops[i].kx.xor_nonce; crypto::onion( reinterpret_cast(frames[j].data()), frames[j].size(), - path_hops[i].shared, + path_hops[i].kx.shared_secret, _onion_nonce, _onion_nonce); } @@ -590,7 +682,7 @@ namespace llarp::path _build_stats.attempts++; - return Frames::serialize(std::move(frames)); + return ONION::serialize_frames(std::move(frames)); } bool PathHandler::build3(RouterID upstream, std::string payload, std::function handler) @@ -598,70 +690,59 @@ namespace llarp::path return _router.send_control_message(std::move(upstream), "path_build", std::move(payload), std::move(handler)); } + // called within the scope of a locked mutex void PathHandler::build(std::vector hops) { - if (pre_build(hops); auto new_path = build1(hops)) + // error message logs in function scope + if (not pre_build(hops)) + return; + + if (auto new_path = build1(hops)) { assert(new_path); auto payload = build2(new_path); - auto pivot = new_path->pivot_rid(); auto upstream = new_path->upstream_rid(); - if (not build3( - std::move(upstream), std::move(payload), [this, new_path, pivot](oxen::quic::message m) mutable { - if (m) - { - log::critical(logcat, "PATH ESTABLISHED: {}", new_path->HopsString()); - return path_build_succeeded(std::move(new_path)); - } - - try + if (not build3(std::move(upstream), std::move(payload), [this, new_path](oxen::quic::message m) mutable { + if (m) + { + log::critical(logcat, "PATH ESTABLISHED: {}", new_path->hop_string()); + return path_build_succeeded(std::move(new_path)); + } + + try + { + // TODO: inform failure (what this means needs revisiting, badly) + if (m.timed_out) { - // TODO: inform failure (what this means needs revisiting, badly) - if (m.timed_out) - { - log::warning(logcat, "Path build request timed out!"); - } - else - { - oxenc::bt_dict_consumer d{m.body()}; - auto status = d.require(messages::STATUS_KEY); - log::warning(logcat, "Path build returned failure status: {}", status); - } + log::warning(logcat, "Path build request timed out!"); } - catch (const std::exception& e) + else { - log::warning( - logcat, - "Exception caught parsing path build response: {}; input: {}", - e.what(), - m.body()); + oxenc::bt_dict_consumer d{m.body()}; + auto status = d.require(messages::STATUS_KEY); + log::warning(logcat, "Path build returned failure status: {}", status); } - - path_build_failed(pivot, std::move(new_path), m.timed_out); - })) + } + catch (const std::exception& e) + { + log::warning( + logcat, "Exception caught parsing path build response: {}; input: {}", e.what(), m.body()); + } + + path_build_failed(std::move(new_path), m.timed_out); + })) { log::warning(logcat, "Error sending path_build control message"); - path_build_failed(pivot, new_path); + path_build_failed(new_path); } } } - void PathHandler::drop_path(const RouterID& remote) - { - Lock_t l{paths_mutex}; - - if (auto itr = _paths.find(remote); itr != _paths.end()) - _paths.erase(itr); - } - - void PathHandler::path_build_failed(const RouterID& remote, std::shared_ptr p, bool timeout) + void PathHandler::path_build_failed(std::shared_ptr p, bool timeout) { - if (not timeout) - dissociate_hop_ids(p); - - drop_path(remote); + drop_path(p); if (timeout) { @@ -669,7 +750,7 @@ namespace llarp::path _build_stats.timeouts += 1; } else - _build_stats.build_fails -= 1; + _build_stats.build_fails += 1; path_build_backoff(); } @@ -696,24 +777,4 @@ namespace llarp::path log::warning(logcat, "Path {} died post-build", p->to_string()); _build_stats.path_fails++; } - - void PathHandler::associate_hop_ids(std::shared_ptr& p) - { - for (auto& h : p->hops) - { - auto rid = p->pivot_rid(); - _path_lookup.emplace(h.rxID, rid); - _path_lookup.emplace(h.txID, rid); - } - } - - void PathHandler::dissociate_hop_ids(std::shared_ptr& p) - { - for (auto& h : p->hops) - { - _path_lookup.erase(h.txID); - _path_lookup.erase(h.rxID); - } - } - } // namespace llarp::path diff --git a/llarp/path/path_handler.hpp b/llarp/path/path_handler.hpp index c67f271ba1..63db6e3795 100644 --- a/llarp/path/path_handler.hpp +++ b/llarp/path/path_handler.hpp @@ -3,8 +3,7 @@ #include "path_types.hpp" #include -#include -#include +#include #include #include #include @@ -17,7 +16,7 @@ namespace std template <> struct hash> { - size_t operator()(const std::pair& i) const + size_t operator()(const std::pair& i) const noexcept { return hash{}(i.first) ^ hash{}(i.second); } @@ -34,7 +33,7 @@ namespace llarp inline constexpr size_t MAX_PATHS{32}; // default number of paths per PathHandler - inline constexpr size_t DEFAULT_PATHS_HELD{1}; + inline constexpr size_t DEFAULT_PATHS_HELD{4}; // forward declare struct Path; @@ -60,13 +59,13 @@ namespace llarp /// Stats about all our path builds struct BuildStats { - static constexpr double MinGoodRatio = 0.25; + static constexpr double THRESHOLD{0.25}; - uint64_t attempts = 0; - uint64_t success = 0; - uint64_t build_fails = 0; // path build failures - uint64_t path_fails = 0; // path failures post-build - uint64_t timeouts = 0; + uint64_t attempts{}; + uint64_t success{}; + uint64_t build_fails{}; // path build failures + uint64_t path_fails{}; // path failures post-build + uint64_t timeouts{}; nlohmann::json ExtractStatus() const; @@ -91,47 +90,44 @@ namespace llarp struct PathHandler { + friend struct Path; + private: - std::chrono::milliseconds last_warn_time = 0s; + std::chrono::milliseconds last_warn_time{0s}; std::unordered_map> path_cache; void path_build_backoff(); - void associate_hop_ids(std::shared_ptr& p); - protected: - void dissociate_hop_ids(std::shared_ptr& p); - /// flag for ::Stop() std::atomic _running; - size_t num_paths_desired; + const size_t num_paths_desired; BuildStats _build_stats; using Lock_t = util::NullLock; mutable util::NullMutex paths_mutex; - // TODO: make into templated map object - std::unordered_map _path_lookup; - std::unordered_map> _paths; + // TESTNET: path mapping + std::unordered_map> _paths; /// return true if we hit our soft limit for building paths too fast on a first hop bool build_cooldown_hit(RouterID edge) const; - void drop_path(const RouterID& remote); + void drop_path(const std::shared_ptr& p); virtual void path_died(std::shared_ptr p); - void path_build_failed(const RouterID& remote, std::shared_ptr p, bool timeout = false); + void path_build_failed(std::shared_ptr p, bool timeout = false); virtual void path_build_succeeded(std::shared_ptr p); public: Router& _router; size_t num_hops; - std::chrono::milliseconds _last_build = 0s; - std::chrono::milliseconds build_interval_limit = MIN_PATH_BUILD_INTERVAL; + std::chrono::milliseconds last_build{0s}; + std::chrono::milliseconds build_interval_limit{MIN_PATH_BUILD_INTERVAL}; std::set snode_blacklist; @@ -157,10 +153,7 @@ namespace llarp std::optional> get_path(HopID id) const; - std::optional> get_path(const RouterID& router) const; - - std::optional> get_path_intros_conditional( - std::function filter) const; + intro_set get_current_client_intros() const; nlohmann::json ExtractStatus() const; @@ -180,15 +173,15 @@ namespace llarp std::optional>> get_n_random_paths_conditional( size_t n, std::function)> filter, bool exact = false); - /// count the number of paths that will exist at this timestamp in future - size_t paths_at_time(std::chrono::milliseconds futureTime) const; - virtual void reset_path_state(); /// return true if we hit our soft limit for building paths too fast bool build_cooldown() const; - /// get the number of paths in this status + /// get the number of ACTIVE paths in this status + size_t num_active_paths() const; + + /// get the number of ALL paths (both active and those being currently build) size_t num_paths() const; const BuildStats& build_stats() const { return _build_stats; } @@ -212,18 +205,23 @@ namespace llarp bool build_path_to_random(); - bool build_path_aligned_to_remote(const NetworkAddress& remote); + bool build_path_aligned_to_remote(const RouterID& remote); + + // TESTNET: testing methods + // std::optional> specific_hops_to_remote(std::vector hops); + + std::optional> aligned_hops_between(const RouterID& edge, const RouterID& pivot); std::optional> aligned_hops_to_remote( const RouterID& pivot, const std::set& exclude = {}); // The build logic is segmented into functions designed to be called sequentially. - // - pre_build() : This handles all checking of the vector of hops, verifying with buildlimiter, ensuring - // there is no ongoing path-build, etc + // - pre_build() : This handles all checking of the vector of hops, verifying with buildlimiter, etc // - build1() : This can be re-implemented by inheriting classes that want to pass different parameters to // the created path. This is useful ßin cases like Outbound Sessions, Paths are constructed with the // respective is_client and is_exit booleans set. Regardless, the implementation needs to return the - // created shared_ptr to be passed by reference to build2(...) and build3(...). + // created shared_ptr to be passed by reference to build2(...) and build3(...). The implementation MUST + // also check if the upstream rxid is already being used for a current path (very unlikely) // - build2() : This contains the bulk of the code that is identical across all instances of path building. // It returns the payload holding the encoded frames for each hop. // - build3() : Inheriting classes can pass their own response handler functions as the second parameter, @@ -232,16 +230,16 @@ namespace llarp // failures for that respective remote or not. // - build() : This function calls pre_build() + build{1,2,3}() in the correct order and is used for the // usual times that PathBuilder initiates a path build + void build(std::vector hops); + bool pre_build(std::vector& hops); virtual std::shared_ptr build1(std::vector& hops); - std::string build2(std::shared_ptr& path); + std::string build2(const std::shared_ptr& path); bool build3(RouterID upstream, std::string payload, std::function handler); - void build(std::vector hops); - void for_each_path(std::function&)> visit) const; /// pick a first hop diff --git a/llarp/path/path_types.hpp b/llarp/path/path_types.hpp index 1a248b6cca..f26b501f74 100644 --- a/llarp/path/path_types.hpp +++ b/llarp/path/path_types.hpp @@ -1,9 +1,9 @@ #pragma once #include +#include #include #include -#include #include namespace llarp @@ -11,49 +11,14 @@ namespace llarp struct HopID final : public AlignedBuffer { using AlignedBuffer::AlignedBuffer; - }; - namespace path - { - /// configuration for a single hop when building a path - struct PathHopConfig + static HopID make_random() { - /// path id - HopID txID, rxID; - // router contact of router - RemoteRC rc; - /// nonce for key exchange - SymmNonce nonce; - /// shared secret at this hop - SharedSecret shared; - /// hash of shared secret used for nonce mutation - SymmNonce nonceXOR; - /// next hop's router id - RouterID upstream; - // lifetime - std::chrono::milliseconds lifetime = DEFAULT_LIFETIME; - - nlohmann::json ExtractStatus() const; - - bool operator<(const PathHopConfig& other) const - { - return std::tie(txID, rxID, rc, upstream, lifetime) - < std::tie(other.txID, other.rxID, other.rc, other.upstream, other.lifetime); - } - - bool operator==(const PathHopConfig& other) const - { - return std::tie(txID, rxID, rc, upstream, lifetime) - == std::tie(other.txID, other.rxID, other.rc, other.upstream, other.lifetime); - } - - bool operator!=(const PathHopConfig& other) const { return not(*this == other); } - }; - - // milliseconds waiting between builds on a path per router - static constexpr auto MIN_PATH_BUILD_INTERVAL = 500ms; - static constexpr auto PATH_BUILD_RATE = 100ms; - } // namespace path + HopID h; + h.Randomize(); + return h; + } + }; } // namespace llarp namespace std diff --git a/llarp/path/transit_hop.cpp b/llarp/path/transit_hop.cpp index dc46efd374..9f7879e1e3 100644 --- a/llarp/path/transit_hop.cpp +++ b/llarp/path/transit_hop.cpp @@ -9,77 +9,81 @@ namespace llarp::path { static auto logcat = log::Cat("transit-hop"); - std::shared_ptr TransitHop::deserialize_hop( - oxenc::bt_dict_consumer&& btdc, const RouterID& src, Router& r, const PubKey& remote_pk, const SymmNonce& nonce) + void TransitHop::deserialize(oxenc::bt_dict_consumer&& btdc, const RouterID& src, const Router& r) { - auto hop = std::make_shared(); - try { - hop->lifetime = btdc.require("l") * 1ms; - hop->_rxid.from_string(btdc.require("r")); - hop->_txid.from_string(btdc.require("t")); - hop->_upstream.from_string(btdc.require("u")); + bt_decode(std::move(btdc)); } catch (const std::exception& e) { - log::warning(logcat, "TransitHop caught bt parsing exception:{}", e.what()); + log::warning(logcat, "TransitHop caught bt parsing exception: {}", e.what()); throw std::runtime_error{messages::ERROR_RESPONSE}; } - if (hop->rxid().is_zero() || hop->txid().is_zero()) - throw std::runtime_error{PathBuildMessage::BAD_PATHID}; - - if (hop->lifetime > path::DEFAULT_LIFETIME) - throw std::runtime_error{PathBuildMessage::BAD_LIFETIME}; + if (_rxid.is_zero() || _txid.is_zero()) + throw std::runtime_error{PATH::BUILD::BAD_PATHID}; - hop->downstream() = src; + if (r.path_context()->has_transit_hop(_rxid) || r.path_context()->has_transit_hop(_txid)) + throw std::runtime_error{PATH::BUILD::BAD_PATHID}; - if (r.path_context()->has_transit_hop(hop)) - throw std::runtime_error{PathBuildMessage::BAD_PATHID}; + _downstream = src; - // TODO: get this from the first dh - if (!crypto::dh_server(hop->shared, remote_pk, r.identity(), nonce)) - throw std::runtime_error{PathBuildMessage::BAD_CRYPTO}; + if (_upstream == r.local_rid()) + terminal_hop = true; // generate hash of hop key for nonce mutation - ShortHash xor_hash; - crypto::shorthash(xor_hash, hop->shared.data(), hop->shared.size()); - hop->nonceXOR = xor_hash.data(); // nonceXOR is 24 bytes, ShortHash is 32; this will truncate - - log::debug(logcat, "TransitHop data successfully deserialized"); + kx.generate_xor(); - return hop; + log::trace(logcat, "TransitHop data successfully deserialized: {}", to_string()); } - bool TransitHop::is_expired(std::chrono::milliseconds now) const + void TransitHop::bt_decode(oxenc::bt_dict_consumer&& btdc) { - return destroy || (now >= expiry_time()); + _rxid.from_string(btdc.require("r")); + _txid.from_string(btdc.require("t")); + _upstream.from_string(btdc.require("u")); + expiry = llarp::time_now_ms() + path::DEFAULT_LIFETIME; } - std::chrono::milliseconds TransitHop::expiry_time() const + std::string TransitHop::bt_encode() const { - return started + lifetime; - } + oxenc::bt_dict_producer btdp; - std::string TransitHop::to_string() const - { - return "[TransitHop: tx={} rx={} upstream={} downstream={} started={} lifetime={}"_format( - _txid, _rxid, _upstream, _downstream, started.count(), lifetime.count()); + btdp.append("r", _rxid.to_view()); + btdp.append("t", _txid.to_view()); + btdp.append("u", _upstream.to_view()); + + return std::move(btdp).str(); } - void TransitHop::Stop() + std::optional> TransitHop::next_id(const HopID& h) const { - // TODO: still need this concept? + std::optional> ret = std::nullopt; + + if (h == _rxid) + ret = {_upstream, _txid}; + else if (h == _txid) + ret = {_downstream, _rxid}; + + return ret; } - void TransitHop::SetSelfDestruct() + nlohmann::json TransitHop::ExtractStatus() const { - destroy = true; + return { + {"rid", router_id().ToHex()}, + {"rxid", rxid().ToHex()}, + {"txid", txid().ToHex()}, + {"expiry", to_json(expiry)}, + {"txid", _txid.ToHex()}, + {"rxid", _rxid.ToHex()}}; } - void TransitHop::QueueDestroySelf(Router* r) + std::string TransitHop::to_string() const { - r->loop()->call([self = shared_from_this()] { self->SetSelfDestruct(); }); + return "TransitHop:[ terminal={}, tx={}, rx={}, upstream={}, downstream={}, expiry={} ]"_format( + terminal_hop, _txid, _rxid, _upstream, _downstream, expiry.count()); } + } // namespace llarp::path diff --git a/llarp/path/transit_hop.hpp b/llarp/path/transit_hop.hpp index 989d1ba4ce..07d82b24a5 100644 --- a/llarp/path/transit_hop.hpp +++ b/llarp/path/transit_hop.hpp @@ -1,10 +1,10 @@ #pragma once +#include "path_types.hpp" + #include -#include -#include +#include #include -#include namespace llarp { @@ -14,53 +14,44 @@ namespace llarp { struct TransitHop : std::enable_shared_from_this { - private: HopID _txid, _rxid; + RouterID _upstream; + RouterID _rid; RouterID _downstream; - public: TransitHop() = default; - TransitHop(Router& r, const RouterID& src, ustring symmkey, ustring symmnonce); - - // This static factory function is used in path-build logic. The exceptions thrown are the exact response - // bodies passed to message::respond(...) function - static std::shared_ptr deserialize_hop( - oxenc::bt_dict_consumer&& btdc, - const RouterID& src, - Router& r, - const PubKey& remote_pk, - const SymmNonce& nonce); - - SharedSecret shared; - SymmNonce nonceXOR; - std::chrono::milliseconds started = 0s; - // 10 minutes default - std::chrono::milliseconds lifetime = DEFAULT_LIFETIME; + void deserialize(oxenc::bt_dict_consumer&& btdc, const RouterID& src, const Router& r); + + shared_kx_data kx{}; + + std::chrono::milliseconds expiry{0s}; + uint8_t version; - std::chrono::milliseconds _last_activity = 0s; + std::chrono::milliseconds _last_activity{0s}; bool terminal_hop{false}; - RouterID& upstream() { return _upstream; } + void bt_decode(oxenc::bt_dict_consumer&& btdc); - const RouterID& upstream() const { return _upstream; } + std::string bt_encode() const; - RouterID& downstream() { return _downstream; } + RouterID router_id() { return _rid; } + const RouterID& router_id() const { return _rid; } + RouterID upstream() { return _upstream; } + const RouterID& upstream() const { return _upstream; } + + RouterID downstream() { return _downstream; } const RouterID& downstream() const { return _downstream; } HopID rxid() { return _rxid; } - const HopID& rxid() const { return _rxid; } HopID txid() { return _txid; } - const HopID& txid() const { return _txid; } - void Stop(); - - bool destroy = false; + std::optional> next_id(const HopID& h) const; bool operator<(const TransitHop& other) const { @@ -76,39 +67,14 @@ namespace llarp bool operator!=(const TransitHop& other) const { return !(*this == other); } - std::chrono::milliseconds expiry_time() const; - std::chrono::milliseconds last_activity() const { return _last_activity; } - std::string to_string() const; - - bool is_expired(std::chrono::milliseconds now) const; - - bool ExpiresSoon(std::chrono::milliseconds now, std::chrono::milliseconds dlt) const - { - return now >= expiry_time() - dlt; - } + bool is_expired(std::chrono::milliseconds now = llarp::time_now_ms()) const { return now >= expiry; }; - void QueueDestroySelf(Router* r); + nlohmann::json ExtractStatus() const; + std::string to_string() const; static constexpr bool to_string_formattable = true; - - private: - void SetSelfDestruct(); }; } // namespace path } // namespace llarp - -namespace std -{ - // template <> - // struct hash - // { - // std::size_t operator()(const llarp::path::TransitHopInfo& a) const - // { - // hash RHash{}; - // hash PHash{}; - // return RHash(a.upstream) ^ RHash(a.downstream) ^ PHash(a.txID) ^ PHash(a.rxID); - // } - // }; -} // namespace std diff --git a/llarp/profiling.cpp b/llarp/profiling.cpp index 85caaf2657..957f31e8fb 100644 --- a/llarp/profiling.cpp +++ b/llarp/profiling.cpp @@ -1,5 +1,7 @@ #include "profiling.hpp" +#include "path/path.hpp" +#include "router/router.hpp" #include "util/file.hpp" #include @@ -85,7 +87,7 @@ namespace llarp last_decay = llarp::time_now_ms(); } - void RouterProfile::Tick() + void RouterProfile::tick() { static constexpr auto updateInterval = 30s; const auto now = llarp::time_now_ms(); @@ -121,22 +123,11 @@ namespace llarp return checkIsGood(path_fail, path_success, chances); } - Profiling::Profiling() : _profiling_disabled(false) {} + void Profiling::disable() { _profiling_disabled.store(true); } - void Profiling::disable() - { - _profiling_disabled.store(true); - } + void Profiling::enable() { _profiling_disabled.store(false); } - void Profiling::enable() - { - _profiling_disabled.store(false); - } - - bool Profiling::is_enabled() const - { - return not _profiling_disabled.load(); - } + bool Profiling::is_enabled() const { return not _profiling_disabled.load(); } bool Profiling::is_bad_for_connect(const RouterID& r, uint64_t chances) { @@ -177,7 +168,7 @@ namespace llarp return; util::Lock lock(_m); for (auto& [rid, profile] : _profiles) - profile.Tick(); + profile.tick(); } void Profiling::connect_timeout(const RouterID& r) @@ -204,6 +195,9 @@ namespace llarp void Profiling::hop_fail(const RouterID& r) { + if (_profiling_disabled.load()) + return; + util::Lock lock{_m}; auto& profile = _profiles[r]; profile.path_fail += 1; @@ -212,6 +206,9 @@ namespace llarp void Profiling::path_fail(path::Path* p) { + if (_profiling_disabled.load()) + return; + util::Lock lock{_m}; bool first = true; for (const auto& hop : p->hops) @@ -221,7 +218,7 @@ namespace llarp first = false; else { - auto& profile = _profiles[hop.rc.router_id()]; + auto& profile = _profiles[hop.router_id()]; profile.path_fail += 1; profile.last_update = llarp::time_now_ms(); } @@ -230,10 +227,13 @@ namespace llarp void Profiling::path_timeout(path::Path* p) { + if (_profiling_disabled.load()) + return; + util::Lock lock{_m}; for (const auto& hop : p->hops) { - auto& profile = _profiles[hop.rc.router_id()]; + auto& profile = _profiles[hop.router_id()]; profile.path_timeout += 1; profile.last_update = llarp::time_now_ms(); } @@ -241,11 +241,14 @@ namespace llarp void Profiling::path_success(path::Path* p) { + if (_profiling_disabled.load()) + return; + util::Lock lock{_m}; const auto sz = p->hops.size(); for (const auto& hop : p->hops) { - auto& profile = _profiles[hop.rc.router_id()]; + auto& profile = _profiles[hop.router_id()]; // redeem previous fails by halfing the fail count and setting timeout to zero profile.path_fail /= 2; profile.path_timeout = 0; @@ -255,7 +258,25 @@ namespace llarp } } - bool Profiling::save(const fs::path fpath) + void Profiling::stop_save_ticker() + { + if (_disk_saver) + { + log::trace(logcat, "Stopping router profile disk saving"); + _disk_saver->stop(); + _disk_saver.reset(); + } + } + + void Profiling::start_save_ticker(Router& r) + { + _disk_saver = r.loop()->call_every(SAVE_INTERVAL, [this]() { + log::debug(logcat, "Writing router profiles to disk..."); + save_to_disk(); + }); + } + + bool Profiling::save_to_disk() { std::string buf; { @@ -276,11 +297,11 @@ namespace llarp try { - util::buffer_to_file(fpath, buf); + util::buffer_to_file(_profile_file, buf); } catch (const std::exception& e) { - log::warning(logcat, "Failed to save profiling data to {}: {}", fpath, e.what()); + log::warning(logcat, "Failed to save profiling data to {}: {}", _profile_file, e.what()); return false; } @@ -307,17 +328,17 @@ namespace llarp } } - bool Profiling::load(const fs::path fname) + bool Profiling::load_from_disk() { try { - std::string data = util::file_to_string(fname); + std::string data = util::file_to_string(_profile_file); util::Lock lock{_m}; BDecode(bt_dict_consumer{data}); } catch (const std::exception& e) { - log::warning(logcat, "failed to load router profiles from {}: {}", fname, e.what()); + log::warning(logcat, "failed to load router profiles from {}: {}", _profile_file, e.what()); return false; } _last_save = llarp::time_now_ms(); diff --git a/llarp/profiling.hpp b/llarp/profiling.hpp index 447e859926..6851c328cb 100644 --- a/llarp/profiling.hpp +++ b/llarp/profiling.hpp @@ -1,7 +1,8 @@ #pragma once -#include "path/path.hpp" -#include "router_id.hpp" +#include "constants/proto.hpp" +#include "contact/router_id.hpp" +#include "ev/types.hpp" #include "util/thread/threading.hpp" #include @@ -14,16 +15,24 @@ namespace oxenc namespace llarp { + struct Router; + + namespace path + { + struct Path; + } + struct RouterProfile { - static constexpr size_t MaxSize = 256; - uint64_t conn_timeout = 0; - uint64_t conn_success = 0; - uint64_t path_success = 0; - uint64_t path_fail = 0; - uint64_t path_timeout = 0; - std::chrono::milliseconds last_update = 0s; - std::chrono::milliseconds last_decay = 0s; + static constexpr size_t MaxSize{256}; + + uint64_t conn_timeout{}; + uint64_t conn_success{}; + uint64_t path_success{}; + uint64_t path_fail{}; + uint64_t path_timeout{}; + std::chrono::milliseconds last_update{0s}; + std::chrono::milliseconds last_decay{0s}; uint64_t version = llarp::constants::proto_version; RouterProfile() = default; @@ -45,14 +54,18 @@ namespace llarp void decay(); // rotate stats if timeout reached - void Tick(); + void tick(); }; struct Profiling { - Profiling(); + static constexpr std::chrono::milliseconds SAVE_INTERVAL{10min}; + + friend struct Router; - inline static const int profiling_chances = 4; + Profiling() = default; + + inline static const int profiling_chances{4}; /// generic variant bool is_bad(const RouterID& r, uint64_t chances = profiling_chances); @@ -79,9 +92,9 @@ namespace llarp void tick(); - bool load(const fs::path fname); + bool load_from_disk(); - bool save(const fs::path fname); + bool save_to_disk(); bool should_save(std::chrono::milliseconds now) const; @@ -92,14 +105,21 @@ namespace llarp bool is_enabled() const; private: + void start_save_ticker(Router& r); + + void stop_save_ticker(); + void BEncode(oxenc::bt_dict_producer& dict) const; void BDecode(oxenc::bt_dict_consumer dict); + std::shared_ptr _disk_saver; + mutable util::Mutex _m; + fs::path _profile_file; std::map _profiles; - std::chrono::milliseconds _last_save = 0s; - std::atomic _profiling_disabled; + std::chrono::milliseconds _last_save{0s}; + std::atomic _profiling_disabled{false}; }; } // namespace llarp diff --git a/llarp/router/route_poker.cpp b/llarp/router/route_poker.cpp index 02c58ee993..1bc7162313 100644 --- a/llarp/router/route_poker.cpp +++ b/llarp/router/route_poker.cpp @@ -8,9 +8,11 @@ namespace llarp { static auto logcat = log::Cat("route_poker"); + RoutePoker::RoutePoker(Router& r) : _router{r} {} + void RoutePoker::add_route(oxen::quic::Address ip) { - if (not is_up) + if (not _is_up) return; bool has_existing = poked_routes.count(ip); @@ -38,7 +40,7 @@ namespace llarp { if (ip.is_set() and gateway.is_set() and is_enabled()) { - vpn::AbstractRouteManager& route = router.vpn_platform()->RouteManager(); + vpn::AbstractRouteManager& route = _router.vpn_platform()->RouteManager(); route.delete_route(ip, gateway); } } @@ -47,7 +49,7 @@ namespace llarp { if (ip.is_set() and gateway.is_set() and is_enabled()) { - vpn::AbstractRouteManager& route = router.vpn_platform()->RouteManager(); + vpn::AbstractRouteManager& route = _router.vpn_platform()->RouteManager(); route.add_route(ip, gateway); } } @@ -65,7 +67,10 @@ namespace llarp void RoutePoker::start() { if (not is_enabled()) + { + log::info(logcat, "Route poker is NOT enabled for this lokinet instance!"); return; + } // router.loop()->call_every(100ms, weak_from_this(), [self = weak_from_this()]() { // if (auto ptr = self.lock()) @@ -96,10 +101,10 @@ namespace llarp RoutePoker::~RoutePoker() { - if (not router.vpn_platform()) + if (not _router.vpn_platform()) return; - auto& route = router.vpn_platform()->RouteManager(); + auto& route = _router.vpn_platform()->RouteManager(); for (const auto& [ip, gateway] : poked_routes) { if (gateway.is_set() and ip.is_set()) @@ -108,16 +113,6 @@ namespace llarp route.delete_blackhole(); } - bool RoutePoker::is_enabled() const - { - if (router.is_service_node()) - return false; - if (const auto& conf = router.config()) - return conf->network.enable_route_poker; - - throw std::runtime_error{"Attempting to use RoutePoker with router with no config set"}; - } - void RoutePoker::update() { // // ensure we have an endpoint @@ -173,8 +168,9 @@ namespace llarp void RoutePoker::put_up() { - bool was_up = is_up; - is_up = true; + bool was_up = _is_up; + _is_up = true; + if (not was_up) { if (not is_enabled()) @@ -189,16 +185,17 @@ namespace llarp { log::info(logcat, "RoutePoker coming up; poking routes"); - vpn::AbstractRouteManager& route = router.vpn_platform()->RouteManager(); + vpn::AbstractRouteManager& route = _router.vpn_platform()->RouteManager(); // black hole all routes if enabled - if (router.config()->network.blackhole_routes) + if (_router.config()->network.blackhole_routes) route.add_blackhole(); // explicit route pokes for first hops - router.for_each_connection([this](link::Connection conn) { add_route(conn.conn->remote()); }); + _router.for_each_connection( + [this](const RouterID&, link::Connection& conn) { add_route(conn.conn->remote()); }); - add_route(router.link_manager()->local()); + add_route(_router.link_manager()->local()); // add default route // const auto ep = router.hidden_service_context().GetDefault(); // if (auto* vpn = ep->GetVPNInterface()) @@ -213,8 +210,9 @@ namespace llarp void RoutePoker::put_down() { // unpoke routes for first hops - router.for_each_connection([this](link::Connection conn) { delete_route(conn.conn->remote()); }); - if (is_enabled() and is_up) + _router.for_each_connection( + [this](const RouterID&, link::Connection& conn) { delete_route(conn.conn->remote()); }); + if (is_enabled() and _is_up) { // vpn::AbstractRouteManager& route = router.vpn_platform()->RouteManager(); // const auto ep = router.hidden_service_context().GetDefault(); diff --git a/llarp/router/route_poker.hpp b/llarp/router/route_poker.hpp index c99d32da79..860b448396 100644 --- a/llarp/router/route_poker.hpp +++ b/llarp/router/route_poker.hpp @@ -1,7 +1,5 @@ #pragma once -#include - #include #include @@ -15,7 +13,7 @@ namespace llarp struct RoutePoker : public std::enable_shared_from_this { - RoutePoker(Router& r) : router{r} {} + RoutePoker(Router& r); void add_route(oxen::quic::Address ip); @@ -35,11 +33,11 @@ namespace llarp /// pass in if we are using exit node mode right now as a bool void set_dns_mode(bool /// using_exit_mode) const; + bool is_enabled() const { return _is_enabled; } + private: void update(); - bool is_enabled() const; - void delete_all_routes(); void disable_all_routes(); @@ -51,10 +49,10 @@ namespace llarp void disable_route(oxen::quic::Address ip, oxen::quic::Address gateway); std::unordered_map poked_routes; - std::optional current_gateway; - Router& router; - bool is_up{false}; + Router& _router; + bool _is_up{false}; + bool _is_enabled{false}; }; } // namespace llarp diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index fbf15dac5a..86a2be9193 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -2,9 +2,8 @@ #include #include +#include #include -#include -#include #include #include #include @@ -68,7 +67,6 @@ namespace llarp {"running", true}, {"numNodesKnown", _node_db->num_rcs()}, {"links", _link_manager->extract_status()}}; } - // TODO: investigate changes needed for libquic integration nlohmann::json Router::ExtractSummaryStatus() const { // if (!is_running) @@ -198,6 +196,7 @@ namespace llarp if (is_service_node()) { + _rpc_client->start_pings(); _link_manager->start_tickers(); if (not _testing_disabled) @@ -218,17 +217,13 @@ namespace llarp } else { + _session_endpoint->start_tickers(); // Resolve needed ONS values now that we have the necessary things prefigured _session_endpoint->resolve_ons_mappings(); } } - bool Router::fully_meshed() const - { - if (auto n_conns = num_router_connections(); n_conns) - return n_conns >= _node_db->num_rcs(); - return false; - } + bool Router::is_fully_meshed() const { return num_router_connections() >= _node_db->num_rcs(); } void Router::persist_connection_until(const RouterID& remote, std::chrono::milliseconds until) { @@ -246,9 +241,11 @@ namespace llarp return _link_manager->send_control_message(remote, std::move(ep), std::move(body), std::move(func)); } - void Router::for_each_connection(std::function func) + std::set Router::get_current_remotes() const { return _link_manager->get_current_remotes(); } + + void Router::for_each_connection(std::function func) { - return _link_manager->for_each_connection(func); + return _link_manager->for_each_connection(std::move(func)); } bool Router::ensure_identity() @@ -277,8 +274,6 @@ namespace llarp { _key_manager->update_idkey(rpc_client()->obtain_identity_key()); log::warning(logcat, "Obtained lokid identity key: {}", _key_manager->router_id()); - - rpc_client()->start_pings(); break; } catch (const std::exception& e) @@ -366,7 +361,7 @@ namespace llarp auto& conf = *_config; // Router config - client_router_connections = conf.router.client_router_connections; + min_client_outbounds = conf.router.client_router_connections; std::optional paddr = (conf.router.public_ip) ? conf.router.public_ip : (conf.links.public_addr) ? conf.links.public_addr @@ -378,10 +373,15 @@ namespace llarp if (pport.has_value() and not paddr.has_value()) throw std::runtime_error{"If public-port is specified, public-addr must be as well!"}; - if (conf.links.listen_addr) + if (conf.links.listen_addr or not _is_service_node) { - _listen_address = *conf.links.listen_addr; - log::critical(logcat, "Using listen address from link config: {}", _listen_address); + _listen_address = conf.links.listen_addr.value_or(DEFAULT_CLIENT_LISTEN_ADDR); + + log::critical( + logcat, + "Using {} listen address: {}", + conf.links.listen_addr ? "link config" : "default", + _listen_address); } else { @@ -414,10 +414,11 @@ namespace llarp if (auto maybe_addr = net().get_best_public_address(true, _port)) _public_address = std::move(*maybe_addr); else - throw std::runtime_error{"Could not find net interface on current platform!"}; + log::critical(logcat, "Could not find net interface on current platform!"); + // throw std::runtime_error{"Could not find net interface on current platform!"}; } - RouterContact::BLOCK_BOGONS = conf.router.block_bogons; + RelayContact::BLOCK_BOGONS = conf.router.block_bogons; } void Router::process_netconfig() @@ -434,20 +435,13 @@ namespace llarp bool find_if_addr = true; - // If an ip range is set in the config, then the address and ip optionls are as well + // If an ip range is set in the config, then the address and ip optionals are as well if (not(conf._local_ip_range and !conf._local_addr->is_any_addr())) { - log::debug( - logcat, - "Finding free range for config values (range:{}, addr:{})", - conf._local_ip_range, - conf._local_addr); const auto maybe = net().find_free_range(ipv6_enabled); if (not maybe.has_value()) - { - throw std::runtime_error("cannot find free address range"); - } + throw std::runtime_error("cannot find free address range!"); _local_range = *maybe; _local_addr = _local_range.address(); @@ -531,51 +525,50 @@ namespace llarp } } - /// build a set of strictConnectPubkeys - if (not conf.strict_connect.empty()) + // parse strict-connet pubkeys + if (auto& conf_edges = conf.pinned_edges; not conf_edges.empty()) { - const auto& val = conf.strict_connect; - if (is_service_node()) throw std::runtime_error("cannot use strict-connect option as service node"); - if (val.size() < 2) - throw std::runtime_error("Must specify more than one strict-connect router if using strict-connect"); + auto n_edges = conf_edges.size(); - _node_db->pinned_edges().insert(val.begin(), val.end()); - log::debug(logcat, "{} strict-connect routers configured", val.size()); - } + // bad inputs throw in config parsing, so we should never have 0 pinned_edges + assert(n_edges); - // profiling - _profile_file = _config->router.data_dir / "profiles.dat"; + if (not n_edges) + throw std::runtime_error( + "Must specify at least ONE valid strict-connect relay if using [network]:strict-connect"); - // Network config - if (not _testnet and _config->network.enable_profiling) - { - log::debug(logcat, "Router profiling enabled"); - if (not fs::exists(_profile_file)) - { - log::debug(logcat, "No profiles file found at {}; skipping...", _profile_file); - } - else + _node_db->pinned_edges() = std::move(conf_edges); + _node_db->_strict_connect = true; + + // TODO: load strict-connects as bootstraps as well + + log::debug(logcat, "Local client configured to strictly use {} edge relays", n_edges); + + if (min_client_outbounds > n_edges) { - log::debug(logcat, "Loading router profiles from {}", _profile_file); - _router_profiling.load(_profile_file); + min_client_outbounds = n_edges; + log::debug( + logcat, + "Local client holds only {} strict-connect edge relays; adjusting minimum router connections " + "commensurately", + n_edges); } } else - { - _router_profiling.disable(); - log::debug(logcat, "Router profiling disabled"); - } + log::debug( + logcat, "Local client configured to maintain {} router connections at minimum", min_client_outbounds); + + if (not min_client_outbounds) + throw std::runtime_error{"Client must be configured to have at least 1 outbound router connection!"}; } void Router::init_tun() { if (_tun = _loop->template make_shared(*this); _tun != nullptr) - { _tun->configure(); - } else throw std::runtime_error{"Failed to construct TunEndpoint API!"}; } @@ -594,7 +587,9 @@ namespace llarp const auto& netid = conf.router.net_id; _is_service_node = conf.router.is_relay; - _is_exit_node = conf.network.allow_exit; + + // accept either config entry + _is_exit_node = conf.network.allow_exit || conf.exit.exit_enabled; if (_is_exit_node and _is_service_node) throw std::runtime_error{ @@ -608,7 +603,7 @@ namespace llarp _testnet = netid == llarp::LOKINET_TESTNET_NETID; _testing_disabled = conf.lokid.disable_testing; - RouterContact::ACTIVE_NETID = netid; + RelayContact::ACTIVE_NETID = netid; if (_testing_disabled and not _testnet) throw std::runtime_error{"Error: reachability testing can only be disabled on testnet!"}; @@ -632,10 +627,11 @@ namespace llarp init_rpc(); + log::trace(logcat, "Starting OMQ server"); + _lmq->start(); + if (_is_service_node) { - log::trace(logcat, "Starting OMQ server"); - _lmq->start(); log::trace(logcat, "RPC client connecting to RPC bind address"); _rpc_client->connect_async(rpc_addr); } @@ -648,7 +644,11 @@ namespace llarp process_routerconfig(); - log::critical(logcat, "public addr={}, listen addr={}", *_public_address, _listen_address); + log::critical( + logcat, + "public addr={}, listen addr={}", + _public_address ? _public_address->to_string() : "< NONE >", + _listen_address); // We process the relevant netconfig values (ip_range, address, and ip) here; in case the range or interface // is bad, we search for a free one and set it BACK into the config. Every subsequent object configuring @@ -661,10 +661,10 @@ namespace llarp if (not ensure_identity()) throw std::runtime_error{"EnsureIdentity() failed"}; - router_contact = + relay_contact = LocalRC::make(identity(), _is_service_node and _public_address ? *_public_address : _listen_address); - _path_context = std::make_shared(local_rid()); + _path_context = std::make_shared(*this); _session_endpoint = std::make_shared(*this); _session_endpoint->configure(); @@ -681,7 +681,7 @@ namespace llarp // All relays have TUN if (_using_tun = conf.network.init_tun; _using_tun) { - log::critical(logcat, "Initializing virtual TUN device..."); + log::debug(logcat, "Initializing virtual TUN device..."); init_tun(); } @@ -689,15 +689,9 @@ namespace llarp }); } - bool Router::is_service_node() const - { - return _is_service_node; - } + bool Router::is_service_node() const { return _is_service_node; } - bool Router::is_exit_node() const - { - return _is_exit_node; - } + bool Router::is_exit_node() const { return _is_exit_node; } bool Router::insufficient_peers() const { @@ -714,51 +708,36 @@ namespace llarp return std::nullopt; } - bool Router::have_snode_whitelist() const - { - return whitelist_received; - } + bool Router::has_whitelist() const { return whitelist_received; } bool Router::appears_decommed() const { - return _is_service_node and have_snode_whitelist() and node_db()->greylist().count(local_rid()); + return _is_service_node and has_whitelist() and not node_db()->registered_routers().count(local_rid()); } bool Router::appears_funded() const { - return _is_service_node and have_snode_whitelist() and node_db()->is_connection_allowed(local_rid()); + return _is_service_node and has_whitelist() and node_db()->is_connection_allowed(local_rid()); } bool Router::appears_registered() const { - return _is_service_node and have_snode_whitelist() and node_db()->registered_routers().count(local_rid()); + return _is_service_node and has_whitelist() and node_db()->registered_routers().count(local_rid()); } - bool Router::can_test_routers() const - { - return appears_funded() and not _testing_disabled; - } + bool Router::can_test_routers() const { return appears_funded() and not _testing_disabled; } - size_t Router::num_router_connections() const + size_t Router::num_router_connections(bool active_only) const { - return _link_manager->get_num_connected_routers(); + return _link_manager->get_num_connected_routers(active_only); } - size_t Router::num_client_connections() const - { - return _link_manager->get_num_connected_clients(); - } + size_t Router::num_client_connections() const { return _link_manager->get_num_connected_clients(); } void Router::save_rc() { - // _node_db->put_rc(router_contact.view()); log::info(logcat, "Saving RC file to {}", our_rc_file); - queue_disk_io([&]() { router_contact.write(our_rc_file); }); - } - - bool Router::is_bootstrap_node(const RouterID r) const - { - return _node_db->is_bootstrap_node(r); + queue_disk_io([&]() { relay_contact.write(our_rc_file); }); } bool Router::should_report_stats(std::chrono::milliseconds now) const @@ -770,9 +749,10 @@ namespace llarp { auto [_in, _out, _relay, _client] = _link_manager->connection_stats(); auto [_rcs, _rids, _bstraps] = _node_db->db_stats(); + auto [_npaths, _nhops] = _path_context->path_ctx_stats(); - return "{} RCs, {} RIDs, {} bstraps, conns [{}:{} in:out, {}:{} relay:client]"_format( - _rcs, _rids, _bstraps, _in, _out, _relay, _client); + return "{} RCs, {} RIDs, {} bstraps, {} paths, {} hops, conns=[{}:{} in:out, {}:{} relay:client]"_format( + _rcs, _rids, _bstraps, _npaths, _nhops, _in, _out, _relay, _client); } void Router::report_stats() @@ -781,13 +761,13 @@ namespace llarp log::critical(logcat, "Local {}: {}", is_service_node() ? "Service Node" : "Client", _stats_line()); - if (is_service_node() and fully_meshed()) + if (is_service_node() and is_fully_meshed()) { log::critical(logcat, "SERVICE NODE IS FULLY MESHED"); } if (_last_stats_report > 0s) - log::info(logcat, "Last reported stats time {}", now - _last_stats_report); + log::trace(logcat, "Last reported stats time {}", now - _last_stats_report); _last_stats_report = now; @@ -809,7 +789,6 @@ namespace llarp { log::trace(logcat, "{} called", __PRETTY_FUNCTION__); - // auto now_timepoint = std::chrono::system_clock::time_point(now); const auto& local = local_rid(); // TESTNET: @@ -834,7 +813,8 @@ namespace llarp _link_manager->check_persisting_conns(now); const bool is_decommed = appears_decommed(); - auto num_router_conns = num_router_connections(); + // we want ALL router-connections, including in-progress connections because full-mesh + auto num_router_conns = num_router_connections(false); auto num_rcs = node_db()->num_rcs(); if (now >= _next_decomm_warning) @@ -863,10 +843,12 @@ namespace llarp if (num_router_conns < num_rcs) { - log::critical( + log::debug( logcat, "Service Node connecting to {} random routers to achieve full mesh", FULL_MESH_ITERATION); - _link_manager->connect_to_random(FULL_MESH_ITERATION); + _link_manager->connect_to_keep_alive(FULL_MESH_ITERATION); } + + _path_context->expire_hops(now); } void Router::_client_tick(std::chrono::milliseconds now) @@ -875,45 +857,45 @@ namespace llarp llarp::sys::service_manager->report_periodic_stats(); _pathbuild_limiter.Decay(now); - // _router_profiling.tick(); + _router_profiling.tick(); if (should_report_stats(now)) report_stats(); - if (auto should_proceed = _node_db->tick(now); should_proceed == false) + if (not _node_db->tick(now)) { - log::debug(logcat, "Router awaiting NodeDB completion to proceed with ::tick() logic..."); + log::trace(logcat, "Router awaiting NodeDB completion to proceed with ::tick() logic..."); return; } - _link_manager->check_persisting_conns(now); - - auto _num_router_conns = num_router_connections(); - - const auto& pinned_edges = _node_db->pinned_edges(); - const auto pinned_count = pinned_edges.size(); - - auto min_client_conns = - (pinned_count and MIN_CLIENT_ROUTER_CONNS > pinned_count) ? pinned_count : MIN_CLIENT_ROUTER_CONNS; - + // TODO: make "use_pinned_edges" boolean to only connect to pinned edges // if we need more sessions to routers we shall connect out to others - if (_num_router_conns < min_client_conns) + if (auto n_conns = num_router_connections(); n_conns < min_client_outbounds) { - size_t needed = min_client_conns - _num_router_conns; + // result could maybe be negative with this subtraction, so we HAVE to check nconns < min in the conditional + auto num_needed = min_client_outbounds - n_conns; + log::critical( logcat, "Client connecting to {} random routers to keep alive (current:{}, needed:{})", - needed, - _num_router_conns, - min_client_conns); - _link_manager->connect_to_random(needed); - } + num_needed, + n_conns, + min_client_outbounds); + _link_manager->connect_to_keep_alive(num_needed); - _session_endpoint->tick(now); + if (num_needed == min_client_outbounds - 1) // subtract bootstrap + { + log::info( + logcat, + "Client has 0 non-bootstrap router connections currently; bypassing SessionEndpoint tick..."); + return; + } + } + else + initial_client_connect_complete = true; - // save profiles - if (_router_profiling.is_enabled() and _config->network.save_profiles and _router_profiling.should_save(now)) - queue_disk_io([&]() { _router_profiling.save(_profile_file); }); + if (initial_client_connect_complete) + _session_endpoint->tick(now); } void Router::tick() @@ -942,17 +924,11 @@ namespace llarp _last_tick = llarp::time_now_ms(); } - const std::set& Router::get_whitelist() const - { - return _node_db->whitelist(); - } + const std::set& Router::get_whitelist() const { return _node_db->registered_routers(); } - void Router::set_router_whitelist( - const std::vector& whitelist, - const std::vector& greylist, - const std::vector& unfundedlist) + void Router::set_router_whitelist(const std::vector& whitelist) { - node_db()->set_router_whitelist(whitelist, greylist, unfundedlist); + node_db()->set_router_whitelist(whitelist); whitelist_received = true; } @@ -965,7 +941,7 @@ namespace llarp if (is_service_node()) { - if (not router_contact.is_public_addressable()) + if (not relay_contact.is_public_addressable()) { log::error(logcat, "Router is configured as relay but has no reachable addresses!"); return false; @@ -981,18 +957,42 @@ namespace llarp log::info(logcat, "Router initialized as service node!"); } + else if (not _testnet and _config->network.enable_profiling) + { + _router_profiling._profile_file = _config->router.data_dir / "profiles.dat"; + + log::debug(logcat, "Router profiling enabled"); + if (not fs::exists(_router_profiling._profile_file)) + { + log::debug(logcat, "No profiles file found at {}; skipping...", _router_profiling._profile_file); + } + else + { + log::debug(logcat, "Loading router profiles from {}", _router_profiling._profile_file); + _router_profiling.load_from_disk(); + } + + if (_config->network.save_profiles) + { + log::debug(logcat, "Router profile saving enabled"); + _router_profiling.start_save_ticker(*this); + } + } else { - // TESTNET: + _config->network.enable_profiling = false; _router_profiling.disable(); + log::info(logcat, "Router profiling disabled"); } // This must be constructed AFTER router creates its LocalRC - _contacts = std::make_unique(*this); + _contact_db = std::make_unique(*this); log::debug(logcat, "Creating Router::Tick() repeating event..."); _loop_ticker = _loop->call_every( - ROUTER_TICK_INTERVAL, [this] { tick(); }, false, true); + ROUTER_TICK_INTERVAL, [this] { tick(); }, false); + + // _route_poker->start(); _systemd_ticker = _loop->call_every( SERVICE_MANAGER_REPORT_INTERVAL, []() { sys::service_manager->report_periodic_stats(); }, false, true); @@ -1069,17 +1069,12 @@ namespace llarp false); } - log::critical(logcat, "\n\n\tLOCAL INSTANCE ROUTER ID: {}\n", local_rid()); + log::critical(logcat, "\n\n\tLOCAL INSTANCE ROUTER ID: {}\n", local_rid().to_network_address(_is_service_node)); llarp::sys::service_manager->ready(); return _is_running.load(); } - bool Router::is_running() const - { - return _is_running; - } - std::chrono::milliseconds Router::Uptime() const { const std::chrono::milliseconds _now = now(); @@ -1184,22 +1179,14 @@ namespace llarp _session_endpoint->stop(true); - _loop->call_later(200ms, [this] { cleanup(); }); - } + if (not _is_service_node) + _router_profiling.stop_save_ticker(); - uint32_t Router::NextPathBuildNumber() - { - return _path_build_count++; + _loop->call_later(200ms, [this] { cleanup(); }); } - oxen::quic::Address Router::listen_addr() const - { - return _listen_address; - } + oxen::quic::Address Router::listen_addr() const { return _listen_address; } - const llarp::net::Platform& Router::net() const - { - return *llarp::net::Platform::Default_ptr(); - } + const llarp::net::Platform& Router::net() const { return *llarp::net::Platform::Default_ptr(); } } // namespace llarp diff --git a/llarp/router/router.hpp b/llarp/router/router.hpp index 8b999926a6..02b29d3979 100644 --- a/llarp/router/router.hpp +++ b/llarp/router/router.hpp @@ -5,13 +5,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include #include @@ -52,8 +52,8 @@ namespace llarp // inline constexpr size_t INTROSET_STORAGE_REDUNDANCY{(INTROSET_RELAY_REDUNDANCY * INTROSET_REQS_PER_RELAY)}; // TESTNET: these constants are shortened for testing purposes - inline constexpr std::chrono::milliseconds TESTNET_GOSSIP_INTERVAL{10min}; - inline constexpr std::chrono::milliseconds RC_UPDATE_INTERVAL{10min}; + inline constexpr std::chrono::milliseconds TESTNET_GOSSIP_INTERVAL{5min}; + inline constexpr std::chrono::milliseconds RC_UPDATE_INTERVAL{5min}; inline constexpr std::chrono::milliseconds INITIAL_ATTEMPT_INTERVAL{30s}; // as we advance towards full mesh, we try to connect to this number per tick inline constexpr int FULL_MESH_ITERATION{1}; @@ -69,11 +69,11 @@ namespace llarp inline constexpr auto SERVICE_MANAGER_REPORT_INTERVAL{5s}; - struct Contacts; + struct ContactDB; struct Router : std::enable_shared_from_this { - friend class NodeDB; + // friend class NodeDB; friend struct LinkManager; explicit Router( @@ -95,7 +95,7 @@ namespace llarp bool use_file_logging{false}; // our router contact - LocalRC router_contact; + LocalRC relay_contact; std::shared_ptr _lmq; path::BuildLimiter _pathbuild_limiter; @@ -132,7 +132,7 @@ namespace llarp std::shared_ptr _vpn; std::shared_ptr _path_context; - std::shared_ptr _contacts; + std::shared_ptr _contact_db; std::shared_ptr _node_db; std::shared_ptr _loop_ticker; @@ -149,21 +149,16 @@ namespace llarp std::shared_ptr _config; - uint32_t _path_build_count{0}; - std::unique_ptr _rpc_server; - const std::chrono::milliseconds _random_start_delay{ - platform::is_simulation ? std::chrono::milliseconds{(llarp::randint() % 1250) + 2000} : 0s}; - std::shared_ptr _rpc_client; bool whitelist_received{false}; oxenmq::address rpc_addr; Profiling _router_profiling; - fs::path _profile_file; - int client_router_connections; + size_t min_client_outbounds{}; + std::atomic initial_client_connect_complete{false}; // should we be sending padded messages every interval? bool send_padding{false}; @@ -201,7 +196,7 @@ namespace llarp public: void start(); - bool fully_meshed() const; + bool is_fully_meshed() const; bool using_tun_if() const { return _using_tun; } @@ -209,9 +204,11 @@ namespace llarp bool is_bootstrap_seed() const { return _bootstrap_seed; } - int required_num_client_conns() const { return client_router_connections; } + size_t client_outbounds_needed() const { return min_client_outbounds; } + + std::set get_current_remotes() const; - void for_each_connection(std::function func); + void for_each_connection(std::function func); const std::shared_ptr& tun_endpoint() const { return _tun; } @@ -221,9 +218,9 @@ namespace llarp const std::shared_ptr& quic_tunnel() const { return _quic_tun; } - const Contacts& contacts() const { return *_contacts; } + const ContactDB& contacts() const { return *_contact_db; } - Contacts& contacts() { return *_contacts; } + ContactDB& contact_db() { return *_contact_db; } std::shared_ptr config() const { return _config; } @@ -239,9 +236,7 @@ namespace llarp const Ed25519SecretKey& identity() const { return _key_manager->identity_key; } - const RouterID& router_id() const { return _key_manager->public_key; } - - const RouterID& local_rid() const { return router_contact.router_id(); } + const RouterID& local_rid() const { return _key_manager->public_key; } Profiling& router_profiling() { return _router_profiling; } @@ -255,7 +250,7 @@ namespace llarp const std::shared_ptr& path_context() const { return _path_context; } - const LocalRC& rc() const { return router_contact; } + const LocalRC& rc() const { return relay_contact; } oxen::quic::Address listen_addr() const; @@ -265,10 +260,7 @@ namespace llarp const std::set& get_whitelist() const; - void set_router_whitelist( - const std::vector& whitelist, - const std::vector& greylist, - const std::vector& unfunded); + void set_router_whitelist(const std::vector& whitelist); template void queue_work(Callable&& func) @@ -284,7 +276,7 @@ namespace llarp /// Return true if we are operating as a service node and have received a service node /// whitelist - bool have_snode_whitelist() const; + bool has_whitelist() const; /// return true if we look like we are a decommissioned service node bool appears_decommed() const; @@ -303,7 +295,7 @@ namespace llarp std::chrono::milliseconds Uptime() const; - std::chrono::milliseconds _last_tick = 0s; + std::chrono::milliseconds _last_tick{0s}; std::function _router_close_cb; @@ -319,7 +311,9 @@ namespace llarp std::string status_line(); - bool is_running() const; + bool is_running() const { return _is_running; } + + bool is_stopping() const { return _is_stopping; } bool is_service_node() const; @@ -354,18 +348,16 @@ namespace llarp std::string body, std::function func = nullptr); - bool is_bootstrap_node(RouterID rid) const; + // bool is_bootstrap_node(RouterID rid) const; std::chrono::milliseconds now() const { return llarp::time_now_ms(); } /// count the number of unique service nodes connected via pubkey - size_t num_router_connections() const; + size_t num_router_connections(bool active_only = true) const; /// count the number of unique clients connected by pubkey size_t num_client_connections() const; - uint32_t NextPathBuildNumber(); - void teardown(); void cleanup(); diff --git a/llarp/router_version.cpp b/llarp/router_version.cpp index 2d882b2b2f..b43cdc60c7 100644 --- a/llarp/router_version.cpp +++ b/llarp/router_version.cpp @@ -15,10 +15,7 @@ namespace llarp : _version(router), _proto(proto) {} - bool RouterVersion::is_compatible_with(const RouterVersion& other) const - { - return _proto == other._proto; - } + bool RouterVersion::is_compatible_with(const RouterVersion& other) const { return _proto == other._proto; } std::string RouterVersion::bt_encode() const { @@ -46,10 +43,7 @@ namespace llarp assert(is_empty()); } - bool RouterVersion::is_empty() const - { - return *this == emptyRouterVersion; - } + bool RouterVersion::is_empty() const { return *this == emptyRouterVersion; } bool RouterVersion::bt_decode(std::string_view buf) { diff --git a/llarp/rpc/json_conversions.cpp b/llarp/rpc/json_conversions.cpp index 68fcd5f52c..cc4fa16c20 100644 --- a/llarp/rpc/json_conversions.cpp +++ b/llarp/rpc/json_conversions.cpp @@ -6,10 +6,7 @@ namespace llarp { static auto logcat = log::Cat("RPC-conversions"); - void to_json(nlohmann::json& j, const IPRange& ipr) - { - j = ipr.to_string(); - } + void to_json(nlohmann::json& j, const IPRange& ipr) { j = ipr.to_string(); } void from_json(const nlohmann::json& j, IPRange& ipr) { diff --git a/llarp/rpc/param_parser.hpp b/llarp/rpc/param_parser.hpp index 401c4f9c1b..e557394eb2 100644 --- a/llarp/rpc/param_parser.hpp +++ b/llarp/rpc/param_parser.hpp @@ -94,10 +94,7 @@ namespace llarp::rpc // false if it advanced beyond the requested name. This is exactly the same as // `d.skip_until(name)`, but is here so we can also overload an equivalent function for json // iteration. - inline bool skip_until(oxenc::bt_dict_consumer& d, std::string_view name) - { - return d.skip_until(name); - } + inline bool skip_until(oxenc::bt_dict_consumer& d, std::string_view name) { return d.skip_until(name); } // Equivalent to the above but for a json object iterator. inline bool skip_until(json_range& it_range, std::string_view name) { diff --git a/llarp/rpc/rpc_client.cpp b/llarp/rpc/rpc_client.cpp index 2df2042328..66f03484a7 100644 --- a/llarp/rpc/rpc_client.cpp +++ b/llarp/rpc/rpc_client.cpp @@ -165,20 +165,20 @@ namespace llarp::rpc void RPCClient::start_pings() { - constexpr auto PingInterval = 30s; + log::trace(logcat, "{} called", __PRETTY_FUNCTION__); auto router = _router.lock(); if (not router) return; - auto makePingRequest = router->loop()->make_caller([self = shared_from_this()]() { + log::info(logcat, "Starting RPCClient ping ticker..."); + _ping_ticker = router->loop()->call_every(PING_INTERVAL, [this]() { // send a ping - PubKey pk{}; - auto r = self->_router.lock(); + auto r = _router.lock(); if (not r) return; // router has gone away, maybe shutting down? - pk = r->local_rid(); + auto pk = r->local_rid(); nlohmann::json payload = { {"pubkey_ed25519", oxenc::to_hex(pk.begin(), pk.end())}, @@ -187,39 +187,33 @@ namespace llarp::rpc if (auto err = r->OxendErrorState()) payload["error"] = *err; - self->request( + request( "admin.lokinet_ping", - [](bool success, std::vector data) { - (void)data; + [](bool success, std::vector /* data */) { log::debug(logcat, "Received response for ping. Successful: {}", success); }, payload.dump()); // subscribe to block updates - self->request("sub.block", [](bool success, std::vector data) { + request("sub.block", [](bool success, std::vector data) { if (data.empty() or not success) - { log::error(logcat, "Failed to subscribe to new blocks"); - return; - } - log::debug(logcat, "Subscribed to new blocks: {}", data[0]); + else + log::debug(logcat, "Subscribed to new blocks: {}", data[0]); }); + // Trigger an update on a regular timer as well in case we missed a block notify for // some reason (e.g. oxend restarts and loses the subscription); we poll using the last // known hash so that the poll is very cheap (basically empty) if the block hasn't // advanced. - self->update_service_node_list(); + update_service_node_list(); }); - - // Fire one ping off right away to get things going. - makePingRequest(); - m_lokiMQ->add_timer(std::move(makePingRequest), PingInterval); } void RPCClient::handle_new_service_node_list(const nlohmann::json& j) { std::unordered_map keymap; - std::vector activeNodeList, decommNodeList, unfundedNodeList; + std::vector active_list; if (not j.is_array()) throw std::runtime_error{"Invalid service node list: expected array of service node states"}; @@ -235,10 +229,6 @@ namespace llarp::rpc if (active_itr == snode.end() or not active_itr->is_boolean()) continue; const bool active = active_itr->get(); - const auto funded_itr = snode.find("funded"); - if (funded_itr == snode.end() or not funded_itr->is_boolean()) - continue; - const bool funded = funded_itr->get(); RouterID rid; PubKey pk; @@ -246,10 +236,11 @@ namespace llarp::rpc continue; keymap[rid] = pk; - (active ? activeNodeList : funded ? decommNodeList : unfundedNodeList).push_back(std::move(rid)); + if (active) + active_list.emplace_back(std::move(rid)); } - if (activeNodeList.empty()) + if (active_list.empty()) { log::warning(logcat, "Received empty service node list, ignoring."); return; @@ -260,14 +251,11 @@ namespace llarp::rpc { auto& loop = router->loop(); loop->call([this, - active = std::move(activeNodeList), - decomm = std::move(decommNodeList), - unfunded = std::move(unfundedNodeList), + active = std::move(active_list), keymap = std::move(keymap), router = std::move(router)]() mutable { _key_map = std::move(keymap); - - router->set_router_whitelist(active, decomm, unfunded); + router->set_router_whitelist(active); }); } else @@ -338,19 +326,19 @@ namespace llarp::rpc } void RPCClient::lookup_ons_hash( - std::string namehash, std::function)> resultHandler) + std::string namehash, std::function)> resultHandler) { log::debug(logcat, "Looking Up ONS NameHash {}", namehash); const nlohmann::json req{{"type", 2}, {"name_hash", oxenc::to_hex(namehash)}}; request( "rpc.lns_resolve", [this, resultHandler](bool success, std::vector data) { - std::optional maybe = std::nullopt; + std::optional maybe = std::nullopt; if (success) { try { - service::EncryptedONSRecord result; + EncryptedSNSRecord result; const auto j = nlohmann::json::parse(data[1]); j.dump(); result.ciphertext = oxenc::from_hex(j["encrypted_value"].get()); diff --git a/llarp/rpc/rpc_client.hpp b/llarp/rpc/rpc_client.hpp index df3b2cff75..370e864cf2 100644 --- a/llarp/rpc/rpc_client.hpp +++ b/llarp/rpc/rpc_client.hpp @@ -1,9 +1,10 @@ #pragma once +#include +#include #include #include -#include -#include +#include #include #include @@ -14,6 +15,8 @@ namespace llarp namespace rpc { + inline constexpr auto PING_INTERVAL{30s}; + /// The LokidRpcClient uses loki-mq to talk to make API requests to lokid. struct RPCClient : public std::enable_shared_from_this { @@ -30,7 +33,7 @@ namespace llarp uint64_t block_height() const { return _block_height; } void lookup_ons_hash( - std::string namehash, std::function)> resultHandler); + std::string namehash, std::function)> resultHandler); /// inform that if connected to a router successfully void inform_connection(RouterID router, bool success); @@ -64,6 +67,8 @@ namespace llarp // Handles notification of a new block void handle_new_block(oxenmq::Message& msg); + std::shared_ptr _ping_ticker; + std::optional m_Connection; std::shared_ptr m_lokiMQ; diff --git a/llarp/rpc/rpc_request.hpp b/llarp/rpc/rpc_request.hpp index 7da29c8453..d1b96a0e63 100644 --- a/llarp/rpc/rpc_request.hpp +++ b/llarp/rpc/rpc_request.hpp @@ -60,7 +60,7 @@ namespace llarp::rpc if (not std::is_base_of_v) { - server.m_Router.loop()->call_soon(std::move(handler)); + server._router.loop()->call_soon(std::move(handler)); } else { diff --git a/llarp/rpc/rpc_request_definitions.hpp b/llarp/rpc/rpc_request_definitions.hpp index 4908392ec2..53d2bbcd4e 100644 --- a/llarp/rpc/rpc_request_definitions.hpp +++ b/llarp/rpc/rpc_request_definitions.hpp @@ -291,6 +291,24 @@ namespace llarp::rpc } request; }; + // RPC: find_cc + // Lookup client contact via path request + // + // Inputs: + // "pk" : client pubkey + // + // Returns: + // "cc" : client contact + struct FindCC : RPCRequest + { + static constexpr auto name = "find_cc"sv; + + struct request_parameterss + { + std::string pk; + } request; + }; + // List of all RPC request structs to allow compile-time enumeration of all supported types using rpc_request_types = tools::type_list< Halt, @@ -300,6 +318,7 @@ namespace llarp::rpc QuicConnect, // debug QuicListener, // debug LookupSnode, + FindCC, MapExit, ListExits, SwapExits, diff --git a/llarp/rpc/rpc_request_parser.cpp b/llarp/rpc/rpc_request_parser.cpp index 7eafc4cd83..3d08eed0a8 100644 --- a/llarp/rpc/rpc_request_parser.cpp +++ b/llarp/rpc/rpc_request_parser.cpp @@ -38,6 +38,8 @@ namespace llarp::rpc quiclistener.request.srvProto); } + void parse_request(FindCC& findcc, rpc_input input) { get_values(input, "pk", findcc.request.pk); } + void parse_request(LookupSnode& lookupsnode, rpc_input input) { get_values(input, "routerID", lookupsnode.request.routerID); diff --git a/llarp/rpc/rpc_request_parser.hpp b/llarp/rpc/rpc_request_parser.hpp index e6e3c4dccd..bc0b6c7a81 100644 --- a/llarp/rpc/rpc_request_parser.hpp +++ b/llarp/rpc/rpc_request_parser.hpp @@ -4,7 +4,6 @@ #include -// #include #include #include @@ -18,6 +17,7 @@ namespace llarp::rpc void parse_request(QuicConnect& quicconnect, rpc_input input); void parse_request(QuicListener& quiclistener, rpc_input input); + void parse_request(FindCC& findcc, rpc_input input); void parse_request(LookupSnode& lookupsnode, rpc_input input); void parse_request(MapExit& mapexit, rpc_input input); void parse_request(UnmapExit& unmapexit, rpc_input input); diff --git a/llarp/rpc/rpc_server.cpp b/llarp/rpc/rpc_server.cpp index d84a199a91..913a473a7f 100644 --- a/llarp/rpc/rpc_server.cpp +++ b/llarp/rpc/rpc_server.cpp @@ -5,9 +5,11 @@ #include #include #include +#include #include #include #include +#include #include @@ -71,7 +73,7 @@ namespace llarp::rpc } RPCServer::RPCServer(LMQ_ptr lmq, Router& r) - : m_LMQ{std::move(lmq)}, m_Router(r), log_subs{*m_LMQ, llarp::logRingBuffer} + : m_LMQ{std::move(lmq)}, _router(r), log_subs{*m_LMQ, llarp::logRingBuffer} { // copied logic loop as placeholder for (const auto& addr : r.config()->api.rpc_bind_addrs) @@ -113,31 +115,31 @@ namespace llarp::rpc void RPCServer::invoke(Halt& halt) { - if (not m_Router.is_running()) + if (not _router.is_running()) { SetJSONError("Router is not running", halt.response); return; } SetJSONResponse("OK", halt.response); - m_Router.stop(); + _router.stop(); } void RPCServer::invoke(Version& version) { - nlohmann::json result{{"version", llarp::LOKINET_VERSION_FULL}, {"uptime", to_json(m_Router.Uptime())}}; + nlohmann::json result{{"version", llarp::LOKINET_VERSION_FULL}, {"uptime", to_json(_router.Uptime())}}; SetJSONResponse(result, version.response); } void RPCServer::invoke(Status& status) { - (m_Router.is_running()) ? SetJSONResponse(m_Router.ExtractStatus(), status.response) - : SetJSONError("Router is not yet ready", status.response); + (_router.is_running()) ? SetJSONResponse(_router.ExtractStatus(), status.response) + : SetJSONError("Router is not yet ready", status.response); } void RPCServer::invoke(GetStatus& getstatus) { - SetJSONResponse(m_Router.ExtractSummaryStatus(), getstatus.response); + SetJSONResponse(_router.ExtractSummaryStatus(), getstatus.response); } void RPCServer::invoke(QuicConnect& quicconnect) @@ -157,7 +159,7 @@ namespace llarp::rpc } // auto endpoint = - // (req.endpoint.empty()) ? GetEndpointByName(m_Router, "default") : GetEndpointByName(m_Router, + // (req.endpoint.empty()) ? GetEndpointByName(_router, "default") : GetEndpointByName(_router, // req.endpoint); // if (not endpoint) @@ -213,7 +215,7 @@ namespace llarp::rpc } // auto endpoint = - // (req.endpoint.empty()) ? GetEndpointByName(m_Router, "default") : GetEndpointByName(m_Router, + // (req.endpoint.empty()) ? GetEndpointByName(_router, "default") : GetEndpointByName(_router, // req.endpoint); // if (not endpoint) @@ -270,10 +272,49 @@ namespace llarp::rpc } } + void RPCServer::invoke(FindCC& findcc) + { + if (_router.is_service_node()) + { + SetJSONError("Not supported", findcc.response); + return; + } + + RouterID pk; + + if (findcc.request.pk.empty()) + { + SetJSONError("No pubkey provided!", findcc.response); + return; + } + + if (not pk.from_string(oxenc::from_base32z(findcc.request.pk))) + { + SetJSONError("Invalid pubkey provided: " + findcc.request.pk, findcc.response); + return; + } + + _router.loop()->call([&]() { + _router.session_endpoint()->lookup_client_intro(pk, [&](std::optional cc) { + if (cc) + { + auto cc_str = "{}"_format(*cc); + log::info(logcat, "RPC call to `find_cc` returned successfully: {}", cc_str); + SetJSONResponse(cc_str, findcc.response); + } + else + { + log::warning(logcat, "RPC call to `find_cc` failed!"); + SetJSONError("ERROR", findcc.response); + } + }); + }); + } + // TODO: fix this because it's bad void RPCServer::invoke(LookupSnode& lookupsnode) { - if (not m_Router.is_service_node()) + if (not _router.is_service_node()) { SetJSONError("Not supported", lookupsnode.response); return; @@ -292,8 +333,8 @@ namespace llarp::rpc return; } - // m_Router.loop()->call([&]() { - // auto endpoint = m_Router.exit_context().get_exit_endpoint("default"); + // _router.loop()->call([&]() { + // auto endpoint = _router.exit_context().get_exit_endpoint("default"); // if (endpoint == nullptr) // { @@ -323,7 +364,7 @@ namespace llarp::rpc exit_request.replier.emplace(mapexit.move()); // TODO: connect this to remote service session management (service::Handler) - // m_Router.hidden_service_context().GetDefault()->map_exit( + // _router.hidden_service_context().GetDefault()->map_exit( // mapexit.request.address, // mapexit.request.token, // mapexit.request.ip_range, @@ -338,13 +379,13 @@ namespace llarp::rpc void RPCServer::invoke(ListExits& listexits) { (void)listexits; - // if (not m_Router.hidden_service_context().hasEndpoints()) + // if (not _router.hidden_service_context().hasEndpoints()) // { // SetJSONError("No mapped endpoints found", listexits.response); // return; // } - // auto status = m_Router.hidden_service_context().GetDefault()->ExtractStatus()["exitMap"]; + // auto status = _router.hidden_service_context().GetDefault()->ExtractStatus()["exitMap"]; // SetJSONResponse((status.empty()) ? "No exits" : status, listexits.response); } @@ -354,7 +395,7 @@ namespace llarp::rpc try { // for (auto& ip : unmapexit.request.ip_range) - // m_Router.hidden_service_context().GetDefault()->UnmapExitRange(ip); + // _router.hidden_service_context().GetDefault()->UnmapExitRange(ip); } catch (std::exception& e) { @@ -375,7 +416,7 @@ namespace llarp::rpc (void)swapexits; // MapExit map_request; // UnmapExit unmap_request; - // auto endpoint = m_Router.hidden_service_context().GetDefault(); + // auto endpoint = _router.hidden_service_context().GetDefault(); // auto current_exits = endpoint->ExtractStatus()["exitMap"]; // if (current_exits.empty()) @@ -452,8 +493,8 @@ namespace llarp::rpc dns::Message msg{dns::Question{qname, qtype}}; - // auto endpoint = (dnsquery.request.endpoint.empty()) ? GetEndpointByName(m_Router, "default") - // : GetEndpointByName(m_Router, dnsquery.request.endpoint); + // auto endpoint = (dnsquery.request.endpoint.empty()) ? GetEndpointByName(_router, "default") + // : GetEndpointByName(_router, dnsquery.request.endpoint); // if (endpoint == nullptr) // { @@ -491,7 +532,7 @@ namespace llarp::rpc return; } - if (not ends_with(config.request.filename, ".ini")) + if (config.request.filename.ends_with(".ini")) { SetJSONError("Must append '.ini' to filename", config.response); return; diff --git a/llarp/rpc/rpc_server.hpp b/llarp/rpc/rpc_server.hpp index 0a3da79657..003a2a1b2a 100644 --- a/llarp/rpc/rpc_server.hpp +++ b/llarp/rpc/rpc_server.hpp @@ -70,10 +70,7 @@ namespace llarp::rpc j["result"] = result; } - inline void SetJSONError(std::string_view msg, json& j) - { - j["error"] = msg; - } + inline void SetJSONError(std::string_view msg, json& j) { j["error"] = msg; } class RPCServer { @@ -92,6 +89,7 @@ namespace llarp::rpc void invoke(QuicConnect& quicconnect); void invoke(QuicListener& quiclistener); void invoke(LookupSnode& lookupsnode); + void invoke(FindCC& findcc); void invoke(MapExit& mapexit); void invoke(ListExits& listexits); void invoke(UnmapExit& unmapexit); @@ -100,7 +98,7 @@ namespace llarp::rpc void invoke(Config& config); LMQ_ptr m_LMQ; - Router& m_Router; + Router& _router; oxen::log::PubsubLogger log_subs; }; diff --git a/llarp/service/identity.cpp b/llarp/service/identity.cpp deleted file mode 100644 index 6cd2ee2316..0000000000 --- a/llarp/service/identity.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include "identity.hpp" - -#include - -namespace llarp::service -{ - static auto logcat = log::Cat("Identity"); - - std::string Identity::bt_encode() const - { - oxenc::bt_dict_producer btdp; - - btdp.append("s", _idkey.to_view()); - btdp.append("v", version); - - return std::move(btdp).str(); - } - - void Identity::bt_decode(std::string buf) - { - try - { - oxenc::bt_dict_consumer btdc{buf}; - - _idkey.from_string(btdc.require("s")); - version = btdc.require("v"); - } - catch (...) - { - log::warning(logcat, "Identity failed to parse bt-encoded contents!"); - throw; - } - } - - void Identity::Clear() - { - _idkey.zero(); - _enckey.zero(); - pq.zero(); - derivedSignKey.zero(); - vanity.zero(); - } - - void Identity::regenerate_keys() - { - crypto::identity_keygen(_idkey); - crypto::encryption_keygen(_enckey); - - pub.update(seckey_to_pubkey(_idkey), seckey_to_pubkey(_enckey)); - - crypto::pqe_keygen(pq); - - if (not crypto::derive_subkey_private(derivedSignKey, _idkey, 1)) - { - throw std::runtime_error("failed to derive subkey"); - } - } - - bool Identity::KeyExchange( - path_dh_func dh, SharedSecret& result, const ServiceInfo& other, const KeyExchangeNonce& N) const - { - return dh(result, other.encryption_pubkey(), _enckey, N); - } - - bool Identity::Sign(Signature& sig, uint8_t* buf, size_t size) const - { - return crypto::sign(sig, _idkey, buf, size); - } - - std::optional Identity::encrypt_and_sign_introset( - const IntroSet& other_i, std::chrono::milliseconds now) const - { - EncryptedIntroSet encrypted; - - if (other_i.intros.empty()) - return std::nullopt; - - IntroSet i{other_i}; - encrypted.nonce.Randomize(); - // set timestamp - // TODO: round to nearest 1000 ms - i.time_signed = now; - encrypted.signed_at = now; - // set service info - i.address_keys = pub; - // set public encryption key - i.sntru_pubkey = pq_keypair_to_pubkey(pq); - - auto bte = i.bt_encode(); - - const SharedSecret k{i.address_keys.address().pubkey()}; - crypto::xchacha20(reinterpret_cast(bte.data()), bte.size(), k, encrypted.nonce); - - std::memcpy(encrypted.introset_payload.data(), bte.data(), bte.size()); - - if (not encrypted.sign(derivedSignKey)) - return std::nullopt; - return encrypted; - } -} // namespace llarp::service diff --git a/llarp/service/identity.hpp b/llarp/service/identity.hpp deleted file mode 100644 index 625ccaaeee..0000000000 --- a/llarp/service/identity.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include "info.hpp" -#include "intro_set.hpp" -#include "vanity.hpp" - -#include -#include -#include - -#include -#include - -namespace llarp::service -{ - // private keys - struct Identity - { - Ed25519SecretKey _idkey; - Ed25519SecretKey _enckey; - Ed25519Hash derivedSignKey; - PQKeyPair pq; - uint64_t version = llarp::constants::proto_version; - VanityNonce vanity; - - // public service info - ServiceInfo pub; - - // regenerate secret keys - void regenerate_keys(); - - std::string bt_encode() const; - - void bt_decode(std::string); - - bool KeyExchange( - path_dh_func dh, SharedSecret& sharedkey, const ServiceInfo& other, const KeyExchangeNonce& N) const; - - std::optional encrypt_and_sign_introset( - const IntroSet& i, std::chrono::milliseconds now) const; - - bool Sign(Signature& sig, uint8_t* buf, size_t size) const; - - /// zero out all secret key members - void Clear(); - }; - - inline bool operator==(const Identity& lhs, const Identity& rhs) - { - return std::tie(lhs._enckey, lhs._idkey, lhs.pq, lhs.version, lhs.vanity) - == std::tie(rhs._enckey, rhs._idkey, rhs.pq, rhs.version, rhs.vanity); - } -} // namespace llarp::service diff --git a/llarp/service/info.cpp b/llarp/service/info.cpp deleted file mode 100644 index 6b5de46333..0000000000 --- a/llarp/service/info.cpp +++ /dev/null @@ -1,102 +0,0 @@ -#include "info.hpp" - -#include - -namespace llarp::service -{ - static auto logcat = log::Cat("service_info"); - - bool ServiceInfo::verify(uint8_t* buf, size_t size, const Signature& sig) const - { - return crypto::verify(signkey, buf, size, sig); - } - - bool ServiceInfo::update(const uint8_t* sign, const uint8_t* enc, const std::optional& nonce) - { - signkey = sign; - enckey = enc; - if (nonce) - { - vanity = *nonce; - } - - return update_address(); - } - - void ServiceInfo::bt_decode(oxenc::bt_dict_consumer& btdc) - { - try - { - enckey.from_hex(btdc.require("e")); - signkey.from_hex(btdc.require("s")); - vanity.from_string(btdc.require("x")); - } - catch (...) - { - log::critical(logcat, "ServiceInfo failed to populate with bt encoded contents"); - throw; - } - } - - bool ServiceInfo::bt_decode(std::string_view buf) - { - try - { - oxenc::bt_dict_consumer btdc{buf}; - - bt_decode(btdc); - } - catch (const std::exception& e) - { - // DISCUSS: rethrow or print warning/return false...? - auto err = "ServiceInfo parsing exception: {}"_format(e.what()); - log::warning(logcat, "{}", err); - throw std::runtime_error{err}; - } - - return true; - } - - void ServiceInfo::bt_encode(oxenc::bt_dict_producer& btdp) const - { - btdp.append("e", enckey.to_view()); - btdp.append("s", signkey.to_view()); - - if (not vanity.is_zero()) - btdp.append("x", vanity.to_view()); - } - - std::string ServiceInfo::name() const - { - if (_cached_addr.is_empty()) - { - PubKey pk; - calculate_address(pk); - return pk.to_string(); - } - - return _cached_addr.to_string(); - } - - bool ServiceInfo::calculate_address(PubKey& data) const - { - data = PubKey{signkey.as_array()}; - return true; - } - - bool ServiceInfo::update_address() - { - if (_cached_addr.is_empty()) - { - return calculate_address(_cached_addr.pubkey()); - } - - return true; - } - - std::string ServiceInfo::to_string() const - { - return fmt::format("[ServiceInfo e={} s={} v={} x={}]", enckey, signkey, version, vanity); - } - -} // namespace llarp::service diff --git a/llarp/service/info.hpp b/llarp/service/info.hpp deleted file mode 100644 index 98cbff9b87..0000000000 --- a/llarp/service/info.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#pragma once - -#include "vanity.hpp" - -#include -#include -#include - -#include - -#include - -namespace llarp::service -{ - struct ServiceInfo - { - private: - PubKey enckey; - PubKey signkey; - mutable NetworkAddress _cached_addr; - - public: - VanityNonce vanity; - uint64_t version = llarp::constants::proto_version; - - void randomize_vanity() { vanity.Randomize(); } - - bool verify(uint8_t* buf, size_t size, const Signature& sig) const; - - const PubKey& encryption_pubkey() const - { - if (_cached_addr.is_empty()) - calculate_address(_cached_addr.pubkey()); - - return enckey; - } - - bool update(const uint8_t* sign, const uint8_t* enc, const std::optional& nonce = {}); - - bool operator==(const ServiceInfo& other) const - { - return enckey == other.enckey && signkey == other.signkey && version == other.version - && vanity == other.vanity; - } - - bool operator!=(const ServiceInfo& other) const { return !(*this == other); } - - bool operator<(const ServiceInfo& other) const { return address() < other.address(); } - - std::string to_string() const; - - /// .loki address - std::string name() const; - - bool update_address(); - - const NetworkAddress& address() const - { - if (_cached_addr.is_empty()) - calculate_address(_cached_addr.pubkey()); - - return _cached_addr; - } - - /// calculate our address - bool calculate_address(PubKey& data) const; - - bool bt_decode(std::string_view buf); - - void bt_decode(oxenc::bt_dict_consumer& btdc); - - void bt_encode(oxenc::bt_dict_producer& btdp) const; - }; -} // namespace llarp::service diff --git a/llarp/service/intro.cpp b/llarp/service/intro.cpp deleted file mode 100644 index 4719ca5224..0000000000 --- a/llarp/service/intro.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#include "intro.hpp" - -#include - -namespace llarp::service -{ - static auto logcat = log::Cat("introduction"); - - nlohmann::json Introduction::ExtractStatus() const - { - nlohmann::json obj{ - {"router", pivot_router.ToHex()}, - {"path", pivot_hop_id.ToHex()}, - {"expiresAt", to_json(expiry)}, - {"latency", to_json(latency)}, - {"version", uint64_t(version)}}; - return obj; - } - - Introduction::Introduction(std::string buf) - { - try - { - oxenc::bt_dict_consumer btdc{std::move(buf)}; - - pivot_router.from_relay_address(btdc.require("k")); - latency = std::chrono::milliseconds{btdc.require("l")}; - pivot_hop_id.from_string(btdc.require("p")); - expiry = std::chrono::milliseconds{btdc.require("x")}; - } - catch (...) - { - log::critical(logcat, "Error: Introduction failed to populate with bt encoded contents"); - } - } - - void Introduction::bt_encode(oxenc::bt_list_producer& btlp) const - { - try - { - auto subdict = btlp.append_dict(); - bt_encode(subdict); - } - catch (...) - { - log::critical(logcat, "Error: Introduction failed to bt encode contents!"); - } - } - - void Introduction::bt_encode(oxenc::bt_dict_producer& subdict) const - { - try - { - subdict.append("k", pivot_router.to_view()); - subdict.append("l", latency.count()); - subdict.append("p", pivot_hop_id.to_view()); - subdict.append("x", expiry.count()); - } - catch (...) - { - log::critical(logcat, "Error: Introduction failed to bt encode contents!"); - } - } - - bool Introduction::bt_decode(std::string_view buf) - { - try - { - oxenc::bt_dict_consumer btdc{buf}; - bt_decode(btdc); - } - catch (const std::exception& e) - { - // DISCUSS: rethrow or print warning/return false...? - auto err = "Introduction parsing exception: {}"_format(e.what()); - log::warning(logcat, "{}", err); - throw std::runtime_error{err}; - } - - return true; - } - - void Introduction::bt_decode(oxenc::bt_dict_consumer& btdc) - { - try - { - pivot_router.from_string(btdc.require("k")); - latency = std::chrono::milliseconds{btdc.require("l")}; - pivot_hop_id.from_string(btdc.require("p")); - expiry = std::chrono::milliseconds{btdc.require("x")}; - } - catch (...) - { - log::critical(logcat, "Introcuction failed to populate with bt encoded contents"); - throw; - } - } - - void Introduction::clear() - { - pivot_router.zero(); - pivot_hop_id.zero(); - latency = 0s; - expiry = 0s; - } - - std::string Introduction::to_string() const - { - return fmt::format( - "[Intro k={} l={} p={} v={} x={}]", - RouterID{pivot_router}, - latency.count(), - pivot_hop_id, - version, - expiry.count()); - } - -} // namespace llarp::service diff --git a/llarp/service/intro.hpp b/llarp/service/intro.hpp deleted file mode 100644 index a7fa5375a9..0000000000 --- a/llarp/service/intro.hpp +++ /dev/null @@ -1,85 +0,0 @@ -#pragma once - -#include -#include - -#include - -#include -#include - -namespace llarp::service -{ - struct Introduction - { - RouterID pivot_router; - HopID pivot_hop_id; - std::chrono::milliseconds latency = 0s; - std::chrono::milliseconds expiry = 0s; - uint64_t version = llarp::constants::proto_version; - - Introduction() = default; - Introduction(std::string buf); - - nlohmann::json ExtractStatus() const; - - bool is_expired(std::chrono::milliseconds now) const { return now >= expiry; } - - bool expires_soon(std::chrono::milliseconds now, std::chrono::milliseconds dlt = 30s) const - { - return is_expired(now + dlt); - } - - std::string to_string() const; - - void bt_encode(oxenc::bt_list_producer& btlp) const; - - void bt_encode(oxenc::bt_dict_producer& subdict) const; - - bool bt_decode(std::string_view buf); - - void bt_decode(oxenc::bt_dict_consumer& btdc); - - void clear(); - - bool operator<(const Introduction& other) const - { - return std::tie(expiry, pivot_hop_id, pivot_router, version, latency) - < std::tie(other.expiry, other.pivot_hop_id, other.pivot_router, other.version, other.latency); - } - - bool operator==(const Introduction& other) const - { - return std::tie(expiry, pivot_hop_id, pivot_router, version, latency) - == std::tie(other.expiry, other.pivot_hop_id, other.pivot_router, other.version, other.latency); - } - - bool operator!=(const Introduction& other) const { return !(*this == other); } - - static constexpr bool to_string_formattable = true; - }; - - /// comparator for introduction timestamp in order of nearest to furthest expiry time - struct IntroExpiryComparator - { - bool operator()(const Introduction& left, const Introduction& right) const - { - return left.expiry < right.expiry; - } - }; - - using IntroductionSet = std::set; - -} // namespace llarp::service - -namespace std -{ - template <> - struct hash - { - size_t operator()(const llarp::service::Introduction& i) const - { - return std::hash{}(i.pivot_router) ^ std::hash{}(i.pivot_hop_id); - } - }; -} // namespace std diff --git a/llarp/service/intro_set.cpp b/llarp/service/intro_set.cpp deleted file mode 100644 index 2b4499c91e..0000000000 --- a/llarp/service/intro_set.cpp +++ /dev/null @@ -1,459 +0,0 @@ -#include "intro_set.hpp" - -#include - -#include - -namespace llarp::service -{ - static auto logcat = log::Cat("EncIntro"); - - EncryptedIntroSet::EncryptedIntroSet( - std::string signing_key, - std::chrono::milliseconds signed_at, - std::string enc_payload, - std::string nonce, - std::string s) - : signed_at{signed_at}, - introset_payload{reinterpret_cast(enc_payload.data()), enc_payload.size()}, - nonce{reinterpret_cast(nonce.data())} - { - derived_signing_key.from_hex(signing_key); - sig.from_string(std::move(s)); - } - - EncryptedIntroSet::EncryptedIntroSet(std::string bt_payload) - { - try - { - oxenc::bt_dict_consumer btdc{bt_payload}; - bt_decode(btdc); - } - catch (...) - { - log::critical(logcat, "Error: EncryptedIntroSet failed to bt encode contents!"); - } - } - - nlohmann::json EncryptedIntroSet::ExtractStatus() const - { - const auto sz = introset_payload.size(); - return {{"location", derived_signing_key.to_string()}, {"signedAt", to_json(signed_at)}, {"size", sz}}; - } - - bool EncryptedIntroSet::bt_decode(std::string_view buf) - { - try - { - oxenc::bt_dict_consumer btdc{buf}; - return bt_decode(btdc); - } - catch (const std::exception& e) - { - // DISCUSS: rethrow or print warning/return false...? - auto err = "EncryptedIntroSet parsing exception: {}"_format(e.what()); - log::warning(logcat, "{}", err); - throw std::runtime_error{err}; - } - } - - bool EncryptedIntroSet::bt_decode(oxenc::bt_dict_consumer& btdc) - { - try - { - derived_signing_key.from_string(btdc.require("d")); - nonce.from_string(btdc.require("n")); - signed_at = std::chrono::milliseconds{btdc.require("s")}; - introset_payload = btdc.require("x"); - sig.from_string(btdc.require("z")); - - return true; - } - catch (...) - { - log::critical(logcat, "EncryptedIntroSet failed to decode bt payload!"); - throw; - } - } - - std::string EncryptedIntroSet::bt_encode() const - { - oxenc::bt_dict_producer btdp; - - try - { - btdp.append("d", derived_signing_key.to_view()); - btdp.append("n", nonce.to_view()); - btdp.append("s", signed_at.count()); - btdp.append( - "x", std::string_view{reinterpret_cast(introset_payload.data()), introset_payload.size()}); - btdp.append("z", sig.to_view()); - } - catch (...) - { - log::critical(logcat, "Error: EncryptedIntroSet failed to bt encode contents!"); - } - - return std::move(btdp).str(); - } - - std::optional EncryptedIntroSet::construct(std::string bt) - { - if (EncryptedIntroSet ret; ret.bt_decode(std::move(bt))) - return ret; - - return std::nullopt; - } - - bool EncryptedIntroSet::other_is_newer(const EncryptedIntroSet& other) const - { - return signed_at < other.signed_at; - } - - std::string EncryptedIntroSet::to_string() const - { - return fmt::format( - "[EncIntroSet d={} n={} s={} x=[{} bytes] z={}]", - derived_signing_key, - nonce, - signed_at.count(), - introset_payload.size(), - sig); - } - - std::optional EncryptedIntroSet::decrypt(const PubKey& root) const - { - std::optional ret = std::nullopt; - - SharedSecret k(root); - std::string payload{reinterpret_cast(introset_payload.data()), introset_payload.size()}; - - if (crypto::xchacha20(reinterpret_cast(payload.data()), payload.size(), k, nonce)) - ret = IntroSet{payload}; - - return ret; - } - - bool EncryptedIntroSet::is_expired(std::chrono::milliseconds now) const - { - return now >= signed_at + path::DEFAULT_LIFETIME; - } - - bool EncryptedIntroSet::sign(const Ed25519Hash& k) - { - signed_at = llarp::time_now_ms(); - derived_signing_key = k.to_pubkey(); - sig.zero(); - auto bte = bt_encode(); - - if (not crypto::sign(sig, k, reinterpret_cast(bte.data()), bte.size())) - return false; - - log::debug(logcat, "Singed encrypted introset: {}", *this); - return true; - } - - bool EncryptedIntroSet::verify() const - { - if (is_expired()) - return false; - - EncryptedIntroSet copy(*this); - copy.sig.zero(); - - auto bte = copy.bt_encode(); - return crypto::verify(derived_signing_key, reinterpret_cast(bte.data()), bte.size(), sig); - } - - bool EncryptedIntroSet::verify(uint8_t* introset, size_t introset_size, uint8_t* key, uint8_t* sig) - { - return crypto::verify(key, introset, introset_size, sig); - } - - bool EncryptedIntroSet::verify(std::string introset, std::string key, std::string sig) - { - return crypto::verify( - reinterpret_cast(key.data()), - reinterpret_cast(introset.data()), - introset.size(), - reinterpret_cast(sig.data())); - } - - nlohmann::json IntroSet::ExtractStatus() const - { - nlohmann::json obj{{"published", to_json(time_signed)}}; - // TODO: this - // std::vector introsObjs; - // std::transform( - // intros.begin(), - // intros.end(), - // std::back_inserter(introsObjs), - // [](const auto& intro) -> nlohmann::json { return intro.ExtractStatus(); }); - // obj["intros"] = introsObjs; - // if (!topic.IsZero()) - // obj["topic"] = topic.to_string(); - - // std::vector protocols; - // std::transform( - // supported_protocols.begin(), - // supported_protocols.end(), - // std::back_inserter(protocols), - // [](const auto& proto) -> nlohmann::json { return service::to_string(proto); }); - // obj["protos"] = protocols; - // std::vector ranges; - // std::transform( - // owned_ranges.begin(), - // owned_ranges.end(), - // std::back_inserter(ranges), - // [](const auto& range) -> nlohmann::json { return range.to_string(); }); - - // obj["advertisedRanges"] = ranges; - // if (exit_policy) - // obj["exitPolicy"] = exit_policy->ExtractStatus(); - - return obj; - } - - IntroSet::IntroSet(std::string bt_payload) - { - try - { - oxenc::bt_dict_consumer btdc{bt_payload}; - bt_decode(btdc); - } - catch (...) - { - log::critical(logcat, "Error: EncryptedIntroSet failed to bt encode contents!"); - } - } - - bool IntroSet::bt_decode(std::string_view buf) - { - try - { - oxenc::bt_dict_consumer btdc{buf}; - bt_decode(btdc); - } - catch (const std::exception& e) - { - // DISCUSS: rethrow or print warning/return false...? - auto err = "IntroSet parsing exception: {}"_format(e.what()); - log::warning(logcat, "{}", err); - throw std::runtime_error{err}; - } - - return true; - } - - void IntroSet::bt_decode(oxenc::bt_dict_consumer& btdc) - { - try - { - { - auto [key, subdict] = btdc.next_dict_consumer(); - - if (key != "a") - throw std::invalid_argument{ - "IntroSet received unexpected key (expected:'a', actual:{})"_format(key)}; - - address_keys.bt_decode(subdict); - } - - if (auto maybe_subdict = btdc.maybe("e"); maybe_subdict) - { - oxenc::bt_dict_consumer subdict{*maybe_subdict}; - exit_policy->bt_decode(subdict); - } - - { - auto [key, sublist] = btdc.next_list_consumer(); - - if (key != "i") - throw std::invalid_argument{ - "IntroSet received unexpected key (expected:'i', actual:{})"_format(key)}; - - while (not sublist.is_finished()) - { - intros.insert(sublist.consume_string()); - } - } - - sntru_pubkey.from_string(btdc.require("k")); - - if (auto maybe_supportedprotos = btdc.maybe("p"); maybe_supportedprotos) - { - oxenc::bt_list_consumer sublist{*maybe_supportedprotos}; - - while (not sublist.is_finished()) - { - supported_protocols.push_back(ProtocolType{sublist.consume_integer()}); - } - } - - if (auto maybe_ownedranges = btdc.maybe("i"); maybe_ownedranges) - { - oxenc::bt_list_consumer sublist{*maybe_ownedranges}; - - while (not sublist.is_finished()) - { - _routed_ranges.emplace(sublist.consume_string()); - } - } - - if (auto maybe_srvs = btdc.maybe("s"); maybe_srvs) - { - oxenc::bt_list_consumer sublist{*maybe_srvs}; - - while (not sublist.is_finished()) - { - SRVs.emplace_back(sublist.consume_string()); - } - } - - time_signed = std::chrono::milliseconds{btdc.require("t")}; - signature.from_string(btdc.require("z")); - } - catch (...) - { - log::critical(logcat, "IntroSet failed to decode bt payload!"); - throw; - } - } - - std::string IntroSet::bt_encode() const - { - oxenc::bt_dict_producer btdp; - - try - { - { - auto subdict = btdp.append_dict("a"); - address_keys.bt_encode(subdict); - } - - if (exit_policy) - { - auto subdict = btdp.append_dict("e"); - exit_policy->bt_encode(subdict); - } - - { - auto sublist = btdp.append_list("i"); - for (auto& i : intros) - i.bt_encode(sublist); - } - - btdp.append("k", sntru_pubkey.to_view()); - - if (not supported_protocols.empty()) - { - auto sublist = btdp.append_list("p"); - for (auto& p : supported_protocols) - sublist.append(static_cast(p)); - } - - if (not _routed_ranges.empty()) - { - auto sublist = btdp.append_list("r"); - for (auto& r : _routed_ranges) - r.bt_encode(sublist); - } - - if (not SRVs.empty()) - { - auto sublist = btdp.append_list("s"); - for (auto& s : SRVs) - sublist.append(s.bt_encode()); - } - - btdp.append("t", time_signed.count()); - btdp.append("z", signature.to_view()); - } - catch (...) - { - log::critical(logcat, "Error: IntroSet failed to bt encode contents!"); - } - - return std::move(btdp).str(); - } - - bool IntroSet::HasExpiredIntros(std::chrono::milliseconds now) const - { - for (const auto& intro : intros) - if (now >= intro.expiry) - return true; - return false; - } - - bool IntroSet::HasStaleIntros(std::chrono::milliseconds now, std::chrono::milliseconds delta) const - { - for (const auto& intro : intros) - if (intro.expires_soon(now, delta)) - return true; - return false; - } - - bool IntroSet::IsExpired(std::chrono::milliseconds now) const - { - return GetNewestIntroExpiration() < now; - } - - std::vector IntroSet::GetMatchingSRVRecords(std::string_view service_proto) const - { - std::vector records; - - for (const auto& srv : SRVs) - { - if (srv.service_proto == service_proto) - { - records.push_back(srv); - } - } - - return records; - } - - bool IntroSet::verify(std::chrono::milliseconds now) const - { - IntroSet copy; - copy = *this; - copy.signature.zero(); - - auto bte = copy.bt_encode(); - - if (!address_keys.verify(reinterpret_cast(bte.data()), bte.size(), signature)) - { - return false; - } - // valid timestamps - // add max clock skew - now += MAX_INTROSET_TIME_DELTA; - for (const auto& intro : intros) - { - if (intro.expiry > now && intro.expiry - now > path::DEFAULT_LIFETIME) - { - return false; - } - } - return not IsExpired(now); - } - - std::chrono::milliseconds IntroSet::GetNewestIntroExpiration() const - { - std::chrono::milliseconds maxTime = 0s; - for (const auto& intro : intros) - maxTime = std::max(intro.expiry, maxTime); - return maxTime; - } - - std::string IntroSet::to_string() const - { - return "[IntroSet addressKeys={} intros={{}} sntrupKey={} topic={} signedAt={} v={} sig={}]"_format( - address_keys.to_string(), - "{}"_format(fmt::join(intros, ",")), - sntru_pubkey, - time_signed.count(), - version, - signature.to_view()); - } -} // namespace llarp::service diff --git a/llarp/service/intro_set.hpp b/llarp/service/intro_set.hpp deleted file mode 100644 index ab0b71ee28..0000000000 --- a/llarp/service/intro_set.hpp +++ /dev/null @@ -1,165 +0,0 @@ -#pragma once - -#include "info.hpp" -#include "intro.hpp" -#include "types.hpp" - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace llarp::service -{ - constexpr std::size_t MAX_INTROSET_SIZE = 4096; - // 10 seconds clock skew permitted for introset expiration - constexpr std::chrono::milliseconds MAX_INTROSET_TIME_DELTA = 10s; - - struct IntroSet - { - ServiceInfo address_keys; - IntroductionSet intros; - PQPubKey sntru_pubkey; - std::vector SRVs; - std::chrono::milliseconds time_signed = 0s; - - IntroSet() = default; - - explicit IntroSet(std::string bt_payload); - - /// ethertypes we advertise that we speak - std::vector supported_protocols; - /// aonnuce that these ranges are reachable via our endpoint - /// only set when we support exit traffic ethertype is supported - std::set _routed_ranges; - - /// policies about traffic that we are willing to carry - /// a protocol/range whitelist or blacklist - /// only set when we support exit traffic ethertype - std::optional exit_policy = std::nullopt; - - Signature signature; - uint64_t version = llarp::constants::proto_version; - - bool OtherIsNewer(const IntroSet& other) const { return time_signed < other.time_signed; } - - std::string to_string() const; - - std::chrono::milliseconds GetNewestIntroExpiration() const; - - bool HasExpiredIntros(std::chrono::milliseconds now) const; - - /// return true if any of our intros expires soon given a delta - bool HasStaleIntros(std::chrono::milliseconds now, std::chrono::milliseconds delta) const; - - bool IsExpired(std::chrono::milliseconds now) const; - - std::vector GetMatchingSRVRecords(std::string_view service_proto) const; - - std::string bt_encode() const; - - bool bt_decode(std::string_view buf); - - void bt_decode(oxenc::bt_dict_consumer& btdc); - - bool verify(std::chrono::milliseconds now) const; - - nlohmann::json ExtractStatus() const; - - static constexpr bool to_string_formattable = true; - }; - - inline bool operator<(const IntroSet& lhs, const IntroSet& rhs) - { - return lhs.address_keys < rhs.address_keys; - } - - inline bool operator==(const IntroSet& lhs, const IntroSet& rhs) - { - return std::tie(lhs.address_keys, lhs.intros, lhs.sntru_pubkey, lhs.time_signed, lhs.version, lhs.signature) - == std::tie(rhs.address_keys, rhs.intros, rhs.sntru_pubkey, rhs.time_signed, rhs.version, rhs.signature); - } - - inline bool operator!=(const IntroSet& lhs, const IntroSet& rhs) - { - return !(lhs == rhs); - } - - /// public version of the introset that is encrypted - struct EncryptedIntroSet - { - private: - explicit EncryptedIntroSet(std::string bt_payload); - bool bt_decode(oxenc::bt_dict_consumer& btdc); - - public: - PubKey derived_signing_key; - std::chrono::milliseconds signed_at = 0s; - ustring introset_payload; - SymmNonce nonce; - Signature sig; - - EncryptedIntroSet() = default; - - explicit EncryptedIntroSet( - std::string signing_key, - std::chrono::milliseconds signed_at, - std::string enc_payload, - std::string nonce, - std::string sig); - - bool sign(const Ed25519Hash& k); - - bool is_expired(std::chrono::milliseconds now = time_now_ms()) const; - - std::string bt_encode() const; - - bool bt_decode(std::string_view buf); - - bool other_is_newer(const EncryptedIntroSet& other) const; - - /// verify signature and timestamp - bool verify() const; - - static bool verify(uint8_t* introset, size_t introset_size, uint8_t* key, uint8_t* sig); - - static bool verify(std::string introset, std::string key, std::string sig); - - // this constructor will throw if ::bt_decode fails - static std::optional construct(std::string bt); - - std::string to_string() const; - - nlohmann::json ExtractStatus() const; - - std::optional decrypt(const PubKey& root) const; - static constexpr bool to_string_formattable = true; - }; - - inline bool operator<(const EncryptedIntroSet& lhs, const EncryptedIntroSet& rhs) - { - return lhs.derived_signing_key < rhs.derived_signing_key; - } - - inline bool operator==(const EncryptedIntroSet& lhs, const EncryptedIntroSet& rhs) - { - return std::tie(lhs.signed_at, lhs.derived_signing_key, lhs.nonce, lhs.sig) - == std::tie(rhs.signed_at, rhs.derived_signing_key, rhs.nonce, rhs.sig); - } - - inline bool operator!=(const EncryptedIntroSet& lhs, const EncryptedIntroSet& rhs) - { - return !(lhs == rhs); - } - - using EncryptedIntroSetLookupHandler = std::function&)>; - using IntroSetLookupHandler = std::function&)>; - -} // namespace llarp::service diff --git a/llarp/service/name.cpp b/llarp/service/name.cpp deleted file mode 100644 index 2a23c5916e..0000000000 --- a/llarp/service/name.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "name.hpp" - -#include -#include - -namespace llarp::service -{ - static auto logcat = llarp::log::Cat("ONSRecord"); - - std::optional EncryptedONSRecord::construct(std::string bt) - { - if (EncryptedONSRecord ret; ret.bt_decode(std::move(bt))) - return ret; - - return std::nullopt; - } - - EncryptedONSRecord::EncryptedONSRecord(std::string bt) - { - try - { - // The constructor calls the ::bt_decode() overload that re-throws any exception it hits - oxenc::bt_dict_consumer btdc{bt}; - bt_decode(btdc); - } - catch (const std::exception& e) - { - log::warning(logcat, "EncryptedONSRecord exception: {}", e.what()); - } - } - - bool EncryptedONSRecord::bt_decode(oxenc::bt_dict_consumer& btdc) - { - try - { - ciphertext = btdc.require("c"); - nonce.from_string(btdc.require("n")); - - return true; - } - catch (...) - { - log::warning(logcat, "EncryptedONSRecord exception"); - throw; - } - } - - bool EncryptedONSRecord::bt_decode(std::string bt) - { - try - { - oxenc::bt_dict_consumer btdc{bt}; - return bt_decode(btdc); - } - catch (...) - { - log::warning(logcat, "EncryptedONSRecord exception"); - return false; - } - } - - std::string EncryptedONSRecord::bt_encode() const - { - oxenc::bt_dict_producer btdp; - - btdp.append("c", ciphertext); - btdp.append("n", nonce.to_view()); - - return std::move(btdp).str(); - } - - std::optional EncryptedONSRecord::decrypt(std::string_view ons_name) const - { - std::optional ret = std::nullopt; - - if (ciphertext.empty()) - return ret; - - if (auto maybe = crypto::maybe_decrypt_name(ciphertext, nonce, ons_name)) - { - auto _name = "{}.loki"_format(maybe->to_view()); - ret = NetworkAddress::from_network_addr(std::move(_name)); - } - - return ret; - } -} // namespace llarp::service diff --git a/llarp/service/name.hpp b/llarp/service/name.hpp deleted file mode 100644 index c524af4231..0000000000 --- a/llarp/service/name.hpp +++ /dev/null @@ -1,96 +0,0 @@ -#pragma once - -#include -#include - -#include - -namespace llarp -{ - struct NetworkAddress; - - namespace service - { - /** Holds an entire ONS Record returned from a succfessful request to the `lookup_name` endpoint. - When transmitted over the wire back to the calling instance, it is bt-encoded and the ONS hash - ('ciphertext') is decrypted using the ons_name. - - bt-encoded keys: - 'c' : ciphertext - 'n' : nonce - */ - struct EncryptedONSRecord - { - private: - explicit EncryptedONSRecord(std::string bt); - bool bt_decode(oxenc::bt_dict_consumer& btdc); - - public: - SymmNonce nonce; - std::string ciphertext; - - EncryptedONSRecord() = default; - - static std::optional construct(std::string bt); - - std::string bt_encode() const; - - bool bt_decode(std::string bt); - - std::optional decrypt(std::string_view ons_name) const; - }; - - /// check if an ons name complies with the registration rules - inline bool is_valid_ons(std::string_view ons_name) - { - // make sure it ends with .loki because no fucking shit right? - if (not ons_name.ends_with(".loki")) - return false; - - // strip off .loki suffix - ons_name.remove_suffix(5); - - // ensure chars are sane - for (const auto ch : ons_name) - { - if (ch == '-') - continue; - if (ch == '.') - continue; - if (ch >= 'a' and ch <= 'z') - continue; - if (ch >= '0' and ch <= '9') - continue; - return false; - } - - // split into domain parts - const auto parts = split(ons_name, "."); - - // get root domain - const auto primaryName = parts[parts.size() - 1]; - constexpr size_t MaxNameLen = 32; - constexpr size_t MaxPunycodeNameLen = 63; - - // check against lns name blacklist - if (primaryName == "localhost") - return false; - if (primaryName == "loki") - return false; - if (primaryName == "snode") - return false; - // check for dashes - if (primaryName.find("-") == std::string_view::npos) - return primaryName.size() <= MaxNameLen; - // check for dashes and end or beginning - if (*primaryName.begin() == '-' or *(primaryName.end() - 1) == '-') - return false; - // check for punycode name length - if (primaryName.size() > MaxPunycodeNameLen) - return false; - // check for xn-- - return (primaryName[2] == '-' and primaryName[3] == '-') ? (primaryName[0] == 'x' and primaryName[1] == 'n') - : true; - } - } // namespace service -} // namespace llarp diff --git a/llarp/service/tag.cpp b/llarp/service/tag.cpp deleted file mode 100644 index 93185ba514..0000000000 --- a/llarp/service/tag.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "tag.hpp" - -namespace llarp::service -{ - SessionTag SessionTag::make_random() - { - SessionTag t; - t.Randomize(); - return t; - } - - void SessionTag::Randomize() - { - llarp::AlignedBuffer<16>::Randomize(); - /// ensure we are in the fc00 range - llarp::AlignedBuffer<16>::operator[](0) = 0xfc; - } - - sockaddr_in6 SessionTag::to_v6() const - { - sockaddr_in6 saddr{}; - saddr.sin6_family = AF_INET6; - std::copy_n(data(), size(), saddr.sin6_addr.s6_addr); - return saddr; - } - - void SessionTag::from_v6(sockaddr_in6 saddr) - { - std::copy_n(saddr.sin6_addr.s6_addr, size(), data()); - } - -} // namespace llarp::service diff --git a/llarp/service/tag.hpp b/llarp/service/tag.hpp deleted file mode 100644 index 7f2b70cba6..0000000000 --- a/llarp/service/tag.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include -#include - -namespace llarp::service -{ - struct SessionTag final : AlignedBuffer<16> - { - using AlignedBuffer<16>::AlignedBuffer; - - static SessionTag make_random(); - - void Randomize() override; - - // DISCUSS: TONUKE: maybe these...? - sockaddr_in6 to_v6() const; - void from_v6(sockaddr_in6 saddr); - }; -} // namespace llarp::service - -namespace std -{ - template <> - struct hash - { - size_t operator()(const llarp::service::SessionTag& tag) const - { - std::hash h{}; - return h(std::string_view{reinterpret_cast(tag.data()), tag.size()}); - } - }; -} // namespace std diff --git a/llarp/service/types.cpp b/llarp/service/types.cpp deleted file mode 100644 index 91148cb26b..0000000000 --- a/llarp/service/types.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include "types.hpp" - -#include - -namespace llarp::service::util -{ - // void ExpireSNodeSessions(std::chrono::milliseconds now, SNodeConnectionMap& sessions) - // { - // auto itr = sessions.begin(); - // while (itr != sessions.end()) - // { - // if (itr->second->ShouldRemove() && itr->second->IsStopped()) - // { - // itr = sessions.erase(itr); - // continue; - // } - // // expunge next tick - // if (itr->second->IsExpired(now)) - // { - // itr->second->Stop(); - // } - // else - // { - // itr->second->Tick(now); - // } - - // ++itr; - // } - // } - - // void DeregisterDeadSessions(std::chrono::milliseconds now, ConnectionMap& sessions) - // { - // auto itr = sessions.begin(); - // while (itr != sessions.end()) - // { - // if (itr->second->IsDone(now)) - // { - // itr = sessions.erase(itr); - // } - // else - // { - // ++itr; - // } - // } - // } - - // void TickRemoteSessions( - // std::chrono::milliseconds now, - // ConnectionMap& remoteSessions, - // ConnectionMap& deadSessions, - // std::unordered_map& sessions) - // { - // auto itr = remoteSessions.begin(); - // while (itr != remoteSessions.end()) - // { - // itr->second->Tick(now); - // if (itr->second->Pump(now)) - // { - // LogInfo( - // "marking session as dead T=", - // itr->second->get_current_tag(), - // " to ", - // itr->second->Addr()); - // itr->second->Stop(); - // sessions.erase(itr->second->get_current_tag()); - // deadSessions.emplace(std::move(*itr)); - // itr = remoteSessions.erase(itr); - // } - // else - // { - // ++itr; - // } - // } - // for (auto& item : deadSessions) - // { - // item.second->Tick(now); - // } - // } - - // void ExpireConvoSessions( - // std::chrono::milliseconds now, std::unordered_map& sessions) - // { - // auto itr = sessions.begin(); - // while (itr != sessions.end()) - // { - // if (itr->second.IsExpired(now)) - // { - // LogInfo("Expire session T=", itr->first, " to ", itr->second.Addr()); - // itr = sessions.erase(itr); - // } - // else - // ++itr; - // } - // } - - // void StopRemoteSessions(ConnectionMap& remoteSessions) - // { - // for (auto& item : remoteSessions) - // { - // item.second->Stop(); - // } - // } - - // void StopSnodeSessions(SNodeConnectionMap& sessions) - // { - // for (auto& item : sessions) - // { - // item.second->Stop(); - // } - // } - - // bool HasPathToService(const Address& addr, const ConnectionMap& remoteSessions) - // { - // auto range = remoteSessions.equal_range(addr); - // auto itr = range.first; - // while (itr != range.second) - // { - // if (itr->second->ReadyToSend()) - // return true; - // ++itr; - // } - // return false; - // } - - // bool GetConvoTagsForService( - // const std::unordered_map& sessions, - // const Address& info, - // std::set& tags) - // { - // bool inserted = false; - // auto itr = sessions.begin(); - // while (itr != sessions.end()) - // { - // if (itr->second.remote.Addr() == info) - // { - // if (tags.emplace(itr->first).second) - // { - // inserted = true; - // } - // } - // ++itr; - // } - // return inserted; - // } -} // namespace llarp::service::util diff --git a/llarp/service/types.hpp b/llarp/service/types.hpp deleted file mode 100644 index 92b71ebd05..0000000000 --- a/llarp/service/types.hpp +++ /dev/null @@ -1,123 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include -#include - -/** TODO: - - The commented functions are utilities for managing sessions to remote hidden services. They - need to be redefined with the correct parameters and called from service::Handler, as - service::Endpoint is for local hidden service management - - ^^ Is this still true...? -*/ - -using namespace std::literals; - -namespace llarp -{ - namespace session - { - struct OutboundSession; - } - namespace path - { - struct Path; - } - namespace routing - { - struct PathTransferMessage; - } - - namespace service - { - // Supported protocol types; the values are given explicitly because they are specifically - // used when sending over the wire. - enum class ProtocolType : uint64_t - { - Control = 0UL, - TrafficV4 = 1UL, - TrafficV6 = 2UL, - Exit = 3UL, - Auth = 4UL, - TCP2QUIC = 5UL, - }; - - inline constexpr std::string_view to_string(ProtocolType t) - { - return t == ProtocolType::Control ? "Control"sv - : t == ProtocolType::TrafficV4 ? "TrafficV4"sv - : t == ProtocolType::TrafficV6 ? "TrafficV6"sv - : t == ProtocolType::Exit ? "Exit"sv - : t == ProtocolType::Auth ? "Auth"sv - : t == ProtocolType::TCP2QUIC ? "TCP->QUIC"sv - : "(unknown-protocol-type)"sv; - } - - // namespace util - // { - // static void ExpireSNodeSessions(/* std::chrono::milliseconds now, SNodeConnectionMap& sessions */); - - // static void DeregisterDeadSessions(/* std::chrono::milliseconds now, ConnectionMap& sessions */); - - // static void TickRemoteSessions( - // /* std::chrono::milliseconds now, - // ConnectionMap& remoteSessions, - // ConnectionMap& deadSessions, - // std::unordered_map& sessions */); - - // static void ExpireConvoSessions( - // /* std::chrono::milliseconds now, std::unordered_map& sessions */); - - // static void StopRemoteSessions(/* ConnectionMap& remoteSessions */); - - // static void StopSnodeSessions(/* SNodeConnectionMap& sessions */); - - // static bool HasPathToService( - // /* const Address& addr, const ConnectionMap& remoteSessions */); - - // static bool GetConvoTagsForService( - // /* const std::unordered_map& sessions, - // const Address& addr, - // std::set& tags */); - // } // namespace util - - // template - // static std:: - // unordered_set, path::Endpoint_Hash, path::endpoint_comparator> - // GetManyPathsWithUniqueEndpoints( - // /* Endpoint_t* ep, - // size_t N, - // std::optional maybeLocation = std::nullopt, - // size_t tries = 10 */) - // { - // // std::unordered_set exclude; - // std::unordered_set, path::Endpoint_Hash, path::endpoint_comparator> - // paths; - // // do - // // { - // // --tries; - // // std::shared_ptr path; - // // if (maybeLocation) - // // { - // // path = ep->GetEstablishedPathClosestTo(RouterID{maybeLocation->as_array()}, - // // exclude); - // // } - // // else - // // { - // // path = ep->PickRandomEstablishedPath(); - // // } - // // if (path and path->IsReady()) - // // { - // // paths.emplace(path); - // // exclude.insert(path->Endpoint()); - // // } - // // } while (tries > 0 and paths.size() < N); - // return paths; - // } - } // namespace service - -} // namespace llarp diff --git a/llarp/service/vanity.hpp b/llarp/service/vanity.hpp deleted file mode 100644 index 0599eef234..0000000000 --- a/llarp/service/vanity.hpp +++ /dev/null @@ -1,16 +0,0 @@ - -#ifndef LLARP_SERVICE_VANITY_HPP -#define LLARP_SERVICE_VANITY_HPP - -#include - -namespace llarp -{ - namespace service - { - /// hidden service address - - using VanityNonce = AlignedBuffer<16>; - } // namespace service -} // namespace llarp -#endif diff --git a/llarp/session/map.hpp b/llarp/session/map.hpp index 38f43ce19f..d72fc2adcf 100644 --- a/llarp/session/map.hpp +++ b/llarp/session/map.hpp @@ -7,6 +7,8 @@ namespace llarp { + // TESTNET: TODO: revisit this later; if we never need NetworkAddr as a key, just use tag + /** This class will accept any types satisfying the concepts SessionType and NetworkAddrType NetworkAddrType: must be inherited from NetworkAddress SessionType: must be inherited from BaseSession @@ -18,7 +20,7 @@ namespace llarp struct session_map { protected: - std::unordered_map _session_lookup; + std::unordered_map _session_lookup; std::unordered_map> _sessions; using Lock_t = util::NullLock; @@ -81,7 +83,7 @@ namespace llarp return {_2->second, b1 & b2}; } - std::optional get_remote(const service::SessionTag& tag) const + std::optional get_remote(const session_tag& tag) const { Lock_t l{session_mutex}; @@ -105,19 +107,19 @@ namespace llarp return ret; } - std::shared_ptr get_session(const service::SessionTag& tag) const + std::shared_ptr get_session(const session_tag& tag) const { Lock_t l{session_mutex}; std::shared_ptr ret = nullptr; - if (auto itr = get_remote(tag); itr != std::nullopt) - ret = get_session(itr->second); + if (auto remote = get_remote(tag); remote != std::nullopt) + ret = get_session(*remote); return ret; } - void unmap(const service::SessionTag& tag) + void unmap(const session_tag& tag) { Lock_t l{session_mutex}; @@ -147,7 +149,7 @@ namespace llarp } } - bool have_session(const service::SessionTag& tag) const + bool have_session(const session_tag& tag) const { Lock_t l{session_mutex}; @@ -164,7 +166,7 @@ namespace llarp return _sessions.count(local); } - std::shared_ptr operator[](const service::SessionTag& tag) { return get_session(tag); } + std::shared_ptr operator[](const session_tag& tag) { return get_session(tag); } std::shared_ptr operator[](const net_addr_t& local) { return get_session(local); } }; diff --git a/llarp/session/session.cpp b/llarp/session/session.cpp index 1b3943e3e4..0a0ca7c460 100644 --- a/llarp/session/session.cpp +++ b/llarp/session/session.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include @@ -18,55 +18,66 @@ namespace llarp::session std::shared_ptr _p, handlers::SessionEndpoint& parent, NetworkAddress remote, - service::SessionTag _t, + HopID remote_pivot_txid, + session_tag _t, bool use_tun, - bool is_exit, - bool is_outbound) + bool is_outbound, + std::optional kx_data) : _r{r}, _parent{parent}, _tag{std::move(_t)}, _remote{std::move(remote)}, + _remote_pivot_txid{std::move(remote_pivot_txid)}, _use_tun{use_tun}, - _is_outbound{is_outbound}, - _is_exit_session{is_exit} + _is_outbound{is_outbound} { + if (kx_data.has_value()) + session_keys = std::move(*kx_data); + + if (_use_tun) + _recv_dgram = [this](std::vector data) { + _r.tun_endpoint()->handle_inbound_packet(IPPacket{std::move(data)}, _tag, _remote); + }; + else + _recv_dgram = [this](std::vector data) { + _ep->manually_receive_packet( + NetworkPacket{oxen::quic::Path{}, bstring{reinterpret_cast(data.data()), data.size()}}); + }; + set_new_current_path(std::move(_p)); } bool BaseSession::send_path_control_message( - std::string method, std::string body, std::function func) + std::string method, std::string body, std::function func) { return _current_path->send_path_control_message(std::move(method), std::move(body), std::move(func)); } bool BaseSession::send_path_data_message(std::string data) { - return _current_path->send_path_data_message(std::move(data)); + auto inner_payload = PATH::DATA::serialize_inner(std::move(data), _tag); + auto intermediate_payload = PATH::DATA::serialize_intermediate(std::move(inner_payload), _remote_pivot_txid); + return _r.send_data_message( + _current_path->upstream_rid(), _current_path->make_path_message(std::move(intermediate_payload))); } - void BaseSession::recv_path_data_message(bstring body) + void BaseSession::recv_path_data_message(std::vector data) { - _current_path->recv_path_data_message(std::move(body)); + if (_recv_dgram) + _recv_dgram(std::move(data)); + else + throw std::runtime_error{"Session does not have hook to receive datagrams!"}; } void BaseSession::set_new_current_path(std::shared_ptr _new_path) { if (_current_path) - _current_path->unlink_session(); + _current_path->unlink_session(_tag); _current_path = std::move(_new_path); + _pivot_txid = _current_path->pivot_txid(); - _current_hop_id = _current_path->intro.pivot_hop_id; - - if (_use_tun) - _current_path->link_session([this](bstring data) { - _r.tun_endpoint()->handle_inbound_packet( - IPPacket{std::move(data)}, _remote, _is_exit_session, _is_outbound); - }); - else - _current_path->link_session([this](bstring data) { - _ep->manually_receive_packet(NetworkPacket{oxen::quic::Path{}, std::move(data)}); - }); + _current_path->link_session(_tag); assert(_current_path->is_linked()); } @@ -75,8 +86,7 @@ namespace llarp::session { _ep = _r.quic_tunnel()->net()->endpoint( LOCALHOST_BLANK, oxen::quic::opt::manual_routing{[this](const oxen::quic::Path&, bstring_view data) { - _current_path->send_path_data_message( - std::string{reinterpret_cast(data.data()), data.size()}); + send_path_data_message(std::string{reinterpret_cast(data.data()), data.size()}); }}); } @@ -146,7 +156,7 @@ namespace llarp::session _r.quic_tunnel()->creds(), [addr = *bind, hook = std::move(cb)](oxen::quic::connection_interface&) { hook(addr.to_ipv4()); }, [](oxen::quic::connection_interface&, uint64_t) { - // + // TESTNET: TODO: }); } @@ -154,38 +164,36 @@ namespace llarp::session NetworkAddress remote, handlers::SessionEndpoint& parent, std::shared_ptr path, - service::SessionTag _t, - bool is_exit) + HopID remote_pivot_txid, + session_tag _t, + std::optional kx_data) : PathHandler{parent._router, path::DEFAULT_PATHS_HELD}, BaseSession{ _router, std::move(path), parent, std::move(remote), + std::move(remote_pivot_txid), std::move(_t), _router.using_tun_if(), - is_exit, - true}, - _last_use{_router.now()}, - _is_snode_session{not _remote.is_client()} + true, + std::move(kx_data)}, + _is_snode_session{not _remote.is_client()}, + _last_use{_router.now()} { // These can both be false but CANNOT both be true if (_is_exit_session and _is_snode_session) throw std::runtime_error{"Cannot create OutboundSession for a remote exit and remote service!"}; add_path(_current_path); - - if (_is_snode_session) - _session_key = _router.identity(); - else - crypto::identity_keygen(_session_key); } OutboundSession::~OutboundSession() = default; - void OutboundSession::path_died(std::shared_ptr p) + void OutboundSession::path_died([[maybe_unused]] std::shared_ptr p) { - p->rebuild(); + log::debug(logcat, "{} called", __PRETTY_FUNCTION__); + // p->rebuild(); } nlohmann::json OutboundSession::ExtractStatus() const @@ -198,10 +206,7 @@ namespace llarp::session return obj; } - void OutboundSession::blacklist_snode(const RouterID& snode) - { - (void)snode; - } + void OutboundSession::blacklist_snode(const RouterID& snode) { (void)snode; } bool OutboundSession::is_path_dead(std::shared_ptr, std::chrono::milliseconds dlt) { @@ -210,6 +215,7 @@ namespace llarp::session void OutboundSession::path_build_succeeded(std::shared_ptr p) { + log::debug(logcat, "{} called", __PRETTY_FUNCTION__); path::PathHandler::path_build_succeeded(p); // TODO: why the fuck did we used to do this here...? @@ -256,8 +262,11 @@ namespace llarp::session for (auto& [_, p] : _paths) { - log::debug(logcat, "Sending close message on path {}", p->to_string()); - send_path_close(p); + if (p and p->is_ready()) + { + log::debug(logcat, "Sending close message on path {}", p->to_string()); + // send_path_close(p); + } } }); @@ -273,16 +282,10 @@ namespace llarp::session { size_t count{0}; log::debug( - logcat, - "OutboundSession building {} paths (needed: {}) to remote:{}", - n, - path::DEFAULT_PATHS_HELD, - _remote); + logcat, "OutboundSession building {} paths (needed:{}) to remote:{}", n, path::DEFAULT_PATHS_HELD, _remote); for (size_t i = 0; i < n; ++i) - { - count += build_path_aligned_to_remote(_remote); - } + count += build_path_aligned_to_remote(_remote.router_id()); if (count == n) log::debug(logcat, "OutboundSession successfully initiated {} path-builds", n); @@ -294,27 +297,28 @@ namespace llarp::session { auto path = std::make_shared(_router, hops, get_weak(), true, _remote.is_client()); - log::info(logcat, "Building path -> {} : {}", path->to_string(), path->HopsString()); + log::info(logcat, "Building path -> {} : {}", path->to_string(), path->hop_string()); return path; } void OutboundSession::send_path_close(std::shared_ptr p) { - if (p->close_exit(_session_key, p->upstream_txid().to_string())) - log::info(logcat, "Sent path close on path {}", p->to_string()); - else - log::warning(logcat, "Failed to send path close on path {}", p->to_string()); + (void)p; + // if (p->close_exit(_session_key, p->upstream_txid().to_string())) + // log::info(logcat, "Sent path close on path {}", p->to_string()); + // else + // log::warning(logcat, "Failed to send path close on path {}", p->to_string()); } bool OutboundSession::is_ready() const { - if (_current_hop_id.is_zero()) + if (_pivot_txid.is_zero()) return false; const size_t expect = (1 + (num_paths_desired / 2)); - return num_paths() >= expect; + return num_active_paths() >= expect; } bool OutboundSession::is_expired(std::chrono::milliseconds now) const @@ -326,25 +330,21 @@ namespace llarp::session NetworkAddress remote, std::shared_ptr _path, handlers::SessionEndpoint& parent, - service::SessionTag _t, - bool use_tun) + HopID remote_pivot_txid, + session_tag _t, + bool use_tun, + std::optional kx_data) : BaseSession{ parent._router, std::move(_path), parent, std::move(remote), + std::move(remote_pivot_txid), std::move(_t), use_tun, - parent.is_exit_node(), - false} - { - if (not _current_path->is_client_path() and _remote.is_client()) - throw std::runtime_error{ - "NetworkAddress and Path do not agree on InboundSession remote's identity (client vs server)!"}; - } + false, + std::move(kx_data)} + {} - void InboundSession::set_new_tag(const service::SessionTag& tag) - { - _tag = tag; - } + void InboundSession::set_new_tag(const session_tag& tag) { _tag = tag; } } // namespace llarp::session diff --git a/llarp/session/session.hpp b/llarp/session/session.hpp index 71a9ae5a58..42d266e4dd 100644 --- a/llarp/session/session.hpp +++ b/llarp/session/session.hpp @@ -1,11 +1,11 @@ #pragma once #include -#include #include +#include #include +#include #include -#include #include @@ -15,11 +15,12 @@ namespace llarp { using on_session_init_hook = std::function; + using recv_session_dgram_cb = std::function)>; namespace link { class TunnelManager; - } + } // namespace link namespace handlers { @@ -31,7 +32,6 @@ namespace llarp - client to snode: - the traffic to the pivot is encrypted - the pivot is the terminus, so data doesn't need to be encrypted - - could set HopID to 0 to indicate */ namespace session @@ -45,16 +45,23 @@ namespace llarp Router& _r; handlers::SessionEndpoint& _parent; - service::SessionTag _tag; + session_tag _tag; NetworkAddress _remote; + shared_kx_data session_keys{}; + + // used for bridging data messages across aligned paths + HopID _remote_pivot_txid; + bool _use_tun; bool _is_outbound; const bool _is_exit_session{false}; std::shared_ptr _current_path; - HopID _current_hop_id; + HopID _pivot_txid; + + recv_session_dgram_cb _recv_dgram; // manually routed QUIC endpoint std::shared_ptr _ep; @@ -76,10 +83,11 @@ namespace llarp std::shared_ptr _p, handlers::SessionEndpoint& parent, NetworkAddress remote, - service::SessionTag _t, + HopID remote_pivot_txid, + session_tag _t, bool use_tun, - bool is_exit, - bool is_outbound); + bool is_outbound, + std::optional kx_data = std::nullopt); virtual ~BaseSession() = default; @@ -90,11 +98,13 @@ namespace llarp NetworkAddress remote() { return _remote; } bool send_path_control_message( - std::string method, std::string body, std::function func = nullptr); + std::string method, std::string body, std::function func); bool send_path_data_message(std::string data); - void recv_path_data_message(bstring data); + void recv_path_data_message(std::vector data); + + // void recv_path_data_message(bstring data); void set_new_current_path(std::shared_ptr _new_path); @@ -104,9 +114,9 @@ namespace llarp bool using_tun() const { return _use_tun; } - service::SessionTag tag() { return _tag; } + session_tag tag() { return _tag; } - const service::SessionTag& tag() const { return _tag; } + const session_tag& tag() const { return _tag; } bool is_exit_session() const { return _is_exit_session; } }; @@ -120,30 +130,22 @@ namespace llarp NetworkAddress _remote, handlers::SessionEndpoint& parent, std::shared_ptr path, - service::SessionTag _t, - bool is_exit); + HopID remote_pivot_txid, + session_tag _t, + std::optional kx_data = std::nullopt); ~OutboundSession() override; private: - Ed25519SecretKey _session_key; // DISCUSS: is this useful? - - std::chrono::milliseconds _last_use; - const bool _is_snode_session{false}; + std::chrono::milliseconds _last_use; public: std::shared_ptr get_self() override { return shared_from_this(); } std::weak_ptr get_weak() override { return weak_from_this(); } - std::shared_ptr current_path() - { - if (auto itr = _paths.find(_remote.router_id()); itr != _paths.end()) - return itr->second; - - return nullptr; - } + std::shared_ptr current_path() { return _current_path; } void blacklist_snode(const RouterID& snode) override; @@ -169,12 +171,12 @@ namespace llarp const RouterID& remote_endpoint() const { return _remote.router_id(); } - std::optional current_hop_id() const + std::optional current_pivot_txid() const { - if (_current_hop_id.is_zero()) + if (_pivot_txid.is_zero()) return std::nullopt; - return _current_hop_id; + return _pivot_txid; } bool is_expired(std::chrono::milliseconds now) const; @@ -186,12 +188,14 @@ namespace llarp NetworkAddress _remote, std::shared_ptr _path, handlers::SessionEndpoint& parent, - service::SessionTag _t, - bool use_tun); + HopID remote_pivot_txid, + session_tag _t, + bool use_tun, + std::optional kx_data = std::nullopt); ~InboundSession() = default; - void set_new_tag(const service::SessionTag& tag); + void set_new_tag(const session_tag& tag); }; } // namespace session diff --git a/llarp/simulation/sim_context.cpp b/llarp/simulation/sim_context.cpp index c0de6c0d33..a98a74e30e 100644 --- a/llarp/simulation/sim_context.cpp +++ b/llarp/simulation/sim_context.cpp @@ -20,9 +20,6 @@ namespace llarp return itr->second; } - void Simulation::DelNode(const std::string& name) - { - m_Nodes.erase(name); - } + void Simulation::DelNode(const std::string& name) { m_Nodes.erase(name); } } // namespace simulate } // namespace llarp diff --git a/llarp/util/aligned.hpp b/llarp/util/aligned.hpp index 6b3038a8ed..9ac3909194 100644 --- a/llarp/util/aligned.hpp +++ b/llarp/util/aligned.hpp @@ -2,6 +2,7 @@ #include "formattable.hpp" #include "logging.hpp" +#include "random.hpp" #include #include @@ -17,8 +18,6 @@ extern "C" { - extern void randombytes(unsigned char* const ptr, unsigned long long sz); - extern int sodium_is_zero(const unsigned char* n, const size_t nlen); } namespace llarp @@ -63,7 +62,9 @@ namespace llarp return ret; } - bool operator==(const AlignedBuffer& other) const { return _data == other._data; } + auto operator<=>(const AlignedBuffer& other) const { return _data <=> other._data; } + + bool operator==(const AlignedBuffer& other) const { return (*this <=> other) == 0; } bool operator!=(const AlignedBuffer& other) const { return _data != other._data; } @@ -298,8 +299,6 @@ namespace llarp static constexpr bool to_string_formattable = true; }; - // auto bt_printer::log_cat = log::Cat("bt_printer"); - } // namespace llarp namespace std @@ -309,9 +308,14 @@ namespace std { std::size_t operator()(const llarp::AlignedBuffer& buf) const noexcept { - std::size_t h = 0; - std::memcpy(&h, buf.data(), sizeof(std::size_t)); - return h; + if constexpr (alignof(llarp::AlignedBuffer) >= sizeof(size_t)) + return *reinterpret_cast(buf.data()); + else + { + std::size_t h{}; + std::memcpy(&h, buf.data(), sizeof(h)); + return h; + } } }; } // namespace std diff --git a/llarp/util/buffer.cpp b/llarp/util/buffer.cpp index 744b34aa3e..c5e084c85a 100644 --- a/llarp/util/buffer.cpp +++ b/llarp/util/buffer.cpp @@ -45,35 +45,17 @@ namespace } // namespace -bool llarp_buffer_t::put_uint16(uint16_t i) -{ - return put(*this, i); -} +bool llarp_buffer_t::put_uint16(uint16_t i) { return put(*this, i); } -bool llarp_buffer_t::put_uint64(uint64_t i) -{ - return put(*this, i); -} +bool llarp_buffer_t::put_uint64(uint64_t i) { return put(*this, i); } -bool llarp_buffer_t::put_uint32(uint32_t i) -{ - return put(*this, i); -} +bool llarp_buffer_t::put_uint32(uint32_t i) { return put(*this, i); } -bool llarp_buffer_t::read_uint16(uint16_t& i) -{ - return read(*this, i); -} +bool llarp_buffer_t::read_uint16(uint16_t& i) { return read(*this, i); } -bool llarp_buffer_t::read_uint32(uint32_t& i) -{ - return read(*this, i); -} +bool llarp_buffer_t::read_uint32(uint32_t& i) { return read(*this, i); } -bool llarp_buffer_t::read_uint64(uint64_t& i) -{ - return read(*this, i); -} +bool llarp_buffer_t::read_uint64(uint64_t& i) { return read(*this, i); } size_t llarp_buffer_t::read_until(char c_delim, uint8_t* result, size_t resultsize) { diff --git a/llarp/util/buffer.hpp b/llarp/util/buffer.hpp index 3478dc3008..ba0b98dd11 100644 --- a/llarp/util/buffer.hpp +++ b/llarp/util/buffer.hpp @@ -3,7 +3,7 @@ #include "common.hpp" #include "mem.h" -#include +#include #include #include @@ -21,71 +21,27 @@ namespace llarp { - using uspan = std::span; + using cspan = oxenc::const_span; + using uspan = oxenc::const_span; + using span = oxenc::const_span; + using ustring = std::basic_string; using ustring_view = std::basic_string_view; - using bspan = std::span; using bstring = std::basic_string; using bstring_view = std::basic_string_view; - namespace detail - { - template - struct bsv_literal - { - consteval bsv_literal(const char (&s)[N]) - { - for (size_t i = 0; i < N; i++) - str[i] = static_cast(s[i]); - } - std::byte str[N]; // we keep the null on the end, in case you pass .data() to a C func - using size = std::integral_constant; - }; - template - struct usv_literal - { - consteval usv_literal(const char (&s)[N]) - { - for (size_t i = 0; i < N; i++) - str[i] = static_cast(s[i]); - } - unsigned char str[N]; // we keep the null on the end, in case you pass .data() to a C func - using size = std::integral_constant; - }; - } // namespace detail - inline ustring operator""_us(const char* str, size_t len) noexcept { return {reinterpret_cast(str), len}; } - template - constexpr ustring_view operator""_usv() noexcept - { - return {UStr.str, decltype(UStr)::size::value}; - } - - template - constexpr bstring_view operator""_bsv() - { - return {BStr.str, decltype(BStr)::size::value}; - } - - inline bstring operator""_bs(const char* str, size_t len) noexcept - { - return {reinterpret_cast(str), len}; - } - // Helper function to switch between string_view and ustring_view - inline ustring_view to_usv(std::string_view v) - { - return {reinterpret_cast(v.data()), v.size()}; - } + inline ustring_view to_usv(std::string_view v) { return {reinterpret_cast(v.data()), v.size()}; } template - inline uspan to_uspan(std::basic_string& v) + inline std::span to_uspan(std::basic_string& v) { - return uspan{reinterpret_cast(v.data()), v.size()}; + return std::span{reinterpret_cast(v.data()), v.size()}; } } // namespace llarp diff --git a/llarp/util/concept.hpp b/llarp/util/concept.hpp deleted file mode 100644 index add80bdbd3..0000000000 --- a/llarp/util/concept.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include - -namespace llarp::concepts -{ - // Types can opt-in to being fmt-formattable by ensuring they have a ::to_string() method defined - template - concept to_string_formattable = oxen::quic::concepts::ToStringFormattable; - -#ifndef __cpp_lib_is_scoped_enum - template - struct is_scoped_enum_st : std::bool_constant < requires - { - requires std::is_enum_v; - requires not std::is_convertible_v>; - } > {}; - - template - inline constexpr bool is_scoped_enum_v = is_scoped_enum_st::value; -#endif - - template - concept is_scoped_enum = -#ifdef __cpp_lib_is_scoped_enum - std::is_scoped_enum_v; -#else - is_scoped_enum_v; -#endif - -} // namespace llarp::concepts diff --git a/llarp/util/formattable.hpp b/llarp/util/formattable.hpp index dbd5cf8ba6..c4314d5038 100644 --- a/llarp/util/formattable.hpp +++ b/llarp/util/formattable.hpp @@ -1,8 +1,9 @@ #pragma once -#include "concept.hpp" +#include "meta.hpp" #include +#include #include @@ -10,6 +11,14 @@ namespace llarp { using namespace std::literals; using namespace oxen::log::literals; + + namespace concepts + { + // Types can opt-in to being fmt-formattable by ensuring they have a ::to_string() method defined + template + concept to_string_formattable = oxen::quic::concepts::ToStringFormattable; + } // namespace concepts + } // namespace llarp #if !defined(USE_GHC_FILESYSTEM) && FMT_VERSION >= 80102 @@ -88,7 +97,7 @@ namespace fmt namespace fmt { - template + template struct formatter : formatter { template @@ -97,32 +106,4 @@ namespace fmt return formatter::format(to_string(val), ctx); } }; - - // template T> - // struct formatter, char> : formatter - // { - // template - // auto format(const std::optional& opt, FormatContext& ctx) - // { - // if (opt) - // { - // return formatter::format(*opt, ctx); - // } - // return format_to(ctx.out(), "[-nullopt-]"); - // } - // }; - - // template - // struct formatter, char> : formatter - // { - // template - // auto format(const std::optional& opt, FormatContext& ctx) - // { - // if (opt) - // { - // return formatter::format(opt->to_string(), ctx); - // } - // return format_to(ctx.out(), "[-nullopt-]"); - // } - // }; } // namespace fmt diff --git a/llarp/util/meta.hpp b/llarp/util/meta.hpp new file mode 100644 index 0000000000..bb2e0f0051 --- /dev/null +++ b/llarp/util/meta.hpp @@ -0,0 +1,106 @@ +#pragma once + +#include "random.hpp" + +#include +#include + +namespace llarp +{ + namespace concepts + { + template > + concept forward_iterable = std::forward_iterator; + + template + concept scoped_enum = +#ifdef __cpp_lib_scoped_enum + std::is_scoped_enum_v; +#else + std::is_enum_v && !std::is_convertible_v>; +#endif + } // namespace concepts + + /** Namespace meta: + Aggregates functional implementations utilized across broadly different contexts. + */ + namespace meta + { + inline namespace enums + { + // __cpp_lib_to_underlying is a C++23 feature + template >> + requires std::is_enum_v + inline constexpr U to_underlying(T e) + { + return static_cast(e); + } + } // namespace enums + + /** Aggregated generalized algorithms for sort, selection, etc + */ + inline namespace sampling + { + /** One-pass random selection algorithm + - https://en.wikipedia.org/wiki/Reservoir_sampling + */ + template ::value_type> + std::optional sample(const T& container, std::function hook) + { + size_t i = 0; + std::optional ret = std::nullopt; + + for (const auto& e : container) + { + if (not hook(e)) + continue; + + if (++i <= 1) + { + ret = e; + continue; + } + + size_t x = csrng() % (i + 1); + if (x <= 1) + ret = e; + } + + return ret; + } + + template ::value_type> + std::optional> sample_n( + const T& container, std::function hook, size_t n, bool exact = false) + { + auto ret = std::make_optional>(); + ret->reserve(n); + + size_t i = 0; + + for (const auto& e : container) + { + if (not hook(e)) + continue; + + if (++i <= n) + { + ret->emplace_back(e); + continue; + } + + size_t x = csrng() % (i + 1); + if (x < n) + (*ret)[x] = e; + } + + if (ret->size() < (exact ? n : 1)) + ret.reset(); + + return ret; + } + } // namespace sampling + + } // namespace meta + +} // namespace llarp diff --git a/llarp/util/priority_queue.hpp b/llarp/util/priority_queue.hpp deleted file mode 100644 index cc87382a69..0000000000 --- a/llarp/util/priority_queue.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include -#include - -namespace llarp::util -{ - /// priority queue that uses operator > instead of operator < - template > - using ascending_priority_queue = std::priority_queue>; -} // namespace llarp::util diff --git a/llarp/util/random.hpp b/llarp/util/random.hpp new file mode 100644 index 0000000000..6a505a60a1 --- /dev/null +++ b/llarp/util/random.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include "common.hpp" + +#include + +extern "C" +{ + extern void randombytes(unsigned char* const ptr, unsigned long long sz); +} + +namespace llarp +{ + struct CSRNG + { + using result_type = uint64_t; + + static constexpr uint64_t min() { return std::numeric_limits::min(); } + + static constexpr uint64_t max() { return std::numeric_limits::max(); } + + uint64_t randint() + { + uint64_t i; + randombytes((uint8_t*)&i, sizeof(i)); + return i; + } + + uint64_t operator()() { return randint(); } + }; + + extern CSRNG csrng; +} // namespace llarp diff --git a/llarp/util/thread/queue.hpp b/llarp/util/thread/queue.hpp index c627762b02..db2968e272 100644 --- a/llarp/util/thread/queue.hpp +++ b/llarp/util/thread/queue.hpp @@ -19,7 +19,7 @@ namespace llarp::thread { // This class provides a thread-safe, lock-free, fixed-size queue. public: - static constexpr size_t Alignment = 64; + static constexpr size_t Alignment{64}; private: Type* m_data; diff --git a/llarp/util/thread/queue_manager.cpp b/llarp/util/thread/queue_manager.cpp index a290318515..4d1dd383e6 100644 --- a/llarp/util/thread/queue_manager.cpp +++ b/llarp/util/thread/queue_manager.cpp @@ -32,50 +32,26 @@ namespace llarp::thread // Max number of combinations of index and generations. static constexpr uint32_t NUM_COMBINED_INDEXES = DISABLED_STATE_MASK; - bool isDisabledFlagSet(uint32_t encodedIndex) - { - return (encodedIndex & DISABLED_STATE_MASK); - } + bool isDisabledFlagSet(uint32_t encodedIndex) { return (encodedIndex & DISABLED_STATE_MASK); } - uint32_t discardDisabledFlag(uint32_t encodedIndex) - { - return (encodedIndex & ~DISABLED_STATE_MASK); - } + uint32_t discardDisabledFlag(uint32_t encodedIndex) { return (encodedIndex & ~DISABLED_STATE_MASK); } uint32_t encodeElement(uint32_t generation, ElementState state) { return (generation << GENERATION_COUNT_SHIFT) | to_underlying(state); } - uint32_t decodeGenerationFromElementState(uint32_t state) - { - return state >> GENERATION_COUNT_SHIFT; - } + uint32_t decodeGenerationFromElementState(uint32_t state) { return state >> GENERATION_COUNT_SHIFT; } - ElementState decodeStateFromElementState(uint32_t state) - { - return ElementState(state & ELEMENT_STATE_MASK); - } + ElementState decodeStateFromElementState(uint32_t state) { return ElementState(state & ELEMENT_STATE_MASK); } - QueueManager::AtomicIndex& QueueManager::pushIndex() - { - return m_pushIndex; - } + QueueManager::AtomicIndex& QueueManager::pushIndex() { return m_pushIndex; } - QueueManager::AtomicIndex& QueueManager::popIndex() - { - return m_popIndex; - } + QueueManager::AtomicIndex& QueueManager::popIndex() { return m_popIndex; } - const QueueManager::AtomicIndex& QueueManager::pushIndex() const - { - return m_pushIndex; - } + const QueueManager::AtomicIndex& QueueManager::pushIndex() const { return m_pushIndex; } - const QueueManager::AtomicIndex& QueueManager::popIndex() const - { - return m_popIndex; - } + const QueueManager::AtomicIndex& QueueManager::popIndex() const { return m_popIndex; } uint32_t QueueManager::nextCombinedIndex(uint32_t index) const { @@ -97,10 +73,7 @@ namespace llarp::thread return generation + 1; } - size_t QueueManager::capacity() const - { - return m_capacity; - } + size_t QueueManager::capacity() const { return m_capacity; } int32_t QueueManager::circularDifference(uint32_t startingValue, uint32_t subtractValue, uint32_t modulo) { @@ -148,10 +121,7 @@ namespace llarp::thread } } - QueueManager::~QueueManager() - { - delete[] m_states; - } + QueueManager::~QueueManager() { delete[] m_states; } QueueReturn QueueManager::reservePushIndex(uint32_t& generation, uint32_t& index) { @@ -503,8 +473,5 @@ namespace llarp::thread return 0; } - bool QueueManager::enabled() const - { - return !isDisabledFlagSet(pushIndex().load()); - } + bool QueueManager::enabled() const { return !isDisabledFlagSet(pushIndex().load()); } } // namespace llarp::thread diff --git a/llarp/util/time.cpp b/llarp/util/time.cpp index b35bc369d0..682601fae7 100644 --- a/llarp/util/time.cpp +++ b/llarp/util/time.cpp @@ -15,20 +15,11 @@ namespace llarp static const auto started_at_steady = std::chrono::steady_clock::now(); } // namespace - std::chrono::steady_clock::time_point get_time() - { - return std::chrono::steady_clock::now(); - } + std::chrono::steady_clock::time_point get_time() { return std::chrono::steady_clock::now(); } - std::chrono::nanoseconds get_timestamp() - { - return std::chrono::steady_clock::now().time_since_epoch(); - } + std::chrono::nanoseconds get_timestamp() { return std::chrono::steady_clock::now().time_since_epoch(); } - uint64_t to_milliseconds(std::chrono::milliseconds ms) - { - return ms.count(); - } + uint64_t to_milliseconds(std::chrono::milliseconds ms) { return ms.count(); } /// get our uptime in ms std::chrono::milliseconds uptime() @@ -51,10 +42,7 @@ namespace llarp return t + time_since_epoch(started_at_system); } - nlohmann::json to_json(const std::chrono::milliseconds& t) - { - return to_milliseconds(t); - } + nlohmann::json to_json(const std::chrono::milliseconds& t) { return to_milliseconds(t); } static auto extract_h_m_s_ms(const std::chrono::milliseconds& dur) { diff --git a/llarp/vpn/egres_packet_router.hpp b/llarp/vpn/egres_packet_router.hpp index 1963eca553..5b986c7b6d 100644 --- a/llarp/vpn/egres_packet_router.hpp +++ b/llarp/vpn/egres_packet_router.hpp @@ -1,8 +1,7 @@ #pragma once #include -#include -#include +#include #include #include diff --git a/llarp/vpn/linux.hpp b/llarp/vpn/linux.hpp index 55c3ab3fc8..d2c7b6d1f7 100644 --- a/llarp/vpn/linux.hpp +++ b/llarp/vpn/linux.hpp @@ -28,6 +28,8 @@ namespace llarp::vpn inline constexpr ipv4 ipv4_subnet{255, 255, 255, 255}; + inline constexpr std::array if_ipv6_addrs{ipv6{}, ipv6{0x4000}, ipv6{0x8000}, ipv6{0xc000}}; + struct in6_ifreq { in6_addr addr; @@ -43,24 +45,25 @@ namespace llarp::vpn LinuxInterface(InterfaceInfo info) : NetworkInterface{std::move(info)}, _fd{::open("/dev/net/tun", O_RDWR)} { if (_fd == -1) - throw std::runtime_error("cannot open /dev/net/tun " + std::string{strerror(errno)}); + throw std::runtime_error("cannot open /dev/net/tun {}"_format(strerror(errno))); if (fcntl(_fd, F_SETFL, O_NONBLOCK) == -1) - throw std::runtime_error{"Failed to set Linux interface fd non-block!s"}; + throw std::runtime_error{"Failed to set `O_NONBLOCK` on Linux interface FD!"}; ifreq ifr{}; in6_ifreq ifr6{}; ifr.ifr_flags = IFF_TUN | IFF_NO_PI; - std::copy_n(_info.ifname.c_str(), std::min(_info.ifname.size(), sizeof(ifr.ifr_name)), ifr.ifr_name); + std::memcpy( + ifr.ifr_name, _info.ifname.c_str(), std::min(_info.ifname.size(), static_cast(IFNAMSIZ))); if (::ioctl(_fd, TUNSETIFF, &ifr) == -1) - throw std::runtime_error("cannot set interface name: " + std::string{strerror(errno)}); + throw std::runtime_error("cannot set interface name: {}"_format(strerror(errno))); IOCTL control{AF_INET}; control.ioctl(SIOCGIFFLAGS, &ifr); - const int flags = ifr.ifr_flags; + short int flags = ifr.ifr_flags; control.ioctl(SIOCGIFINDEX, &ifr); _info.index = ifr.ifr_ifindex; @@ -81,8 +84,8 @@ namespace llarp::vpn control.ioctl(SIOCSIFADDR, &ifr); - auto subnet_mask = (ipv4_subnet / range.mask()).base; - log::trace(logcat, "IP Range:{}, subnet mask: {}", range, subnet_mask); + auto subnet_mask = (ipv4_subnet / range.mask()).ip; + log::debug(logcat, "IP Range:{}, subnet mask: {}", range, subnet_mask); ((sockaddr_in*)&ifr.ifr_netmask)->sin_addr.s_addr = oxenc::load_host_to_big(&subnet_mask.addr); @@ -109,7 +112,7 @@ namespace llarp::vpn } } - ifr.ifr_flags = static_cast(flags | IFF_UP | IFF_NO_PI); + ifr.ifr_flags = static_cast(flags | IFF_UP); control.ioctl(SIOCSIFFLAGS, &ifr); } @@ -122,6 +125,7 @@ namespace llarp::vpn std::vector buf; buf.resize(MAX_PACKET_SIZE); const auto sz = read(_fd, buf.data(), buf.capacity()); + // log::trace(logcat, "{} bytes read from fd {} (err?:{})", sz, _fd, strerror(errno)); if (sz < 0) { if (errno == EAGAIN or errno == EWOULDBLOCK) @@ -133,13 +137,13 @@ namespace llarp::vpn } buf.resize(sz); - return IPPacket{std::move(buf)}; } bool write_packet(IPPacket pkt) override { const auto sz = write(_fd, pkt.data(), pkt.size()); + // log::trace(logcat, "{} bytes written to fd {} (err?:{})", sz, _fd, strerror(errno)); if (sz <= 0) return false; return sz == static_cast(pkt.size()); @@ -188,31 +192,15 @@ namespace llarp::vpn /* Helper structure for ip address data and attributes */ struct _inet_addr { - unsigned char family; - unsigned char bitlen; - unsigned char data[sizeof(struct in6_addr)]; - - _inet_addr() = default; - - _inet_addr(oxen::quic::Address addr) - { - const auto& v4 = addr.is_ipv4(); - - family = (v4) ? AF_INET : AF_INET6; - bitlen = (v4) ? 32 : 128; - std::memcpy(data, addr.host().data(), (v4) ? 4 : 16); - } - - _inet_addr(ipv4 v4) + private: + void _init_internals(const ipv4& v4) { family = AF_INET; bitlen = 32; - - auto bigly = oxenc::host_to_big(v4.addr); - inet_ntop(AF_INET, &bigly, reinterpret_cast(data), sizeof(struct in6_addr)); + oxenc::write_host_as_big(v4.addr, &data); } - _inet_addr(ipv6 v6) + void _init_internals(const ipv6& v6) { family = AF_INET6; bitlen = 128; @@ -220,19 +208,24 @@ namespace llarp::vpn std::memcpy(&data, &in6, sizeof(in6_addr)); } - _inet_addr(net::ipv4addr_t addr, size_t bits = 32) - { - family = AF_INET; - bitlen = bits; - std::memcpy(data, &addr.n, 4); - } + public: + unsigned char family; + unsigned char bitlen; + unsigned char data[sizeof(struct in6_addr)]; + + _inet_addr() = default; - _inet_addr(net::ipv6addr_t addr, size_t bits = 128) + _inet_addr(oxen::quic::Address addr) { - family = AF_INET6; - bitlen = bits; - std::memcpy(data, &addr.n, 16); + if (addr.is_ipv4()) + _init_internals(addr.to_ipv4()); + else + _init_internals(addr.to_ipv6()); } + + _inet_addr(ipv4 v4) { _init_internals(v4); } + + _inet_addr(ipv6 v6) { _init_internals(v6); } }; void make_blackhole(int cmd, int flags, int af) @@ -249,12 +242,12 @@ namespace llarp::vpn nl_request.r.rtm_scope = RT_SCOPE_UNIVERSE; if (af == AF_INET) { - uint32_t addr{}; - nl_request.AddData(RTA_DST, &addr, sizeof(addr)); + ipv4 addr{}; + nl_request.AddData(RTA_DST, &addr.addr, sizeof(addr.addr)); } else { - uint128_t addr{}; + std::array addr{}; nl_request.AddData(RTA_DST, &addr, sizeof(addr)); } send(fd, &nl_request, sizeof(nl_request), 0); @@ -336,10 +329,10 @@ namespace llarp::vpn if (const auto maybe6 = Net().get_interface_ipv6_addr(info.ifname)) { const _inet_addr gateway6{*maybe6}; - for (const std::string str : {"::", "4000::", "8000::", "c000::"}) + + for (const auto& v6 : if_ipv6_addrs) { - const _inet_addr hole6{net::ipv6addr_t::from_string(str), 2}; - make_route(cmd, flags, hole6, gateway6, GatewayMode::eUpperDefault, info.index); + make_route(cmd, flags, _inet_addr{v6}, gateway6, GatewayMode::eUpperDefault, info.index); } } } diff --git a/llarp/vpn/packet_io.hpp b/llarp/vpn/packet_io.hpp index b88256750e..4191760646 100644 --- a/llarp/vpn/packet_io.hpp +++ b/llarp/vpn/packet_io.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include diff --git a/llarp/vpn/packet_router.cpp b/llarp/vpn/packet_router.cpp index 817c0a4967..37b2d62993 100644 --- a/llarp/vpn/packet_router.cpp +++ b/llarp/vpn/packet_router.cpp @@ -1,7 +1,11 @@ #include "packet_router.hpp" +#include + namespace llarp::vpn { + static auto logcat = log::Cat("packet_router"); + struct UDPPacketHandler : public Layer4Handler { ip_pkt_hook _base_handler; @@ -12,10 +16,12 @@ namespace llarp::vpn void add_sub_handler(uint16_t localport, ip_pkt_hook handler) override { _port_mapped_handlers.emplace(localport, std::move(handler)); + log::debug(logcat, "UDP packet sub-handler registered for local port {}", localport); } void handle_ip_packet(IPPacket pkt) override { + log::trace(logcat, "udp pkt: ", pkt.info_line()); auto dstport = pkt.dest_port(); if (not dstport) @@ -38,8 +44,9 @@ namespace llarp::vpn explicit GenericLayer4Handler(ip_pkt_hook baseHandler) : _base_handler{std::move(baseHandler)} {} - void handle_ip_packet(IPPacket) override + void handle_ip_packet(IPPacket pkt) override { + log::critical(logcat, "(FIXME) l4 pkt: {}", pkt.info_line()); // TOFIX: // _base_handler(IPPacket::from_udp(std::move(pkt))); } @@ -47,15 +54,17 @@ namespace llarp::vpn PacketRouter::PacketRouter(ip_pkt_hook baseHandler) : _handler{std::move(baseHandler)} {} - void PacketRouter::handle_ip_packet(IPPacket pkt) + void PacketRouter::handle_ip_packet(IPPacket pkt) const { + if (pkt.is_ipv4()) + log::trace(logcat, "ipv4 pkt: {}", pkt.info_line()); auto dest_port = pkt.dest_port(); if (not dest_port) return _handler(std::move(pkt)); auto proto = pkt.protocol(); - if (const auto itr = _ip_proto_handler.find(*proto); itr != _ip_proto_handler.end()) + if (auto itr = _ip_proto_handler.find(proto); itr != _ip_proto_handler.end()) itr->second->handle_ip_packet(std::move(pkt)); else _handler(std::move(pkt)); @@ -63,16 +72,17 @@ namespace llarp::vpn void PacketRouter::add_udp_handler(uint16_t localport, ip_pkt_hook func) { - constexpr uint8_t udp_proto = 0x11; + auto [it, b] = _ip_proto_handler.try_emplace(net::IPProtocol::UDP, nullptr); - if (_ip_proto_handler.find(udp_proto) == _ip_proto_handler.end()) - { - _ip_proto_handler.emplace(udp_proto, std::make_unique(_handler)); - } - _ip_proto_handler[udp_proto]->add_sub_handler(localport, func); + if (b) + it->second = std::make_unique(_handler); + else + log::info(logcat, "Packet router already holds registered UDP packet handler!"); + + it->second->add_sub_handler(localport, std::move(func)); } - void PacketRouter::add_ip_proto_handler(uint8_t proto, ip_pkt_hook func) + void PacketRouter::add_ip_proto_handler(net::IPProtocol proto, ip_pkt_hook func) { _ip_proto_handler[proto] = std::make_unique(std::move(func)); } diff --git a/llarp/vpn/packet_router.hpp b/llarp/vpn/packet_router.hpp index 9836f5ee79..9c6dfec2de 100644 --- a/llarp/vpn/packet_router.hpp +++ b/llarp/vpn/packet_router.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include @@ -12,17 +12,18 @@ namespace llarp::vpn class PacketRouter { ip_pkt_hook _handler; - std::unordered_map> _ip_proto_handler; + // std::unordered_map> _ip_proto_handler; + std::unordered_map> _ip_proto_handler; public: /// baseHandler will be called if no other handlers matches a packet explicit PacketRouter(ip_pkt_hook baseHandler); /// feed in an ip packet for handling - void handle_ip_packet(IPPacket pkt); + void handle_ip_packet(IPPacket pkt) const; /// add a non udp packet handler using ip protocol proto - void add_ip_proto_handler(uint8_t proto, ip_pkt_hook func); + void add_ip_proto_handler(net::IPProtocol proto, ip_pkt_hook func); /// helper that adds a udp packet handler for UDP destined for localport void add_udp_handler(uint16_t port, ip_pkt_hook func); diff --git a/llarp/vpn/platform.cpp b/llarp/vpn/platform.cpp index 12f6f2763b..73e80d13b9 100644 --- a/llarp/vpn/platform.cpp +++ b/llarp/vpn/platform.cpp @@ -14,10 +14,7 @@ namespace llarp::vpn { - const llarp::net::Platform* AbstractRouteManager::net_ptr() const - { - return llarp::net::Platform::Default_ptr(); - } + const llarp::net::Platform* AbstractRouteManager::net_ptr() const { return llarp::net::Platform::Default_ptr(); } std::shared_ptr MakeNativePlatform(llarp::Context* ctx) { diff --git a/llarp/vpn/platform.hpp b/llarp/vpn/platform.hpp index 4b79670767..c4214fa278 100644 --- a/llarp/vpn/platform.hpp +++ b/llarp/vpn/platform.hpp @@ -2,8 +2,8 @@ #include "packet_io.hpp" -#include #include +#include #include #include @@ -32,7 +32,7 @@ namespace llarp::vpn } }; - struct InterfaceInfo + struct [[deprecated("Use net::if_info instead!")]] InterfaceInfo { std::string ifname; unsigned int index; diff --git a/llarp/win32/exec.cpp b/llarp/win32/exec.cpp index 7560518491..4a9c859f0d 100644 --- a/llarp/win32/exec.cpp +++ b/llarp/win32/exec.cpp @@ -25,10 +25,7 @@ namespace llarp::win32 } // namespace - void Exec(std::string exe, std::string args) - { - OneShotExec{exe, args}; - } + void Exec(std::string exe, std::string args) { OneShotExec{exe, args}; } OneShotExec::OneShotExec(std::string cmd, std::chrono::milliseconds timeout) : _si{}, _pi{}, _timeout{static_cast(timeout.count())} @@ -49,8 +46,5 @@ namespace llarp::win32 : OneShotExec{fmt::format("{}\\{} {}", SystemExeDir(), cmd, args), timeout} {} - DeferExec::~DeferExec() - { - OneShotExec{std::move(_exe), std::move(_args), std::move(_timeout)}; - } + DeferExec::~DeferExec() { OneShotExec{std::move(_exe), std::move(_args), std::move(_timeout)}; } } // namespace llarp::win32 diff --git a/llarp/win32/service_manager.cpp b/llarp/win32/service_manager.cpp index e7cbfda297..1877970daa 100644 --- a/llarp/win32/service_manager.cpp +++ b/llarp/win32/service_manager.cpp @@ -33,10 +33,7 @@ namespace llarp::sys } } // namespace - SVC_Manager::SVC_Manager() - { - _status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; - } + SVC_Manager::SVC_Manager() { _status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; } void SVC_Manager::system_changed_our_state(ServiceState st) { diff --git a/pybind/common.hpp b/pybind/common.hpp index f2315357ce..9484ffef7d 100644 --- a/pybind/common.hpp +++ b/pybind/common.hpp @@ -21,7 +21,7 @@ namespace llarp void RouterID_Init(py::module& mod); - void RouterContact_Init(py::module& mod); + void RelayContact_Init(py::module& mod); void Config_Init(py::module& mod); diff --git a/pybind/llarp/config.cpp b/pybind/llarp/config.cpp index 1926902086..5d2c034cc0 100644 --- a/pybind/llarp/config.cpp +++ b/pybind/llarp/config.cpp @@ -5,10 +5,7 @@ namespace llarp { - void in_addr_set(in_addr* addr, const char* str) - { - inet_aton(str, addr); - } + void in_addr_set(in_addr* addr, const char* str) { inet_aton(str, addr); } void Config_Init(py::module& mod) { @@ -104,7 +101,7 @@ namespace llarp self.routers.clear(); for (const auto& arg : args) { - RouterContact rc{}; + RelayContact rc{}; if (rc.Read(arg)) self.routers.emplace(std::move(rc)); else diff --git a/pybind/llarp/router_contact.cpp b/pybind/llarp/router_contact.cpp index 84596f1ba9..806fa795db 100644 --- a/pybind/llarp/router_contact.cpp +++ b/pybind/llarp/router_contact.cpp @@ -1,29 +1,28 @@ #include "common.hpp" #include -#include +#include #include namespace llarp { - void RouterContact_Init(py::module& mod) + void RelayContact_Init(py::module& mod) { - py::class_(mod, "RouterContact") + py::class_(mod, "RelayContact") .def(py::init<>()) .def_property_readonly( - "routerID", - [](const RouterContact* const rc) -> llarp::RouterID { return llarp::RouterID(rc->pubkey); }) + "routerID", [](const RelayContact* const rc) -> llarp::RouterID { return llarp::RouterID(rc->pubkey); }) .def_property_readonly( "AsDHTKey", - [](const RouterContact* const rc) -> llarp::dht::Key_t { + [](const RelayContact* const rc) -> llarp::dht::Key_t { return llarp::dht::Key_t{rc->pubkey.as_array()}; }) - .def("ReadFile", &RouterContact::Read) - .def("WriteFile", &RouterContact::Write) - .def("ToString", &RouterContact::ToString) - .def("__str__", &RouterContact::ToString) - .def("__repr__", &RouterContact::ToString) - .def("Verify", [](const RouterContact* const rc) -> bool { + .def("ReadFile", &RelayContact::Read) + .def("WriteFile", &RelayContact::Write) + .def("ToString", &RelayContact::ToString) + .def("__str__", &RelayContact::ToString) + .def("__repr__", &RelayContact::ToString) + .def("Verify", [](const RelayContact* const rc) -> bool { const std::chrono::milliseconds now = llarp::time_now_ms(); return rc->Verify(now); }); diff --git a/pybind/llarp/router_id.cpp b/pybind/llarp/router_id.cpp index ba896387be..2c9816fe8d 100644 --- a/pybind/llarp/router_id.cpp +++ b/pybind/llarp/router_id.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/pybind/module.cpp b/pybind/module.cpp index cbd07a4971..7d1749f484 100644 --- a/pybind/module.cpp +++ b/pybind/module.cpp @@ -11,7 +11,7 @@ PYBIND11_MODULE(pyllarp, m) llarp::PeerDb_Init(m); llarp::PeerStats_Init(m); llarp::RouterID_Init(m); - llarp::RouterContact_Init(m); + llarp::RelayContact_Init(m); llarp::CryptoTypes_Init(m); llarp::Context_Init(m); tooling::HiveContext_Init(m); diff --git a/test/crypto/test_llarp_key_manager.cpp b/test/crypto/test_llarp_key_manager.cpp index 31714e7870..d2780b301f 100644 --- a/test/crypto/test_llarp_key_manager.cpp +++ b/test/crypto/test_llarp_key_manager.cpp @@ -32,7 +32,7 @@ struct KeyManagerTest bool generateRcFile() { - RouterContact rc; + RelayContact rc; return rc.Write(our_rc_filename); } }; diff --git a/test/nodedb/test_nodedb.cpp b/test/nodedb/test_nodedb.cpp index 9286e345b3..29a5e2ee22 100644 --- a/test/nodedb/test_nodedb.cpp +++ b/test/nodedb/test_nodedb.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include #include using llarp_nodedb = llarp::NodeDB; @@ -13,7 +13,7 @@ TEST_CASE("FindClosestTo returns correct number of elements", "[nodedb][dht]") constexpr uint64_t numRCs = 3; for (uint64_t i = 0; i < numRCs; ++i) { - llarp::RouterContact rc; + llarp::RelayContact rc; rc.pubkey[0] = i; nodeDB.Put(rc); } @@ -22,7 +22,7 @@ TEST_CASE("FindClosestTo returns correct number of elements", "[nodedb][dht]") llarp::dht::Key_t key; - std::vector results = nodeDB.FindManyClosestTo(key, 4); + std::vector results = nodeDB.FindManyClosestTo(key, 4); // we asked for more entries than nodedb had REQUIRE(numRCs == results.size()); @@ -33,15 +33,15 @@ TEST_CASE("FindClosestTo returns properly ordered set", "[nodedb][dht]") llarp_nodedb nodeDB{fs::current_path(), nullptr}; // insert some RCs: a < b < c - llarp::RouterContact a; + llarp::RelayContact a; a.pubkey[0] = 1; nodeDB.Put(a); - llarp::RouterContact b; + llarp::RelayContact b; b.pubkey[0] = 2; nodeDB.Put(b); - llarp::RouterContact c; + llarp::RelayContact c; c.pubkey[0] = 3; nodeDB.Put(c); @@ -49,7 +49,7 @@ TEST_CASE("FindClosestTo returns properly ordered set", "[nodedb][dht]") llarp::dht::Key_t key; - std::vector results = nodeDB.FindManyClosestTo(key, 2); + std::vector results = nodeDB.FindManyClosestTo(key, 2); REQUIRE(2 == results.size()); // we xor'ed with 0x0, so order should be a,b,c diff --git a/test/path/test_path.cpp b/test/path/test_path.cpp index d25a1c68e7..60d5f706be 100644 --- a/test/path/test_path.cpp +++ b/test/path/test_path.cpp @@ -4,7 +4,7 @@ using Path_t = llarp::path::Path; using Path_ptr = llarp::path::Path_ptr; using Set_t = llarp::path::Path::UniqueEndpointSet_t; -using RC_t = llarp::RouterContact; +using RC_t = llarp::RelayContact; static RC_t MakeHop(const char name) diff --git a/test/peerstats/test_peer_db.cpp b/test/peerstats/test_peer_db.cpp index c3ed14c791..51b1f193f0 100644 --- a/test/peerstats/test_peer_db.cpp +++ b/test/peerstats/test_peer_db.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include @@ -120,10 +120,10 @@ TEST_CASE("Test PeerDb handleGossipedRC", "[PeerDb]") { const llarp::RouterID id = llarp::test::makeBuf(0xCA); - auto rcLifetime = llarp::RouterContact::Lifetime; + auto rcLifetime = llarp::RelayContact::Lifetime; llarp_time_t now = 0s; - llarp::RouterContact rc; + llarp::RelayContact rc; rc.pubkey = llarp::PubKey(id); rc.last_updated = 10s; @@ -162,13 +162,13 @@ TEST_CASE("Test PeerDb handleGossipedRC expiry calcs", "[PeerDb]") // see comments in peer_db.cpp above PeerDb::handleGossipedRC() for some context around these // tests and esp. these numbers const llarp_time_t ref = 48h; - const llarp_time_t rcLifetime = llarp::RouterContact::Lifetime; + const llarp_time_t rcLifetime = llarp::RelayContact::Lifetime; // rc1, first rc received const llarp_time_t s1 = ref; const llarp_time_t r1 = s1 + 30s; const llarp_time_t e1 = s1 + rcLifetime; - llarp::RouterContact rc1; + llarp::RelayContact rc1; rc1.pubkey = llarp::PubKey(id); rc1.last_updated = s1; @@ -177,7 +177,7 @@ TEST_CASE("Test PeerDb handleGossipedRC expiry calcs", "[PeerDb]") const llarp_time_t s2 = s1 + 8h; const llarp_time_t r2 = s2 + 30s; // healthy recv time const llarp_time_t e2 = s2 + rcLifetime; - llarp::RouterContact rc2; + llarp::RelayContact rc2; rc2.pubkey = llarp::PubKey(id); rc2.last_updated = s2; @@ -185,7 +185,7 @@ TEST_CASE("Test PeerDb handleGossipedRC expiry calcs", "[PeerDb]") // received "unhealthily" (after rc2 expires) const llarp_time_t s3 = s2 + 8h; const llarp_time_t r3 = e2 + 1h; // received after e2 - llarp::RouterContact rc3; + llarp::RelayContact rc3; rc3.pubkey = llarp::PubKey(id); rc3.last_updated = s3; diff --git a/test/service/test_llarp_service_identity.cpp b/test/service/test_llarp_service_identity.cpp index 5d0bd42bec..bc819631cf 100644 --- a/test/service/test_llarp_service_identity.cpp +++ b/test/service/test_llarp_service_identity.cpp @@ -1,9 +1,6 @@ #include -#include #include #include -#include -#include #include #include @@ -194,7 +191,7 @@ TEST_CASE("Test sign and encrypt introset", "[crypto]") ident.RegenerateKeys(); service::Address addr; CHECK(ident.pub.CalculateAddress(addr.as_array())); - service::IntroSet introset; + service::IntroSetOld introset; auto now = time_now_ms(); introset.timestampSignedAt = now; while(introset.intros.size() < 10) diff --git a/test/test_llarp_router_contact.cpp b/test/test_llarp_router_contact.cpp index f3a7611e5d..6cd3db8965 100644 --- a/test/test_llarp_router_contact.cpp +++ b/test/test_llarp_router_contact.cpp @@ -1,16 +1,16 @@ #include #include -#include +#include #include #include namespace llarp { -TEST_CASE("RouterContact Sign and Verify", "[RC][RouterContact][signature][sign][verify]") +TEST_CASE("RelayContact Sign and Verify", "[RC][RelayContact][signature][sign][verify]") { - RouterContact rc; + RelayContact rc; SecretKey sign; crypto::identity_keygen(sign); @@ -25,9 +25,9 @@ TEST_CASE("RouterContact Sign and Verify", "[RC][RouterContact][signature][sign] REQUIRE(rc.Verify(time_now_ms())); } -TEST_CASE("RouterContact Decode Version 1", "[RC][RouterContact][V1]") +TEST_CASE("RelayContact Decode Version 1", "[RC][RelayContact][V1]") { - RouterContact rc; + RelayContact rc; SecretKey sign; crypto::identity_keygen(sign); @@ -50,7 +50,7 @@ TEST_CASE("RouterContact Decode Version 1", "[RC][RouterContact][V1]") encoded_llarp.sz = encoded_llarp.cur - encoded_llarp.base; encoded_llarp.cur = encoded_llarp.base; - RouterContact decoded_rc; + RelayContact decoded_rc; REQUIRE(decoded_rc.BDecode(&encoded_llarp)); @@ -59,9 +59,9 @@ TEST_CASE("RouterContact Decode Version 1", "[RC][RouterContact][V1]") REQUIRE(decoded_rc == rc); } -TEST_CASE("RouterContact Decode Mixed Versions", "[RC][RouterContact]") +TEST_CASE("RelayContact Decode Mixed Versions", "[RC][RelayContact]") { - RouterContact rc1, rc2, rc3, rc4; + RelayContact rc1, rc2, rc3, rc4; rc1.version = 0; rc2.version = 1; @@ -94,7 +94,7 @@ TEST_CASE("RouterContact Decode Mixed Versions", "[RC][RouterContact]") REQUIRE(rc3.Sign(sign3)); REQUIRE(rc4.Sign(sign4)); - std::vector rc_vec; + std::vector rc_vec; rc_vec.push_back(rc1); rc_vec.push_back(rc2); rc_vec.push_back(rc3); @@ -107,7 +107,7 @@ TEST_CASE("RouterContact Decode Mixed Versions", "[RC][RouterContact]") encoded_llarp.sz = encoded_llarp.cur - encoded_llarp.base; encoded_llarp.cur = encoded_llarp.base; - std::vector rc_vec_out; + std::vector rc_vec_out; BEncodeReadList(rc_vec_out, &encoded_llarp);