First Commit
This commit is contained in:
+697
@@ -0,0 +1,697 @@
|
||||
/*
|
||||
* Copyright (c)2013-2020 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "AES.hpp"
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
#endif
|
||||
|
||||
#define Te1_r(x) ZT_ROR32(Te0[x], 8U)
|
||||
#define Te2_r(x) ZT_ROR32(Te0[x], 16U)
|
||||
#define Te3_r(x) ZT_ROR32(Te0[x], 24U)
|
||||
#define Td1_r(x) ZT_ROR32(Td0[x], 8U)
|
||||
#define Td2_r(x) ZT_ROR32(Td0[x], 16U)
|
||||
#define Td3_r(x) ZT_ROR32(Td0[x], 24U)
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
// GMAC ---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
namespace {
|
||||
|
||||
#define s_bmul32(N, x, y, rh, rl) \
|
||||
uint32_t x0t_##N = (x) & 0x11111111U; \
|
||||
uint32_t x1t_##N = (x) & 0x22222222U; \
|
||||
uint32_t x2t_##N = (x) & 0x44444444U; \
|
||||
uint32_t x3t_##N = (x) & 0x88888888U; \
|
||||
uint32_t y0t_##N = (y) & 0x11111111U; \
|
||||
uint32_t y1t_##N = (y) & 0x22222222U; \
|
||||
uint32_t y2t_##N = (y) & 0x44444444U; \
|
||||
uint32_t y3t_##N = (y) & 0x88888888U; \
|
||||
uint64_t z0t_##N = (((uint64_t)x0t_##N * y0t_##N) ^ ((uint64_t)x1t_##N * y3t_##N) ^ ((uint64_t)x2t_##N * y2t_##N) ^ ((uint64_t)x3t_##N * y1t_##N)) & 0x1111111111111111ULL; \
|
||||
uint64_t z1t_##N = (((uint64_t)x0t_##N * y1t_##N) ^ ((uint64_t)x1t_##N * y0t_##N) ^ ((uint64_t)x2t_##N * y3t_##N) ^ ((uint64_t)x3t_##N * y2t_##N)) & 0x2222222222222222ULL; \
|
||||
uint64_t z2t_##N = (((uint64_t)x0t_##N * y2t_##N) ^ ((uint64_t)x1t_##N * y1t_##N) ^ ((uint64_t)x2t_##N * y0t_##N) ^ ((uint64_t)x3t_##N * y3t_##N)) & 0x4444444444444444ULL; \
|
||||
z0t_##N |= z1t_##N; \
|
||||
z2t_##N |= z0t_##N; \
|
||||
uint64_t zt_##N = z2t_##N | ((((uint64_t)x0t_##N * y3t_##N) ^ ((uint64_t)x1t_##N * y2t_##N) ^ ((uint64_t)x2t_##N * y1t_##N) ^ ((uint64_t)x3t_##N * y0t_##N)) & 0x8888888888888888ULL); \
|
||||
(rh) = (uint32_t)(zt_##N >> 32U); \
|
||||
(rl) = (uint32_t)zt_##N;
|
||||
|
||||
void s_gfmul(const uint64_t hh, const uint64_t hl, uint64_t &y0, uint64_t &y1) noexcept
|
||||
{
|
||||
uint32_t hhh = (uint32_t)(hh >> 32U);
|
||||
uint32_t hhl = (uint32_t)hh;
|
||||
uint32_t hlh = (uint32_t)(hl >> 32U);
|
||||
uint32_t hll = (uint32_t)hl;
|
||||
uint32_t hhXlh = hhh ^hlh;
|
||||
uint32_t hhXll = hhl ^hll;
|
||||
uint64_t yl = Utils::ntoh(y0);
|
||||
uint64_t yh = Utils::ntoh(y1);
|
||||
uint32_t cilh = (uint32_t)(yh >> 32U);
|
||||
uint32_t cill = (uint32_t)yh;
|
||||
uint32_t cihh = (uint32_t)(yl >> 32U);
|
||||
uint32_t cihl = (uint32_t)yl;
|
||||
uint32_t cihXlh = cihh ^cilh;
|
||||
uint32_t cihXll = cihl ^cill;
|
||||
uint32_t aah, aal, abh, abl, ach, acl;
|
||||
s_bmul32(M0, cihh, hhh, aah, aal);
|
||||
s_bmul32(M1, cihl, hhl, abh, abl);
|
||||
s_bmul32(M2, cihh ^ cihl, hhh ^ hhl, ach, acl);
|
||||
ach ^= aah ^ abh;
|
||||
acl ^= aal ^ abl;
|
||||
aal ^= ach;
|
||||
abh ^= acl;
|
||||
uint32_t bah, bal, bbh, bbl, bch, bcl;
|
||||
s_bmul32(M3, cilh, hlh, bah, bal);
|
||||
s_bmul32(M4, cill, hll, bbh, bbl);
|
||||
s_bmul32(M5, cilh ^ cill, hlh ^ hll, bch, bcl);
|
||||
bch ^= bah ^ bbh;
|
||||
bcl ^= bal ^ bbl;
|
||||
bal ^= bch;
|
||||
bbh ^= bcl;
|
||||
uint32_t cah, cal, cbh, cbl, cch, ccl;
|
||||
s_bmul32(M6, cihXlh, hhXlh, cah, cal);
|
||||
s_bmul32(M7, cihXll, hhXll, cbh, cbl);
|
||||
s_bmul32(M8, cihXlh ^ cihXll, hhXlh ^ hhXll, cch, ccl);
|
||||
cch ^= cah ^ cbh;
|
||||
ccl ^= cal ^ cbl;
|
||||
cal ^= cch;
|
||||
cbh ^= ccl;
|
||||
cah ^= bah ^ aah;
|
||||
cal ^= bal ^ aal;
|
||||
cbh ^= bbh ^ abh;
|
||||
cbl ^= bbl ^ abl;
|
||||
uint64_t zhh = ((uint64_t)aah << 32U) | aal;
|
||||
uint64_t zhl = (((uint64_t)abh << 32U) | abl) ^(((uint64_t)cah << 32U) | cal);
|
||||
uint64_t zlh = (((uint64_t)bah << 32U) | bal) ^(((uint64_t)cbh << 32U) | cbl);
|
||||
uint64_t zll = ((uint64_t)bbh << 32U) | bbl;
|
||||
zhh = zhh << 1U | zhl >> 63U;
|
||||
zhl = zhl << 1U | zlh >> 63U;
|
||||
zlh = zlh << 1U | zll >> 63U;
|
||||
zll <<= 1U;
|
||||
zlh ^= (zll << 63U) ^ (zll << 62U) ^ (zll << 57U);
|
||||
zhh ^= zlh ^ (zlh >> 1U) ^ (zlh >> 2U) ^ (zlh >> 7U);
|
||||
zhl ^= zll ^ (zll >> 1U) ^ (zll >> 2U) ^ (zll >> 7U) ^ (zlh << 63U) ^ (zlh << 62U) ^ (zlh << 57U);
|
||||
y0 = Utils::hton(zhh);
|
||||
y1 = Utils::hton(zhl);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void AES::GMAC::update(const void *const data, unsigned int len) noexcept
|
||||
{
|
||||
const uint8_t *in = reinterpret_cast<const uint8_t *>(data);
|
||||
_len += len;
|
||||
|
||||
#ifdef ZT_AES_AESNI
|
||||
if (likely(Utils::CPUID.aes)) {
|
||||
p_aesNIUpdate(in, len);
|
||||
return;
|
||||
}
|
||||
#endif // ZT_AES_AESNI
|
||||
|
||||
#ifdef ZT_AES_NEON
|
||||
if (Utils::ARMCAP.pmull) {
|
||||
p_armUpdate(in, len);
|
||||
return;
|
||||
}
|
||||
#endif // ZT_AES_NEON
|
||||
|
||||
const uint64_t h0 = _aes.p_k.sw.h[0];
|
||||
const uint64_t h1 = _aes.p_k.sw.h[1];
|
||||
uint64_t y0 = _y[0];
|
||||
uint64_t y1 = _y[1];
|
||||
|
||||
if (_rp) {
|
||||
for (;;) {
|
||||
if (!len) {
|
||||
return;
|
||||
}
|
||||
--len;
|
||||
_r[_rp++] = *(in++);
|
||||
if (_rp == 16) {
|
||||
y0 ^= Utils::loadMachineEndian< uint64_t >(_r);
|
||||
y1 ^= Utils::loadMachineEndian< uint64_t >(_r + 8);
|
||||
s_gfmul(h0, h1, y0, y1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (len >= 16) {
|
||||
y0 ^= Utils::loadMachineEndian< uint64_t >(in);
|
||||
y1 ^= Utils::loadMachineEndian< uint64_t >(in + 8);
|
||||
in += 16;
|
||||
s_gfmul(h0, h1, y0, y1);
|
||||
len -= 16;
|
||||
}
|
||||
|
||||
_y[0] = y0;
|
||||
_y[1] = y1;
|
||||
|
||||
for (unsigned int i = 0; i < len; ++i) {
|
||||
_r[i] = in[i];
|
||||
}
|
||||
_rp = len; // len is always less than 16 here
|
||||
}
|
||||
|
||||
void AES::GMAC::finish(uint8_t tag[16]) noexcept
|
||||
{
|
||||
#ifdef ZT_AES_AESNI
|
||||
if (likely(Utils::CPUID.aes)) {
|
||||
p_aesNIFinish(tag);
|
||||
return;
|
||||
}
|
||||
#endif // ZT_AES_AESNI
|
||||
|
||||
#ifdef ZT_AES_NEON
|
||||
if (Utils::ARMCAP.pmull) {
|
||||
p_armFinish(tag);
|
||||
return;
|
||||
}
|
||||
#endif // ZT_AES_NEON
|
||||
|
||||
const uint64_t h0 = _aes.p_k.sw.h[0];
|
||||
const uint64_t h1 = _aes.p_k.sw.h[1];
|
||||
uint64_t y0 = _y[0];
|
||||
uint64_t y1 = _y[1];
|
||||
|
||||
if (_rp) {
|
||||
while (_rp < 16) {
|
||||
_r[_rp++] = 0;
|
||||
}
|
||||
y0 ^= Utils::loadMachineEndian< uint64_t >(_r);
|
||||
y1 ^= Utils::loadMachineEndian< uint64_t >(_r + 8);
|
||||
s_gfmul(h0, h1, y0, y1);
|
||||
}
|
||||
|
||||
y0 ^= Utils::hton((uint64_t)_len << 3U);
|
||||
s_gfmul(h0, h1, y0, y1);
|
||||
|
||||
uint64_t iv2[2];
|
||||
Utils::copy< 12 >(iv2, _iv);
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
reinterpret_cast<uint32_t *>(iv2)[3] = 0x00000001;
|
||||
#else
|
||||
reinterpret_cast<uint32_t *>(iv2)[3] = 0x01000000;
|
||||
#endif
|
||||
_aes.encrypt(iv2, iv2);
|
||||
|
||||
Utils::storeMachineEndian< uint64_t >(tag, iv2[0] ^ y0);
|
||||
Utils::storeMachineEndian< uint64_t >(tag + 8, iv2[1] ^ y1);
|
||||
}
|
||||
|
||||
// AES-CTR ------------------------------------------------------------------------------------------------------------
|
||||
|
||||
void AES::CTR::crypt(const void *const input, unsigned int len) noexcept
|
||||
{
|
||||
const uint8_t *in = reinterpret_cast<const uint8_t *>(input);
|
||||
uint8_t *out = _out;
|
||||
|
||||
#ifdef ZT_AES_AESNI
|
||||
if (likely(Utils::CPUID.aes)) {
|
||||
p_aesNICrypt(in, out, len);
|
||||
return;
|
||||
}
|
||||
#endif // ZT_AES_AESNI
|
||||
|
||||
#ifdef ZT_AES_NEON
|
||||
if (Utils::ARMCAP.aes) {
|
||||
p_armCrypt(in, out, len);
|
||||
return;
|
||||
}
|
||||
#endif // ZT_AES_NEON
|
||||
|
||||
uint64_t keyStream[2];
|
||||
uint32_t ctr = Utils::ntoh(reinterpret_cast<uint32_t *>(_ctr)[3]);
|
||||
|
||||
unsigned int totalLen = _len;
|
||||
if ((totalLen & 15U)) {
|
||||
for (;;) {
|
||||
if (!len) {
|
||||
_len = (totalLen + len);
|
||||
return;
|
||||
}
|
||||
--len;
|
||||
out[totalLen++] = *(in++);
|
||||
if (!(totalLen & 15U)) {
|
||||
_aes.p_encryptSW(reinterpret_cast<const uint8_t *>(_ctr), reinterpret_cast<uint8_t *>(keyStream));
|
||||
reinterpret_cast<uint32_t *>(_ctr)[3] = Utils::hton(++ctr);
|
||||
uint8_t *outblk = out + (totalLen - 16);
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
outblk[i] ^= reinterpret_cast<uint8_t *>(keyStream)[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out += totalLen;
|
||||
_len = (totalLen + len);
|
||||
|
||||
if (likely(len >= 16)) {
|
||||
const uint32_t *const restrict rk = _aes.p_k.sw.ek;
|
||||
const uint32_t ctr0rk0 = Utils::ntoh(reinterpret_cast<const uint32_t *>(_ctr)[0]) ^rk[0];
|
||||
const uint32_t ctr1rk1 = Utils::ntoh(reinterpret_cast<const uint32_t *>(_ctr)[1]) ^rk[1];
|
||||
const uint32_t ctr2rk2 = Utils::ntoh(reinterpret_cast<const uint32_t *>(_ctr)[2]) ^rk[2];
|
||||
const uint32_t m8 = 0x000000ff;
|
||||
const uint32_t m8_8 = 0x0000ff00;
|
||||
const uint32_t m8_16 = 0x00ff0000;
|
||||
const uint32_t m8_24 = 0xff000000;
|
||||
if (likely((((uintptr_t)out & 7U) == 0U) && (((uintptr_t)in & 7U) == 0U))) {
|
||||
do {
|
||||
uint32_t s0, s1, s2, s3, t0, t1, t2, t3;
|
||||
s0 = ctr0rk0;
|
||||
s1 = ctr1rk1;
|
||||
s2 = ctr2rk2;
|
||||
s3 = ctr++ ^ rk[3];
|
||||
|
||||
const uint64_t in0 = *reinterpret_cast<const uint64_t *>(in);
|
||||
const uint64_t in1 = *reinterpret_cast<const uint64_t *>(in + 8);
|
||||
in += 16;
|
||||
|
||||
t0 = Te0[s0 >> 24U] ^ Te1_r((s1 >> 16U) & m8) ^ Te2_r((s2 >> 8U) & m8) ^ Te3_r(s3 & m8) ^ rk[4];
|
||||
t1 = Te0[s1 >> 24U] ^ Te1_r((s2 >> 16U) & m8) ^ Te2_r((s3 >> 8U) & m8) ^ Te3_r(s0 & m8) ^ rk[5];
|
||||
t2 = Te0[s2 >> 24U] ^ Te1_r((s3 >> 16U) & m8) ^ Te2_r((s0 >> 8U) & m8) ^ Te3_r(s1 & m8) ^ rk[6];
|
||||
t3 = Te0[s3 >> 24U] ^ Te1_r((s0 >> 16U) & m8) ^ Te2_r((s1 >> 8U) & m8) ^ Te3_r(s2 & m8) ^ rk[7];
|
||||
s0 = Te0[t0 >> 24U] ^ Te1_r((t1 >> 16U) & m8) ^ Te2_r((t2 >> 8U) & m8) ^ Te3_r(t3 & m8) ^ rk[8];
|
||||
s1 = Te0[t1 >> 24U] ^ Te1_r((t2 >> 16U) & m8) ^ Te2_r((t3 >> 8U) & m8) ^ Te3_r(t0 & m8) ^ rk[9];
|
||||
s2 = Te0[t2 >> 24U] ^ Te1_r((t3 >> 16U) & m8) ^ Te2_r((t0 >> 8U) & m8) ^ Te3_r(t1 & m8) ^ rk[10];
|
||||
s3 = Te0[t3 >> 24U] ^ Te1_r((t0 >> 16U) & m8) ^ Te2_r((t1 >> 8U) & m8) ^ Te3_r(t2 & m8) ^ rk[11];
|
||||
t0 = Te0[s0 >> 24U] ^ Te1_r((s1 >> 16U) & m8) ^ Te2_r((s2 >> 8U) & m8) ^ Te3_r(s3 & m8) ^ rk[12];
|
||||
t1 = Te0[s1 >> 24U] ^ Te1_r((s2 >> 16U) & m8) ^ Te2_r((s3 >> 8U) & m8) ^ Te3_r(s0 & m8) ^ rk[13];
|
||||
t2 = Te0[s2 >> 24U] ^ Te1_r((s3 >> 16U) & m8) ^ Te2_r((s0 >> 8U) & m8) ^ Te3_r(s1 & m8) ^ rk[14];
|
||||
t3 = Te0[s3 >> 24U] ^ Te1_r((s0 >> 16U) & m8) ^ Te2_r((s1 >> 8U) & m8) ^ Te3_r(s2 & m8) ^ rk[15];
|
||||
s0 = Te0[t0 >> 24U] ^ Te1_r((t1 >> 16U) & m8) ^ Te2_r((t2 >> 8U) & m8) ^ Te3_r(t3 & m8) ^ rk[16];
|
||||
s1 = Te0[t1 >> 24U] ^ Te1_r((t2 >> 16U) & m8) ^ Te2_r((t3 >> 8U) & m8) ^ Te3_r(t0 & m8) ^ rk[17];
|
||||
s2 = Te0[t2 >> 24U] ^ Te1_r((t3 >> 16U) & m8) ^ Te2_r((t0 >> 8U) & m8) ^ Te3_r(t1 & m8) ^ rk[18];
|
||||
s3 = Te0[t3 >> 24U] ^ Te1_r((t0 >> 16U) & m8) ^ Te2_r((t1 >> 8U) & m8) ^ Te3_r(t2 & m8) ^ rk[19];
|
||||
t0 = Te0[s0 >> 24U] ^ Te1_r((s1 >> 16U) & m8) ^ Te2_r((s2 >> 8U) & m8) ^ Te3_r(s3 & m8) ^ rk[20];
|
||||
t1 = Te0[s1 >> 24U] ^ Te1_r((s2 >> 16U) & m8) ^ Te2_r((s3 >> 8U) & m8) ^ Te3_r(s0 & m8) ^ rk[21];
|
||||
t2 = Te0[s2 >> 24U] ^ Te1_r((s3 >> 16U) & m8) ^ Te2_r((s0 >> 8U) & m8) ^ Te3_r(s1 & m8) ^ rk[22];
|
||||
t3 = Te0[s3 >> 24U] ^ Te1_r((s0 >> 16U) & m8) ^ Te2_r((s1 >> 8U) & m8) ^ Te3_r(s2 & m8) ^ rk[23];
|
||||
s0 = Te0[t0 >> 24U] ^ Te1_r((t1 >> 16U) & m8) ^ Te2_r((t2 >> 8U) & m8) ^ Te3_r(t3 & m8) ^ rk[24];
|
||||
s1 = Te0[t1 >> 24U] ^ Te1_r((t2 >> 16U) & m8) ^ Te2_r((t3 >> 8U) & m8) ^ Te3_r(t0 & m8) ^ rk[25];
|
||||
s2 = Te0[t2 >> 24U] ^ Te1_r((t3 >> 16U) & m8) ^ Te2_r((t0 >> 8U) & m8) ^ Te3_r(t1 & m8) ^ rk[26];
|
||||
s3 = Te0[t3 >> 24U] ^ Te1_r((t0 >> 16U) & m8) ^ Te2_r((t1 >> 8U) & m8) ^ Te3_r(t2 & m8) ^ rk[27];
|
||||
t0 = Te0[s0 >> 24U] ^ Te1_r((s1 >> 16U) & m8) ^ Te2_r((s2 >> 8U) & m8) ^ Te3_r(s3 & m8) ^ rk[28];
|
||||
t1 = Te0[s1 >> 24U] ^ Te1_r((s2 >> 16U) & m8) ^ Te2_r((s3 >> 8U) & m8) ^ Te3_r(s0 & m8) ^ rk[29];
|
||||
t2 = Te0[s2 >> 24U] ^ Te1_r((s3 >> 16U) & m8) ^ Te2_r((s0 >> 8U) & m8) ^ Te3_r(s1 & m8) ^ rk[30];
|
||||
t3 = Te0[s3 >> 24U] ^ Te1_r((s0 >> 16U) & m8) ^ Te2_r((s1 >> 8U) & m8) ^ Te3_r(s2 & m8) ^ rk[31];
|
||||
s0 = Te0[t0 >> 24U] ^ Te1_r((t1 >> 16U) & m8) ^ Te2_r((t2 >> 8U) & m8) ^ Te3_r(t3 & m8) ^ rk[32];
|
||||
s1 = Te0[t1 >> 24U] ^ Te1_r((t2 >> 16U) & m8) ^ Te2_r((t3 >> 8U) & m8) ^ Te3_r(t0 & m8) ^ rk[33];
|
||||
s2 = Te0[t2 >> 24U] ^ Te1_r((t3 >> 16U) & m8) ^ Te2_r((t0 >> 8U) & m8) ^ Te3_r(t1 & m8) ^ rk[34];
|
||||
s3 = Te0[t3 >> 24U] ^ Te1_r((t0 >> 16U) & m8) ^ Te2_r((t1 >> 8U) & m8) ^ Te3_r(t2 & m8) ^ rk[35];
|
||||
t0 = Te0[s0 >> 24U] ^ Te1_r((s1 >> 16U) & m8) ^ Te2_r((s2 >> 8U) & m8) ^ Te3_r(s3 & m8) ^ rk[36];
|
||||
t1 = Te0[s1 >> 24U] ^ Te1_r((s2 >> 16U) & m8) ^ Te2_r((s3 >> 8U) & m8) ^ Te3_r(s0 & m8) ^ rk[37];
|
||||
t2 = Te0[s2 >> 24U] ^ Te1_r((s3 >> 16U) & m8) ^ Te2_r((s0 >> 8U) & m8) ^ Te3_r(s1 & m8) ^ rk[38];
|
||||
t3 = Te0[s3 >> 24U] ^ Te1_r((s0 >> 16U) & m8) ^ Te2_r((s1 >> 8U) & m8) ^ Te3_r(s2 & m8) ^ rk[39];
|
||||
s0 = Te0[t0 >> 24U] ^ Te1_r((t1 >> 16U) & m8) ^ Te2_r((t2 >> 8U) & m8) ^ Te3_r(t3 & m8) ^ rk[40];
|
||||
s1 = Te0[t1 >> 24U] ^ Te1_r((t2 >> 16U) & m8) ^ Te2_r((t3 >> 8U) & m8) ^ Te3_r(t0 & m8) ^ rk[41];
|
||||
s2 = Te0[t2 >> 24U] ^ Te1_r((t3 >> 16U) & m8) ^ Te2_r((t0 >> 8U) & m8) ^ Te3_r(t1 & m8) ^ rk[42];
|
||||
s3 = Te0[t3 >> 24U] ^ Te1_r((t0 >> 16U) & m8) ^ Te2_r((t1 >> 8U) & m8) ^ Te3_r(t2 & m8) ^ rk[43];
|
||||
t0 = Te0[s0 >> 24U] ^ Te1_r((s1 >> 16U) & m8) ^ Te2_r((s2 >> 8U) & m8) ^ Te3_r(s3 & m8) ^ rk[44];
|
||||
t1 = Te0[s1 >> 24U] ^ Te1_r((s2 >> 16U) & m8) ^ Te2_r((s3 >> 8U) & m8) ^ Te3_r(s0 & m8) ^ rk[45];
|
||||
t2 = Te0[s2 >> 24U] ^ Te1_r((s3 >> 16U) & m8) ^ Te2_r((s0 >> 8U) & m8) ^ Te3_r(s1 & m8) ^ rk[46];
|
||||
t3 = Te0[s3 >> 24U] ^ Te1_r((s0 >> 16U) & m8) ^ Te2_r((s1 >> 8U) & m8) ^ Te3_r(s2 & m8) ^ rk[47];
|
||||
s0 = Te0[t0 >> 24U] ^ Te1_r((t1 >> 16U) & m8) ^ Te2_r((t2 >> 8U) & m8) ^ Te3_r(t3 & m8) ^ rk[48];
|
||||
s1 = Te0[t1 >> 24U] ^ Te1_r((t2 >> 16U) & m8) ^ Te2_r((t3 >> 8U) & m8) ^ Te3_r(t0 & m8) ^ rk[49];
|
||||
s2 = Te0[t2 >> 24U] ^ Te1_r((t3 >> 16U) & m8) ^ Te2_r((t0 >> 8U) & m8) ^ Te3_r(t1 & m8) ^ rk[50];
|
||||
s3 = Te0[t3 >> 24U] ^ Te1_r((t0 >> 16U) & m8) ^ Te2_r((t1 >> 8U) & m8) ^ Te3_r(t2 & m8) ^ rk[51];
|
||||
t0 = Te0[s0 >> 24U] ^ Te1_r((s1 >> 16U) & m8) ^ Te2_r((s2 >> 8U) & m8) ^ Te3_r(s3 & m8) ^ rk[52];
|
||||
t1 = Te0[s1 >> 24U] ^ Te1_r((s2 >> 16U) & m8) ^ Te2_r((s3 >> 8U) & m8) ^ Te3_r(s0 & m8) ^ rk[53];
|
||||
t2 = Te0[s2 >> 24U] ^ Te1_r((s3 >> 16U) & m8) ^ Te2_r((s0 >> 8U) & m8) ^ Te3_r(s1 & m8) ^ rk[54];
|
||||
t3 = Te0[s3 >> 24U] ^ Te1_r((s0 >> 16U) & m8) ^ Te2_r((s1 >> 8U) & m8) ^ Te3_r(s2 & m8) ^ rk[55];
|
||||
s0 = (Te2_r(t0 >> 24U) & m8_24) ^ (Te3_r((t1 >> 16U) & m8) & m8_16) ^ (Te0[(t2 >> 8U) & m8] & m8_8) ^ (Te1_r(t3 & m8) & m8) ^ rk[56];
|
||||
s1 = (Te2_r(t1 >> 24U) & m8_24) ^ (Te3_r((t2 >> 16U) & m8) & m8_16) ^ (Te0[(t3 >> 8U) & m8] & m8_8) ^ (Te1_r(t0 & m8) & m8) ^ rk[57];
|
||||
s2 = (Te2_r(t2 >> 24U) & m8_24) ^ (Te3_r((t3 >> 16U) & m8) & m8_16) ^ (Te0[(t0 >> 8U) & m8] & m8_8) ^ (Te1_r(t1 & m8) & m8) ^ rk[58];
|
||||
s3 = (Te2_r(t3 >> 24U) & m8_24) ^ (Te3_r((t0 >> 16U) & m8) & m8_16) ^ (Te0[(t1 >> 8U) & m8] & m8_8) ^ (Te1_r(t2 & m8) & m8) ^ rk[59];
|
||||
|
||||
*reinterpret_cast<uint64_t *>(out) = in0 ^ Utils::hton(((uint64_t)s0 << 32U) | (uint64_t)s1);
|
||||
*reinterpret_cast<uint64_t *>(out + 8) = in1 ^ Utils::hton(((uint64_t)s2 << 32U) | (uint64_t)s3);
|
||||
out += 16;
|
||||
} while ((len -= 16) >= 16);
|
||||
} else {
|
||||
do {
|
||||
uint32_t s0, s1, s2, s3, t0, t1, t2, t3;
|
||||
s0 = ctr0rk0;
|
||||
s1 = ctr1rk1;
|
||||
s2 = ctr2rk2;
|
||||
s3 = ctr++ ^ rk[3];
|
||||
|
||||
t0 = Te0[s0 >> 24U] ^ Te1_r((s1 >> 16U) & m8) ^ Te2_r((s2 >> 8U) & m8) ^ Te3_r(s3 & m8) ^ rk[4];
|
||||
t1 = Te0[s1 >> 24U] ^ Te1_r((s2 >> 16U) & m8) ^ Te2_r((s3 >> 8U) & m8) ^ Te3_r(s0 & m8) ^ rk[5];
|
||||
t2 = Te0[s2 >> 24U] ^ Te1_r((s3 >> 16U) & m8) ^ Te2_r((s0 >> 8U) & m8) ^ Te3_r(s1 & m8) ^ rk[6];
|
||||
t3 = Te0[s3 >> 24U] ^ Te1_r((s0 >> 16U) & m8) ^ Te2_r((s1 >> 8U) & m8) ^ Te3_r(s2 & m8) ^ rk[7];
|
||||
s0 = Te0[t0 >> 24U] ^ Te1_r((t1 >> 16U) & m8) ^ Te2_r((t2 >> 8U) & m8) ^ Te3_r(t3 & m8) ^ rk[8];
|
||||
s1 = Te0[t1 >> 24U] ^ Te1_r((t2 >> 16U) & m8) ^ Te2_r((t3 >> 8U) & m8) ^ Te3_r(t0 & m8) ^ rk[9];
|
||||
s2 = Te0[t2 >> 24U] ^ Te1_r((t3 >> 16U) & m8) ^ Te2_r((t0 >> 8U) & m8) ^ Te3_r(t1 & m8) ^ rk[10];
|
||||
s3 = Te0[t3 >> 24U] ^ Te1_r((t0 >> 16U) & m8) ^ Te2_r((t1 >> 8U) & m8) ^ Te3_r(t2 & m8) ^ rk[11];
|
||||
t0 = Te0[s0 >> 24U] ^ Te1_r((s1 >> 16U) & m8) ^ Te2_r((s2 >> 8U) & m8) ^ Te3_r(s3 & m8) ^ rk[12];
|
||||
t1 = Te0[s1 >> 24U] ^ Te1_r((s2 >> 16U) & m8) ^ Te2_r((s3 >> 8U) & m8) ^ Te3_r(s0 & m8) ^ rk[13];
|
||||
t2 = Te0[s2 >> 24U] ^ Te1_r((s3 >> 16U) & m8) ^ Te2_r((s0 >> 8U) & m8) ^ Te3_r(s1 & m8) ^ rk[14];
|
||||
t3 = Te0[s3 >> 24U] ^ Te1_r((s0 >> 16U) & m8) ^ Te2_r((s1 >> 8U) & m8) ^ Te3_r(s2 & m8) ^ rk[15];
|
||||
s0 = Te0[t0 >> 24U] ^ Te1_r((t1 >> 16U) & m8) ^ Te2_r((t2 >> 8U) & m8) ^ Te3_r(t3 & m8) ^ rk[16];
|
||||
s1 = Te0[t1 >> 24U] ^ Te1_r((t2 >> 16U) & m8) ^ Te2_r((t3 >> 8U) & m8) ^ Te3_r(t0 & m8) ^ rk[17];
|
||||
s2 = Te0[t2 >> 24U] ^ Te1_r((t3 >> 16U) & m8) ^ Te2_r((t0 >> 8U) & m8) ^ Te3_r(t1 & m8) ^ rk[18];
|
||||
s3 = Te0[t3 >> 24U] ^ Te1_r((t0 >> 16U) & m8) ^ Te2_r((t1 >> 8U) & m8) ^ Te3_r(t2 & m8) ^ rk[19];
|
||||
t0 = Te0[s0 >> 24U] ^ Te1_r((s1 >> 16U) & m8) ^ Te2_r((s2 >> 8U) & m8) ^ Te3_r(s3 & m8) ^ rk[20];
|
||||
t1 = Te0[s1 >> 24U] ^ Te1_r((s2 >> 16U) & m8) ^ Te2_r((s3 >> 8U) & m8) ^ Te3_r(s0 & m8) ^ rk[21];
|
||||
t2 = Te0[s2 >> 24U] ^ Te1_r((s3 >> 16U) & m8) ^ Te2_r((s0 >> 8U) & m8) ^ Te3_r(s1 & m8) ^ rk[22];
|
||||
t3 = Te0[s3 >> 24U] ^ Te1_r((s0 >> 16U) & m8) ^ Te2_r((s1 >> 8U) & m8) ^ Te3_r(s2 & m8) ^ rk[23];
|
||||
s0 = Te0[t0 >> 24U] ^ Te1_r((t1 >> 16U) & m8) ^ Te2_r((t2 >> 8U) & m8) ^ Te3_r(t3 & m8) ^ rk[24];
|
||||
s1 = Te0[t1 >> 24U] ^ Te1_r((t2 >> 16U) & m8) ^ Te2_r((t3 >> 8U) & m8) ^ Te3_r(t0 & m8) ^ rk[25];
|
||||
s2 = Te0[t2 >> 24U] ^ Te1_r((t3 >> 16U) & m8) ^ Te2_r((t0 >> 8U) & m8) ^ Te3_r(t1 & m8) ^ rk[26];
|
||||
s3 = Te0[t3 >> 24U] ^ Te1_r((t0 >> 16U) & m8) ^ Te2_r((t1 >> 8U) & m8) ^ Te3_r(t2 & m8) ^ rk[27];
|
||||
t0 = Te0[s0 >> 24U] ^ Te1_r((s1 >> 16U) & m8) ^ Te2_r((s2 >> 8U) & m8) ^ Te3_r(s3 & m8) ^ rk[28];
|
||||
t1 = Te0[s1 >> 24U] ^ Te1_r((s2 >> 16U) & m8) ^ Te2_r((s3 >> 8U) & m8) ^ Te3_r(s0 & m8) ^ rk[29];
|
||||
t2 = Te0[s2 >> 24U] ^ Te1_r((s3 >> 16U) & m8) ^ Te2_r((s0 >> 8U) & m8) ^ Te3_r(s1 & m8) ^ rk[30];
|
||||
t3 = Te0[s3 >> 24U] ^ Te1_r((s0 >> 16U) & m8) ^ Te2_r((s1 >> 8U) & m8) ^ Te3_r(s2 & m8) ^ rk[31];
|
||||
s0 = Te0[t0 >> 24U] ^ Te1_r((t1 >> 16U) & m8) ^ Te2_r((t2 >> 8U) & m8) ^ Te3_r(t3 & m8) ^ rk[32];
|
||||
s1 = Te0[t1 >> 24U] ^ Te1_r((t2 >> 16U) & m8) ^ Te2_r((t3 >> 8U) & m8) ^ Te3_r(t0 & m8) ^ rk[33];
|
||||
s2 = Te0[t2 >> 24U] ^ Te1_r((t3 >> 16U) & m8) ^ Te2_r((t0 >> 8U) & m8) ^ Te3_r(t1 & m8) ^ rk[34];
|
||||
s3 = Te0[t3 >> 24U] ^ Te1_r((t0 >> 16U) & m8) ^ Te2_r((t1 >> 8U) & m8) ^ Te3_r(t2 & m8) ^ rk[35];
|
||||
t0 = Te0[s0 >> 24U] ^ Te1_r((s1 >> 16U) & m8) ^ Te2_r((s2 >> 8U) & m8) ^ Te3_r(s3 & m8) ^ rk[36];
|
||||
t1 = Te0[s1 >> 24U] ^ Te1_r((s2 >> 16U) & m8) ^ Te2_r((s3 >> 8U) & m8) ^ Te3_r(s0 & m8) ^ rk[37];
|
||||
t2 = Te0[s2 >> 24U] ^ Te1_r((s3 >> 16U) & m8) ^ Te2_r((s0 >> 8U) & m8) ^ Te3_r(s1 & m8) ^ rk[38];
|
||||
t3 = Te0[s3 >> 24U] ^ Te1_r((s0 >> 16U) & m8) ^ Te2_r((s1 >> 8U) & m8) ^ Te3_r(s2 & m8) ^ rk[39];
|
||||
s0 = Te0[t0 >> 24U] ^ Te1_r((t1 >> 16U) & m8) ^ Te2_r((t2 >> 8U) & m8) ^ Te3_r(t3 & m8) ^ rk[40];
|
||||
s1 = Te0[t1 >> 24U] ^ Te1_r((t2 >> 16U) & m8) ^ Te2_r((t3 >> 8U) & m8) ^ Te3_r(t0 & m8) ^ rk[41];
|
||||
s2 = Te0[t2 >> 24U] ^ Te1_r((t3 >> 16U) & m8) ^ Te2_r((t0 >> 8U) & m8) ^ Te3_r(t1 & m8) ^ rk[42];
|
||||
s3 = Te0[t3 >> 24U] ^ Te1_r((t0 >> 16U) & m8) ^ Te2_r((t1 >> 8U) & m8) ^ Te3_r(t2 & m8) ^ rk[43];
|
||||
t0 = Te0[s0 >> 24U] ^ Te1_r((s1 >> 16U) & m8) ^ Te2_r((s2 >> 8U) & m8) ^ Te3_r(s3 & m8) ^ rk[44];
|
||||
t1 = Te0[s1 >> 24U] ^ Te1_r((s2 >> 16U) & m8) ^ Te2_r((s3 >> 8U) & m8) ^ Te3_r(s0 & m8) ^ rk[45];
|
||||
t2 = Te0[s2 >> 24U] ^ Te1_r((s3 >> 16U) & m8) ^ Te2_r((s0 >> 8U) & m8) ^ Te3_r(s1 & m8) ^ rk[46];
|
||||
t3 = Te0[s3 >> 24U] ^ Te1_r((s0 >> 16U) & m8) ^ Te2_r((s1 >> 8U) & m8) ^ Te3_r(s2 & m8) ^ rk[47];
|
||||
s0 = Te0[t0 >> 24U] ^ Te1_r((t1 >> 16U) & m8) ^ Te2_r((t2 >> 8U) & m8) ^ Te3_r(t3 & m8) ^ rk[48];
|
||||
s1 = Te0[t1 >> 24U] ^ Te1_r((t2 >> 16U) & m8) ^ Te2_r((t3 >> 8U) & m8) ^ Te3_r(t0 & m8) ^ rk[49];
|
||||
s2 = Te0[t2 >> 24U] ^ Te1_r((t3 >> 16U) & m8) ^ Te2_r((t0 >> 8U) & m8) ^ Te3_r(t1 & m8) ^ rk[50];
|
||||
s3 = Te0[t3 >> 24U] ^ Te1_r((t0 >> 16U) & m8) ^ Te2_r((t1 >> 8U) & m8) ^ Te3_r(t2 & m8) ^ rk[51];
|
||||
t0 = Te0[s0 >> 24U] ^ Te1_r((s1 >> 16U) & m8) ^ Te2_r((s2 >> 8U) & m8) ^ Te3_r(s3 & m8) ^ rk[52];
|
||||
t1 = Te0[s1 >> 24U] ^ Te1_r((s2 >> 16U) & m8) ^ Te2_r((s3 >> 8U) & m8) ^ Te3_r(s0 & m8) ^ rk[53];
|
||||
t2 = Te0[s2 >> 24U] ^ Te1_r((s3 >> 16U) & m8) ^ Te2_r((s0 >> 8U) & m8) ^ Te3_r(s1 & m8) ^ rk[54];
|
||||
t3 = Te0[s3 >> 24U] ^ Te1_r((s0 >> 16U) & m8) ^ Te2_r((s1 >> 8U) & m8) ^ Te3_r(s2 & m8) ^ rk[55];
|
||||
s0 = (Te2_r(t0 >> 24U) & m8_24) ^ (Te3_r((t1 >> 16U) & m8) & m8_16) ^ (Te0[(t2 >> 8U) & m8] & m8_8) ^ (Te1_r(t3 & m8) & m8) ^ rk[56];
|
||||
s1 = (Te2_r(t1 >> 24U) & m8_24) ^ (Te3_r((t2 >> 16U) & m8) & m8_16) ^ (Te0[(t3 >> 8U) & m8] & m8_8) ^ (Te1_r(t0 & m8) & m8) ^ rk[57];
|
||||
s2 = (Te2_r(t2 >> 24U) & m8_24) ^ (Te3_r((t3 >> 16U) & m8) & m8_16) ^ (Te0[(t0 >> 8U) & m8] & m8_8) ^ (Te1_r(t1 & m8) & m8) ^ rk[58];
|
||||
s3 = (Te2_r(t3 >> 24U) & m8_24) ^ (Te3_r((t0 >> 16U) & m8) & m8_16) ^ (Te0[(t1 >> 8U) & m8] & m8_8) ^ (Te1_r(t2 & m8) & m8) ^ rk[59];
|
||||
|
||||
out[0] = in[0] ^ (uint8_t)(s0 >> 24U);
|
||||
out[1] = in[1] ^ (uint8_t)(s0 >> 16U);
|
||||
out[2] = in[2] ^ (uint8_t)(s0 >> 8U);
|
||||
out[3] = in[3] ^ (uint8_t)s0;
|
||||
out[4] = in[4] ^ (uint8_t)(s1 >> 24U);
|
||||
out[5] = in[5] ^ (uint8_t)(s1 >> 16U);
|
||||
out[6] = in[6] ^ (uint8_t)(s1 >> 8U);
|
||||
out[7] = in[7] ^ (uint8_t)s1;
|
||||
out[8] = in[8] ^ (uint8_t)(s2 >> 24U);
|
||||
out[9] = in[9] ^ (uint8_t)(s2 >> 16U);
|
||||
out[10] = in[10] ^ (uint8_t)(s2 >> 8U);
|
||||
out[11] = in[11] ^ (uint8_t)s2;
|
||||
out[12] = in[12] ^ (uint8_t)(s3 >> 24U);
|
||||
out[13] = in[13] ^ (uint8_t)(s3 >> 16U);
|
||||
out[14] = in[14] ^ (uint8_t)(s3 >> 8U);
|
||||
out[15] = in[15] ^ (uint8_t)s3;
|
||||
out += 16;
|
||||
in += 16;
|
||||
} while ((len -= 16) >= 16);
|
||||
}
|
||||
reinterpret_cast<uint32_t *>(_ctr)[3] = Utils::hton(ctr);
|
||||
}
|
||||
|
||||
// Any remaining input is placed in _out. This will be picked up and crypted
|
||||
// on subsequent calls to crypt() or finish() as it'll mean _len will not be
|
||||
// an even multiple of 16.
|
||||
while (len) {
|
||||
--len;
|
||||
*(out++) = *(in++);
|
||||
}
|
||||
}
|
||||
|
||||
void AES::CTR::finish() noexcept
|
||||
{
|
||||
uint8_t tmp[16];
|
||||
const unsigned int rem = _len & 15U;
|
||||
if (rem) {
|
||||
_aes.encrypt(_ctr, tmp);
|
||||
for (unsigned int i = 0, j = _len - rem; i < rem; ++i) {
|
||||
_out[j + i] ^= tmp[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Software AES and AES key expansion ---------------------------------------------------------------------------------
|
||||
|
||||
const uint32_t AES::Te0[256] = {0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153,
|
||||
0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f,
|
||||
0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba,
|
||||
0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d,
|
||||
0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437,
|
||||
0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818,
|
||||
0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878,
|
||||
0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a};
|
||||
const uint32_t AES::Te4[256] = {0x63636363, 0x7c7c7c7c, 0x77777777, 0x7b7b7b7b, 0xf2f2f2f2, 0x6b6b6b6b, 0x6f6f6f6f, 0xc5c5c5c5, 0x30303030, 0x01010101, 0x67676767, 0x2b2b2b2b, 0xfefefefe, 0xd7d7d7d7, 0xabababab, 0x76767676, 0xcacacaca, 0x82828282, 0xc9c9c9c9, 0x7d7d7d7d, 0xfafafafa, 0x59595959, 0x47474747, 0xf0f0f0f0, 0xadadadad, 0xd4d4d4d4, 0xa2a2a2a2, 0xafafafaf, 0x9c9c9c9c, 0xa4a4a4a4, 0x72727272, 0xc0c0c0c0, 0xb7b7b7b7, 0xfdfdfdfd, 0x93939393, 0x26262626, 0x36363636, 0x3f3f3f3f, 0xf7f7f7f7, 0xcccccccc, 0x34343434, 0xa5a5a5a5, 0xe5e5e5e5, 0xf1f1f1f1, 0x71717171, 0xd8d8d8d8, 0x31313131,
|
||||
0x15151515, 0x04040404, 0xc7c7c7c7, 0x23232323, 0xc3c3c3c3, 0x18181818, 0x96969696, 0x05050505, 0x9a9a9a9a, 0x07070707, 0x12121212, 0x80808080, 0xe2e2e2e2, 0xebebebeb, 0x27272727, 0xb2b2b2b2, 0x75757575,
|
||||
0x09090909, 0x83838383, 0x2c2c2c2c, 0x1a1a1a1a, 0x1b1b1b1b, 0x6e6e6e6e, 0x5a5a5a5a, 0xa0a0a0a0, 0x52525252, 0x3b3b3b3b, 0xd6d6d6d6, 0xb3b3b3b3, 0x29292929, 0xe3e3e3e3, 0x2f2f2f2f, 0x84848484, 0x53535353, 0xd1d1d1d1, 0x00000000, 0xedededed, 0x20202020, 0xfcfcfcfc, 0xb1b1b1b1, 0x5b5b5b5b, 0x6a6a6a6a, 0xcbcbcbcb, 0xbebebebe, 0x39393939, 0x4a4a4a4a, 0x4c4c4c4c, 0x58585858, 0xcfcfcfcf, 0xd0d0d0d0, 0xefefefef, 0xaaaaaaaa, 0xfbfbfbfb, 0x43434343, 0x4d4d4d4d, 0x33333333, 0x85858585, 0x45454545, 0xf9f9f9f9, 0x02020202, 0x7f7f7f7f, 0x50505050, 0x3c3c3c3c, 0x9f9f9f9f,
|
||||
0xa8a8a8a8, 0x51515151, 0xa3a3a3a3, 0x40404040, 0x8f8f8f8f, 0x92929292, 0x9d9d9d9d, 0x38383838, 0xf5f5f5f5, 0xbcbcbcbc, 0xb6b6b6b6, 0xdadadada, 0x21212121, 0x10101010, 0xffffffff, 0xf3f3f3f3, 0xd2d2d2d2,
|
||||
0xcdcdcdcd, 0x0c0c0c0c, 0x13131313, 0xecececec, 0x5f5f5f5f, 0x97979797, 0x44444444, 0x17171717, 0xc4c4c4c4, 0xa7a7a7a7, 0x7e7e7e7e, 0x3d3d3d3d, 0x64646464, 0x5d5d5d5d, 0x19191919, 0x73737373, 0x60606060, 0x81818181, 0x4f4f4f4f, 0xdcdcdcdc, 0x22222222, 0x2a2a2a2a, 0x90909090, 0x88888888, 0x46464646, 0xeeeeeeee, 0xb8b8b8b8, 0x14141414, 0xdededede, 0x5e5e5e5e, 0x0b0b0b0b, 0xdbdbdbdb, 0xe0e0e0e0, 0x32323232, 0x3a3a3a3a, 0x0a0a0a0a, 0x49494949, 0x06060606, 0x24242424, 0x5c5c5c5c, 0xc2c2c2c2, 0xd3d3d3d3, 0xacacacac, 0x62626262, 0x91919191, 0x95959595, 0xe4e4e4e4,
|
||||
0x79797979, 0xe7e7e7e7, 0xc8c8c8c8, 0x37373737, 0x6d6d6d6d, 0x8d8d8d8d, 0xd5d5d5d5, 0x4e4e4e4e, 0xa9a9a9a9, 0x6c6c6c6c, 0x56565656, 0xf4f4f4f4, 0xeaeaeaea, 0x65656565, 0x7a7a7a7a, 0xaeaeaeae, 0x08080808,
|
||||
0xbabababa, 0x78787878, 0x25252525, 0x2e2e2e2e, 0x1c1c1c1c, 0xa6a6a6a6, 0xb4b4b4b4, 0xc6c6c6c6, 0xe8e8e8e8, 0xdddddddd, 0x74747474, 0x1f1f1f1f, 0x4b4b4b4b, 0xbdbdbdbd, 0x8b8b8b8b, 0x8a8a8a8a, 0x70707070, 0x3e3e3e3e, 0xb5b5b5b5, 0x66666666, 0x48484848, 0x03030303, 0xf6f6f6f6, 0x0e0e0e0e, 0x61616161, 0x35353535, 0x57575757, 0xb9b9b9b9, 0x86868686, 0xc1c1c1c1, 0x1d1d1d1d, 0x9e9e9e9e, 0xe1e1e1e1, 0xf8f8f8f8, 0x98989898, 0x11111111, 0x69696969, 0xd9d9d9d9, 0x8e8e8e8e, 0x94949494, 0x9b9b9b9b, 0x1e1e1e1e, 0x87878787, 0xe9e9e9e9, 0xcececece, 0x55555555, 0x28282828,
|
||||
0xdfdfdfdf, 0x8c8c8c8c, 0xa1a1a1a1, 0x89898989, 0x0d0d0d0d, 0xbfbfbfbf, 0xe6e6e6e6, 0x42424242, 0x68686868, 0x41414141, 0x99999999, 0x2d2d2d2d, 0x0f0f0f0f, 0xb0b0b0b0, 0x54545454, 0xbbbbbbbb, 0x16161616};
|
||||
const uint32_t AES::Td0[256] = {0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c,
|
||||
0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c,
|
||||
0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1,
|
||||
0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8,
|
||||
0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e,
|
||||
0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6,
|
||||
0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14,
|
||||
0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742};
|
||||
const uint8_t AES::Td4[256] = {0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d,
|
||||
0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
|
||||
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c,
|
||||
0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d};
|
||||
const uint32_t AES::rcon[15] = {0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000, 0x6c000000, 0xd8000000, 0xab000000, 0x4d000000, 0x9a000000};
|
||||
|
||||
void AES::p_initSW(const uint8_t *key) noexcept
|
||||
{
|
||||
uint32_t *rk = p_k.sw.ek;
|
||||
|
||||
rk[0] = Utils::loadBigEndian< uint32_t >(key);
|
||||
rk[1] = Utils::loadBigEndian< uint32_t >(key + 4);
|
||||
rk[2] = Utils::loadBigEndian< uint32_t >(key + 8);
|
||||
rk[3] = Utils::loadBigEndian< uint32_t >(key + 12);
|
||||
rk[4] = Utils::loadBigEndian< uint32_t >(key + 16);
|
||||
rk[5] = Utils::loadBigEndian< uint32_t >(key + 20);
|
||||
rk[6] = Utils::loadBigEndian< uint32_t >(key + 24);
|
||||
rk[7] = Utils::loadBigEndian< uint32_t >(key + 28);
|
||||
for (int i = 0;;) {
|
||||
uint32_t temp = rk[7];
|
||||
rk[8] = rk[0] ^ (Te2_r((temp >> 16U) & 0xffU) & 0xff000000U) ^ (Te3_r((temp >> 8U) & 0xffU) & 0x00ff0000U) ^ (Te0[(temp) & 0xffU] & 0x0000ff00U) ^ (Te1_r(temp >> 24U) & 0x000000ffU) ^ rcon[i];
|
||||
rk[9] = rk[1] ^ rk[8];
|
||||
rk[10] = rk[2] ^ rk[9];
|
||||
rk[11] = rk[3] ^ rk[10];
|
||||
if (++i == 7) {
|
||||
break;
|
||||
}
|
||||
temp = rk[11];
|
||||
rk[12] = rk[4] ^ (Te2_r(temp >> 24U) & 0xff000000U) ^ (Te3_r((temp >> 16U) & 0xffU) & 0x00ff0000U) ^ (Te0[(temp >> 8U) & 0xffU] & 0x0000ff00U) ^ (Te1_r((temp) & 0xffU) & 0x000000ffU);
|
||||
rk[13] = rk[5] ^ rk[12];
|
||||
rk[14] = rk[6] ^ rk[13];
|
||||
rk[15] = rk[7] ^ rk[14];
|
||||
rk += 8;
|
||||
}
|
||||
|
||||
p_encryptSW((const uint8_t *)Utils::ZERO256, (uint8_t *)p_k.sw.h);
|
||||
p_k.sw.h[0] = Utils::ntoh(p_k.sw.h[0]);
|
||||
p_k.sw.h[1] = Utils::ntoh(p_k.sw.h[1]);
|
||||
|
||||
for (int i = 0; i < 60; ++i) {
|
||||
p_k.sw.dk[i] = p_k.sw.ek[i];
|
||||
}
|
||||
rk = p_k.sw.dk;
|
||||
|
||||
for (int i = 0, j = 56; i < j; i += 4, j -= 4) {
|
||||
uint32_t temp = rk[i];
|
||||
rk[i] = rk[j];
|
||||
rk[j] = temp;
|
||||
temp = rk[i + 1];
|
||||
rk[i + 1] = rk[j + 1];
|
||||
rk[j + 1] = temp;
|
||||
temp = rk[i + 2];
|
||||
rk[i + 2] = rk[j + 2];
|
||||
rk[j + 2] = temp;
|
||||
temp = rk[i + 3];
|
||||
rk[i + 3] = rk[j + 3];
|
||||
rk[j + 3] = temp;
|
||||
}
|
||||
for (int i = 1; i < 14; ++i) {
|
||||
rk += 4;
|
||||
rk[0] = Td0[Te4[(rk[0] >> 24U)] & 0xffU] ^ Td1_r(Te4[(rk[0] >> 16U) & 0xffU] & 0xffU) ^ Td2_r(Te4[(rk[0] >> 8U) & 0xffU] & 0xffU) ^ Td3_r(Te4[(rk[0]) & 0xffU] & 0xffU);
|
||||
rk[1] = Td0[Te4[(rk[1] >> 24U)] & 0xffU] ^ Td1_r(Te4[(rk[1] >> 16U) & 0xffU] & 0xffU) ^ Td2_r(Te4[(rk[1] >> 8U) & 0xffU] & 0xffU) ^ Td3_r(Te4[(rk[1]) & 0xffU] & 0xffU);
|
||||
rk[2] = Td0[Te4[(rk[2] >> 24U)] & 0xffU] ^ Td1_r(Te4[(rk[2] >> 16U) & 0xffU] & 0xffU) ^ Td2_r(Te4[(rk[2] >> 8U) & 0xffU] & 0xffU) ^ Td3_r(Te4[(rk[2]) & 0xffU] & 0xffU);
|
||||
rk[3] = Td0[Te4[(rk[3] >> 24U)] & 0xffU] ^ Td1_r(Te4[(rk[3] >> 16U) & 0xffU] & 0xffU) ^ Td2_r(Te4[(rk[3] >> 8U) & 0xffU] & 0xffU) ^ Td3_r(Te4[(rk[3]) & 0xffU] & 0xffU);
|
||||
}
|
||||
}
|
||||
|
||||
void AES::p_encryptSW(const uint8_t *in, uint8_t *out) const noexcept
|
||||
{
|
||||
const uint32_t *const restrict rk = p_k.sw.ek;
|
||||
const uint32_t m8 = 0x000000ff;
|
||||
const uint32_t m8_8 = 0x0000ff00;
|
||||
const uint32_t m8_16 = 0x00ff0000;
|
||||
const uint32_t m8_24 = 0xff000000;
|
||||
uint32_t s0 = Utils::loadBigEndian< uint32_t >(in) ^rk[0];
|
||||
uint32_t s1 = Utils::loadBigEndian< uint32_t >(in + 4) ^rk[1];
|
||||
uint32_t s2 = Utils::loadBigEndian< uint32_t >(in + 8) ^rk[2];
|
||||
uint32_t s3 = Utils::loadBigEndian< uint32_t >(in + 12) ^rk[3];
|
||||
|
||||
uint32_t t0, t1, t2, t3;
|
||||
t0 = Te0[s0 >> 24U] ^ Te1_r((s1 >> 16U) & m8) ^ Te2_r((s2 >> 8U) & m8) ^ Te3_r(s3 & m8) ^ rk[4];
|
||||
t1 = Te0[s1 >> 24U] ^ Te1_r((s2 >> 16U) & m8) ^ Te2_r((s3 >> 8U) & m8) ^ Te3_r(s0 & m8) ^ rk[5];
|
||||
t2 = Te0[s2 >> 24U] ^ Te1_r((s3 >> 16U) & m8) ^ Te2_r((s0 >> 8U) & m8) ^ Te3_r(s1 & m8) ^ rk[6];
|
||||
t3 = Te0[s3 >> 24U] ^ Te1_r((s0 >> 16U) & m8) ^ Te2_r((s1 >> 8U) & m8) ^ Te3_r(s2 & m8) ^ rk[7];
|
||||
s0 = Te0[t0 >> 24U] ^ Te1_r((t1 >> 16U) & m8) ^ Te2_r((t2 >> 8U) & m8) ^ Te3_r(t3 & m8) ^ rk[8];
|
||||
s1 = Te0[t1 >> 24U] ^ Te1_r((t2 >> 16U) & m8) ^ Te2_r((t3 >> 8U) & m8) ^ Te3_r(t0 & m8) ^ rk[9];
|
||||
s2 = Te0[t2 >> 24U] ^ Te1_r((t3 >> 16U) & m8) ^ Te2_r((t0 >> 8U) & m8) ^ Te3_r(t1 & m8) ^ rk[10];
|
||||
s3 = Te0[t3 >> 24U] ^ Te1_r((t0 >> 16U) & m8) ^ Te2_r((t1 >> 8U) & m8) ^ Te3_r(t2 & m8) ^ rk[11];
|
||||
t0 = Te0[s0 >> 24U] ^ Te1_r((s1 >> 16U) & m8) ^ Te2_r((s2 >> 8U) & m8) ^ Te3_r(s3 & m8) ^ rk[12];
|
||||
t1 = Te0[s1 >> 24U] ^ Te1_r((s2 >> 16U) & m8) ^ Te2_r((s3 >> 8U) & m8) ^ Te3_r(s0 & m8) ^ rk[13];
|
||||
t2 = Te0[s2 >> 24U] ^ Te1_r((s3 >> 16U) & m8) ^ Te2_r((s0 >> 8U) & m8) ^ Te3_r(s1 & m8) ^ rk[14];
|
||||
t3 = Te0[s3 >> 24U] ^ Te1_r((s0 >> 16U) & m8) ^ Te2_r((s1 >> 8U) & m8) ^ Te3_r(s2 & m8) ^ rk[15];
|
||||
s0 = Te0[t0 >> 24U] ^ Te1_r((t1 >> 16U) & m8) ^ Te2_r((t2 >> 8U) & m8) ^ Te3_r(t3 & m8) ^ rk[16];
|
||||
s1 = Te0[t1 >> 24U] ^ Te1_r((t2 >> 16U) & m8) ^ Te2_r((t3 >> 8U) & m8) ^ Te3_r(t0 & m8) ^ rk[17];
|
||||
s2 = Te0[t2 >> 24U] ^ Te1_r((t3 >> 16U) & m8) ^ Te2_r((t0 >> 8U) & m8) ^ Te3_r(t1 & m8) ^ rk[18];
|
||||
s3 = Te0[t3 >> 24U] ^ Te1_r((t0 >> 16U) & m8) ^ Te2_r((t1 >> 8U) & m8) ^ Te3_r(t2 & m8) ^ rk[19];
|
||||
t0 = Te0[s0 >> 24U] ^ Te1_r((s1 >> 16U) & m8) ^ Te2_r((s2 >> 8U) & m8) ^ Te3_r(s3 & m8) ^ rk[20];
|
||||
t1 = Te0[s1 >> 24U] ^ Te1_r((s2 >> 16U) & m8) ^ Te2_r((s3 >> 8U) & m8) ^ Te3_r(s0 & m8) ^ rk[21];
|
||||
t2 = Te0[s2 >> 24U] ^ Te1_r((s3 >> 16U) & m8) ^ Te2_r((s0 >> 8U) & m8) ^ Te3_r(s1 & m8) ^ rk[22];
|
||||
t3 = Te0[s3 >> 24U] ^ Te1_r((s0 >> 16U) & m8) ^ Te2_r((s1 >> 8U) & m8) ^ Te3_r(s2 & m8) ^ rk[23];
|
||||
s0 = Te0[t0 >> 24U] ^ Te1_r((t1 >> 16U) & m8) ^ Te2_r((t2 >> 8U) & m8) ^ Te3_r(t3 & m8) ^ rk[24];
|
||||
s1 = Te0[t1 >> 24U] ^ Te1_r((t2 >> 16U) & m8) ^ Te2_r((t3 >> 8U) & m8) ^ Te3_r(t0 & m8) ^ rk[25];
|
||||
s2 = Te0[t2 >> 24U] ^ Te1_r((t3 >> 16U) & m8) ^ Te2_r((t0 >> 8U) & m8) ^ Te3_r(t1 & m8) ^ rk[26];
|
||||
s3 = Te0[t3 >> 24U] ^ Te1_r((t0 >> 16U) & m8) ^ Te2_r((t1 >> 8U) & m8) ^ Te3_r(t2 & m8) ^ rk[27];
|
||||
t0 = Te0[s0 >> 24U] ^ Te1_r((s1 >> 16U) & m8) ^ Te2_r((s2 >> 8U) & m8) ^ Te3_r(s3 & m8) ^ rk[28];
|
||||
t1 = Te0[s1 >> 24U] ^ Te1_r((s2 >> 16U) & m8) ^ Te2_r((s3 >> 8U) & m8) ^ Te3_r(s0 & m8) ^ rk[29];
|
||||
t2 = Te0[s2 >> 24U] ^ Te1_r((s3 >> 16U) & m8) ^ Te2_r((s0 >> 8U) & m8) ^ Te3_r(s1 & m8) ^ rk[30];
|
||||
t3 = Te0[s3 >> 24U] ^ Te1_r((s0 >> 16U) & m8) ^ Te2_r((s1 >> 8U) & m8) ^ Te3_r(s2 & m8) ^ rk[31];
|
||||
s0 = Te0[t0 >> 24U] ^ Te1_r((t1 >> 16U) & m8) ^ Te2_r((t2 >> 8U) & m8) ^ Te3_r(t3 & m8) ^ rk[32];
|
||||
s1 = Te0[t1 >> 24U] ^ Te1_r((t2 >> 16U) & m8) ^ Te2_r((t3 >> 8U) & m8) ^ Te3_r(t0 & m8) ^ rk[33];
|
||||
s2 = Te0[t2 >> 24U] ^ Te1_r((t3 >> 16U) & m8) ^ Te2_r((t0 >> 8U) & m8) ^ Te3_r(t1 & m8) ^ rk[34];
|
||||
s3 = Te0[t3 >> 24U] ^ Te1_r((t0 >> 16U) & m8) ^ Te2_r((t1 >> 8U) & m8) ^ Te3_r(t2 & m8) ^ rk[35];
|
||||
t0 = Te0[s0 >> 24U] ^ Te1_r((s1 >> 16U) & m8) ^ Te2_r((s2 >> 8U) & m8) ^ Te3_r(s3 & m8) ^ rk[36];
|
||||
t1 = Te0[s1 >> 24U] ^ Te1_r((s2 >> 16U) & m8) ^ Te2_r((s3 >> 8U) & m8) ^ Te3_r(s0 & m8) ^ rk[37];
|
||||
t2 = Te0[s2 >> 24U] ^ Te1_r((s3 >> 16U) & m8) ^ Te2_r((s0 >> 8U) & m8) ^ Te3_r(s1 & m8) ^ rk[38];
|
||||
t3 = Te0[s3 >> 24U] ^ Te1_r((s0 >> 16U) & m8) ^ Te2_r((s1 >> 8U) & m8) ^ Te3_r(s2 & m8) ^ rk[39];
|
||||
s0 = Te0[t0 >> 24U] ^ Te1_r((t1 >> 16U) & m8) ^ Te2_r((t2 >> 8U) & m8) ^ Te3_r(t3 & m8) ^ rk[40];
|
||||
s1 = Te0[t1 >> 24U] ^ Te1_r((t2 >> 16U) & m8) ^ Te2_r((t3 >> 8U) & m8) ^ Te3_r(t0 & m8) ^ rk[41];
|
||||
s2 = Te0[t2 >> 24U] ^ Te1_r((t3 >> 16U) & m8) ^ Te2_r((t0 >> 8U) & m8) ^ Te3_r(t1 & m8) ^ rk[42];
|
||||
s3 = Te0[t3 >> 24U] ^ Te1_r((t0 >> 16U) & m8) ^ Te2_r((t1 >> 8U) & m8) ^ Te3_r(t2 & m8) ^ rk[43];
|
||||
t0 = Te0[s0 >> 24U] ^ Te1_r((s1 >> 16U) & m8) ^ Te2_r((s2 >> 8U) & m8) ^ Te3_r(s3 & m8) ^ rk[44];
|
||||
t1 = Te0[s1 >> 24U] ^ Te1_r((s2 >> 16U) & m8) ^ Te2_r((s3 >> 8U) & m8) ^ Te3_r(s0 & m8) ^ rk[45];
|
||||
t2 = Te0[s2 >> 24U] ^ Te1_r((s3 >> 16U) & m8) ^ Te2_r((s0 >> 8U) & m8) ^ Te3_r(s1 & m8) ^ rk[46];
|
||||
t3 = Te0[s3 >> 24U] ^ Te1_r((s0 >> 16U) & m8) ^ Te2_r((s1 >> 8U) & m8) ^ Te3_r(s2 & m8) ^ rk[47];
|
||||
s0 = Te0[t0 >> 24U] ^ Te1_r((t1 >> 16U) & m8) ^ Te2_r((t2 >> 8U) & m8) ^ Te3_r(t3 & m8) ^ rk[48];
|
||||
s1 = Te0[t1 >> 24U] ^ Te1_r((t2 >> 16U) & m8) ^ Te2_r((t3 >> 8U) & m8) ^ Te3_r(t0 & m8) ^ rk[49];
|
||||
s2 = Te0[t2 >> 24U] ^ Te1_r((t3 >> 16U) & m8) ^ Te2_r((t0 >> 8U) & m8) ^ Te3_r(t1 & m8) ^ rk[50];
|
||||
s3 = Te0[t3 >> 24U] ^ Te1_r((t0 >> 16U) & m8) ^ Te2_r((t1 >> 8U) & m8) ^ Te3_r(t2 & m8) ^ rk[51];
|
||||
t0 = Te0[s0 >> 24U] ^ Te1_r((s1 >> 16U) & m8) ^ Te2_r((s2 >> 8U) & m8) ^ Te3_r(s3 & m8) ^ rk[52];
|
||||
t1 = Te0[s1 >> 24U] ^ Te1_r((s2 >> 16U) & m8) ^ Te2_r((s3 >> 8U) & m8) ^ Te3_r(s0 & m8) ^ rk[53];
|
||||
t2 = Te0[s2 >> 24U] ^ Te1_r((s3 >> 16U) & m8) ^ Te2_r((s0 >> 8U) & m8) ^ Te3_r(s1 & m8) ^ rk[54];
|
||||
t3 = Te0[s3 >> 24U] ^ Te1_r((s0 >> 16U) & m8) ^ Te2_r((s1 >> 8U) & m8) ^ Te3_r(s2 & m8) ^ rk[55];
|
||||
s0 = (Te2_r(t0 >> 24U) & m8_24) ^ (Te3_r((t1 >> 16U) & m8) & m8_16) ^ (Te0[(t2 >> 8U) & m8] & m8_8) ^ (Te1_r(t3 & m8) & m8) ^ rk[56];
|
||||
s1 = (Te2_r(t1 >> 24U) & m8_24) ^ (Te3_r((t2 >> 16U) & m8) & m8_16) ^ (Te0[(t3 >> 8U) & m8] & m8_8) ^ (Te1_r(t0 & m8) & m8) ^ rk[57];
|
||||
s2 = (Te2_r(t2 >> 24U) & m8_24) ^ (Te3_r((t3 >> 16U) & m8) & m8_16) ^ (Te0[(t0 >> 8U) & m8] & m8_8) ^ (Te1_r(t1 & m8) & m8) ^ rk[58];
|
||||
s3 = (Te2_r(t3 >> 24U) & m8_24) ^ (Te3_r((t0 >> 16U) & m8) & m8_16) ^ (Te0[(t1 >> 8U) & m8] & m8_8) ^ (Te1_r(t2 & m8) & m8) ^ rk[59];
|
||||
|
||||
Utils::storeBigEndian< uint32_t >(out, s0);
|
||||
Utils::storeBigEndian< uint32_t >(out + 4, s1);
|
||||
Utils::storeBigEndian< uint32_t >(out + 8, s2);
|
||||
Utils::storeBigEndian< uint32_t >(out + 12, s3);
|
||||
}
|
||||
|
||||
void AES::p_decryptSW(const uint8_t *in, uint8_t *out) const noexcept
|
||||
{
|
||||
const uint32_t *restrict rk = p_k.sw.dk;
|
||||
const uint32_t m8 = 0x000000ff;
|
||||
uint32_t s0 = Utils::loadBigEndian< uint32_t >(in) ^rk[0];
|
||||
uint32_t s1 = Utils::loadBigEndian< uint32_t >(in + 4) ^rk[1];
|
||||
uint32_t s2 = Utils::loadBigEndian< uint32_t >(in + 8) ^rk[2];
|
||||
uint32_t s3 = Utils::loadBigEndian< uint32_t >(in + 12) ^rk[3];
|
||||
|
||||
uint32_t t0, t1, t2, t3;
|
||||
t0 = Td0[s0 >> 24U] ^ Td1_r((s3 >> 16U) & m8) ^ Td2_r((s2 >> 8U) & m8) ^ Td3_r(s1 & m8) ^ rk[4];
|
||||
t1 = Td0[s1 >> 24U] ^ Td1_r((s0 >> 16U) & m8) ^ Td2_r((s3 >> 8U) & m8) ^ Td3_r(s2 & m8) ^ rk[5];
|
||||
t2 = Td0[s2 >> 24U] ^ Td1_r((s1 >> 16U) & m8) ^ Td2_r((s0 >> 8U) & m8) ^ Td3_r(s3 & m8) ^ rk[6];
|
||||
t3 = Td0[s3 >> 24U] ^ Td1_r((s2 >> 16U) & m8) ^ Td2_r((s1 >> 8U) & m8) ^ Td3_r(s0 & m8) ^ rk[7];
|
||||
s0 = Td0[t0 >> 24U] ^ Td1_r((t3 >> 16U) & m8) ^ Td2_r((t2 >> 8U) & m8) ^ Td3_r(t1 & m8) ^ rk[8];
|
||||
s1 = Td0[t1 >> 24U] ^ Td1_r((t0 >> 16U) & m8) ^ Td2_r((t3 >> 8U) & m8) ^ Td3_r(t2 & m8) ^ rk[9];
|
||||
s2 = Td0[t2 >> 24U] ^ Td1_r((t1 >> 16U) & m8) ^ Td2_r((t0 >> 8U) & m8) ^ Td3_r(t3 & m8) ^ rk[10];
|
||||
s3 = Td0[t3 >> 24U] ^ Td1_r((t2 >> 16U) & m8) ^ Td2_r((t1 >> 8U) & m8) ^ Td3_r(t0 & m8) ^ rk[11];
|
||||
t0 = Td0[s0 >> 24U] ^ Td1_r((s3 >> 16U) & m8) ^ Td2_r((s2 >> 8U) & m8) ^ Td3_r(s1 & m8) ^ rk[12];
|
||||
t1 = Td0[s1 >> 24U] ^ Td1_r((s0 >> 16U) & m8) ^ Td2_r((s3 >> 8U) & m8) ^ Td3_r(s2 & m8) ^ rk[13];
|
||||
t2 = Td0[s2 >> 24U] ^ Td1_r((s1 >> 16U) & m8) ^ Td2_r((s0 >> 8U) & m8) ^ Td3_r(s3 & m8) ^ rk[14];
|
||||
t3 = Td0[s3 >> 24U] ^ Td1_r((s2 >> 16U) & m8) ^ Td2_r((s1 >> 8U) & m8) ^ Td3_r(s0 & m8) ^ rk[15];
|
||||
s0 = Td0[t0 >> 24U] ^ Td1_r((t3 >> 16U) & m8) ^ Td2_r((t2 >> 8U) & m8) ^ Td3_r(t1 & m8) ^ rk[16];
|
||||
s1 = Td0[t1 >> 24U] ^ Td1_r((t0 >> 16U) & m8) ^ Td2_r((t3 >> 8U) & m8) ^ Td3_r(t2 & m8) ^ rk[17];
|
||||
s2 = Td0[t2 >> 24U] ^ Td1_r((t1 >> 16U) & m8) ^ Td2_r((t0 >> 8U) & m8) ^ Td3_r(t3 & m8) ^ rk[18];
|
||||
s3 = Td0[t3 >> 24U] ^ Td1_r((t2 >> 16U) & m8) ^ Td2_r((t1 >> 8U) & m8) ^ Td3_r(t0 & m8) ^ rk[19];
|
||||
t0 = Td0[s0 >> 24U] ^ Td1_r((s3 >> 16U) & m8) ^ Td2_r((s2 >> 8U) & m8) ^ Td3_r(s1 & m8) ^ rk[20];
|
||||
t1 = Td0[s1 >> 24U] ^ Td1_r((s0 >> 16U) & m8) ^ Td2_r((s3 >> 8U) & m8) ^ Td3_r(s2 & m8) ^ rk[21];
|
||||
t2 = Td0[s2 >> 24U] ^ Td1_r((s1 >> 16U) & m8) ^ Td2_r((s0 >> 8U) & m8) ^ Td3_r(s3 & m8) ^ rk[22];
|
||||
t3 = Td0[s3 >> 24U] ^ Td1_r((s2 >> 16U) & m8) ^ Td2_r((s1 >> 8U) & m8) ^ Td3_r(s0 & m8) ^ rk[23];
|
||||
s0 = Td0[t0 >> 24U] ^ Td1_r((t3 >> 16U) & m8) ^ Td2_r((t2 >> 8U) & m8) ^ Td3_r(t1 & m8) ^ rk[24];
|
||||
s1 = Td0[t1 >> 24U] ^ Td1_r((t0 >> 16U) & m8) ^ Td2_r((t3 >> 8U) & m8) ^ Td3_r(t2 & m8) ^ rk[25];
|
||||
s2 = Td0[t2 >> 24U] ^ Td1_r((t1 >> 16U) & m8) ^ Td2_r((t0 >> 8U) & m8) ^ Td3_r(t3 & m8) ^ rk[26];
|
||||
s3 = Td0[t3 >> 24U] ^ Td1_r((t2 >> 16U) & m8) ^ Td2_r((t1 >> 8U) & m8) ^ Td3_r(t0 & m8) ^ rk[27];
|
||||
t0 = Td0[s0 >> 24U] ^ Td1_r((s3 >> 16U) & m8) ^ Td2_r((s2 >> 8U) & m8) ^ Td3_r(s1 & m8) ^ rk[28];
|
||||
t1 = Td0[s1 >> 24U] ^ Td1_r((s0 >> 16U) & m8) ^ Td2_r((s3 >> 8U) & m8) ^ Td3_r(s2 & m8) ^ rk[29];
|
||||
t2 = Td0[s2 >> 24U] ^ Td1_r((s1 >> 16U) & m8) ^ Td2_r((s0 >> 8U) & m8) ^ Td3_r(s3 & m8) ^ rk[30];
|
||||
t3 = Td0[s3 >> 24U] ^ Td1_r((s2 >> 16U) & m8) ^ Td2_r((s1 >> 8U) & m8) ^ Td3_r(s0 & m8) ^ rk[31];
|
||||
s0 = Td0[t0 >> 24U] ^ Td1_r((t3 >> 16U) & m8) ^ Td2_r((t2 >> 8U) & m8) ^ Td3_r(t1 & m8) ^ rk[32];
|
||||
s1 = Td0[t1 >> 24U] ^ Td1_r((t0 >> 16U) & m8) ^ Td2_r((t3 >> 8U) & m8) ^ Td3_r(t2 & m8) ^ rk[33];
|
||||
s2 = Td0[t2 >> 24U] ^ Td1_r((t1 >> 16U) & m8) ^ Td2_r((t0 >> 8U) & m8) ^ Td3_r(t3 & m8) ^ rk[34];
|
||||
s3 = Td0[t3 >> 24U] ^ Td1_r((t2 >> 16U) & m8) ^ Td2_r((t1 >> 8U) & m8) ^ Td3_r(t0 & m8) ^ rk[35];
|
||||
t0 = Td0[s0 >> 24U] ^ Td1_r((s3 >> 16U) & m8) ^ Td2_r((s2 >> 8U) & m8) ^ Td3_r(s1 & m8) ^ rk[36];
|
||||
t1 = Td0[s1 >> 24U] ^ Td1_r((s0 >> 16U) & m8) ^ Td2_r((s3 >> 8U) & m8) ^ Td3_r(s2 & m8) ^ rk[37];
|
||||
t2 = Td0[s2 >> 24U] ^ Td1_r((s1 >> 16U) & m8) ^ Td2_r((s0 >> 8U) & m8) ^ Td3_r(s3 & m8) ^ rk[38];
|
||||
t3 = Td0[s3 >> 24U] ^ Td1_r((s2 >> 16U) & m8) ^ Td2_r((s1 >> 8U) & m8) ^ Td3_r(s0 & m8) ^ rk[39];
|
||||
s0 = Td0[t0 >> 24U] ^ Td1_r((t3 >> 16U) & m8) ^ Td2_r((t2 >> 8U) & m8) ^ Td3_r(t1 & m8) ^ rk[40];
|
||||
s1 = Td0[t1 >> 24U] ^ Td1_r((t0 >> 16U) & m8) ^ Td2_r((t3 >> 8U) & m8) ^ Td3_r(t2 & m8) ^ rk[41];
|
||||
s2 = Td0[t2 >> 24U] ^ Td1_r((t1 >> 16U) & m8) ^ Td2_r((t0 >> 8U) & m8) ^ Td3_r(t3 & m8) ^ rk[42];
|
||||
s3 = Td0[t3 >> 24U] ^ Td1_r((t2 >> 16U) & m8) ^ Td2_r((t1 >> 8U) & m8) ^ Td3_r(t0 & m8) ^ rk[43];
|
||||
t0 = Td0[s0 >> 24U] ^ Td1_r((s3 >> 16U) & m8) ^ Td2_r((s2 >> 8U) & m8) ^ Td3_r(s1 & m8) ^ rk[44];
|
||||
t1 = Td0[s1 >> 24U] ^ Td1_r((s0 >> 16U) & m8) ^ Td2_r((s3 >> 8U) & m8) ^ Td3_r(s2 & m8) ^ rk[45];
|
||||
t2 = Td0[s2 >> 24U] ^ Td1_r((s1 >> 16U) & m8) ^ Td2_r((s0 >> 8U) & m8) ^ Td3_r(s3 & m8) ^ rk[46];
|
||||
t3 = Td0[s3 >> 24U] ^ Td1_r((s2 >> 16U) & m8) ^ Td2_r((s1 >> 8U) & m8) ^ Td3_r(s0 & m8) ^ rk[47];
|
||||
s0 = Td0[t0 >> 24U] ^ Td1_r((t3 >> 16U) & m8) ^ Td2_r((t2 >> 8U) & m8) ^ Td3_r(t1 & m8) ^ rk[48];
|
||||
s1 = Td0[t1 >> 24U] ^ Td1_r((t0 >> 16U) & m8) ^ Td2_r((t3 >> 8U) & m8) ^ Td3_r(t2 & m8) ^ rk[49];
|
||||
s2 = Td0[t2 >> 24U] ^ Td1_r((t1 >> 16U) & m8) ^ Td2_r((t0 >> 8U) & m8) ^ Td3_r(t3 & m8) ^ rk[50];
|
||||
s3 = Td0[t3 >> 24U] ^ Td1_r((t2 >> 16U) & m8) ^ Td2_r((t1 >> 8U) & m8) ^ Td3_r(t0 & m8) ^ rk[51];
|
||||
t0 = Td0[s0 >> 24U] ^ Td1_r((s3 >> 16U) & m8) ^ Td2_r((s2 >> 8U) & m8) ^ Td3_r(s1 & m8) ^ rk[52];
|
||||
t1 = Td0[s1 >> 24U] ^ Td1_r((s0 >> 16U) & m8) ^ Td2_r((s3 >> 8U) & m8) ^ Td3_r(s2 & m8) ^ rk[53];
|
||||
t2 = Td0[s2 >> 24U] ^ Td1_r((s1 >> 16U) & m8) ^ Td2_r((s0 >> 8U) & m8) ^ Td3_r(s3 & m8) ^ rk[54];
|
||||
t3 = Td0[s3 >> 24U] ^ Td1_r((s2 >> 16U) & m8) ^ Td2_r((s1 >> 8U) & m8) ^ Td3_r(s0 & m8) ^ rk[55];
|
||||
s0 = (Td4[t0 >> 24U] << 24U) ^ (Td4[(t3 >> 16U) & m8] << 16U) ^ (Td4[(t2 >> 8U) & m8] << 8U) ^ (Td4[(t1) & m8]) ^ rk[56];
|
||||
s1 = (Td4[t1 >> 24U] << 24U) ^ (Td4[(t0 >> 16U) & m8] << 16U) ^ (Td4[(t3 >> 8U) & m8] << 8U) ^ (Td4[(t2) & m8]) ^ rk[57];
|
||||
s2 = (Td4[t2 >> 24U] << 24U) ^ (Td4[(t1 >> 16U) & m8] << 16U) ^ (Td4[(t0 >> 8U) & m8] << 8U) ^ (Td4[(t3) & m8]) ^ rk[58];
|
||||
s3 = (Td4[t3 >> 24U] << 24U) ^ (Td4[(t2 >> 16U) & m8] << 16U) ^ (Td4[(t1 >> 8U) & m8] << 8U) ^ (Td4[(t0) & m8]) ^ rk[59];
|
||||
|
||||
Utils::storeBigEndian< uint32_t >(out, s0);
|
||||
Utils::storeBigEndian< uint32_t >(out + 4, s1);
|
||||
Utils::storeBigEndian< uint32_t >(out + 8, s2);
|
||||
Utils::storeBigEndian< uint32_t >(out + 12, s3);
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
+597
@@ -0,0 +1,597 @@
|
||||
/*
|
||||
* Copyright (c)2013-2020 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_AES_HPP
|
||||
#define ZT_AES_HPP
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "SHA512.hpp"
|
||||
|
||||
// Uncomment to disable all hardware acceleration (usually for testing)
|
||||
//#define ZT_AES_NO_ACCEL
|
||||
|
||||
#if !defined(ZT_AES_NO_ACCEL) && defined(ZT_ARCH_X64)
|
||||
#define ZT_AES_AESNI 1
|
||||
#endif
|
||||
#if !defined(ZT_AES_NO_ACCEL) && defined(ZT_ARCH_ARM_HAS_NEON) && defined(ZT_ARCH_ARM_HAS_CRYPTO)
|
||||
#define ZT_AES_NEON 1
|
||||
#endif
|
||||
|
||||
#ifndef ZT_INLINE
|
||||
#define ZT_INLINE inline
|
||||
#endif
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* AES-256 and pals including GMAC, CTR, etc.
|
||||
*
|
||||
* This includes hardware acceleration for certain processors. The software
|
||||
* mode is fallback and is significantly slower.
|
||||
*/
|
||||
class AES
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @return True if this system has hardware AES acceleration
|
||||
*/
|
||||
static ZT_INLINE bool accelerated()
|
||||
{
|
||||
#ifdef ZT_AES_AESNI
|
||||
return Utils::CPUID.aes;
|
||||
#else
|
||||
#ifdef ZT_AES_NEON
|
||||
return Utils::ARMCAP.aes;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an un-initialized AES instance (must call init() before use)
|
||||
*/
|
||||
ZT_INLINE AES() noexcept
|
||||
{}
|
||||
|
||||
/**
|
||||
* Create an AES instance with the given key
|
||||
*
|
||||
* @param key 256-bit key
|
||||
*/
|
||||
explicit ZT_INLINE AES(const void *const key) noexcept
|
||||
{ this->init(key); }
|
||||
|
||||
ZT_INLINE ~AES()
|
||||
{ Utils::burn(&p_k, sizeof(p_k)); }
|
||||
|
||||
/**
|
||||
* Set (or re-set) this AES256 cipher's key
|
||||
*
|
||||
* @param key 256-bit / 32-byte key
|
||||
*/
|
||||
ZT_INLINE void init(const void *const key) noexcept
|
||||
{
|
||||
#ifdef ZT_AES_AESNI
|
||||
if (likely(Utils::CPUID.aes)) {
|
||||
p_init_aesni(reinterpret_cast<const uint8_t *>(key));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#ifdef ZT_AES_NEON
|
||||
if (Utils::ARMCAP.aes) {
|
||||
p_init_armneon_crypto(reinterpret_cast<const uint8_t *>(key));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
p_initSW(reinterpret_cast<const uint8_t *>(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt a single AES block
|
||||
*
|
||||
* @param in Input block
|
||||
* @param out Output block (can be same as input)
|
||||
*/
|
||||
ZT_INLINE void encrypt(const void *const in, void *const out) const noexcept
|
||||
{
|
||||
#ifdef ZT_AES_AESNI
|
||||
if (likely(Utils::CPUID.aes)) {
|
||||
p_encrypt_aesni(in, out);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#ifdef ZT_AES_NEON
|
||||
if (Utils::ARMCAP.aes) {
|
||||
p_encrypt_armneon_crypto(in, out);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
p_encryptSW(reinterpret_cast<const uint8_t *>(in), reinterpret_cast<uint8_t *>(out));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt a single AES block
|
||||
*
|
||||
* @param in Input block
|
||||
* @param out Output block (can be same as input)
|
||||
*/
|
||||
ZT_INLINE void decrypt(const void *const in, void *const out) const noexcept
|
||||
{
|
||||
#ifdef ZT_AES_AESNI
|
||||
if (likely(Utils::CPUID.aes)) {
|
||||
p_decrypt_aesni(in, out);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#ifdef ZT_AES_NEON
|
||||
if (Utils::ARMCAP.aes) {
|
||||
p_decrypt_armneon_crypto(in, out);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
p_decryptSW(reinterpret_cast<const uint8_t *>(in), reinterpret_cast<uint8_t *>(out));
|
||||
}
|
||||
|
||||
class GMACSIVEncryptor;
|
||||
class GMACSIVDecryptor;
|
||||
|
||||
/**
|
||||
* Streaming GMAC calculator
|
||||
*/
|
||||
class GMAC
|
||||
{
|
||||
friend class GMACSIVEncryptor;
|
||||
friend class GMACSIVDecryptor;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @return True if this system has hardware GMAC acceleration
|
||||
*/
|
||||
static ZT_INLINE bool accelerated()
|
||||
{
|
||||
#ifdef ZT_AES_AESNI
|
||||
return Utils::CPUID.aes;
|
||||
#else
|
||||
#ifdef ZT_AES_NEON
|
||||
return Utils::ARMCAP.pmull;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of GMAC (must be initialized with init() before use)
|
||||
*
|
||||
* @param aes Keyed AES instance to use
|
||||
*/
|
||||
ZT_INLINE GMAC(const AES &aes) : _aes(aes)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Reset and initialize for a new GMAC calculation
|
||||
*
|
||||
* @param iv 96-bit initialization vector (pad with zeroes if actual IV is shorter)
|
||||
*/
|
||||
ZT_INLINE void init(const uint8_t iv[12]) noexcept
|
||||
{
|
||||
_rp = 0;
|
||||
_len = 0;
|
||||
// We fill the least significant 32 bits in the _iv field with 1 since in GCM mode
|
||||
// this would hold the counter, but we're not doing GCM. The counter is therefore
|
||||
// always 1.
|
||||
#ifdef ZT_AES_AESNI // also implies an x64 processor
|
||||
*reinterpret_cast<uint64_t *>(_iv) = *reinterpret_cast<const uint64_t *>(iv);
|
||||
*reinterpret_cast<uint32_t *>(_iv + 8) = *reinterpret_cast<const uint64_t *>(iv + 8);
|
||||
*reinterpret_cast<uint32_t *>(_iv + 12) = 0x01000000; // 0x00000001 in big-endian byte order
|
||||
#else
|
||||
for(int i=0;i<12;++i) {
|
||||
_iv[i] = iv[i];
|
||||
}
|
||||
_iv[12] = 0;
|
||||
_iv[13] = 0;
|
||||
_iv[14] = 0;
|
||||
_iv[15] = 1;
|
||||
#endif
|
||||
_y[0] = 0;
|
||||
_y[1] = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process data through GMAC
|
||||
*
|
||||
* @param data Bytes to process
|
||||
* @param len Length of input
|
||||
*/
|
||||
void update(const void *data, unsigned int len) noexcept;
|
||||
|
||||
/**
|
||||
* Process any remaining cached bytes and generate tag
|
||||
*
|
||||
* Don't call finish() more than once or you'll get an invalid result.
|
||||
*
|
||||
* @param tag 128-bit GMAC tag (can be truncated)
|
||||
*/
|
||||
void finish(uint8_t tag[16]) noexcept;
|
||||
|
||||
private:
|
||||
#ifdef ZT_AES_AESNI
|
||||
void p_aesNIUpdate(const uint8_t *in, unsigned int len) noexcept;
|
||||
void p_aesNIFinish(uint8_t tag[16]) noexcept;
|
||||
#endif
|
||||
#ifdef ZT_AES_NEON
|
||||
void p_armUpdate(const uint8_t *in, unsigned int len) noexcept;
|
||||
void p_armFinish(uint8_t tag[16]) noexcept;
|
||||
#endif
|
||||
const AES &_aes;
|
||||
unsigned int _rp;
|
||||
unsigned int _len;
|
||||
uint8_t _r[16]; // remainder
|
||||
uint8_t _iv[16];
|
||||
uint64_t _y[2];
|
||||
};
|
||||
|
||||
/**
|
||||
* Streaming AES-CTR encrypt/decrypt
|
||||
*
|
||||
* NOTE: this doesn't support overflow of the counter in the least significant 32 bits.
|
||||
* AES-GMAC-CTR doesn't need this, so we don't support it as an optimization.
|
||||
*/
|
||||
class CTR
|
||||
{
|
||||
friend class GMACSIVEncryptor;
|
||||
friend class GMACSIVDecryptor;
|
||||
|
||||
public:
|
||||
ZT_INLINE CTR(const AES &aes) noexcept: _aes(aes)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Initialize this CTR instance to encrypt a new stream
|
||||
*
|
||||
* @param iv Unique initialization vector and initial 32-bit counter (least significant 32 bits, big-endian)
|
||||
* @param output Buffer to which to store output (MUST be large enough for total bytes processed!)
|
||||
*/
|
||||
ZT_INLINE void init(const uint8_t iv[16], void *const output) noexcept
|
||||
{
|
||||
Utils::copy< 16 >(_ctr, iv);
|
||||
_out = reinterpret_cast<uint8_t *>(output);
|
||||
_len = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize this CTR instance to encrypt a new stream
|
||||
*
|
||||
* @param iv Unique initialization vector
|
||||
* @param ic Initial counter (must be in big-endian byte order!)
|
||||
* @param output Buffer to which to store output (MUST be large enough for total bytes processed!)
|
||||
*/
|
||||
ZT_INLINE void init(const uint8_t iv[12], const uint32_t ic, void *const output) noexcept
|
||||
{
|
||||
Utils::copy< 12 >(_ctr, iv);
|
||||
reinterpret_cast<uint32_t *>(_ctr)[3] = ic;
|
||||
_out = reinterpret_cast<uint8_t *>(output);
|
||||
_len = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt or decrypt data, writing result to the output provided to init()
|
||||
*
|
||||
* @param input Input data
|
||||
* @param len Length of input
|
||||
*/
|
||||
void crypt(const void *input, unsigned int len) noexcept;
|
||||
|
||||
/**
|
||||
* Finish any remaining bytes if total bytes processed wasn't a multiple of 16
|
||||
*
|
||||
* Don't call more than once for a given stream or data may be corrupted.
|
||||
*/
|
||||
void finish() noexcept;
|
||||
|
||||
private:
|
||||
#ifdef ZT_AES_AESNI
|
||||
void p_aesNICrypt(const uint8_t *in, uint8_t *out, unsigned int len) noexcept;
|
||||
#endif
|
||||
#ifdef ZT_AES_NEON
|
||||
void p_armCrypt(const uint8_t *in, uint8_t *out, unsigned int len) noexcept;
|
||||
#endif
|
||||
const AES &_aes;
|
||||
uint64_t _ctr[2];
|
||||
uint8_t *_out;
|
||||
unsigned int _len;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encryptor for AES-GMAC-SIV.
|
||||
*
|
||||
* Encryption requires two passes. The first pass starts after init
|
||||
* with aad (if any) followed by update1() and finish1(). Then the
|
||||
* update2() and finish2() methods must be used over the same data
|
||||
* (but NOT AAD) again.
|
||||
*
|
||||
* This supports encryption of a maximum of 2^31 bytes of data per
|
||||
* call to init().
|
||||
*/
|
||||
class GMACSIVEncryptor
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Create a new AES-GMAC-SIV encryptor keyed with the provided AES instances
|
||||
*
|
||||
* @param k0 First of two AES instances keyed with K0
|
||||
* @param k1 Second of two AES instances keyed with K1
|
||||
*/
|
||||
ZT_INLINE GMACSIVEncryptor(const AES &k0, const AES &k1) noexcept :
|
||||
_gmac(k0),
|
||||
_ctr(k1)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Initialize AES-GMAC-SIV
|
||||
*
|
||||
* @param iv IV in network byte order (byte order in which it will appear on the wire)
|
||||
* @param output Pointer to buffer to receive ciphertext, must be large enough for all to-be-processed data!
|
||||
*/
|
||||
ZT_INLINE void init(const uint64_t iv, void *const output) noexcept
|
||||
{
|
||||
// Output buffer to receive the result of AES-CTR encryption.
|
||||
_output = output;
|
||||
|
||||
// Initialize GMAC with 64-bit IV (and remaining 32 bits padded to zero).
|
||||
_tag[0] = iv;
|
||||
_tag[1] = 0;
|
||||
_gmac.init(reinterpret_cast<const uint8_t *>(_tag));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process AAD (additional authenticated data) that is not being encrypted.
|
||||
*
|
||||
* If such data exists this must be called before update1() and finish1().
|
||||
*
|
||||
* Note: current code only supports one single chunk of AAD. Don't call this
|
||||
* multiple times per message.
|
||||
*
|
||||
* @param aad Additional authenticated data
|
||||
* @param len Length of AAD in bytes
|
||||
*/
|
||||
ZT_INLINE void aad(const void *const aad, unsigned int len) noexcept
|
||||
{
|
||||
// Feed ADD into GMAC first
|
||||
_gmac.update(aad, len);
|
||||
|
||||
// End of AAD is padded to a multiple of 16 bytes to ensure unique encoding.
|
||||
len &= 0xfU;
|
||||
if (len != 0) {
|
||||
_gmac.update(Utils::ZERO256, 16 - len);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* First pass plaintext input function
|
||||
*
|
||||
* @param input Plaintext chunk
|
||||
* @param len Length of plaintext chunk
|
||||
*/
|
||||
ZT_INLINE void update1(const void *const input, const unsigned int len) noexcept
|
||||
{ _gmac.update(input, len); }
|
||||
|
||||
/**
|
||||
* Finish first pass, compute CTR IV, initialize second pass.
|
||||
*/
|
||||
ZT_INLINE void finish1() noexcept
|
||||
{
|
||||
// Compute 128-bit GMAC tag.
|
||||
uint64_t tmp[2];
|
||||
_gmac.finish(reinterpret_cast<uint8_t *>(tmp));
|
||||
|
||||
// Shorten to 64 bits, concatenate with message IV, and encrypt with AES to
|
||||
// yield the CTR IV and opaque IV/MAC blob. In ZeroTier's use of GMAC-SIV
|
||||
// this get split into the packet ID (64 bits) and the MAC (64 bits) in each
|
||||
// packet and then recombined on receipt for legacy reasons (but with no
|
||||
// cryptographic or performance impact).
|
||||
_tag[1] = tmp[0] ^ tmp[1];
|
||||
_ctr._aes.encrypt(_tag, _tag);
|
||||
|
||||
// Initialize CTR with 96-bit CTR nonce and 32-bit counter. The counter
|
||||
// incorporates 31 more bits of entropy which should raise our security margin
|
||||
// a bit, but this is not included in the worst case analysis of GMAC-SIV.
|
||||
// The most significant bit of the counter is masked to zero to allow up to
|
||||
// 2^31 bytes to be encrypted before the counter loops. Some CTR implementations
|
||||
// increment the whole big-endian 128-bit integer in which case this could be
|
||||
// used for more than 2^31 bytes, but ours does not for performance reasons
|
||||
// and so 2^31 should be considered the input limit.
|
||||
tmp[0] = _tag[0];
|
||||
tmp[1] = _tag[1] & ZT_CONST_TO_BE_UINT64(0xffffffff7fffffffULL);
|
||||
_ctr.init(reinterpret_cast<const uint8_t *>(tmp), _output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Second pass plaintext input function
|
||||
*
|
||||
* The same plaintext must be fed in the second time in the same order,
|
||||
* though chunk boundaries do not have to be the same.
|
||||
*
|
||||
* @param input Plaintext chunk
|
||||
* @param len Length of plaintext chunk
|
||||
*/
|
||||
ZT_INLINE void update2(const void *const input, const unsigned int len) noexcept
|
||||
{ _ctr.crypt(input, len); }
|
||||
|
||||
/**
|
||||
* Finish second pass and return a pointer to the opaque 128-bit IV+MAC block
|
||||
*
|
||||
* The returned pointer remains valid as long as this object exists and init()
|
||||
* is not called again.
|
||||
*
|
||||
* @return Pointer to 128-bit opaque IV+MAC (packed into two 64-bit integers)
|
||||
*/
|
||||
ZT_INLINE const uint64_t *finish2()
|
||||
{
|
||||
_ctr.finish();
|
||||
return _tag;
|
||||
}
|
||||
|
||||
private:
|
||||
void *_output;
|
||||
uint64_t _tag[2];
|
||||
AES::GMAC _gmac;
|
||||
AES::CTR _ctr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decryptor for AES-GMAC-SIV.
|
||||
*
|
||||
* GMAC-SIV decryption is single-pass. AAD (if any) must be processed first.
|
||||
*/
|
||||
class GMACSIVDecryptor
|
||||
{
|
||||
public:
|
||||
ZT_INLINE GMACSIVDecryptor(const AES &k0, const AES &k1) noexcept:
|
||||
_ctr(k1),
|
||||
_gmac(k0)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Initialize decryptor for a new message
|
||||
*
|
||||
* @param tag 128-bit combined IV/MAC originally created by GMAC-SIV encryption
|
||||
* @param output Buffer in which to write output plaintext (must be large enough!)
|
||||
*/
|
||||
ZT_INLINE void init(const uint64_t tag[2], void *const output) noexcept
|
||||
{
|
||||
uint64_t tmp[2];
|
||||
tmp[0] = tag[0];
|
||||
tmp[1] = tag[1] & ZT_CONST_TO_BE_UINT64(0xffffffff7fffffffULL);
|
||||
_ctr.init(reinterpret_cast<const uint8_t *>(tmp), output);
|
||||
|
||||
_ctr._aes.decrypt(tag, _ivMac);
|
||||
|
||||
tmp[0] = _ivMac[0];
|
||||
tmp[1] = 0;
|
||||
_gmac.init(reinterpret_cast<const uint8_t *>(tmp));
|
||||
|
||||
_output = output;
|
||||
_decryptedLen = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process AAD (additional authenticated data) that wasn't encrypted
|
||||
*
|
||||
* @param aad Additional authenticated data
|
||||
* @param len Length of AAD in bytes
|
||||
*/
|
||||
ZT_INLINE void aad(const void *const aad, unsigned int len) noexcept
|
||||
{
|
||||
_gmac.update(aad, len);
|
||||
len &= 0xfU;
|
||||
if (len != 0) {
|
||||
_gmac.update(Utils::ZERO256, 16 - len);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Feed ciphertext into the decryptor
|
||||
*
|
||||
* Unlike encryption, GMAC-SIV decryption requires only one pass.
|
||||
*
|
||||
* @param input Input ciphertext
|
||||
* @param len Length of ciphertext
|
||||
*/
|
||||
ZT_INLINE void update(const void *const input, const unsigned int len) noexcept
|
||||
{
|
||||
_ctr.crypt(input, len);
|
||||
_decryptedLen += len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush decryption, compute MAC, and verify
|
||||
*
|
||||
* @return True if resulting plaintext (and AAD) pass message authentication check
|
||||
*/
|
||||
ZT_INLINE bool finish() noexcept
|
||||
{
|
||||
_ctr.finish();
|
||||
|
||||
uint64_t gmacTag[2];
|
||||
_gmac.update(_output, _decryptedLen);
|
||||
_gmac.finish(reinterpret_cast<uint8_t *>(gmacTag));
|
||||
return (gmacTag[0] ^ gmacTag[1]) == _ivMac[1];
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t _ivMac[2];
|
||||
AES::CTR _ctr;
|
||||
AES::GMAC _gmac;
|
||||
void *_output;
|
||||
unsigned int _decryptedLen;
|
||||
};
|
||||
|
||||
private:
|
||||
static const uint32_t Te0[256];
|
||||
static const uint32_t Te4[256];
|
||||
static const uint32_t Td0[256];
|
||||
static const uint8_t Td4[256];
|
||||
static const uint32_t rcon[15];
|
||||
|
||||
void p_initSW(const uint8_t *key) noexcept;
|
||||
void p_encryptSW(const uint8_t *in, uint8_t *out) const noexcept;
|
||||
void p_decryptSW(const uint8_t *in, uint8_t *out) const noexcept;
|
||||
|
||||
union
|
||||
{
|
||||
#ifdef ZT_AES_AESNI
|
||||
struct
|
||||
{
|
||||
__m128i k[28];
|
||||
__m128i h[4]; // h, hh, hhh, hhhh
|
||||
__m128i h2[4]; // _mm_xor_si128(_mm_shuffle_epi32(h, 78), h), etc.
|
||||
} ni;
|
||||
#endif
|
||||
|
||||
#ifdef ZT_AES_NEON
|
||||
struct
|
||||
{
|
||||
uint64_t hsw[2]; // in case it has AES but not PMULL, not sure if that ever happens
|
||||
uint8x16_t ek[15];
|
||||
uint8x16_t dk[15];
|
||||
uint8x16_t h;
|
||||
} neon;
|
||||
#endif
|
||||
|
||||
struct
|
||||
{
|
||||
uint64_t h[2];
|
||||
uint32_t ek[60];
|
||||
uint32_t dk[60];
|
||||
} sw;
|
||||
} p_k;
|
||||
|
||||
#ifdef ZT_AES_AESNI
|
||||
void p_init_aesni(const uint8_t *key) noexcept;
|
||||
void p_encrypt_aesni(const void *in, void *out) const noexcept;
|
||||
void p_decrypt_aesni(const void *in, void *out) const noexcept;
|
||||
#endif
|
||||
|
||||
#ifdef ZT_AES_NEON
|
||||
void p_init_armneon_crypto(const uint8_t *key) noexcept;
|
||||
void p_encrypt_armneon_crypto(const void *in, void *out) const noexcept;
|
||||
void p_decrypt_armneon_crypto(const void *in, void *out) const noexcept;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,677 @@
|
||||
/*
|
||||
* Copyright (c)2013-2020 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "AES.hpp"
|
||||
|
||||
#ifdef ZT_AES_AESNI
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
#endif
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
namespace {
|
||||
|
||||
const __m128i s_sseSwapBytes = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
|
||||
|
||||
#ifdef __GNUC__
|
||||
__attribute__((__target__("ssse3,sse4,sse4.1,sse4.2,pclmul")))
|
||||
#endif
|
||||
__m128i p_gmacPCLMUL128(const __m128i h, __m128i y) noexcept
|
||||
{
|
||||
y = _mm_shuffle_epi8(y, s_sseSwapBytes);
|
||||
__m128i t1 = _mm_clmulepi64_si128(h, y, 0x00);
|
||||
__m128i t2 = _mm_clmulepi64_si128(h, y, 0x01);
|
||||
__m128i t3 = _mm_clmulepi64_si128(h, y, 0x10);
|
||||
__m128i t4 = _mm_clmulepi64_si128(h, y, 0x11);
|
||||
t2 = _mm_xor_si128(t2, t3);
|
||||
t3 = _mm_slli_si128(t2, 8);
|
||||
t2 = _mm_srli_si128(t2, 8);
|
||||
t1 = _mm_xor_si128(t1, t3);
|
||||
t4 = _mm_xor_si128(t4, t2);
|
||||
__m128i t5 = _mm_srli_epi32(t1, 31);
|
||||
t1 = _mm_or_si128(_mm_slli_epi32(t1, 1), _mm_slli_si128(t5, 4));
|
||||
t4 = _mm_or_si128(_mm_or_si128(_mm_slli_epi32(t4, 1), _mm_slli_si128(_mm_srli_epi32(t4, 31), 4)), _mm_srli_si128(t5, 12));
|
||||
t5 = _mm_xor_si128(_mm_xor_si128(_mm_slli_epi32(t1, 31), _mm_slli_epi32(t1, 30)), _mm_slli_epi32(t1, 25));
|
||||
t1 = _mm_xor_si128(t1, _mm_slli_si128(t5, 12));
|
||||
t4 = _mm_xor_si128(_mm_xor_si128(_mm_xor_si128(_mm_xor_si128(_mm_xor_si128(t4, _mm_srli_si128(t5, 4)), t1), _mm_srli_epi32(t1, 2)), _mm_srli_epi32(t1, 7)), _mm_srli_epi32(t1, 1));
|
||||
return _mm_shuffle_epi8(t4, s_sseSwapBytes);
|
||||
}
|
||||
|
||||
/* Disable VAES stuff on compilers too old to compile these intrinsics,
|
||||
* and MinGW64 also seems not to support them so disable on Windows.
|
||||
* The performance gain can be significant but regular SSE is already so
|
||||
* fast it's highly unlikely to be a rate limiting factor except on massive
|
||||
* servers and network infrastructure stuff. */
|
||||
#if !defined(__WINDOWS__) && ((__GNUC__ >= 8) || (__clang_major__ >= 7))
|
||||
|
||||
#define ZT_AES_VAES512 1
|
||||
|
||||
#ifdef __GNUC__
|
||||
__attribute__((__target__("sse4,aes,avx,avx2,vaes,avx512f,avx512bw")))
|
||||
#endif
|
||||
void p_aesCtrInnerVAES512(unsigned int &len, const uint64_t c0, uint64_t &c1, const uint8_t *&in, uint8_t *&out, const __m128i *const k) noexcept
|
||||
{
|
||||
const __m512i kk0 = _mm512_broadcast_i32x4(k[0]);
|
||||
const __m512i kk1 = _mm512_broadcast_i32x4(k[1]);
|
||||
const __m512i kk2 = _mm512_broadcast_i32x4(k[2]);
|
||||
const __m512i kk3 = _mm512_broadcast_i32x4(k[3]);
|
||||
const __m512i kk4 = _mm512_broadcast_i32x4(k[4]);
|
||||
const __m512i kk5 = _mm512_broadcast_i32x4(k[5]);
|
||||
const __m512i kk6 = _mm512_broadcast_i32x4(k[6]);
|
||||
const __m512i kk7 = _mm512_broadcast_i32x4(k[7]);
|
||||
const __m512i kk8 = _mm512_broadcast_i32x4(k[8]);
|
||||
const __m512i kk9 = _mm512_broadcast_i32x4(k[9]);
|
||||
const __m512i kk10 = _mm512_broadcast_i32x4(k[10]);
|
||||
const __m512i kk11 = _mm512_broadcast_i32x4(k[11]);
|
||||
const __m512i kk12 = _mm512_broadcast_i32x4(k[12]);
|
||||
const __m512i kk13 = _mm512_broadcast_i32x4(k[13]);
|
||||
const __m512i kk14 = _mm512_broadcast_i32x4(k[14]);
|
||||
do {
|
||||
__m512i p0 = _mm512_loadu_si512(reinterpret_cast<const __m512i *>(in));
|
||||
__m512i d0 = _mm512_set_epi64(
|
||||
(long long)Utils::hton(c1 + 3ULL), (long long)c0,
|
||||
(long long)Utils::hton(c1 + 2ULL), (long long)c0,
|
||||
(long long)Utils::hton(c1 + 1ULL), (long long)c0,
|
||||
(long long)Utils::hton(c1), (long long)c0);
|
||||
c1 += 4;
|
||||
in += 64;
|
||||
len -= 64;
|
||||
d0 = _mm512_xor_si512(d0, kk0);
|
||||
d0 = _mm512_aesenc_epi128(d0, kk1);
|
||||
d0 = _mm512_aesenc_epi128(d0, kk2);
|
||||
d0 = _mm512_aesenc_epi128(d0, kk3);
|
||||
d0 = _mm512_aesenc_epi128(d0, kk4);
|
||||
d0 = _mm512_aesenc_epi128(d0, kk5);
|
||||
d0 = _mm512_aesenc_epi128(d0, kk6);
|
||||
d0 = _mm512_aesenc_epi128(d0, kk7);
|
||||
d0 = _mm512_aesenc_epi128(d0, kk8);
|
||||
d0 = _mm512_aesenc_epi128(d0, kk9);
|
||||
d0 = _mm512_aesenc_epi128(d0, kk10);
|
||||
d0 = _mm512_aesenc_epi128(d0, kk11);
|
||||
d0 = _mm512_aesenc_epi128(d0, kk12);
|
||||
d0 = _mm512_aesenc_epi128(d0, kk13);
|
||||
d0 = _mm512_aesenclast_epi128(d0, kk14);
|
||||
_mm512_storeu_si512(reinterpret_cast<__m512i *>(out), _mm512_xor_si512(p0, d0));
|
||||
out += 64;
|
||||
} while (likely(len >= 64));
|
||||
}
|
||||
|
||||
#define ZT_AES_VAES256 1
|
||||
|
||||
#ifdef __GNUC__
|
||||
__attribute__((__target__("sse4,aes,avx,avx2,vaes")))
|
||||
#endif
|
||||
void p_aesCtrInnerVAES256(unsigned int &len, const uint64_t c0, uint64_t &c1, const uint8_t *&in, uint8_t *&out, const __m128i *const k) noexcept
|
||||
{
|
||||
const __m256i kk0 = _mm256_broadcastsi128_si256(k[0]);
|
||||
const __m256i kk1 = _mm256_broadcastsi128_si256(k[1]);
|
||||
const __m256i kk2 = _mm256_broadcastsi128_si256(k[2]);
|
||||
const __m256i kk3 = _mm256_broadcastsi128_si256(k[3]);
|
||||
const __m256i kk4 = _mm256_broadcastsi128_si256(k[4]);
|
||||
const __m256i kk5 = _mm256_broadcastsi128_si256(k[5]);
|
||||
const __m256i kk6 = _mm256_broadcastsi128_si256(k[6]);
|
||||
const __m256i kk7 = _mm256_broadcastsi128_si256(k[7]);
|
||||
const __m256i kk8 = _mm256_broadcastsi128_si256(k[8]);
|
||||
const __m256i kk9 = _mm256_broadcastsi128_si256(k[9]);
|
||||
const __m256i kk10 = _mm256_broadcastsi128_si256(k[10]);
|
||||
const __m256i kk11 = _mm256_broadcastsi128_si256(k[11]);
|
||||
const __m256i kk12 = _mm256_broadcastsi128_si256(k[12]);
|
||||
const __m256i kk13 = _mm256_broadcastsi128_si256(k[13]);
|
||||
const __m256i kk14 = _mm256_broadcastsi128_si256(k[14]);
|
||||
do {
|
||||
__m256i p0 = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(in));
|
||||
__m256i p1 = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(in + 32));
|
||||
__m256i d0 = _mm256_set_epi64x(
|
||||
(long long)Utils::hton(c1 + 1ULL), (long long)c0,
|
||||
(long long)Utils::hton(c1), (long long)c0);
|
||||
__m256i d1 = _mm256_set_epi64x(
|
||||
(long long)Utils::hton(c1 + 3ULL), (long long)c0,
|
||||
(long long)Utils::hton(c1 + 2ULL), (long long)c0);
|
||||
c1 += 4;
|
||||
in += 64;
|
||||
len -= 64;
|
||||
d0 = _mm256_xor_si256(d0, kk0);
|
||||
d1 = _mm256_xor_si256(d1, kk0);
|
||||
d0 = _mm256_aesenc_epi128(d0, kk1);
|
||||
d1 = _mm256_aesenc_epi128(d1, kk1);
|
||||
d0 = _mm256_aesenc_epi128(d0, kk2);
|
||||
d1 = _mm256_aesenc_epi128(d1, kk2);
|
||||
d0 = _mm256_aesenc_epi128(d0, kk3);
|
||||
d1 = _mm256_aesenc_epi128(d1, kk3);
|
||||
d0 = _mm256_aesenc_epi128(d0, kk4);
|
||||
d1 = _mm256_aesenc_epi128(d1, kk4);
|
||||
d0 = _mm256_aesenc_epi128(d0, kk5);
|
||||
d1 = _mm256_aesenc_epi128(d1, kk5);
|
||||
d0 = _mm256_aesenc_epi128(d0, kk6);
|
||||
d1 = _mm256_aesenc_epi128(d1, kk6);
|
||||
d0 = _mm256_aesenc_epi128(d0, kk7);
|
||||
d1 = _mm256_aesenc_epi128(d1, kk7);
|
||||
d0 = _mm256_aesenc_epi128(d0, kk8);
|
||||
d1 = _mm256_aesenc_epi128(d1, kk8);
|
||||
d0 = _mm256_aesenc_epi128(d0, kk9);
|
||||
d1 = _mm256_aesenc_epi128(d1, kk9);
|
||||
d0 = _mm256_aesenc_epi128(d0, kk10);
|
||||
d1 = _mm256_aesenc_epi128(d1, kk10);
|
||||
d0 = _mm256_aesenc_epi128(d0, kk11);
|
||||
d1 = _mm256_aesenc_epi128(d1, kk11);
|
||||
d0 = _mm256_aesenc_epi128(d0, kk12);
|
||||
d1 = _mm256_aesenc_epi128(d1, kk12);
|
||||
d0 = _mm256_aesenc_epi128(d0, kk13);
|
||||
d1 = _mm256_aesenc_epi128(d1, kk13);
|
||||
d0 = _mm256_aesenclast_epi128(d0, kk14);
|
||||
d1 = _mm256_aesenclast_epi128(d1, kk14);
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i *>(out), _mm256_xor_si256(d0, p0));
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i *>(out + 32), _mm256_xor_si256(d1, p1));
|
||||
out += 64;
|
||||
} while (likely(len >= 64));
|
||||
}
|
||||
|
||||
#endif // does compiler support AVX2 and AVX512 AES intrinsics?
|
||||
|
||||
#ifdef __GNUC__
|
||||
__attribute__((__target__("ssse3,sse4,sse4.1,sse4.2,aes,pclmul")))
|
||||
#endif
|
||||
__m128i p_init256_1_aesni(__m128i a, __m128i b) noexcept
|
||||
{
|
||||
__m128i x, y;
|
||||
b = _mm_shuffle_epi32(b, 0xff);
|
||||
y = _mm_slli_si128(a, 0x04);
|
||||
x = _mm_xor_si128(a, y);
|
||||
y = _mm_slli_si128(y, 0x04);
|
||||
x = _mm_xor_si128(x, y);
|
||||
y = _mm_slli_si128(y, 0x04);
|
||||
x = _mm_xor_si128(x, y);
|
||||
x = _mm_xor_si128(x, b);
|
||||
return x;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
__attribute__((__target__("ssse3,sse4,sse4.1,sse4.2,aes,pclmul")))
|
||||
#endif
|
||||
__m128i p_init256_2_aesni(__m128i a, __m128i b) noexcept
|
||||
{
|
||||
__m128i x, y, z;
|
||||
y = _mm_aeskeygenassist_si128(a, 0x00);
|
||||
z = _mm_shuffle_epi32(y, 0xaa);
|
||||
y = _mm_slli_si128(b, 0x04);
|
||||
x = _mm_xor_si128(b, y);
|
||||
y = _mm_slli_si128(y, 0x04);
|
||||
x = _mm_xor_si128(x, y);
|
||||
y = _mm_slli_si128(y, 0x04);
|
||||
x = _mm_xor_si128(x, y);
|
||||
x = _mm_xor_si128(x, z);
|
||||
return x;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
#ifdef __GNUC__
|
||||
__attribute__((__target__("ssse3,sse4,sse4.1,sse4.2,pclmul")))
|
||||
#endif
|
||||
void AES::GMAC::p_aesNIUpdate(const uint8_t *in, unsigned int len) noexcept
|
||||
{
|
||||
__m128i y = _mm_loadu_si128(reinterpret_cast<const __m128i *>(_y));
|
||||
|
||||
// Handle anything left over from a previous run that wasn't a multiple of 16 bytes.
|
||||
if (_rp) {
|
||||
for (;;) {
|
||||
if (!len) {
|
||||
return;
|
||||
}
|
||||
--len;
|
||||
_r[_rp++] = *(in++);
|
||||
if (_rp == 16) {
|
||||
y = p_gmacPCLMUL128(_aes.p_k.ni.h[0], _mm_xor_si128(y, _mm_loadu_si128(reinterpret_cast<__m128i *>(_r))));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (likely(len >= 64)) {
|
||||
const __m128i sb = s_sseSwapBytes;
|
||||
const __m128i h = _aes.p_k.ni.h[0];
|
||||
const __m128i hh = _aes.p_k.ni.h[1];
|
||||
const __m128i hhh = _aes.p_k.ni.h[2];
|
||||
const __m128i hhhh = _aes.p_k.ni.h[3];
|
||||
const __m128i h2 = _aes.p_k.ni.h2[0];
|
||||
const __m128i hh2 = _aes.p_k.ni.h2[1];
|
||||
const __m128i hhh2 = _aes.p_k.ni.h2[2];
|
||||
const __m128i hhhh2 = _aes.p_k.ni.h2[3];
|
||||
const uint8_t *const end64 = in + (len & ~((unsigned int)63));
|
||||
len &= 63U;
|
||||
do {
|
||||
__m128i d1 = _mm_shuffle_epi8(_mm_xor_si128(y, _mm_loadu_si128(reinterpret_cast<const __m128i *>(in))), sb);
|
||||
__m128i d2 = _mm_shuffle_epi8(_mm_loadu_si128(reinterpret_cast<const __m128i *>(in + 16)), sb);
|
||||
__m128i d3 = _mm_shuffle_epi8(_mm_loadu_si128(reinterpret_cast<const __m128i *>(in + 32)), sb);
|
||||
__m128i d4 = _mm_shuffle_epi8(_mm_loadu_si128(reinterpret_cast<const __m128i *>(in + 48)), sb);
|
||||
in += 64;
|
||||
__m128i a = _mm_xor_si128(_mm_xor_si128(_mm_clmulepi64_si128(hhhh, d1, 0x00), _mm_clmulepi64_si128(hhh, d2, 0x00)), _mm_xor_si128(_mm_clmulepi64_si128(hh, d3, 0x00), _mm_clmulepi64_si128(h, d4, 0x00)));
|
||||
__m128i b = _mm_xor_si128(_mm_xor_si128(_mm_clmulepi64_si128(hhhh, d1, 0x11), _mm_clmulepi64_si128(hhh, d2, 0x11)), _mm_xor_si128(_mm_clmulepi64_si128(hh, d3, 0x11), _mm_clmulepi64_si128(h, d4, 0x11)));
|
||||
__m128i c = _mm_xor_si128(_mm_xor_si128(_mm_xor_si128(_mm_clmulepi64_si128(hhhh2, _mm_xor_si128(_mm_shuffle_epi32(d1, 78), d1), 0x00), _mm_clmulepi64_si128(hhh2, _mm_xor_si128(_mm_shuffle_epi32(d2, 78), d2), 0x00)), _mm_xor_si128(_mm_clmulepi64_si128(hh2, _mm_xor_si128(_mm_shuffle_epi32(d3, 78), d3), 0x00), _mm_clmulepi64_si128(h2, _mm_xor_si128(_mm_shuffle_epi32(d4, 78), d4), 0x00))), _mm_xor_si128(a, b));
|
||||
a = _mm_xor_si128(_mm_slli_si128(c, 8), a);
|
||||
b = _mm_xor_si128(_mm_srli_si128(c, 8), b);
|
||||
c = _mm_srli_epi32(a, 31);
|
||||
a = _mm_or_si128(_mm_slli_epi32(a, 1), _mm_slli_si128(c, 4));
|
||||
b = _mm_or_si128(_mm_or_si128(_mm_slli_epi32(b, 1), _mm_slli_si128(_mm_srli_epi32(b, 31), 4)), _mm_srli_si128(c, 12));
|
||||
c = _mm_xor_si128(_mm_slli_epi32(a, 31), _mm_xor_si128(_mm_slli_epi32(a, 30), _mm_slli_epi32(a, 25)));
|
||||
a = _mm_xor_si128(a, _mm_slli_si128(c, 12));
|
||||
b = _mm_xor_si128(b, _mm_xor_si128(a, _mm_xor_si128(_mm_xor_si128(_mm_srli_epi32(a, 1), _mm_srli_si128(c, 4)), _mm_xor_si128(_mm_srli_epi32(a, 2), _mm_srli_epi32(a, 7)))));
|
||||
y = _mm_shuffle_epi8(b, sb);
|
||||
} while (likely(in != end64));
|
||||
}
|
||||
|
||||
while (len >= 16) {
|
||||
y = p_gmacPCLMUL128(_aes.p_k.ni.h[0], _mm_xor_si128(y, _mm_loadu_si128(reinterpret_cast<const __m128i *>(in))));
|
||||
in += 16;
|
||||
len -= 16;
|
||||
}
|
||||
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(_y), y);
|
||||
|
||||
// Any overflow is cached for a later run or finish().
|
||||
for (unsigned int i = 0; i < len; ++i) {
|
||||
_r[i] = in[i];
|
||||
}
|
||||
_rp = len; // len is always less than 16 here
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
__attribute__((__target__("ssse3,sse4,sse4.1,sse4.2,pclmul,aes")))
|
||||
#endif
|
||||
void AES::GMAC::p_aesNIFinish(uint8_t tag[16]) noexcept
|
||||
{
|
||||
__m128i y = _mm_loadu_si128(reinterpret_cast<const __m128i *>(_y));
|
||||
|
||||
// Handle any remaining bytes, padding the last block with zeroes.
|
||||
if (_rp) {
|
||||
while (_rp < 16) {
|
||||
_r[_rp++] = 0;
|
||||
}
|
||||
y = p_gmacPCLMUL128(_aes.p_k.ni.h[0], _mm_xor_si128(y, _mm_loadu_si128(reinterpret_cast<__m128i *>(_r))));
|
||||
}
|
||||
|
||||
// Interleave encryption of IV with the final GHASH of y XOR (length * 8).
|
||||
// Then XOR these together to get the final tag.
|
||||
const __m128i *const k = _aes.p_k.ni.k;
|
||||
const __m128i h = _aes.p_k.ni.h[0];
|
||||
y = _mm_xor_si128(y, _mm_set_epi64x(0LL, (long long)Utils::hton((uint64_t)_len << 3U)));
|
||||
y = _mm_shuffle_epi8(y, s_sseSwapBytes);
|
||||
__m128i encIV = _mm_xor_si128(_mm_loadu_si128(reinterpret_cast<const __m128i *>(_iv)), k[0]);
|
||||
__m128i t1 = _mm_clmulepi64_si128(h, y, 0x00);
|
||||
__m128i t2 = _mm_clmulepi64_si128(h, y, 0x01);
|
||||
__m128i t3 = _mm_clmulepi64_si128(h, y, 0x10);
|
||||
__m128i t4 = _mm_clmulepi64_si128(h, y, 0x11);
|
||||
encIV = _mm_aesenc_si128(encIV, k[1]);
|
||||
t2 = _mm_xor_si128(t2, t3);
|
||||
t3 = _mm_slli_si128(t2, 8);
|
||||
encIV = _mm_aesenc_si128(encIV, k[2]);
|
||||
t2 = _mm_srli_si128(t2, 8);
|
||||
t1 = _mm_xor_si128(t1, t3);
|
||||
encIV = _mm_aesenc_si128(encIV, k[3]);
|
||||
t4 = _mm_xor_si128(t4, t2);
|
||||
__m128i t5 = _mm_srli_epi32(t1, 31);
|
||||
t1 = _mm_slli_epi32(t1, 1);
|
||||
__m128i t6 = _mm_srli_epi32(t4, 31);
|
||||
encIV = _mm_aesenc_si128(encIV, k[4]);
|
||||
t4 = _mm_slli_epi32(t4, 1);
|
||||
t3 = _mm_srli_si128(t5, 12);
|
||||
encIV = _mm_aesenc_si128(encIV, k[5]);
|
||||
t6 = _mm_slli_si128(t6, 4);
|
||||
t5 = _mm_slli_si128(t5, 4);
|
||||
encIV = _mm_aesenc_si128(encIV, k[6]);
|
||||
t1 = _mm_or_si128(t1, t5);
|
||||
t4 = _mm_or_si128(t4, t6);
|
||||
encIV = _mm_aesenc_si128(encIV, k[7]);
|
||||
t4 = _mm_or_si128(t4, t3);
|
||||
t5 = _mm_slli_epi32(t1, 31);
|
||||
encIV = _mm_aesenc_si128(encIV, k[8]);
|
||||
t6 = _mm_slli_epi32(t1, 30);
|
||||
t3 = _mm_slli_epi32(t1, 25);
|
||||
encIV = _mm_aesenc_si128(encIV, k[9]);
|
||||
t5 = _mm_xor_si128(t5, t6);
|
||||
t5 = _mm_xor_si128(t5, t3);
|
||||
encIV = _mm_aesenc_si128(encIV, k[10]);
|
||||
t6 = _mm_srli_si128(t5, 4);
|
||||
t4 = _mm_xor_si128(t4, t6);
|
||||
encIV = _mm_aesenc_si128(encIV, k[11]);
|
||||
t5 = _mm_slli_si128(t5, 12);
|
||||
t1 = _mm_xor_si128(t1, t5);
|
||||
t4 = _mm_xor_si128(t4, t1);
|
||||
t5 = _mm_srli_epi32(t1, 1);
|
||||
encIV = _mm_aesenc_si128(encIV, k[12]);
|
||||
t2 = _mm_srli_epi32(t1, 2);
|
||||
t3 = _mm_srli_epi32(t1, 7);
|
||||
encIV = _mm_aesenc_si128(encIV, k[13]);
|
||||
t4 = _mm_xor_si128(t4, t2);
|
||||
t4 = _mm_xor_si128(t4, t3);
|
||||
encIV = _mm_aesenclast_si128(encIV, k[14]);
|
||||
t4 = _mm_xor_si128(t4, t5);
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(tag), _mm_xor_si128(_mm_shuffle_epi8(t4, s_sseSwapBytes), encIV));
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
__attribute__((__target__("ssse3,sse4,sse4.1,sse4.2,aes")))
|
||||
#endif
|
||||
void AES::CTR::p_aesNICrypt(const uint8_t *in, uint8_t *out, unsigned int len) noexcept
|
||||
{
|
||||
const __m128i dd = _mm_set_epi64x(0, (long long)_ctr[0]);
|
||||
uint64_t c1 = Utils::ntoh(_ctr[1]);
|
||||
|
||||
const __m128i *const k = _aes.p_k.ni.k;
|
||||
const __m128i k0 = k[0];
|
||||
const __m128i k1 = k[1];
|
||||
const __m128i k2 = k[2];
|
||||
const __m128i k3 = k[3];
|
||||
const __m128i k4 = k[4];
|
||||
const __m128i k5 = k[5];
|
||||
const __m128i k6 = k[6];
|
||||
const __m128i k7 = k[7];
|
||||
const __m128i k8 = k[8];
|
||||
const __m128i k9 = k[9];
|
||||
const __m128i k10 = k[10];
|
||||
const __m128i k11 = k[11];
|
||||
const __m128i k12 = k[12];
|
||||
const __m128i k13 = k[13];
|
||||
const __m128i k14 = k[14];
|
||||
|
||||
// Complete any unfinished blocks from previous calls to crypt().
|
||||
unsigned int totalLen = _len;
|
||||
if ((totalLen & 15U)) {
|
||||
for (;;) {
|
||||
if (unlikely(!len)) {
|
||||
_ctr[1] = Utils::hton(c1);
|
||||
_len = totalLen;
|
||||
return;
|
||||
}
|
||||
--len;
|
||||
out[totalLen++] = *(in++);
|
||||
if (!(totalLen & 15U)) {
|
||||
__m128i d0 = _mm_insert_epi64(dd, (long long)Utils::hton(c1++), 1);
|
||||
d0 = _mm_xor_si128(d0, k0);
|
||||
d0 = _mm_aesenc_si128(d0, k1);
|
||||
d0 = _mm_aesenc_si128(d0, k2);
|
||||
d0 = _mm_aesenc_si128(d0, k3);
|
||||
d0 = _mm_aesenc_si128(d0, k4);
|
||||
d0 = _mm_aesenc_si128(d0, k5);
|
||||
d0 = _mm_aesenc_si128(d0, k6);
|
||||
d0 = _mm_aesenc_si128(d0, k7);
|
||||
d0 = _mm_aesenc_si128(d0, k8);
|
||||
d0 = _mm_aesenc_si128(d0, k9);
|
||||
d0 = _mm_aesenc_si128(d0, k10);
|
||||
__m128i *const outblk = reinterpret_cast<__m128i *>(out + (totalLen - 16));
|
||||
d0 = _mm_aesenc_si128(d0, k11);
|
||||
const __m128i p0 = _mm_loadu_si128(outblk);
|
||||
d0 = _mm_aesenc_si128(d0, k12);
|
||||
d0 = _mm_aesenc_si128(d0, k13);
|
||||
d0 = _mm_aesenclast_si128(d0, k14);
|
||||
_mm_storeu_si128(outblk, _mm_xor_si128(p0, d0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out += totalLen;
|
||||
_len = totalLen + len;
|
||||
|
||||
if (likely(len >= 64)) {
|
||||
|
||||
#if defined(ZT_AES_VAES512) && defined(ZT_AES_VAES256)
|
||||
if (Utils::CPUID.vaes && (len >= 256)) {
|
||||
if (Utils::CPUID.avx512f) {
|
||||
p_aesCtrInnerVAES512(len, _ctr[0], c1, in, out, k);
|
||||
} else {
|
||||
p_aesCtrInnerVAES256(len, _ctr[0], c1, in, out, k);
|
||||
}
|
||||
goto skip_conventional_aesni_64;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(ZT_AES_VAES512) && defined(ZT_AES_VAES256)
|
||||
if (Utils::CPUID.vaes && (len >= 256)) {
|
||||
p_aesCtrInnerVAES256(len, _ctr[0], c1, in, out, k);
|
||||
goto skip_conventional_aesni_64;
|
||||
}
|
||||
#endif
|
||||
|
||||
const uint8_t *const eof64 = in + (len & ~((unsigned int)63));
|
||||
len &= 63;
|
||||
__m128i d0, d1, d2, d3;
|
||||
do {
|
||||
const uint64_t c10 = Utils::hton(c1);
|
||||
const uint64_t c11 = Utils::hton(c1 + 1ULL);
|
||||
const uint64_t c12 = Utils::hton(c1 + 2ULL);
|
||||
const uint64_t c13 = Utils::hton(c1 + 3ULL);
|
||||
d0 = _mm_insert_epi64(dd, (long long)c10, 1);
|
||||
d1 = _mm_insert_epi64(dd, (long long)c11, 1);
|
||||
d2 = _mm_insert_epi64(dd, (long long)c12, 1);
|
||||
d3 = _mm_insert_epi64(dd, (long long)c13, 1);
|
||||
c1 += 4;
|
||||
d0 = _mm_xor_si128(d0, k0);
|
||||
d1 = _mm_xor_si128(d1, k0);
|
||||
d2 = _mm_xor_si128(d2, k0);
|
||||
d3 = _mm_xor_si128(d3, k0);
|
||||
d0 = _mm_aesenc_si128(d0, k1);
|
||||
d1 = _mm_aesenc_si128(d1, k1);
|
||||
d2 = _mm_aesenc_si128(d2, k1);
|
||||
d3 = _mm_aesenc_si128(d3, k1);
|
||||
d0 = _mm_aesenc_si128(d0, k2);
|
||||
d1 = _mm_aesenc_si128(d1, k2);
|
||||
d2 = _mm_aesenc_si128(d2, k2);
|
||||
d3 = _mm_aesenc_si128(d3, k2);
|
||||
d0 = _mm_aesenc_si128(d0, k3);
|
||||
d1 = _mm_aesenc_si128(d1, k3);
|
||||
d2 = _mm_aesenc_si128(d2, k3);
|
||||
d3 = _mm_aesenc_si128(d3, k3);
|
||||
d0 = _mm_aesenc_si128(d0, k4);
|
||||
d1 = _mm_aesenc_si128(d1, k4);
|
||||
d2 = _mm_aesenc_si128(d2, k4);
|
||||
d3 = _mm_aesenc_si128(d3, k4);
|
||||
d0 = _mm_aesenc_si128(d0, k5);
|
||||
d1 = _mm_aesenc_si128(d1, k5);
|
||||
d2 = _mm_aesenc_si128(d2, k5);
|
||||
d3 = _mm_aesenc_si128(d3, k5);
|
||||
d0 = _mm_aesenc_si128(d0, k6);
|
||||
d1 = _mm_aesenc_si128(d1, k6);
|
||||
d2 = _mm_aesenc_si128(d2, k6);
|
||||
d3 = _mm_aesenc_si128(d3, k6);
|
||||
d0 = _mm_aesenc_si128(d0, k7);
|
||||
d1 = _mm_aesenc_si128(d1, k7);
|
||||
d2 = _mm_aesenc_si128(d2, k7);
|
||||
d3 = _mm_aesenc_si128(d3, k7);
|
||||
d0 = _mm_aesenc_si128(d0, k8);
|
||||
d1 = _mm_aesenc_si128(d1, k8);
|
||||
d2 = _mm_aesenc_si128(d2, k8);
|
||||
d3 = _mm_aesenc_si128(d3, k8);
|
||||
d0 = _mm_aesenc_si128(d0, k9);
|
||||
d1 = _mm_aesenc_si128(d1, k9);
|
||||
d2 = _mm_aesenc_si128(d2, k9);
|
||||
d3 = _mm_aesenc_si128(d3, k9);
|
||||
d0 = _mm_aesenc_si128(d0, k10);
|
||||
d1 = _mm_aesenc_si128(d1, k10);
|
||||
d2 = _mm_aesenc_si128(d2, k10);
|
||||
d3 = _mm_aesenc_si128(d3, k10);
|
||||
d0 = _mm_aesenc_si128(d0, k11);
|
||||
d1 = _mm_aesenc_si128(d1, k11);
|
||||
d2 = _mm_aesenc_si128(d2, k11);
|
||||
d3 = _mm_aesenc_si128(d3, k11);
|
||||
d0 = _mm_aesenc_si128(d0, k12);
|
||||
d1 = _mm_aesenc_si128(d1, k12);
|
||||
d2 = _mm_aesenc_si128(d2, k12);
|
||||
d3 = _mm_aesenc_si128(d3, k12);
|
||||
d0 = _mm_aesenc_si128(d0, k13);
|
||||
d1 = _mm_aesenc_si128(d1, k13);
|
||||
d2 = _mm_aesenc_si128(d2, k13);
|
||||
d3 = _mm_aesenc_si128(d3, k13);
|
||||
d0 = _mm_xor_si128(_mm_aesenclast_si128(d0, k14), _mm_loadu_si128(reinterpret_cast<const __m128i *>(in)));
|
||||
d1 = _mm_xor_si128(_mm_aesenclast_si128(d1, k14), _mm_loadu_si128(reinterpret_cast<const __m128i *>(in + 16)));
|
||||
d2 = _mm_xor_si128(_mm_aesenclast_si128(d2, k14), _mm_loadu_si128(reinterpret_cast<const __m128i *>(in + 32)));
|
||||
d3 = _mm_xor_si128(_mm_aesenclast_si128(d3, k14), _mm_loadu_si128(reinterpret_cast<const __m128i *>(in + 48)));
|
||||
in += 64;
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(out), d0);
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(out + 16), d1);
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(out + 32), d2);
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(out + 48), d3);
|
||||
out += 64;
|
||||
} while (likely(in != eof64));
|
||||
|
||||
}
|
||||
|
||||
skip_conventional_aesni_64:
|
||||
while (len >= 16) {
|
||||
__m128i d0 = _mm_insert_epi64(dd, (long long)Utils::hton(c1++), 1);
|
||||
d0 = _mm_xor_si128(d0, k0);
|
||||
d0 = _mm_aesenc_si128(d0, k1);
|
||||
d0 = _mm_aesenc_si128(d0, k2);
|
||||
d0 = _mm_aesenc_si128(d0, k3);
|
||||
d0 = _mm_aesenc_si128(d0, k4);
|
||||
d0 = _mm_aesenc_si128(d0, k5);
|
||||
d0 = _mm_aesenc_si128(d0, k6);
|
||||
d0 = _mm_aesenc_si128(d0, k7);
|
||||
d0 = _mm_aesenc_si128(d0, k8);
|
||||
d0 = _mm_aesenc_si128(d0, k9);
|
||||
d0 = _mm_aesenc_si128(d0, k10);
|
||||
d0 = _mm_aesenc_si128(d0, k11);
|
||||
d0 = _mm_aesenc_si128(d0, k12);
|
||||
d0 = _mm_aesenc_si128(d0, k13);
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(out), _mm_xor_si128(_mm_aesenclast_si128(d0, k14), _mm_loadu_si128(reinterpret_cast<const __m128i *>(in))));
|
||||
in += 16;
|
||||
len -= 16;
|
||||
out += 16;
|
||||
}
|
||||
|
||||
// Any remaining input is placed in _out. This will be picked up and crypted
|
||||
// on subsequent calls to crypt() or finish() as it'll mean _len will not be
|
||||
// an even multiple of 16.
|
||||
for (unsigned int i = 0; i < len; ++i) {
|
||||
out[i] = in[i];
|
||||
}
|
||||
|
||||
_ctr[1] = Utils::hton(c1);
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
__attribute__((__target__("ssse3,sse4,sse4.1,sse4.2,aes,pclmul")))
|
||||
#endif
|
||||
void AES::p_init_aesni(const uint8_t *key) noexcept
|
||||
{
|
||||
__m128i t1, t2, k1, k2, k3, k4, k5, k6, k7, k8, k9, k10, k11, k12, k13;
|
||||
p_k.ni.k[0] = t1 = _mm_loadu_si128((const __m128i *)key);
|
||||
p_k.ni.k[1] = k1 = t2 = _mm_loadu_si128((const __m128i *)(key + 16));
|
||||
p_k.ni.k[2] = k2 = t1 = p_init256_1_aesni(t1, _mm_aeskeygenassist_si128(t2, 0x01));
|
||||
p_k.ni.k[3] = k3 = t2 = p_init256_2_aesni(t1, t2);
|
||||
p_k.ni.k[4] = k4 = t1 = p_init256_1_aesni(t1, _mm_aeskeygenassist_si128(t2, 0x02));
|
||||
p_k.ni.k[5] = k5 = t2 = p_init256_2_aesni(t1, t2);
|
||||
p_k.ni.k[6] = k6 = t1 = p_init256_1_aesni(t1, _mm_aeskeygenassist_si128(t2, 0x04));
|
||||
p_k.ni.k[7] = k7 = t2 = p_init256_2_aesni(t1, t2);
|
||||
p_k.ni.k[8] = k8 = t1 = p_init256_1_aesni(t1, _mm_aeskeygenassist_si128(t2, 0x08));
|
||||
p_k.ni.k[9] = k9 = t2 = p_init256_2_aesni(t1, t2);
|
||||
p_k.ni.k[10] = k10 = t1 = p_init256_1_aesni(t1, _mm_aeskeygenassist_si128(t2, 0x10));
|
||||
p_k.ni.k[11] = k11 = t2 = p_init256_2_aesni(t1, t2);
|
||||
p_k.ni.k[12] = k12 = t1 = p_init256_1_aesni(t1, _mm_aeskeygenassist_si128(t2, 0x20));
|
||||
p_k.ni.k[13] = k13 = t2 = p_init256_2_aesni(t1, t2);
|
||||
p_k.ni.k[14] = p_init256_1_aesni(t1, _mm_aeskeygenassist_si128(t2, 0x40));
|
||||
p_k.ni.k[15] = _mm_aesimc_si128(k13);
|
||||
p_k.ni.k[16] = _mm_aesimc_si128(k12);
|
||||
p_k.ni.k[17] = _mm_aesimc_si128(k11);
|
||||
p_k.ni.k[18] = _mm_aesimc_si128(k10);
|
||||
p_k.ni.k[19] = _mm_aesimc_si128(k9);
|
||||
p_k.ni.k[20] = _mm_aesimc_si128(k8);
|
||||
p_k.ni.k[21] = _mm_aesimc_si128(k7);
|
||||
p_k.ni.k[22] = _mm_aesimc_si128(k6);
|
||||
p_k.ni.k[23] = _mm_aesimc_si128(k5);
|
||||
p_k.ni.k[24] = _mm_aesimc_si128(k4);
|
||||
p_k.ni.k[25] = _mm_aesimc_si128(k3);
|
||||
p_k.ni.k[26] = _mm_aesimc_si128(k2);
|
||||
p_k.ni.k[27] = _mm_aesimc_si128(k1);
|
||||
|
||||
__m128i h = p_k.ni.k[0]; // _mm_xor_si128(_mm_setzero_si128(),_k.ni.k[0]);
|
||||
h = _mm_aesenc_si128(h, k1);
|
||||
h = _mm_aesenc_si128(h, k2);
|
||||
h = _mm_aesenc_si128(h, k3);
|
||||
h = _mm_aesenc_si128(h, k4);
|
||||
h = _mm_aesenc_si128(h, k5);
|
||||
h = _mm_aesenc_si128(h, k6);
|
||||
h = _mm_aesenc_si128(h, k7);
|
||||
h = _mm_aesenc_si128(h, k8);
|
||||
h = _mm_aesenc_si128(h, k9);
|
||||
h = _mm_aesenc_si128(h, k10);
|
||||
h = _mm_aesenc_si128(h, k11);
|
||||
h = _mm_aesenc_si128(h, k12);
|
||||
h = _mm_aesenc_si128(h, k13);
|
||||
h = _mm_aesenclast_si128(h, p_k.ni.k[14]);
|
||||
__m128i hswap = _mm_shuffle_epi8(h, s_sseSwapBytes);
|
||||
__m128i hh = p_gmacPCLMUL128(hswap, h);
|
||||
__m128i hhh = p_gmacPCLMUL128(hswap, hh);
|
||||
__m128i hhhh = p_gmacPCLMUL128(hswap, hhh);
|
||||
p_k.ni.h[0] = hswap;
|
||||
p_k.ni.h[1] = hh = _mm_shuffle_epi8(hh, s_sseSwapBytes);
|
||||
p_k.ni.h[2] = hhh = _mm_shuffle_epi8(hhh, s_sseSwapBytes);
|
||||
p_k.ni.h[3] = hhhh = _mm_shuffle_epi8(hhhh, s_sseSwapBytes);
|
||||
p_k.ni.h2[0] = _mm_xor_si128(_mm_shuffle_epi32(hswap, 78), hswap);
|
||||
p_k.ni.h2[1] = _mm_xor_si128(_mm_shuffle_epi32(hh, 78), hh);
|
||||
p_k.ni.h2[2] = _mm_xor_si128(_mm_shuffle_epi32(hhh, 78), hhh);
|
||||
p_k.ni.h2[3] = _mm_xor_si128(_mm_shuffle_epi32(hhhh, 78), hhhh);
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
__attribute__((__target__("ssse3,sse4,sse4.1,sse4.2,aes,pclmul")))
|
||||
#endif
|
||||
void AES::p_encrypt_aesni(const void *const in, void *const out) const noexcept
|
||||
{
|
||||
__m128i tmp = _mm_loadu_si128((const __m128i *)in);
|
||||
tmp = _mm_xor_si128(tmp, p_k.ni.k[0]);
|
||||
tmp = _mm_aesenc_si128(tmp, p_k.ni.k[1]);
|
||||
tmp = _mm_aesenc_si128(tmp, p_k.ni.k[2]);
|
||||
tmp = _mm_aesenc_si128(tmp, p_k.ni.k[3]);
|
||||
tmp = _mm_aesenc_si128(tmp, p_k.ni.k[4]);
|
||||
tmp = _mm_aesenc_si128(tmp, p_k.ni.k[5]);
|
||||
tmp = _mm_aesenc_si128(tmp, p_k.ni.k[6]);
|
||||
tmp = _mm_aesenc_si128(tmp, p_k.ni.k[7]);
|
||||
tmp = _mm_aesenc_si128(tmp, p_k.ni.k[8]);
|
||||
tmp = _mm_aesenc_si128(tmp, p_k.ni.k[9]);
|
||||
tmp = _mm_aesenc_si128(tmp, p_k.ni.k[10]);
|
||||
tmp = _mm_aesenc_si128(tmp, p_k.ni.k[11]);
|
||||
tmp = _mm_aesenc_si128(tmp, p_k.ni.k[12]);
|
||||
tmp = _mm_aesenc_si128(tmp, p_k.ni.k[13]);
|
||||
_mm_storeu_si128((__m128i *)out, _mm_aesenclast_si128(tmp, p_k.ni.k[14]));
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
__attribute__((__target__("ssse3,sse4,sse4.1,sse4.2,aes,pclmul")))
|
||||
#endif
|
||||
void AES::p_decrypt_aesni(const void *in, void *out) const noexcept
|
||||
{
|
||||
__m128i tmp = _mm_loadu_si128((const __m128i *)in);
|
||||
tmp = _mm_xor_si128(tmp, p_k.ni.k[14]);
|
||||
tmp = _mm_aesdec_si128(tmp, p_k.ni.k[15]);
|
||||
tmp = _mm_aesdec_si128(tmp, p_k.ni.k[16]);
|
||||
tmp = _mm_aesdec_si128(tmp, p_k.ni.k[17]);
|
||||
tmp = _mm_aesdec_si128(tmp, p_k.ni.k[18]);
|
||||
tmp = _mm_aesdec_si128(tmp, p_k.ni.k[19]);
|
||||
tmp = _mm_aesdec_si128(tmp, p_k.ni.k[20]);
|
||||
tmp = _mm_aesdec_si128(tmp, p_k.ni.k[21]);
|
||||
tmp = _mm_aesdec_si128(tmp, p_k.ni.k[22]);
|
||||
tmp = _mm_aesdec_si128(tmp, p_k.ni.k[23]);
|
||||
tmp = _mm_aesdec_si128(tmp, p_k.ni.k[24]);
|
||||
tmp = _mm_aesdec_si128(tmp, p_k.ni.k[25]);
|
||||
tmp = _mm_aesdec_si128(tmp, p_k.ni.k[26]);
|
||||
tmp = _mm_aesdec_si128(tmp, p_k.ni.k[27]);
|
||||
_mm_storeu_si128((__m128i *)out, _mm_aesdeclast_si128(tmp, p_k.ni.k[0]));
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif // ZT_AES_AESNI
|
||||
@@ -0,0 +1,394 @@
|
||||
/*
|
||||
* Copyright (c)2013-2020 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "AES.hpp"
|
||||
|
||||
#ifdef ZT_AES_NEON
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
namespace {
|
||||
|
||||
ZT_INLINE uint8x16_t s_clmul_armneon_crypto(uint8x16_t h, uint8x16_t y, const uint8_t b[16]) noexcept
|
||||
{
|
||||
uint8x16_t r0, r1, t0, t1;
|
||||
r0 = vld1q_u8(b);
|
||||
const uint8x16_t z = veorq_u8(h, h);
|
||||
y = veorq_u8(r0, y);
|
||||
y = vrbitq_u8(y);
|
||||
const uint8x16_t p = vreinterpretq_u8_u64(vdupq_n_u64(0x0000000000000087));
|
||||
t0 = vextq_u8(y, y, 8);
|
||||
__asm__ __volatile__("pmull %0.1q, %1.1d, %2.1d \n\t" : "=w" (r0) : "w" (h), "w" (y));
|
||||
__asm__ __volatile__("pmull2 %0.1q, %1.2d, %2.2d \n\t" :"=w" (r1) : "w" (h), "w" (y));
|
||||
__asm__ __volatile__("pmull %0.1q, %1.1d, %2.1d \n\t" : "=w" (t1) : "w" (h), "w" (t0));
|
||||
__asm__ __volatile__("pmull2 %0.1q, %1.2d, %2.2d \n\t" :"=w" (t0) : "w" (h), "w" (t0));
|
||||
t0 = veorq_u8(t0, t1);
|
||||
t1 = vextq_u8(z, t0, 8);
|
||||
r0 = veorq_u8(r0, t1);
|
||||
t1 = vextq_u8(t0, z, 8);
|
||||
r1 = veorq_u8(r1, t1);
|
||||
__asm__ __volatile__("pmull2 %0.1q, %1.2d, %2.2d \n\t" :"=w" (t0) : "w" (r1), "w" (p));
|
||||
t1 = vextq_u8(t0, z, 8);
|
||||
r1 = veorq_u8(r1, t1);
|
||||
t1 = vextq_u8(z, t0, 8);
|
||||
r0 = veorq_u8(r0, t1);
|
||||
__asm__ __volatile__("pmull %0.1q, %1.1d, %2.1d \n\t" : "=w" (t0) : "w" (r1), "w" (p));
|
||||
return vrbitq_u8(veorq_u8(r0, t0));
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void AES::GMAC::p_armUpdate(const uint8_t *in, unsigned int len) noexcept
|
||||
{
|
||||
uint8x16_t y = vld1q_u8(reinterpret_cast<const uint8_t *>(_y));
|
||||
const uint8x16_t h = _aes.p_k.neon.h;
|
||||
|
||||
if (_rp) {
|
||||
for(;;) {
|
||||
if (!len) {
|
||||
return;
|
||||
}
|
||||
--len;
|
||||
_r[_rp++] = *(in++);
|
||||
if (_rp == 16) {
|
||||
y = s_clmul_armneon_crypto(h, y, _r);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (len >= 16) {
|
||||
y = s_clmul_armneon_crypto(h, y, in);
|
||||
in += 16;
|
||||
len -= 16;
|
||||
}
|
||||
|
||||
vst1q_u8(reinterpret_cast<uint8_t *>(_y), y);
|
||||
|
||||
for (unsigned int i = 0; i < len; ++i) {
|
||||
_r[i] = in[i];
|
||||
}
|
||||
_rp = len; // len is always less than 16 here
|
||||
}
|
||||
|
||||
void AES::GMAC::p_armFinish(uint8_t tag[16]) noexcept
|
||||
{
|
||||
uint64_t tmp[2];
|
||||
uint8x16_t y = vld1q_u8(reinterpret_cast<const uint8_t *>(_y));
|
||||
const uint8x16_t h = _aes.p_k.neon.h;
|
||||
|
||||
if (_rp) {
|
||||
while (_rp < 16) {
|
||||
_r[_rp++] = 0;
|
||||
}
|
||||
y = s_clmul_armneon_crypto(h, y, _r);
|
||||
}
|
||||
|
||||
tmp[0] = Utils::hton((uint64_t)_len << 3U);
|
||||
tmp[1] = 0;
|
||||
y = s_clmul_armneon_crypto(h, y, reinterpret_cast<const uint8_t *>(tmp));
|
||||
|
||||
Utils::copy< 12 >(tmp, _iv);
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
reinterpret_cast<uint32_t *>(tmp)[3] = 0x00000001;
|
||||
#else
|
||||
reinterpret_cast<uint32_t *>(tmp)[3] = 0x01000000;
|
||||
#endif
|
||||
_aes.encrypt(tmp, tmp);
|
||||
|
||||
uint8x16_t yy = y;
|
||||
Utils::storeMachineEndian< uint64_t >(tag, tmp[0] ^ reinterpret_cast<const uint64_t *>(&yy)[0]);
|
||||
Utils::storeMachineEndian< uint64_t >(tag + 8, tmp[1] ^ reinterpret_cast<const uint64_t *>(&yy)[1]);
|
||||
}
|
||||
|
||||
void AES::CTR::p_armCrypt(const uint8_t *in, uint8_t *out, unsigned int len) noexcept
|
||||
{
|
||||
uint8x16_t dd = vrev32q_u8(vld1q_u8(reinterpret_cast<uint8_t *>(_ctr)));
|
||||
const uint32x4_t one = {0,0,0,1};
|
||||
|
||||
uint8x16_t k0 = _aes.p_k.neon.ek[0];
|
||||
uint8x16_t k1 = _aes.p_k.neon.ek[1];
|
||||
uint8x16_t k2 = _aes.p_k.neon.ek[2];
|
||||
uint8x16_t k3 = _aes.p_k.neon.ek[3];
|
||||
uint8x16_t k4 = _aes.p_k.neon.ek[4];
|
||||
uint8x16_t k5 = _aes.p_k.neon.ek[5];
|
||||
uint8x16_t k6 = _aes.p_k.neon.ek[6];
|
||||
uint8x16_t k7 = _aes.p_k.neon.ek[7];
|
||||
uint8x16_t k8 = _aes.p_k.neon.ek[8];
|
||||
uint8x16_t k9 = _aes.p_k.neon.ek[9];
|
||||
uint8x16_t k10 = _aes.p_k.neon.ek[10];
|
||||
uint8x16_t k11 = _aes.p_k.neon.ek[11];
|
||||
uint8x16_t k12 = _aes.p_k.neon.ek[12];
|
||||
uint8x16_t k13 = _aes.p_k.neon.ek[13];
|
||||
uint8x16_t k14 = _aes.p_k.neon.ek[14];
|
||||
|
||||
unsigned int totalLen = _len;
|
||||
if ((totalLen & 15U) != 0) {
|
||||
for (;;) {
|
||||
if (unlikely(!len)) {
|
||||
vst1q_u8(reinterpret_cast<uint8_t *>(_ctr), vrev32q_u8(dd));
|
||||
_len = totalLen;
|
||||
return;
|
||||
}
|
||||
--len;
|
||||
out[totalLen++] = *(in++);
|
||||
if ((totalLen & 15U) == 0) {
|
||||
uint8_t *const otmp = out + (totalLen - 16);
|
||||
uint8x16_t d0 = vrev32q_u8(dd);
|
||||
uint8x16_t pt = vld1q_u8(otmp);
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k0));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k1));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k2));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k3));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k4));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k5));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k6));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k7));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k8));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k9));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k10));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k11));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k12));
|
||||
d0 = veorq_u8(vaeseq_u8(d0, k13), k14);
|
||||
vst1q_u8(otmp, veorq_u8(pt, d0));
|
||||
dd = (uint8x16_t)vaddq_u32((uint32x4_t)dd, one);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out += totalLen;
|
||||
_len = totalLen + len;
|
||||
|
||||
if (likely(len >= 64)) {
|
||||
const uint32x4_t four = vshlq_n_u32(one, 2);
|
||||
uint8x16_t dd1 = (uint8x16_t)vaddq_u32((uint32x4_t)dd, one);
|
||||
uint8x16_t dd2 = (uint8x16_t)vaddq_u32((uint32x4_t)dd1, one);
|
||||
uint8x16_t dd3 = (uint8x16_t)vaddq_u32((uint32x4_t)dd2, one);
|
||||
for (;;) {
|
||||
len -= 64;
|
||||
uint8x16_t d0 = vrev32q_u8(dd);
|
||||
uint8x16_t d1 = vrev32q_u8(dd1);
|
||||
uint8x16_t d2 = vrev32q_u8(dd2);
|
||||
uint8x16_t d3 = vrev32q_u8(dd3);
|
||||
uint8x16_t pt0 = vld1q_u8(in);
|
||||
uint8x16_t pt1 = vld1q_u8(in + 16);
|
||||
uint8x16_t pt2 = vld1q_u8(in + 32);
|
||||
uint8x16_t pt3 = vld1q_u8(in + 48);
|
||||
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k0));
|
||||
d1 = vaesmcq_u8(vaeseq_u8(d1, k0));
|
||||
d2 = vaesmcq_u8(vaeseq_u8(d2, k0));
|
||||
d3 = vaesmcq_u8(vaeseq_u8(d3, k0));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k1));
|
||||
d1 = vaesmcq_u8(vaeseq_u8(d1, k1));
|
||||
d2 = vaesmcq_u8(vaeseq_u8(d2, k1));
|
||||
d3 = vaesmcq_u8(vaeseq_u8(d3, k1));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k2));
|
||||
d1 = vaesmcq_u8(vaeseq_u8(d1, k2));
|
||||
d2 = vaesmcq_u8(vaeseq_u8(d2, k2));
|
||||
d3 = vaesmcq_u8(vaeseq_u8(d3, k2));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k3));
|
||||
d1 = vaesmcq_u8(vaeseq_u8(d1, k3));
|
||||
d2 = vaesmcq_u8(vaeseq_u8(d2, k3));
|
||||
d3 = vaesmcq_u8(vaeseq_u8(d3, k3));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k4));
|
||||
d1 = vaesmcq_u8(vaeseq_u8(d1, k4));
|
||||
d2 = vaesmcq_u8(vaeseq_u8(d2, k4));
|
||||
d3 = vaesmcq_u8(vaeseq_u8(d3, k4));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k5));
|
||||
d1 = vaesmcq_u8(vaeseq_u8(d1, k5));
|
||||
d2 = vaesmcq_u8(vaeseq_u8(d2, k5));
|
||||
d3 = vaesmcq_u8(vaeseq_u8(d3, k5));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k6));
|
||||
d1 = vaesmcq_u8(vaeseq_u8(d1, k6));
|
||||
d2 = vaesmcq_u8(vaeseq_u8(d2, k6));
|
||||
d3 = vaesmcq_u8(vaeseq_u8(d3, k6));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k7));
|
||||
d1 = vaesmcq_u8(vaeseq_u8(d1, k7));
|
||||
d2 = vaesmcq_u8(vaeseq_u8(d2, k7));
|
||||
d3 = vaesmcq_u8(vaeseq_u8(d3, k7));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k8));
|
||||
d1 = vaesmcq_u8(vaeseq_u8(d1, k8));
|
||||
d2 = vaesmcq_u8(vaeseq_u8(d2, k8));
|
||||
d3 = vaesmcq_u8(vaeseq_u8(d3, k8));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k9));
|
||||
d1 = vaesmcq_u8(vaeseq_u8(d1, k9));
|
||||
d2 = vaesmcq_u8(vaeseq_u8(d2, k9));
|
||||
d3 = vaesmcq_u8(vaeseq_u8(d3, k9));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k10));
|
||||
d1 = vaesmcq_u8(vaeseq_u8(d1, k10));
|
||||
d2 = vaesmcq_u8(vaeseq_u8(d2, k10));
|
||||
d3 = vaesmcq_u8(vaeseq_u8(d3, k10));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k11));
|
||||
d1 = vaesmcq_u8(vaeseq_u8(d1, k11));
|
||||
d2 = vaesmcq_u8(vaeseq_u8(d2, k11));
|
||||
d3 = vaesmcq_u8(vaeseq_u8(d3, k11));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k12));
|
||||
d1 = vaesmcq_u8(vaeseq_u8(d1, k12));
|
||||
d2 = vaesmcq_u8(vaeseq_u8(d2, k12));
|
||||
d3 = vaesmcq_u8(vaeseq_u8(d3, k12));
|
||||
d0 = veorq_u8(vaeseq_u8(d0, k13), k14);
|
||||
d1 = veorq_u8(vaeseq_u8(d1, k13), k14);
|
||||
d2 = veorq_u8(vaeseq_u8(d2, k13), k14);
|
||||
d3 = veorq_u8(vaeseq_u8(d3, k13), k14);
|
||||
|
||||
d0 = veorq_u8(pt0, d0);
|
||||
d1 = veorq_u8(pt1, d1);
|
||||
d2 = veorq_u8(pt2, d2);
|
||||
d3 = veorq_u8(pt3, d3);
|
||||
|
||||
vst1q_u8(out, d0);
|
||||
vst1q_u8(out + 16, d1);
|
||||
vst1q_u8(out + 32, d2);
|
||||
vst1q_u8(out + 48, d3);
|
||||
|
||||
out += 64;
|
||||
in += 64;
|
||||
|
||||
dd = (uint8x16_t)vaddq_u32((uint32x4_t)dd, four);
|
||||
if (unlikely(len < 64)) {
|
||||
break;
|
||||
}
|
||||
dd1 = (uint8x16_t)vaddq_u32((uint32x4_t)dd1, four);
|
||||
dd2 = (uint8x16_t)vaddq_u32((uint32x4_t)dd2, four);
|
||||
dd3 = (uint8x16_t)vaddq_u32((uint32x4_t)dd3, four);
|
||||
}
|
||||
}
|
||||
|
||||
while (len >= 16) {
|
||||
len -= 16;
|
||||
uint8x16_t d0 = vrev32q_u8(dd);
|
||||
uint8x16_t pt = vld1q_u8(in);
|
||||
in += 16;
|
||||
dd = (uint8x16_t)vaddq_u32((uint32x4_t)dd, one);
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k0));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k1));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k2));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k3));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k4));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k5));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k6));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k7));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k8));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k9));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k10));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k11));
|
||||
d0 = vaesmcq_u8(vaeseq_u8(d0, k12));
|
||||
d0 = veorq_u8(vaeseq_u8(d0, k13), k14);
|
||||
vst1q_u8(out, veorq_u8(pt, d0));
|
||||
out += 16;
|
||||
}
|
||||
|
||||
// Any remaining input is placed in _out. This will be picked up and crypted
|
||||
// on subsequent calls to crypt() or finish() as it'll mean _len will not be
|
||||
// an even multiple of 16.
|
||||
for (unsigned int i = 0; i < len; ++i) {
|
||||
out[i] = in[i];
|
||||
}
|
||||
|
||||
vst1q_u8(reinterpret_cast<uint8_t *>(_ctr), vrev32q_u8(dd));
|
||||
}
|
||||
|
||||
#define ZT_INIT_ARMNEON_CRYPTO_SUBWORD(w) ((uint32_t)s_sbox[w & 0xffU] + ((uint32_t)s_sbox[(w >> 8U) & 0xffU] << 8U) + ((uint32_t)s_sbox[(w >> 16U) & 0xffU] << 16U) + ((uint32_t)s_sbox[(w >> 24U) & 0xffU] << 24U))
|
||||
#define ZT_INIT_ARMNEON_CRYPTO_ROTWORD(w) (((w) << 8U) | ((w) >> 24U))
|
||||
#define ZT_INIT_ARMNEON_CRYPTO_NK 8
|
||||
#define ZT_INIT_ARMNEON_CRYPTO_NB 4
|
||||
#define ZT_INIT_ARMNEON_CRYPTO_NR 14
|
||||
|
||||
void AES::p_init_armneon_crypto(const uint8_t *key) noexcept
|
||||
{
|
||||
static const uint8_t s_sbox[256] = {0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c,
|
||||
0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea,
|
||||
0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16};
|
||||
|
||||
uint64_t h[2];
|
||||
uint32_t *const w = reinterpret_cast<uint32_t *>(p_k.neon.ek);
|
||||
|
||||
for (unsigned int i=0;i<ZT_INIT_ARMNEON_CRYPTO_NK;++i) {
|
||||
const unsigned int j = i * 4;
|
||||
w[i] = ((uint32_t)key[j] << 24U) | ((uint32_t)key[j + 1] << 16U) | ((uint32_t)key[j + 2] << 8U) | (uint32_t)key[j + 3];
|
||||
}
|
||||
|
||||
for (unsigned int i=ZT_INIT_ARMNEON_CRYPTO_NK;i<(ZT_INIT_ARMNEON_CRYPTO_NB * (ZT_INIT_ARMNEON_CRYPTO_NR + 1));++i) {
|
||||
uint32_t t = w[i - 1];
|
||||
const unsigned int imod = i & (ZT_INIT_ARMNEON_CRYPTO_NK - 1);
|
||||
if (imod == 0) {
|
||||
t = ZT_INIT_ARMNEON_CRYPTO_SUBWORD(ZT_INIT_ARMNEON_CRYPTO_ROTWORD(t)) ^ rcon[(i - 1) / ZT_INIT_ARMNEON_CRYPTO_NK];
|
||||
} else if (imod == 4) {
|
||||
t = ZT_INIT_ARMNEON_CRYPTO_SUBWORD(t);
|
||||
}
|
||||
w[i] = w[i - ZT_INIT_ARMNEON_CRYPTO_NK] ^ t;
|
||||
}
|
||||
|
||||
for (unsigned int i=0;i<(ZT_INIT_ARMNEON_CRYPTO_NB * (ZT_INIT_ARMNEON_CRYPTO_NR + 1));++i) {
|
||||
w[i] = Utils::hton(w[i]);
|
||||
}
|
||||
|
||||
p_k.neon.dk[0] = p_k.neon.ek[14];
|
||||
for (int i=1;i<14;++i) {
|
||||
p_k.neon.dk[i] = vaesimcq_u8(p_k.neon.ek[14 - i]);
|
||||
}
|
||||
p_k.neon.dk[14] = p_k.neon.ek[0];
|
||||
|
||||
p_encrypt_armneon_crypto(Utils::ZERO256, h);
|
||||
Utils::copy<16>(&(p_k.neon.h), h);
|
||||
p_k.neon.h = vrbitq_u8(p_k.neon.h);
|
||||
p_k.sw.h[0] = Utils::ntoh(h[0]);
|
||||
p_k.sw.h[1] = Utils::ntoh(h[1]);
|
||||
}
|
||||
|
||||
void AES::p_encrypt_armneon_crypto(const void *const in, void *const out) const noexcept
|
||||
{
|
||||
uint8x16_t tmp = vld1q_u8(reinterpret_cast<const uint8_t *>(in));
|
||||
tmp = vaesmcq_u8(vaeseq_u8(tmp, p_k.neon.ek[0]));
|
||||
tmp = vaesmcq_u8(vaeseq_u8(tmp, p_k.neon.ek[1]));
|
||||
tmp = vaesmcq_u8(vaeseq_u8(tmp, p_k.neon.ek[2]));
|
||||
tmp = vaesmcq_u8(vaeseq_u8(tmp, p_k.neon.ek[3]));
|
||||
tmp = vaesmcq_u8(vaeseq_u8(tmp, p_k.neon.ek[4]));
|
||||
tmp = vaesmcq_u8(vaeseq_u8(tmp, p_k.neon.ek[5]));
|
||||
tmp = vaesmcq_u8(vaeseq_u8(tmp, p_k.neon.ek[6]));
|
||||
tmp = vaesmcq_u8(vaeseq_u8(tmp, p_k.neon.ek[7]));
|
||||
tmp = vaesmcq_u8(vaeseq_u8(tmp, p_k.neon.ek[8]));
|
||||
tmp = vaesmcq_u8(vaeseq_u8(tmp, p_k.neon.ek[9]));
|
||||
tmp = vaesmcq_u8(vaeseq_u8(tmp, p_k.neon.ek[10]));
|
||||
tmp = vaesmcq_u8(vaeseq_u8(tmp, p_k.neon.ek[11]));
|
||||
tmp = vaesmcq_u8(vaeseq_u8(tmp, p_k.neon.ek[12]));
|
||||
tmp = veorq_u8(vaeseq_u8(tmp, p_k.neon.ek[13]), p_k.neon.ek[14]);
|
||||
vst1q_u8(reinterpret_cast<uint8_t *>(out), tmp);
|
||||
}
|
||||
|
||||
void AES::p_decrypt_armneon_crypto(const void *const in, void *const out) const noexcept
|
||||
{
|
||||
uint8x16_t tmp = vld1q_u8(reinterpret_cast<const uint8_t *>(in));
|
||||
tmp = vaesimcq_u8(vaesdq_u8(tmp, p_k.neon.dk[0]));
|
||||
tmp = vaesimcq_u8(vaesdq_u8(tmp, p_k.neon.dk[1]));
|
||||
tmp = vaesimcq_u8(vaesdq_u8(tmp, p_k.neon.dk[2]));
|
||||
tmp = vaesimcq_u8(vaesdq_u8(tmp, p_k.neon.dk[3]));
|
||||
tmp = vaesimcq_u8(vaesdq_u8(tmp, p_k.neon.dk[4]));
|
||||
tmp = vaesimcq_u8(vaesdq_u8(tmp, p_k.neon.dk[5]));
|
||||
tmp = vaesimcq_u8(vaesdq_u8(tmp, p_k.neon.dk[6]));
|
||||
tmp = vaesimcq_u8(vaesdq_u8(tmp, p_k.neon.dk[7]));
|
||||
tmp = vaesimcq_u8(vaesdq_u8(tmp, p_k.neon.dk[8]));
|
||||
tmp = vaesimcq_u8(vaesdq_u8(tmp, p_k.neon.dk[9]));
|
||||
tmp = vaesimcq_u8(vaesdq_u8(tmp, p_k.neon.dk[10]));
|
||||
tmp = vaesimcq_u8(vaesdq_u8(tmp, p_k.neon.dk[11]));
|
||||
tmp = vaesimcq_u8(vaesdq_u8(tmp, p_k.neon.dk[12]));
|
||||
tmp = veorq_u8(vaesdq_u8(tmp, p_k.neon.dk[13]), p_k.neon.dk[14]);
|
||||
vst1q_u8(reinterpret_cast<uint8_t *>(out), tmp);
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif // ZT_AES_NEON
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_ADDRESS_HPP
|
||||
#define ZT_ADDRESS_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "Buffer.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* A ZeroTier address
|
||||
*/
|
||||
class Address
|
||||
{
|
||||
public:
|
||||
Address() : _a(0) {}
|
||||
Address(const Address &a) : _a(a._a) {}
|
||||
Address(uint64_t a) : _a(a & 0xffffffffffULL) {}
|
||||
|
||||
/**
|
||||
* @param bits Raw address -- 5 bytes, big-endian byte order
|
||||
* @param len Length of array
|
||||
*/
|
||||
Address(const void *bits,unsigned int len) { setTo(bits,len); }
|
||||
|
||||
inline Address &operator=(const Address &a) { _a = a._a; return *this; }
|
||||
inline Address &operator=(const uint64_t a) { _a = (a & 0xffffffffffULL); return *this; }
|
||||
|
||||
/**
|
||||
* @param bits Raw address -- 5 bytes, big-endian byte order
|
||||
* @param len Length of array
|
||||
*/
|
||||
inline void setTo(const void *bits,const unsigned int len)
|
||||
{
|
||||
if (len < ZT_ADDRESS_LENGTH) {
|
||||
_a = 0;
|
||||
return;
|
||||
}
|
||||
const unsigned char *b = (const unsigned char *)bits;
|
||||
uint64_t a = ((uint64_t)*b++) << 32;
|
||||
a |= ((uint64_t)*b++) << 24;
|
||||
a |= ((uint64_t)*b++) << 16;
|
||||
a |= ((uint64_t)*b++) << 8;
|
||||
a |= ((uint64_t)*b);
|
||||
_a = a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bits Buffer to hold 5-byte address in big-endian byte order
|
||||
* @param len Length of array
|
||||
*/
|
||||
inline void copyTo(void *const bits,const unsigned int len) const
|
||||
{
|
||||
if (len < ZT_ADDRESS_LENGTH) {
|
||||
return;
|
||||
}
|
||||
unsigned char *b = (unsigned char *)bits;
|
||||
*(b++) = (unsigned char)((_a >> 32) & 0xff);
|
||||
*(b++) = (unsigned char)((_a >> 24) & 0xff);
|
||||
*(b++) = (unsigned char)((_a >> 16) & 0xff);
|
||||
*(b++) = (unsigned char)((_a >> 8) & 0xff);
|
||||
*b = (unsigned char)(_a & 0xff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append to a buffer in big-endian byte order
|
||||
*
|
||||
* @param b Buffer to append to
|
||||
*/
|
||||
template<unsigned int C>
|
||||
inline void appendTo(Buffer<C> &b) const
|
||||
{
|
||||
unsigned char *p = (unsigned char *)b.appendField(ZT_ADDRESS_LENGTH);
|
||||
*(p++) = (unsigned char)((_a >> 32) & 0xff);
|
||||
*(p++) = (unsigned char)((_a >> 24) & 0xff);
|
||||
*(p++) = (unsigned char)((_a >> 16) & 0xff);
|
||||
*(p++) = (unsigned char)((_a >> 8) & 0xff);
|
||||
*p = (unsigned char)(_a & 0xff);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Integer containing address (0 to 2^40)
|
||||
*/
|
||||
inline uint64_t toInt() const { return _a; }
|
||||
|
||||
/**
|
||||
* @return Hash code for use with Hashtable
|
||||
*/
|
||||
inline unsigned long hashCode() const { return (unsigned long)_a; }
|
||||
|
||||
/**
|
||||
* @return Hexadecimal string
|
||||
*/
|
||||
inline char *toString(char buf[11]) const { return Utils::hex10(_a,buf); }
|
||||
|
||||
/**
|
||||
* @return True if this address is not zero
|
||||
*/
|
||||
inline operator bool() const { return (_a != 0); }
|
||||
|
||||
/**
|
||||
* Check if this address is reserved
|
||||
*
|
||||
* The all-zero null address and any address beginning with 0xff are
|
||||
* reserved. (0xff is reserved for future use to designate possibly
|
||||
* longer addresses, addresses based on IPv6 innards, etc.)
|
||||
*
|
||||
* @return True if address is reserved and may not be used
|
||||
*/
|
||||
inline bool isReserved() const { return ((!_a)||((_a >> 32) == ZT_ADDRESS_RESERVED_PREFIX)); }
|
||||
|
||||
/**
|
||||
* @param i Value from 0 to 4 (inclusive)
|
||||
* @return Byte at said position (address interpreted in big-endian order)
|
||||
*/
|
||||
inline uint8_t operator[](unsigned int i) const { return (uint8_t)(_a >> (32 - (i * 8))); }
|
||||
|
||||
inline void zero() { _a = 0; }
|
||||
|
||||
inline bool operator==(const uint64_t &a) const { return (_a == (a & 0xffffffffffULL)); }
|
||||
inline bool operator!=(const uint64_t &a) const { return (_a != (a & 0xffffffffffULL)); }
|
||||
inline bool operator>(const uint64_t &a) const { return (_a > (a & 0xffffffffffULL)); }
|
||||
inline bool operator<(const uint64_t &a) const { return (_a < (a & 0xffffffffffULL)); }
|
||||
inline bool operator>=(const uint64_t &a) const { return (_a >= (a & 0xffffffffffULL)); }
|
||||
inline bool operator<=(const uint64_t &a) const { return (_a <= (a & 0xffffffffffULL)); }
|
||||
|
||||
inline bool operator==(const Address &a) const { return (_a == a._a); }
|
||||
inline bool operator!=(const Address &a) const { return (_a != a._a); }
|
||||
inline bool operator>(const Address &a) const { return (_a > a._a); }
|
||||
inline bool operator<(const Address &a) const { return (_a < a._a); }
|
||||
inline bool operator>=(const Address &a) const { return (_a >= a._a); }
|
||||
inline bool operator<=(const Address &a) const { return (_a <= a._a); }
|
||||
|
||||
private:
|
||||
uint64_t _a;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_ATOMICCOUNTER_HPP
|
||||
#define ZT_ATOMICCOUNTER_HPP
|
||||
|
||||
#include "Constants.hpp"
|
||||
|
||||
#ifndef __GNUC__
|
||||
#include <atomic>
|
||||
#endif
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* Simple atomic counter supporting increment and decrement
|
||||
*/
|
||||
class AtomicCounter
|
||||
{
|
||||
public:
|
||||
AtomicCounter() { _v = 0; }
|
||||
|
||||
inline int load() const
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
return __sync_or_and_fetch(const_cast<int *>(&_v),0);
|
||||
#else
|
||||
return _v.load();
|
||||
#endif
|
||||
}
|
||||
|
||||
inline int operator++()
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
return __sync_add_and_fetch(&_v,1);
|
||||
#else
|
||||
return ++_v;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline int operator--()
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
return __sync_sub_and_fetch(&_v,1);
|
||||
#else
|
||||
return --_v;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
AtomicCounter(const AtomicCounter &) {}
|
||||
const AtomicCounter &operator=(const AtomicCounter &) { return *this; }
|
||||
|
||||
#ifdef __GNUC__
|
||||
int _v;
|
||||
#else
|
||||
std::atomic_int _v;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
+2055
File diff suppressed because it is too large
Load Diff
+1573
File diff suppressed because it is too large
Load Diff
+486
@@ -0,0 +1,486 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_BUFFER_HPP
|
||||
#define ZT_BUFFER_HPP
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
#if defined(__GNUC__) && (!defined(ZT_NO_TYPE_PUNNING))
|
||||
#define ZT_VAR_MAY_ALIAS __attribute__((__may_alias__))
|
||||
#else
|
||||
#define ZT_VAR_MAY_ALIAS
|
||||
#endif
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* A variable length but statically allocated buffer
|
||||
*
|
||||
* Bounds-checking is done everywhere, since this is used in security
|
||||
* critical code. This supports construction and assignment from buffers
|
||||
* of differing capacities, provided the data actually in them fits.
|
||||
* It throws std::out_of_range on any boundary violation.
|
||||
*
|
||||
* The at(), append(), etc. methods encode integers larger than 8-bit in
|
||||
* big-endian (network) byte order.
|
||||
*
|
||||
* @tparam C Total capacity
|
||||
*/
|
||||
template<unsigned int C>
|
||||
class Buffer
|
||||
{
|
||||
// I love me!
|
||||
template <unsigned int C2> friend class Buffer;
|
||||
|
||||
public:
|
||||
// STL container idioms
|
||||
typedef unsigned char value_type;
|
||||
typedef unsigned char * pointer;
|
||||
typedef const char * const_pointer;
|
||||
typedef char & reference;
|
||||
typedef const char & const_reference;
|
||||
typedef char * iterator;
|
||||
typedef const char * const_iterator;
|
||||
typedef unsigned int size_type;
|
||||
typedef int difference_type;
|
||||
typedef std::reverse_iterator<iterator> reverse_iterator;
|
||||
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
|
||||
inline iterator begin() { return _b; }
|
||||
inline iterator end() { return (_b + _l); }
|
||||
inline const_iterator begin() const { return _b; }
|
||||
inline const_iterator end() const { return (_b + _l); }
|
||||
inline reverse_iterator rbegin() { return reverse_iterator(begin()); }
|
||||
inline reverse_iterator rend() { return reverse_iterator(end()); }
|
||||
inline const_reverse_iterator rbegin() const { return const_reverse_iterator(begin()); }
|
||||
inline const_reverse_iterator rend() const { return const_reverse_iterator(end()); }
|
||||
|
||||
Buffer() :
|
||||
_l(0)
|
||||
{
|
||||
}
|
||||
|
||||
Buffer(unsigned int l)
|
||||
{
|
||||
if (l > C) {
|
||||
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
|
||||
}
|
||||
_l = l;
|
||||
}
|
||||
|
||||
template<unsigned int C2>
|
||||
Buffer(const Buffer<C2> &b)
|
||||
{
|
||||
*this = b;
|
||||
}
|
||||
|
||||
Buffer(const void *b,unsigned int l)
|
||||
{
|
||||
copyFrom(b,l);
|
||||
}
|
||||
|
||||
template<unsigned int C2>
|
||||
inline Buffer &operator=(const Buffer<C2> &b)
|
||||
{
|
||||
if (unlikely(b._l > C)) {
|
||||
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
|
||||
}
|
||||
if (C2 == C) {
|
||||
memcpy(this,&b,sizeof(Buffer<C>));
|
||||
} else {
|
||||
memcpy(_b,b._b,_l = b._l);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline void copyFrom(const void *b,unsigned int l)
|
||||
{
|
||||
if (unlikely(l > C)) {
|
||||
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
|
||||
}
|
||||
memcpy(_b,b,l);
|
||||
_l = l;
|
||||
}
|
||||
|
||||
unsigned char operator[](const unsigned int i) const
|
||||
{
|
||||
if (unlikely(i >= _l)) {
|
||||
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
|
||||
}
|
||||
return (unsigned char)_b[i];
|
||||
}
|
||||
|
||||
unsigned char &operator[](const unsigned int i)
|
||||
{
|
||||
if (unlikely(i >= _l)) {
|
||||
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
|
||||
}
|
||||
return ((unsigned char *)_b)[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a raw pointer to a field with bounds checking
|
||||
*
|
||||
* This isn't perfectly safe in that the caller could still overflow
|
||||
* the pointer, but its use provides both a sanity check and
|
||||
* documentation / reminder to the calling code to treat the returned
|
||||
* pointer as being of size [l].
|
||||
*
|
||||
* @param i Index of field in buffer
|
||||
* @param l Length of field in bytes
|
||||
* @return Pointer to field data
|
||||
* @throws std::out_of_range Field extends beyond data size
|
||||
*/
|
||||
unsigned char *field(unsigned int i,unsigned int l)
|
||||
{
|
||||
if (unlikely((i + l) > _l)) {
|
||||
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
|
||||
}
|
||||
return (unsigned char *)(_b + i);
|
||||
}
|
||||
const unsigned char *field(unsigned int i,unsigned int l) const
|
||||
{
|
||||
if (unlikely((i + l) > _l)) {
|
||||
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
|
||||
}
|
||||
return (const unsigned char *)(_b + i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Place a primitive integer value at a given position
|
||||
*
|
||||
* @param i Index to place value
|
||||
* @param v Value
|
||||
* @tparam T Integer type (e.g. uint16_t, int64_t)
|
||||
*/
|
||||
template<typename T>
|
||||
inline void setAt(unsigned int i,const T v)
|
||||
{
|
||||
if (unlikely((i + sizeof(T)) > _l)) {
|
||||
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
|
||||
}
|
||||
#ifdef ZT_NO_TYPE_PUNNING
|
||||
uint8_t *p = reinterpret_cast<uint8_t *>(_b + i);
|
||||
for(unsigned int x=1;x<=sizeof(T);++x) {
|
||||
*(p++) = (uint8_t)(v >> (8 * (sizeof(T) - x)));
|
||||
}
|
||||
#else
|
||||
T *const ZT_VAR_MAY_ALIAS p = reinterpret_cast<T *>(_b + i);
|
||||
*p = Utils::hton(v);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a primitive integer value at a given position
|
||||
*
|
||||
* @param i Index to get integer
|
||||
* @tparam T Integer type (e.g. uint16_t, int64_t)
|
||||
* @return Integer value
|
||||
*/
|
||||
template<typename T>
|
||||
inline T at(unsigned int i) const
|
||||
{
|
||||
if (unlikely((i + sizeof(T)) > _l)) {
|
||||
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
|
||||
}
|
||||
#ifdef ZT_NO_TYPE_PUNNING
|
||||
T v = 0;
|
||||
const uint8_t *p = reinterpret_cast<const uint8_t *>(_b + i);
|
||||
for(unsigned int x=0;x<sizeof(T);++x) {
|
||||
v <<= 8;
|
||||
v |= (T)*(p++);
|
||||
}
|
||||
return v;
|
||||
#else
|
||||
const T *const ZT_VAR_MAY_ALIAS p = reinterpret_cast<const T *>(_b + i);
|
||||
return Utils::ntoh(*p);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Append an integer type to this buffer
|
||||
*
|
||||
* @param v Value to append
|
||||
* @tparam T Integer type (e.g. uint16_t, int64_t)
|
||||
* @throws std::out_of_range Attempt to append beyond capacity
|
||||
*/
|
||||
template<typename T>
|
||||
inline void append(const T v)
|
||||
{
|
||||
if (unlikely((_l + sizeof(T)) > C)) {
|
||||
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
|
||||
}
|
||||
#ifdef ZT_NO_TYPE_PUNNING
|
||||
uint8_t *p = reinterpret_cast<uint8_t *>(_b + _l);
|
||||
for(unsigned int x=1;x<=sizeof(T);++x) {
|
||||
*(p++) = (uint8_t)(v >> (8 * (sizeof(T) - x)));
|
||||
}
|
||||
#else
|
||||
T *const ZT_VAR_MAY_ALIAS p = reinterpret_cast<T *>(_b + _l);
|
||||
*p = Utils::hton(v);
|
||||
#endif
|
||||
_l += sizeof(T);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a run of bytes
|
||||
*
|
||||
* @param c Character value to append
|
||||
* @param n Number of times to append
|
||||
* @throws std::out_of_range Attempt to append beyond capacity
|
||||
*/
|
||||
inline void append(unsigned char c,unsigned int n)
|
||||
{
|
||||
if (unlikely((_l + n) > C)) {
|
||||
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
|
||||
}
|
||||
for(unsigned int i=0;i<n;++i) {
|
||||
_b[_l++] = (char)c;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Append secure random bytes
|
||||
*
|
||||
* @param n Number of random bytes to append
|
||||
*/
|
||||
inline void appendRandom(unsigned int n)
|
||||
{
|
||||
if (unlikely((_l + n) > C)) {
|
||||
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
|
||||
}
|
||||
Utils::getSecureRandom(_b + _l,n);
|
||||
_l += n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a C-array of bytes
|
||||
*
|
||||
* @param b Data
|
||||
* @param l Length
|
||||
* @throws std::out_of_range Attempt to append beyond capacity
|
||||
*/
|
||||
inline void append(const void *b,unsigned int l)
|
||||
{
|
||||
if (unlikely((_l + l) > C)) {
|
||||
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
|
||||
}
|
||||
memcpy(_b + _l,b,l);
|
||||
_l += l;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a C string including null termination byte
|
||||
*
|
||||
* @param s C string
|
||||
* @throws std::out_of_range Attempt to append beyond capacity
|
||||
*/
|
||||
inline void appendCString(const char *s)
|
||||
{
|
||||
for(;;) {
|
||||
if (unlikely(_l >= C)) {
|
||||
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
|
||||
}
|
||||
if (!(_b[_l++] = *(s++))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a buffer
|
||||
*
|
||||
* @param b Buffer to append
|
||||
* @tparam C2 Capacity of second buffer (typically inferred)
|
||||
* @throws std::out_of_range Attempt to append beyond capacity
|
||||
*/
|
||||
template<unsigned int C2>
|
||||
inline void append(const Buffer<C2> &b)
|
||||
{
|
||||
append(b._b,b._l);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment size and return pointer to field of specified size
|
||||
*
|
||||
* Nothing is actually written to the memory. This is a shortcut
|
||||
* for addSize() followed by field() to reference the previous
|
||||
* position and the new size.
|
||||
*
|
||||
* @param l Length of field to append
|
||||
* @return Pointer to beginning of appended field of length 'l'
|
||||
*/
|
||||
inline char *appendField(unsigned int l)
|
||||
{
|
||||
if (unlikely((_l + l) > C)) {
|
||||
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
|
||||
}
|
||||
char *r = _b + _l;
|
||||
_l += l;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment size by a given number of bytes
|
||||
*
|
||||
* The contents of new space are undefined.
|
||||
*
|
||||
* @param i Bytes to increment
|
||||
* @throws std::out_of_range Capacity exceeded
|
||||
*/
|
||||
inline void addSize(unsigned int i)
|
||||
{
|
||||
if (unlikely((i + _l) > C)) {
|
||||
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
|
||||
}
|
||||
_l += i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set size of data in buffer
|
||||
*
|
||||
* The contents of new space are undefined.
|
||||
*
|
||||
* @param i New size
|
||||
* @throws std::out_of_range Size larger than capacity
|
||||
*/
|
||||
inline void setSize(const unsigned int i)
|
||||
{
|
||||
if (unlikely(i > C)) {
|
||||
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
|
||||
}
|
||||
_l = i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move everything after 'at' to the buffer's front and truncate
|
||||
*
|
||||
* @param at Truncate before this position
|
||||
* @throws std::out_of_range Position is beyond size of buffer
|
||||
*/
|
||||
inline void behead(const unsigned int at)
|
||||
{
|
||||
if (!at) {
|
||||
return;
|
||||
}
|
||||
if (unlikely(at > _l)) {
|
||||
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
|
||||
}
|
||||
::memmove(_b,_b + at,_l -= at);
|
||||
}
|
||||
|
||||
/**
|
||||
* Erase something from the middle of the buffer
|
||||
*
|
||||
* @param start Starting position
|
||||
* @param length Length of block to erase
|
||||
* @throws std::out_of_range Position plus length is beyond size of buffer
|
||||
*/
|
||||
inline void erase(const unsigned int at,const unsigned int length)
|
||||
{
|
||||
const unsigned int endr = at + length;
|
||||
if (unlikely(endr > _l)) {
|
||||
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
|
||||
}
|
||||
::memmove(_b + at,_b + endr,_l - endr);
|
||||
_l -= length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set buffer data length to zero
|
||||
*/
|
||||
inline void clear() { _l = 0; }
|
||||
|
||||
/**
|
||||
* Zero buffer up to size()
|
||||
*/
|
||||
inline void zero() { memset(_b,0,_l); }
|
||||
|
||||
/**
|
||||
* Zero unused capacity area
|
||||
*/
|
||||
inline void zeroUnused() { memset(_b + _l,0,C - _l); }
|
||||
|
||||
/**
|
||||
* Unconditionally and securely zero buffer's underlying memory
|
||||
*/
|
||||
inline void burn() { Utils::burn(_b,sizeof(_b)); }
|
||||
|
||||
/**
|
||||
* @return Constant pointer to data in buffer
|
||||
*/
|
||||
inline const void *data() const { return _b; }
|
||||
|
||||
/**
|
||||
* @return Non-constant pointer to data in buffer
|
||||
*/
|
||||
inline void *unsafeData() { return _b; }
|
||||
|
||||
/**
|
||||
* @return Size of data in buffer
|
||||
*/
|
||||
inline unsigned int size() const { return _l; }
|
||||
|
||||
/**
|
||||
* @return Capacity of buffer
|
||||
*/
|
||||
inline unsigned int capacity() const { return C; }
|
||||
|
||||
template<unsigned int C2>
|
||||
inline bool operator==(const Buffer<C2> &b) const
|
||||
{
|
||||
return ((_l == b._l)&&(!memcmp(_b,b._b,_l)));
|
||||
}
|
||||
template<unsigned int C2>
|
||||
inline bool operator!=(const Buffer<C2> &b) const
|
||||
{
|
||||
return ((_l != b._l)||(memcmp(_b,b._b,_l)));
|
||||
}
|
||||
template<unsigned int C2>
|
||||
inline bool operator<(const Buffer<C2> &b) const
|
||||
{
|
||||
return (memcmp(_b,b._b,std::min(_l,b._l)) < 0);
|
||||
}
|
||||
template<unsigned int C2>
|
||||
inline bool operator>(const Buffer<C2> &b) const
|
||||
{
|
||||
return (b < *this);
|
||||
}
|
||||
template<unsigned int C2>
|
||||
inline bool operator<=(const Buffer<C2> &b) const
|
||||
{
|
||||
return !(b < *this);
|
||||
}
|
||||
template<unsigned int C2>
|
||||
inline bool operator>=(const Buffer<C2> &b) const
|
||||
{
|
||||
return !(*this < b);
|
||||
}
|
||||
|
||||
private:
|
||||
char ZT_VAR_MAY_ALIAS _b[C];
|
||||
unsigned int _l;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
+2642
File diff suppressed because it is too large
Load Diff
+171
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_C25519_HPP
|
||||
#define ZT_C25519_HPP
|
||||
|
||||
#include "Utils.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
#define ZT_C25519_PUBLIC_KEY_LEN 64
|
||||
#define ZT_C25519_PRIVATE_KEY_LEN 64
|
||||
#define ZT_C25519_SIGNATURE_LEN 96
|
||||
|
||||
/**
|
||||
* A combined Curve25519 ECDH and Ed25519 signature engine
|
||||
*/
|
||||
class C25519
|
||||
{
|
||||
public:
|
||||
struct Public { uint8_t data[ZT_C25519_PUBLIC_KEY_LEN]; };
|
||||
struct Private { uint8_t data[ZT_C25519_PRIVATE_KEY_LEN]; };
|
||||
struct Signature { uint8_t data[ZT_C25519_SIGNATURE_LEN]; };
|
||||
struct Pair { Public pub; Private priv; };
|
||||
|
||||
/**
|
||||
* Generate a C25519 elliptic curve key pair
|
||||
*/
|
||||
static inline Pair generate()
|
||||
{
|
||||
Pair kp;
|
||||
Utils::getSecureRandom(kp.priv.data,ZT_C25519_PRIVATE_KEY_LEN);
|
||||
_calcPubDH(kp);
|
||||
_calcPubED(kp);
|
||||
return kp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a key pair satisfying a condition
|
||||
*
|
||||
* This begins with a random keypair from a random secret key and then
|
||||
* iteratively increments the random secret until cond(kp) returns true.
|
||||
* This is used to compute key pairs in which the public key, its hash
|
||||
* or some other aspect of it satisfies some condition, such as for a
|
||||
* hashcash criteria.
|
||||
*
|
||||
* @param cond Condition function or function object
|
||||
* @return Key pair where cond(kp) returns true
|
||||
* @tparam F Type of 'cond'
|
||||
*/
|
||||
template<typename F>
|
||||
static inline Pair generateSatisfying(F cond)
|
||||
{
|
||||
Pair kp;
|
||||
void *const priv = (void *)kp.priv.data;
|
||||
Utils::getSecureRandom(priv,ZT_C25519_PRIVATE_KEY_LEN);
|
||||
_calcPubED(kp); // do Ed25519 key -- bytes 32-63 of pub and priv
|
||||
do {
|
||||
++(((uint64_t *)priv)[1]);
|
||||
--(((uint64_t *)priv)[2]);
|
||||
_calcPubDH(kp); // keep regenerating bytes 0-31 until satisfied
|
||||
} while (!cond(kp));
|
||||
return kp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform C25519 ECC key agreement
|
||||
*
|
||||
* Actual key bytes are generated from one or more SHA-512 digests of
|
||||
* the raw result of key agreement.
|
||||
*
|
||||
* @param mine My private key
|
||||
* @param their Their public key
|
||||
* @param keybuf Buffer to fill
|
||||
* @param keylen Number of key bytes to generate
|
||||
*/
|
||||
static void agree(const Private &mine,const Public &their,void *keybuf,unsigned int keylen);
|
||||
static inline void agree(const Pair &mine,const Public &their,void *keybuf,unsigned int keylen) { agree(mine.priv,their,keybuf,keylen); }
|
||||
|
||||
/**
|
||||
* Sign a message with a sender's key pair
|
||||
*
|
||||
* This takes the SHA-521 of msg[] and then signs the first 32 bytes of this
|
||||
* digest, returning it and the 64-byte ed25519 signature in signature[].
|
||||
* This results in a signature that verifies both the signer's authenticity
|
||||
* and the integrity of the message.
|
||||
*
|
||||
* This is based on the original ed25519 code from NaCl and the SUPERCOP
|
||||
* cipher benchmark suite, but with the modification that it always
|
||||
* produces a signature of fixed 96-byte length based on the hash of an
|
||||
* arbitrary-length message.
|
||||
*
|
||||
* @param myPrivate My private key
|
||||
* @param myPublic My public key
|
||||
* @param msg Message to sign
|
||||
* @param len Length of message in bytes
|
||||
* @param signature Buffer to fill with signature -- MUST be 96 bytes in length
|
||||
*/
|
||||
static void sign(const Private &myPrivate,const Public &myPublic,const void *msg,unsigned int len,void *signature);
|
||||
static inline void sign(const Pair &mine,const void *msg,unsigned int len,void *signature) { sign(mine.priv,mine.pub,msg,len,signature); }
|
||||
|
||||
/**
|
||||
* Sign a message with a sender's key pair
|
||||
*
|
||||
* @param myPrivate My private key
|
||||
* @param myPublic My public key
|
||||
* @param msg Message to sign
|
||||
* @param len Length of message in bytes
|
||||
* @return Signature
|
||||
*/
|
||||
static inline Signature sign(const Private &myPrivate,const Public &myPublic,const void *msg,unsigned int len)
|
||||
{
|
||||
Signature sig;
|
||||
sign(myPrivate,myPublic,msg,len,sig.data);
|
||||
return sig;
|
||||
}
|
||||
static inline Signature sign(const Pair &mine,const void *msg,unsigned int len)
|
||||
{
|
||||
Signature sig;
|
||||
sign(mine.priv,mine.pub,msg,len,sig.data);
|
||||
return sig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a message's signature
|
||||
*
|
||||
* @param their Public key to verify against
|
||||
* @param msg Message to verify signature integrity against
|
||||
* @param len Length of message in bytes
|
||||
* @param signature 96-byte signature
|
||||
* @return True if signature is valid and the message is authentic and unmodified
|
||||
*/
|
||||
static bool verify(const Public &their,const void *msg,unsigned int len,const void *signature);
|
||||
|
||||
/**
|
||||
* Verify a message's signature
|
||||
*
|
||||
* @param their Public key to verify against
|
||||
* @param msg Message to verify signature integrity against
|
||||
* @param len Length of message in bytes
|
||||
* @param signature 96-byte signature
|
||||
* @return True if signature is valid and the message is authentic and unmodified
|
||||
*/
|
||||
static inline bool verify(const Public &their,const void *msg,unsigned int len,const Signature &signature)
|
||||
{
|
||||
return verify(their,msg,len,signature.data);
|
||||
}
|
||||
|
||||
private:
|
||||
// derive first 32 bytes of kp.pub from first 32 bytes of kp.priv
|
||||
// this is the ECDH key
|
||||
static void _calcPubDH(Pair &kp);
|
||||
|
||||
// derive 2nd 32 bytes of kp.pub from 2nd 32 bytes of kp.priv
|
||||
// this is the Ed25519 sign/verify key
|
||||
static void _calcPubED(Pair &kp);
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#include "Capability.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "Topology.hpp"
|
||||
#include "Switch.hpp"
|
||||
#include "Network.hpp"
|
||||
#include "Node.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
int Capability::verify(const RuntimeEnvironment *RR,void *tPtr) const
|
||||
{
|
||||
try {
|
||||
// There must be at least one entry, and sanity check for bad chain max length
|
||||
if ((_maxCustodyChainLength < 1)||(_maxCustodyChainLength > ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Validate all entries in chain of custody
|
||||
Buffer<(sizeof(Capability) * 2)> tmp;
|
||||
this->serialize(tmp,true);
|
||||
for(unsigned int c=0;c<_maxCustodyChainLength;++c) {
|
||||
if (c == 0) {
|
||||
if ((!_custody[c].to)||(!_custody[c].from)||(_custody[c].from != Network::controllerFor(_nwid))) {
|
||||
return -1; // the first entry must be present and from the network's controller
|
||||
}
|
||||
} else {
|
||||
if (!_custody[c].to) {
|
||||
return 0; // all previous entries were valid, so we are valid
|
||||
} else if ((!_custody[c].from)||(_custody[c].from != _custody[c-1].to)) {
|
||||
return -1; // otherwise if we have another entry it must be from the previous holder in the chain
|
||||
}
|
||||
}
|
||||
|
||||
const Identity id(RR->topology->getIdentity(tPtr,_custody[c].from));
|
||||
if (id) {
|
||||
if (!id.verify(tmp.data(),tmp.size(),_custody[c].signature)) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
RR->sw->requestWhois(tPtr,RR->node->now(),_custody[c].from);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// We reached max custody chain length and everything was valid
|
||||
return 0;
|
||||
} catch ( ... ) {}
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
@@ -0,0 +1,505 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_CAPABILITY_HPP
|
||||
#define ZT_CAPABILITY_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Credential.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "C25519.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "Buffer.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "../include/ZeroTierOne.h"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class RuntimeEnvironment;
|
||||
|
||||
/**
|
||||
* A set of grouped and signed network flow rules
|
||||
*
|
||||
* On the sending side the sender does the following for each packet:
|
||||
*
|
||||
* (1) Evaluates its capabilities in ascending order of ID to determine
|
||||
* which capability allows it to transmit this packet.
|
||||
* (2) If it has not done so lately, it then sends this capability to the
|
||||
* receiving peer ("presents" it).
|
||||
* (3) The sender then sends the packet.
|
||||
*
|
||||
* On the receiving side the receiver evaluates the capabilities presented
|
||||
* by the sender. If any valid un-expired capability allows this packet it
|
||||
* is accepted.
|
||||
*
|
||||
* Note that this is after evaluation of network scope rules and only if
|
||||
* network scope rules do not deliver an explicit match.
|
||||
*
|
||||
* Capabilities support a chain of custody. This is currently unused but
|
||||
* in the future would allow the publication of capabilities that can be
|
||||
* handed off between nodes. Limited transferability of capabilities is
|
||||
* a feature of true capability based security.
|
||||
*/
|
||||
class Capability : public Credential
|
||||
{
|
||||
public:
|
||||
static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_CAPABILITY; }
|
||||
|
||||
Capability() :
|
||||
_nwid(0),
|
||||
_ts(0),
|
||||
_id(0),
|
||||
_maxCustodyChainLength(0),
|
||||
_ruleCount(0)
|
||||
{
|
||||
memset(_rules,0,sizeof(_rules));
|
||||
memset(_custody,0,sizeof(_custody));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param id Capability ID
|
||||
* @param nwid Network ID
|
||||
* @param ts Timestamp (at controller)
|
||||
* @param mccl Maximum custody chain length (1 to create non-transferable capability)
|
||||
* @param rules Network flow rules for this capability
|
||||
* @param ruleCount Number of flow rules
|
||||
*/
|
||||
Capability(uint32_t id,uint64_t nwid,int64_t ts,unsigned int mccl,const ZT_VirtualNetworkRule *rules,unsigned int ruleCount) :
|
||||
_nwid(nwid),
|
||||
_ts(ts),
|
||||
_id(id),
|
||||
_maxCustodyChainLength((mccl > 0) ? ((mccl < ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH) ? mccl : (unsigned int)ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH) : 1),
|
||||
_ruleCount((ruleCount < ZT_MAX_CAPABILITY_RULES) ? ruleCount : ZT_MAX_CAPABILITY_RULES)
|
||||
{
|
||||
if (_ruleCount > 0) {
|
||||
memcpy(_rules,rules,sizeof(ZT_VirtualNetworkRule) * _ruleCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Rules -- see ruleCount() for size of array
|
||||
*/
|
||||
inline const ZT_VirtualNetworkRule *rules() const { return _rules; }
|
||||
|
||||
/**
|
||||
* @return Number of rules in rules()
|
||||
*/
|
||||
inline unsigned int ruleCount() const { return _ruleCount; }
|
||||
|
||||
/**
|
||||
* @return ID and evaluation order of this capability in network
|
||||
*/
|
||||
inline uint32_t id() const { return _id; }
|
||||
|
||||
/**
|
||||
* @return Network ID for which this capability was issued
|
||||
*/
|
||||
inline uint64_t networkId() const { return _nwid; }
|
||||
|
||||
/**
|
||||
* @return Timestamp
|
||||
*/
|
||||
inline int64_t timestamp() const { return _ts; }
|
||||
|
||||
/**
|
||||
* @return Last 'to' address in chain of custody
|
||||
*/
|
||||
inline Address issuedTo() const
|
||||
{
|
||||
Address i2;
|
||||
for(unsigned int i=0;i<ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH;++i) {
|
||||
if (!_custody[i].to) {
|
||||
return i2;
|
||||
} else {
|
||||
i2 = _custody[i].to;
|
||||
}
|
||||
}
|
||||
return i2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign this capability and add signature to its chain of custody
|
||||
*
|
||||
* If this returns false, this object should be considered to be
|
||||
* in an undefined state and should be discarded. False can be returned
|
||||
* if there is no more room for signatures (max chain length reached)
|
||||
* or if the 'from' identity does not include a secret key to allow
|
||||
* it to sign anything.
|
||||
*
|
||||
* @param from Signing identity (must have secret)
|
||||
* @param to Recipient of this signature
|
||||
* @return True if signature successful and chain of custody appended
|
||||
*/
|
||||
inline bool sign(const Identity &from,const Address &to)
|
||||
{
|
||||
try {
|
||||
for(unsigned int i=0;((i<_maxCustodyChainLength)&&(i<ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH));++i) {
|
||||
if (!(_custody[i].to)) {
|
||||
Buffer<(sizeof(Capability) * 2)> tmp;
|
||||
this->serialize(tmp,true);
|
||||
_custody[i].to = to;
|
||||
_custody[i].from = from.address();
|
||||
_custody[i].signature = from.sign(tmp.data(),tmp.size());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch ( ... ) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify this capability's chain of custody and signatures
|
||||
*
|
||||
* @param RR Runtime environment to provide for peer lookup, etc.
|
||||
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or chain
|
||||
*/
|
||||
int verify(const RuntimeEnvironment *RR,void *tPtr) const;
|
||||
|
||||
template<unsigned int C>
|
||||
static inline void serializeRules(Buffer<C> &b,const ZT_VirtualNetworkRule *rules,unsigned int ruleCount)
|
||||
{
|
||||
for(unsigned int i=0;i<ruleCount;++i) {
|
||||
// Each rule consists of its 8-bit type followed by the size of that type's
|
||||
// field followed by field data. The inclusion of the size will allow non-supported
|
||||
// rules to be ignored but still parsed.
|
||||
b.append((uint8_t)rules[i].t);
|
||||
switch((ZT_VirtualNetworkRuleType)(rules[i].t & 0x3f)) {
|
||||
default:
|
||||
b.append((uint8_t)0);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_ACTION_TEE:
|
||||
case ZT_NETWORK_RULE_ACTION_WATCH:
|
||||
case ZT_NETWORK_RULE_ACTION_REDIRECT:
|
||||
b.append((uint8_t)14);
|
||||
b.append((uint64_t)rules[i].v.fwd.address);
|
||||
b.append((uint32_t)rules[i].v.fwd.flags);
|
||||
b.append((uint16_t)rules[i].v.fwd.length); // unused for redirect
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
|
||||
case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS:
|
||||
b.append((uint8_t)5);
|
||||
Address(rules[i].v.zt).appendTo(b);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_VLAN_ID:
|
||||
b.append((uint8_t)2);
|
||||
b.append((uint16_t)rules[i].v.vlanId);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_VLAN_PCP:
|
||||
b.append((uint8_t)1);
|
||||
b.append((uint8_t)rules[i].v.vlanPcp);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_VLAN_DEI:
|
||||
b.append((uint8_t)1);
|
||||
b.append((uint8_t)rules[i].v.vlanDei);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
|
||||
case ZT_NETWORK_RULE_MATCH_MAC_DEST:
|
||||
b.append((uint8_t)6);
|
||||
b.append(rules[i].v.mac,6);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
|
||||
case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
|
||||
b.append((uint8_t)5);
|
||||
b.append(&(rules[i].v.ipv4.ip),4);
|
||||
b.append((uint8_t)rules[i].v.ipv4.mask);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
|
||||
case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
|
||||
b.append((uint8_t)17);
|
||||
b.append(rules[i].v.ipv6.ip,16);
|
||||
b.append((uint8_t)rules[i].v.ipv6.mask);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_IP_TOS:
|
||||
b.append((uint8_t)3);
|
||||
b.append((uint8_t)rules[i].v.ipTos.mask);
|
||||
b.append((uint8_t)rules[i].v.ipTos.value[0]);
|
||||
b.append((uint8_t)rules[i].v.ipTos.value[1]);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL:
|
||||
b.append((uint8_t)1);
|
||||
b.append((uint8_t)rules[i].v.ipProtocol);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_ETHERTYPE:
|
||||
b.append((uint8_t)2);
|
||||
b.append((uint16_t)rules[i].v.etherType);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_ICMP:
|
||||
b.append((uint8_t)3);
|
||||
b.append((uint8_t)rules[i].v.icmp.type);
|
||||
b.append((uint8_t)rules[i].v.icmp.code);
|
||||
b.append((uint8_t)rules[i].v.icmp.flags);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
|
||||
case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
|
||||
b.append((uint8_t)4);
|
||||
b.append((uint16_t)rules[i].v.port[0]);
|
||||
b.append((uint16_t)rules[i].v.port[1]);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS:
|
||||
b.append((uint8_t)8);
|
||||
b.append((uint64_t)rules[i].v.characteristics);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
|
||||
b.append((uint8_t)4);
|
||||
b.append((uint16_t)rules[i].v.frameSize[0]);
|
||||
b.append((uint16_t)rules[i].v.frameSize[1]);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_RANDOM:
|
||||
b.append((uint8_t)4);
|
||||
b.append((uint32_t)rules[i].v.randomProbability);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE:
|
||||
case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND:
|
||||
case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR:
|
||||
case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR:
|
||||
case ZT_NETWORK_RULE_MATCH_TAGS_EQUAL:
|
||||
case ZT_NETWORK_RULE_MATCH_TAG_SENDER:
|
||||
case ZT_NETWORK_RULE_MATCH_TAG_RECEIVER:
|
||||
b.append((uint8_t)8);
|
||||
b.append((uint32_t)rules[i].v.tag.id);
|
||||
b.append((uint32_t)rules[i].v.tag.value);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_INTEGER_RANGE:
|
||||
b.append((uint8_t)19);
|
||||
b.append((uint64_t)rules[i].v.intRange.start);
|
||||
b.append((uint64_t)(rules[i].v.intRange.start + (uint64_t)rules[i].v.intRange.end)); // more future-proof
|
||||
b.append((uint16_t)rules[i].v.intRange.idx);
|
||||
b.append((uint8_t)rules[i].v.intRange.format);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned int C>
|
||||
static inline void deserializeRules(const Buffer<C> &b,unsigned int &p,ZT_VirtualNetworkRule *rules,unsigned int &ruleCount,const unsigned int maxRuleCount)
|
||||
{
|
||||
while ((ruleCount < maxRuleCount)&&(p < b.size())) {
|
||||
rules[ruleCount].t = (uint8_t)b[p++];
|
||||
const unsigned int fieldLen = (unsigned int)b[p++];
|
||||
switch((ZT_VirtualNetworkRuleType)(rules[ruleCount].t & 0x3f)) {
|
||||
default:
|
||||
break;
|
||||
case ZT_NETWORK_RULE_ACTION_TEE:
|
||||
case ZT_NETWORK_RULE_ACTION_WATCH:
|
||||
case ZT_NETWORK_RULE_ACTION_REDIRECT:
|
||||
rules[ruleCount].v.fwd.address = b.template at<uint64_t>(p);
|
||||
rules[ruleCount].v.fwd.flags = b.template at<uint32_t>(p + 8);
|
||||
rules[ruleCount].v.fwd.length = b.template at<uint16_t>(p + 12);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
|
||||
case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS:
|
||||
rules[ruleCount].v.zt = Address(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH).toInt();
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_VLAN_ID:
|
||||
rules[ruleCount].v.vlanId = b.template at<uint16_t>(p);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_VLAN_PCP:
|
||||
rules[ruleCount].v.vlanPcp = (uint8_t)b[p];
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_VLAN_DEI:
|
||||
rules[ruleCount].v.vlanDei = (uint8_t)b[p];
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
|
||||
case ZT_NETWORK_RULE_MATCH_MAC_DEST:
|
||||
memcpy(rules[ruleCount].v.mac,b.field(p,6),6);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
|
||||
case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
|
||||
memcpy(&(rules[ruleCount].v.ipv4.ip),b.field(p,4),4);
|
||||
rules[ruleCount].v.ipv4.mask = (uint8_t)b[p + 4];
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
|
||||
case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
|
||||
memcpy(rules[ruleCount].v.ipv6.ip,b.field(p,16),16);
|
||||
rules[ruleCount].v.ipv6.mask = (uint8_t)b[p + 16];
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_IP_TOS:
|
||||
rules[ruleCount].v.ipTos.mask = (uint8_t)b[p];
|
||||
rules[ruleCount].v.ipTos.value[0] = (uint8_t)b[p+1];
|
||||
rules[ruleCount].v.ipTos.value[1] = (uint8_t)b[p+2];
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL:
|
||||
rules[ruleCount].v.ipProtocol = (uint8_t)b[p];
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_ETHERTYPE:
|
||||
rules[ruleCount].v.etherType = b.template at<uint16_t>(p);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_ICMP:
|
||||
rules[ruleCount].v.icmp.type = (uint8_t)b[p];
|
||||
rules[ruleCount].v.icmp.code = (uint8_t)b[p+1];
|
||||
rules[ruleCount].v.icmp.flags = (uint8_t)b[p+2];
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
|
||||
case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
|
||||
rules[ruleCount].v.port[0] = b.template at<uint16_t>(p);
|
||||
rules[ruleCount].v.port[1] = b.template at<uint16_t>(p + 2);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS:
|
||||
rules[ruleCount].v.characteristics = b.template at<uint64_t>(p);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
|
||||
rules[ruleCount].v.frameSize[0] = b.template at<uint16_t>(p);
|
||||
rules[ruleCount].v.frameSize[1] = b.template at<uint16_t>(p + 2);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_RANDOM:
|
||||
rules[ruleCount].v.randomProbability = b.template at<uint32_t>(p);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE:
|
||||
case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND:
|
||||
case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR:
|
||||
case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR:
|
||||
case ZT_NETWORK_RULE_MATCH_TAGS_EQUAL:
|
||||
case ZT_NETWORK_RULE_MATCH_TAG_SENDER:
|
||||
case ZT_NETWORK_RULE_MATCH_TAG_RECEIVER:
|
||||
rules[ruleCount].v.tag.id = b.template at<uint32_t>(p);
|
||||
rules[ruleCount].v.tag.value = b.template at<uint32_t>(p + 4);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_INTEGER_RANGE:
|
||||
rules[ruleCount].v.intRange.start = b.template at<uint64_t>(p);
|
||||
rules[ruleCount].v.intRange.end = (uint32_t)(b.template at<uint64_t>(p + 8) - rules[ruleCount].v.intRange.start);
|
||||
rules[ruleCount].v.intRange.idx = b.template at<uint16_t>(p + 16);
|
||||
rules[ruleCount].v.intRange.format = (uint8_t)b[p + 18];
|
||||
break;
|
||||
}
|
||||
p += fieldLen;
|
||||
++ruleCount;
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned int C>
|
||||
inline void serialize(Buffer<C> &b,const bool forSign = false) const
|
||||
{
|
||||
if (forSign) {
|
||||
b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
|
||||
}
|
||||
|
||||
// These are the same between Tag and Capability
|
||||
b.append(_nwid);
|
||||
b.append(_ts);
|
||||
b.append(_id);
|
||||
|
||||
b.append((uint16_t)_ruleCount);
|
||||
serializeRules(b,_rules,_ruleCount);
|
||||
b.append((uint8_t)_maxCustodyChainLength);
|
||||
|
||||
if (!forSign) {
|
||||
for(unsigned int i=0;;++i) {
|
||||
if ((i < _maxCustodyChainLength)&&(i < ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH)&&(_custody[i].to)) {
|
||||
_custody[i].to.appendTo(b);
|
||||
_custody[i].from.appendTo(b);
|
||||
b.append((uint8_t)1); // 1 == Ed25519 signature
|
||||
b.append((uint16_t)ZT_C25519_SIGNATURE_LEN); // length of signature
|
||||
b.append(_custody[i].signature.data,ZT_C25519_SIGNATURE_LEN);
|
||||
} else {
|
||||
b.append((unsigned char)0,ZT_ADDRESS_LENGTH); // zero 'to' terminates chain
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is the size of any additional fields, currently 0.
|
||||
b.append((uint16_t)0);
|
||||
|
||||
if (forSign) {
|
||||
b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned int C>
|
||||
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
|
||||
{
|
||||
*this = Capability();
|
||||
|
||||
unsigned int p = startAt;
|
||||
|
||||
_nwid = b.template at<uint64_t>(p);
|
||||
p += 8;
|
||||
_ts = b.template at<uint64_t>(p);
|
||||
p += 8;
|
||||
_id = b.template at<uint32_t>(p);
|
||||
p += 4;
|
||||
|
||||
const unsigned int rc = b.template at<uint16_t>(p);
|
||||
p += 2;
|
||||
if (rc > ZT_MAX_CAPABILITY_RULES) {
|
||||
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
|
||||
}
|
||||
deserializeRules(b,p,_rules,_ruleCount,rc);
|
||||
|
||||
_maxCustodyChainLength = (unsigned int)b[p++];
|
||||
if ((_maxCustodyChainLength < 1)||(_maxCustodyChainLength > ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH)) {
|
||||
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
|
||||
}
|
||||
|
||||
for(unsigned int i=0;;++i) {
|
||||
const Address to(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
|
||||
p += ZT_ADDRESS_LENGTH;
|
||||
if (!to) {
|
||||
break;
|
||||
}
|
||||
if ((i >= _maxCustodyChainLength)||(i >= ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH)) {
|
||||
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
|
||||
}
|
||||
_custody[i].to = to;
|
||||
_custody[i].from.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
|
||||
p += ZT_ADDRESS_LENGTH;
|
||||
if (b[p++] == 1) {
|
||||
if (b.template at<uint16_t>(p) != ZT_C25519_SIGNATURE_LEN) {
|
||||
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
|
||||
}
|
||||
p += 2;
|
||||
memcpy(_custody[i].signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN);
|
||||
p += ZT_C25519_SIGNATURE_LEN;
|
||||
} else {
|
||||
p += 2 + b.template at<uint16_t>(p);
|
||||
}
|
||||
}
|
||||
|
||||
p += 2 + b.template at<uint16_t>(p);
|
||||
if (p > b.size()) {
|
||||
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
|
||||
}
|
||||
|
||||
return (p - startAt);
|
||||
}
|
||||
|
||||
// Provides natural sort order by ID
|
||||
inline bool operator<(const Capability &c) const { return (_id < c._id); }
|
||||
|
||||
inline bool operator==(const Capability &c) const { return (memcmp(this,&c,sizeof(Capability)) == 0); }
|
||||
inline bool operator!=(const Capability &c) const { return (memcmp(this,&c,sizeof(Capability)) != 0); }
|
||||
|
||||
private:
|
||||
uint64_t _nwid;
|
||||
int64_t _ts;
|
||||
uint32_t _id;
|
||||
|
||||
unsigned int _maxCustodyChainLength;
|
||||
|
||||
unsigned int _ruleCount;
|
||||
ZT_VirtualNetworkRule _rules[ZT_MAX_CAPABILITY_RULES];
|
||||
|
||||
struct {
|
||||
Address to;
|
||||
Address from;
|
||||
C25519::Signature signature;
|
||||
} _custody[ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH];
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#include "CertificateOfMembership.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Topology.hpp"
|
||||
#include "Switch.hpp"
|
||||
#include "Network.hpp"
|
||||
#include "Node.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
CertificateOfMembership::CertificateOfMembership(uint64_t timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Identity &issuedTo)
|
||||
{
|
||||
_qualifiers[0].id = COM_RESERVED_ID_TIMESTAMP;
|
||||
_qualifiers[0].value = timestamp;
|
||||
_qualifiers[0].maxDelta = timestampMaxDelta;
|
||||
_qualifiers[1].id = COM_RESERVED_ID_NETWORK_ID;
|
||||
_qualifiers[1].value = nwid;
|
||||
_qualifiers[1].maxDelta = 0;
|
||||
_qualifiers[2].id = COM_RESERVED_ID_ISSUED_TO;
|
||||
_qualifiers[2].value = issuedTo.address().toInt();
|
||||
_qualifiers[2].maxDelta = 0xffffffffffffffffULL;
|
||||
|
||||
// Include hash of full identity public key in COM for hardening purposes. Pack it in
|
||||
// using the original COM format. Format may be revised in the future to make this cleaner.
|
||||
uint64_t idHash[6];
|
||||
issuedTo.publicKeyHash(idHash);
|
||||
for(unsigned long i=0;i<4;++i) {
|
||||
_qualifiers[i + 3].id = (uint64_t)(i + 3);
|
||||
_qualifiers[i + 3].value = Utils::ntoh(idHash[i]);
|
||||
_qualifiers[i + 3].maxDelta = 0xffffffffffffffffULL;
|
||||
}
|
||||
|
||||
_qualifierCount = 7;
|
||||
memset(_signature.data,0,ZT_C25519_SIGNATURE_LEN);
|
||||
}
|
||||
|
||||
bool CertificateOfMembership::agreesWith(const CertificateOfMembership &other, const Identity &otherIdentity) const
|
||||
{
|
||||
if ((_qualifierCount == 0)||(other._qualifierCount == 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::map< uint64_t, uint64_t > otherFields;
|
||||
for(unsigned int i=0;i<other._qualifierCount;++i) {
|
||||
otherFields[other._qualifiers[i].id] = other._qualifiers[i].value;
|
||||
}
|
||||
|
||||
bool fullIdentityVerification = false;
|
||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||
const uint64_t qid = _qualifiers[i].id;
|
||||
if ((qid >= 3)&&(qid <= 6)) {
|
||||
fullIdentityVerification = true;
|
||||
}
|
||||
std::map< uint64_t, uint64_t >::iterator otherQ(otherFields.find(qid));
|
||||
if (otherQ == otherFields.end()) {
|
||||
return false;
|
||||
}
|
||||
const uint64_t a = _qualifiers[i].value;
|
||||
const uint64_t b = otherQ->second;
|
||||
if (((a >= b) ? (a - b) : (b - a)) > _qualifiers[i].maxDelta) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If this COM has a full hash of its identity, assume the other must have this as well.
|
||||
// Otherwise we are on a controller that does not incorporate these.
|
||||
if (fullIdentityVerification) {
|
||||
uint64_t idHash[6];
|
||||
otherIdentity.publicKeyHash(idHash);
|
||||
for(unsigned long i=0;i<4;++i) {
|
||||
std::map< uint64_t, uint64_t >::iterator otherQ(otherFields.find((uint64_t)(i + 3)));
|
||||
if (otherQ == otherFields.end()) {
|
||||
return false;
|
||||
}
|
||||
if (otherQ->second != Utils::ntoh(idHash[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CertificateOfMembership::sign(const Identity &with)
|
||||
{
|
||||
uint64_t buf[ZT_NETWORK_COM_MAX_QUALIFIERS * 3];
|
||||
unsigned int ptr = 0;
|
||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||
buf[ptr++] = Utils::hton(_qualifiers[i].id);
|
||||
buf[ptr++] = Utils::hton(_qualifiers[i].value);
|
||||
buf[ptr++] = Utils::hton(_qualifiers[i].maxDelta);
|
||||
}
|
||||
|
||||
try {
|
||||
_signature = with.sign(buf,ptr * sizeof(uint64_t));
|
||||
_signedBy = with.address();
|
||||
return true;
|
||||
} catch ( ... ) {
|
||||
_signedBy.zero();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int CertificateOfMembership::verify(const RuntimeEnvironment *RR,void *tPtr) const
|
||||
{
|
||||
if ((!_signedBy)||(_signedBy != Network::controllerFor(networkId()))||(_qualifierCount > ZT_NETWORK_COM_MAX_QUALIFIERS)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const Identity id(RR->topology->getIdentity(tPtr,_signedBy));
|
||||
if (!id) {
|
||||
RR->sw->requestWhois(tPtr,RR->node->now(),_signedBy);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint64_t buf[ZT_NETWORK_COM_MAX_QUALIFIERS * 3];
|
||||
unsigned int ptr = 0;
|
||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||
buf[ptr++] = Utils::hton(_qualifiers[i].id);
|
||||
buf[ptr++] = Utils::hton(_qualifiers[i].value);
|
||||
buf[ptr++] = Utils::hton(_qualifiers[i].maxDelta);
|
||||
}
|
||||
return (id.verify(buf,ptr * sizeof(uint64_t),_signature) ? 0 : -1);
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_CERTIFICATEOFMEMBERSHIP_HPP
|
||||
#define ZT_CERTIFICATEOFMEMBERSHIP_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Credential.hpp"
|
||||
#include "Buffer.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "C25519.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
/**
|
||||
* Maximum number of qualifiers allowed in a COM (absolute max: 65535)
|
||||
*/
|
||||
#define ZT_NETWORK_COM_MAX_QUALIFIERS 8
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class RuntimeEnvironment;
|
||||
|
||||
/**
|
||||
* Certificate of network membership
|
||||
*
|
||||
* The COM contains a sorted set of three-element tuples called qualifiers.
|
||||
* These contain an id, a value, and a maximum delta.
|
||||
*
|
||||
* The ID is arbitrary and should be assigned using a scheme that makes
|
||||
* every ID globally unique. IDs beneath 65536 are reserved for global
|
||||
* assignment by ZeroTier Networks.
|
||||
*
|
||||
* The value's meaning is ID-specific and isn't important here. What's
|
||||
* important is the value and the third member of the tuple: the maximum
|
||||
* delta. The maximum delta is the maximum difference permitted between
|
||||
* values for a given ID between certificates for the two certificates to
|
||||
* themselves agree.
|
||||
*
|
||||
* Network membership is checked by checking whether a peer's certificate
|
||||
* agrees with your own. The timestamp provides the fundamental criterion--
|
||||
* each member of a private network must constantly obtain new certificates
|
||||
* often enough to stay within the max delta for this qualifier. But other
|
||||
* criteria could be added in the future for very special behaviors, things
|
||||
* like latitude and longitude for instance.
|
||||
*
|
||||
* This is a memcpy()'able structure and is safe (in a crash sense) to modify
|
||||
* without locks.
|
||||
*/
|
||||
class CertificateOfMembership : public Credential
|
||||
{
|
||||
public:
|
||||
static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_COM; }
|
||||
|
||||
/**
|
||||
* Reserved qualifier IDs
|
||||
*
|
||||
* IDs below 1024 are reserved for use as standard IDs. Others are available
|
||||
* for user-defined use.
|
||||
*
|
||||
* Addition of new required fields requires that code in hasRequiredFields
|
||||
* be updated as well.
|
||||
*/
|
||||
enum ReservedId
|
||||
{
|
||||
/**
|
||||
* Timestamp of certificate
|
||||
*/
|
||||
COM_RESERVED_ID_TIMESTAMP = 0,
|
||||
|
||||
/**
|
||||
* Network ID for which certificate was issued
|
||||
*/
|
||||
COM_RESERVED_ID_NETWORK_ID = 1,
|
||||
|
||||
/**
|
||||
* ZeroTier address to whom certificate was issued
|
||||
*/
|
||||
COM_RESERVED_ID_ISSUED_TO = 2
|
||||
|
||||
// IDs 3-6 reserved for full hash of identity to which this COM was issued.
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an empty certificate of membership
|
||||
*/
|
||||
CertificateOfMembership() :
|
||||
_qualifierCount(0) {}
|
||||
|
||||
/**
|
||||
* Create from required fields common to all networks
|
||||
*
|
||||
* @param timestamp Timestamp of certificate
|
||||
* @param timestampMaxDelta Maximum variation between timestamps on this net
|
||||
* @param nwid Network ID
|
||||
* @param issuedTo Certificate recipient
|
||||
*/
|
||||
CertificateOfMembership(uint64_t timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Identity &issuedTo);
|
||||
|
||||
/**
|
||||
* Create from binary-serialized COM in buffer
|
||||
*
|
||||
* @param b Buffer to deserialize from
|
||||
* @param startAt Position to start in buffer
|
||||
*/
|
||||
template<unsigned int C>
|
||||
CertificateOfMembership(const Buffer<C> &b,unsigned int startAt = 0)
|
||||
{
|
||||
deserialize(b,startAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if there's something here
|
||||
*/
|
||||
inline operator bool() const { return (_qualifierCount != 0); }
|
||||
|
||||
/**
|
||||
* @return Credential ID, always 0 for COMs
|
||||
*/
|
||||
inline uint32_t id() const { return 0; }
|
||||
|
||||
/**
|
||||
* @return Timestamp for this cert and maximum delta for timestamp
|
||||
*/
|
||||
inline int64_t timestamp() const
|
||||
{
|
||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||
if (_qualifiers[i].id == COM_RESERVED_ID_TIMESTAMP) {
|
||||
return _qualifiers[i].value;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Address to which this cert was issued
|
||||
*/
|
||||
inline Address issuedTo() const
|
||||
{
|
||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||
if (_qualifiers[i].id == COM_RESERVED_ID_ISSUED_TO) {
|
||||
return Address(_qualifiers[i].value);
|
||||
}
|
||||
}
|
||||
return Address();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Network ID for which this cert was issued
|
||||
*/
|
||||
inline uint64_t networkId() const
|
||||
{
|
||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||
if (_qualifiers[i].id == COM_RESERVED_ID_NETWORK_ID) {
|
||||
return _qualifiers[i].value;
|
||||
}
|
||||
}
|
||||
return 0ULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two certificates for parameter agreement
|
||||
*
|
||||
* This compares this certificate with the other and returns true if all
|
||||
* parameters in this cert are present in the other and if they agree to
|
||||
* within this cert's max delta value for each given parameter.
|
||||
*
|
||||
* Tuples present in other but not in this cert are ignored, but any
|
||||
* tuples present in this cert but not in other result in 'false'.
|
||||
*
|
||||
* @param other Cert to compare with
|
||||
* @param otherIdentity Identity of other node
|
||||
* @return True if certs agree and 'other' may be communicated with
|
||||
*/
|
||||
bool agreesWith(const CertificateOfMembership &other, const Identity &otherIdentity) const;
|
||||
|
||||
/**
|
||||
* Sign this certificate
|
||||
*
|
||||
* @param with Identity to sign with, must include private key
|
||||
* @return True if signature was successful
|
||||
*/
|
||||
bool sign(const Identity &with);
|
||||
|
||||
/**
|
||||
* Verify this COM and its signature
|
||||
*
|
||||
* @param RR Runtime environment for looking up peers
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or credential
|
||||
*/
|
||||
int verify(const RuntimeEnvironment *RR,void *tPtr) const;
|
||||
|
||||
/**
|
||||
* @return True if signed
|
||||
*/
|
||||
inline bool isSigned() const { return (_signedBy); }
|
||||
|
||||
/**
|
||||
* @return Address that signed this certificate or null address if none
|
||||
*/
|
||||
inline const Address &signedBy() const { return _signedBy; }
|
||||
|
||||
template<unsigned int C>
|
||||
inline void serialize(Buffer<C> &b) const
|
||||
{
|
||||
b.append((uint8_t)1);
|
||||
b.append((uint16_t)_qualifierCount);
|
||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||
b.append(_qualifiers[i].id);
|
||||
b.append(_qualifiers[i].value);
|
||||
b.append(_qualifiers[i].maxDelta);
|
||||
}
|
||||
_signedBy.appendTo(b);
|
||||
if (_signedBy) {
|
||||
b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned int C>
|
||||
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
|
||||
{
|
||||
unsigned int p = startAt;
|
||||
|
||||
_qualifierCount = 0;
|
||||
_signedBy.zero();
|
||||
|
||||
if (b[p++] != 1) {
|
||||
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
|
||||
}
|
||||
|
||||
unsigned int numq = b.template at<uint16_t>(p);
|
||||
p += sizeof(uint16_t);
|
||||
uint64_t lastId = 0;
|
||||
for(unsigned int i=0;i<numq;++i) {
|
||||
const uint64_t qid = b.template at<uint64_t>(p);
|
||||
if (qid < lastId) {
|
||||
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_BAD_ENCODING;
|
||||
} else {
|
||||
lastId = qid;
|
||||
}
|
||||
if (_qualifierCount < ZT_NETWORK_COM_MAX_QUALIFIERS) {
|
||||
_qualifiers[_qualifierCount].id = qid;
|
||||
_qualifiers[_qualifierCount].value = b.template at<uint64_t>(p + 8);
|
||||
_qualifiers[_qualifierCount].maxDelta = b.template at<uint64_t>(p + 16);
|
||||
p += 24;
|
||||
++_qualifierCount;
|
||||
} else {
|
||||
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
|
||||
}
|
||||
}
|
||||
|
||||
_signedBy.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
|
||||
p += ZT_ADDRESS_LENGTH;
|
||||
|
||||
if (_signedBy) {
|
||||
memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN);
|
||||
p += ZT_C25519_SIGNATURE_LEN;
|
||||
}
|
||||
|
||||
return (p - startAt);
|
||||
}
|
||||
|
||||
inline bool operator==(const CertificateOfMembership &c) const
|
||||
{
|
||||
if (_signedBy != c._signedBy) {
|
||||
return false;
|
||||
}
|
||||
if (_qualifierCount != c._qualifierCount) {
|
||||
return false;
|
||||
}
|
||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||
const _Qualifier &a = _qualifiers[i];
|
||||
const _Qualifier &b = c._qualifiers[i];
|
||||
if ((a.id != b.id)||(a.value != b.value)||(a.maxDelta != b.maxDelta)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return (memcmp(_signature.data,c._signature.data,ZT_C25519_SIGNATURE_LEN) == 0);
|
||||
}
|
||||
inline bool operator!=(const CertificateOfMembership &c) const { return (!(*this == c)); }
|
||||
|
||||
private:
|
||||
struct _Qualifier
|
||||
{
|
||||
_Qualifier() : id(0),value(0),maxDelta(0) {}
|
||||
uint64_t id;
|
||||
uint64_t value;
|
||||
uint64_t maxDelta;
|
||||
inline bool operator<(const _Qualifier &q) const { return (id < q.id); } // sort order
|
||||
};
|
||||
|
||||
Address _signedBy;
|
||||
_Qualifier _qualifiers[ZT_NETWORK_COM_MAX_QUALIFIERS];
|
||||
unsigned int _qualifierCount;
|
||||
C25519::Signature _signature;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#include "CertificateOfOwnership.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "Topology.hpp"
|
||||
#include "Switch.hpp"
|
||||
#include "Network.hpp"
|
||||
#include "Node.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
int CertificateOfOwnership::verify(const RuntimeEnvironment *RR,void *tPtr) const
|
||||
{
|
||||
if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId))) {
|
||||
return -1;
|
||||
}
|
||||
const Identity id(RR->topology->getIdentity(tPtr,_signedBy));
|
||||
if (!id) {
|
||||
RR->sw->requestWhois(tPtr,RR->node->now(),_signedBy);
|
||||
return 1;
|
||||
}
|
||||
try {
|
||||
Buffer<(sizeof(CertificateOfOwnership) + 64)> tmp;
|
||||
this->serialize(tmp,true);
|
||||
return (id.verify(tmp.data(),tmp.size(),_signature) ? 0 : -1);
|
||||
} catch ( ... ) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool CertificateOfOwnership::_owns(const CertificateOfOwnership::Thing &t,const void *v,unsigned int l) const
|
||||
{
|
||||
for(unsigned int i=0,j=_thingCount;i<j;++i) {
|
||||
if (_thingTypes[i] == (uint8_t)t) {
|
||||
unsigned int k = 0;
|
||||
while (k < l) {
|
||||
if (reinterpret_cast<const uint8_t *>(v)[k] != _thingValues[i][k]) {
|
||||
break;
|
||||
}
|
||||
++k;
|
||||
}
|
||||
if (k == l) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_CERTIFICATEOFOWNERSHIP_HPP
|
||||
#define ZT_CERTIFICATEOFOWNERSHIP_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Credential.hpp"
|
||||
#include "C25519.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "Buffer.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "MAC.hpp"
|
||||
|
||||
// Max things per CertificateOfOwnership
|
||||
#define ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS 16
|
||||
|
||||
// Maximum size of a thing's value field in bytes
|
||||
#define ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE 16
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class RuntimeEnvironment;
|
||||
|
||||
/**
|
||||
* Certificate indicating ownership of a network identifier
|
||||
*/
|
||||
class CertificateOfOwnership : public Credential
|
||||
{
|
||||
public:
|
||||
static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_COO; }
|
||||
|
||||
enum Thing
|
||||
{
|
||||
THING_NULL = 0,
|
||||
THING_MAC_ADDRESS = 1,
|
||||
THING_IPV4_ADDRESS = 2,
|
||||
THING_IPV6_ADDRESS = 3
|
||||
};
|
||||
|
||||
CertificateOfOwnership()
|
||||
{
|
||||
memset(reinterpret_cast<void *>(this),0,sizeof(CertificateOfOwnership));
|
||||
}
|
||||
|
||||
CertificateOfOwnership(const uint64_t nwid,const int64_t ts,const Address &issuedTo,const uint32_t id)
|
||||
{
|
||||
memset(reinterpret_cast<void *>(this),0,sizeof(CertificateOfOwnership));
|
||||
_networkId = nwid;
|
||||
_ts = ts;
|
||||
_id = id;
|
||||
_issuedTo = issuedTo;
|
||||
}
|
||||
|
||||
inline uint64_t networkId() const { return _networkId; }
|
||||
inline int64_t timestamp() const { return _ts; }
|
||||
inline uint32_t id() const { return _id; }
|
||||
inline unsigned int thingCount() const { return (unsigned int)_thingCount; }
|
||||
|
||||
inline Thing thingType(const unsigned int i) const { return (Thing)_thingTypes[i]; }
|
||||
inline const uint8_t *thingValue(const unsigned int i) const { return _thingValues[i]; }
|
||||
|
||||
inline const Address &issuedTo() const { return _issuedTo; }
|
||||
|
||||
inline bool owns(const InetAddress &ip) const
|
||||
{
|
||||
if (ip.ss_family == AF_INET) {
|
||||
return this->_owns(THING_IPV4_ADDRESS,&(reinterpret_cast<const struct sockaddr_in *>(&ip)->sin_addr.s_addr),4);
|
||||
}
|
||||
if (ip.ss_family == AF_INET6) {
|
||||
return this->_owns(THING_IPV6_ADDRESS,reinterpret_cast<const struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool owns(const MAC &mac) const
|
||||
{
|
||||
uint8_t tmp[6];
|
||||
mac.copyTo(tmp,6);
|
||||
return this->_owns(THING_MAC_ADDRESS,tmp,6);
|
||||
}
|
||||
|
||||
inline void addThing(const InetAddress &ip)
|
||||
{
|
||||
if (_thingCount >= ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS) {
|
||||
return;
|
||||
}
|
||||
if (ip.ss_family == AF_INET) {
|
||||
_thingTypes[_thingCount] = THING_IPV4_ADDRESS;
|
||||
memcpy(_thingValues[_thingCount],&(reinterpret_cast<const struct sockaddr_in *>(&ip)->sin_addr.s_addr),4);
|
||||
++_thingCount;
|
||||
} else if (ip.ss_family == AF_INET6) {
|
||||
_thingTypes[_thingCount] = THING_IPV6_ADDRESS;
|
||||
memcpy(_thingValues[_thingCount],reinterpret_cast<const struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16);
|
||||
++_thingCount;
|
||||
}
|
||||
}
|
||||
|
||||
inline void addThing(const MAC &mac)
|
||||
{
|
||||
if (_thingCount >= ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS) {
|
||||
return;
|
||||
}
|
||||
_thingTypes[_thingCount] = THING_MAC_ADDRESS;
|
||||
mac.copyTo(_thingValues[_thingCount],6);
|
||||
++_thingCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param signer Signing identity, must have private key
|
||||
* @return True if signature was successful
|
||||
*/
|
||||
inline bool sign(const Identity &signer)
|
||||
{
|
||||
if (signer.hasPrivate()) {
|
||||
Buffer<sizeof(CertificateOfOwnership) + 64> tmp;
|
||||
_signedBy = signer.address();
|
||||
this->serialize(tmp,true);
|
||||
_signature = signer.sign(tmp.data(),tmp.size());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RR Runtime environment to allow identity lookup for signedBy
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature
|
||||
*/
|
||||
int verify(const RuntimeEnvironment *RR,void *tPtr) const;
|
||||
|
||||
template<unsigned int C>
|
||||
inline void serialize(Buffer<C> &b,const bool forSign = false) const
|
||||
{
|
||||
if (forSign) {
|
||||
b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
|
||||
}
|
||||
|
||||
b.append(_networkId);
|
||||
b.append(_ts);
|
||||
b.append(_flags);
|
||||
b.append(_id);
|
||||
b.append((uint16_t)_thingCount);
|
||||
for(unsigned int i=0,j=_thingCount;i<j;++i) {
|
||||
b.append((uint8_t)_thingTypes[i]);
|
||||
b.append(_thingValues[i],ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE);
|
||||
}
|
||||
|
||||
_issuedTo.appendTo(b);
|
||||
_signedBy.appendTo(b);
|
||||
if (!forSign) {
|
||||
b.append((uint8_t)1); // 1 == Ed25519
|
||||
b.append((uint16_t)ZT_C25519_SIGNATURE_LEN); // length of signature
|
||||
b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
|
||||
}
|
||||
|
||||
b.append((uint16_t)0); // length of additional fields, currently 0
|
||||
|
||||
if (forSign) {
|
||||
b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned int C>
|
||||
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
|
||||
{
|
||||
unsigned int p = startAt;
|
||||
|
||||
*this = CertificateOfOwnership();
|
||||
|
||||
_networkId = b.template at<uint64_t>(p);
|
||||
p += 8;
|
||||
_ts = b.template at<uint64_t>(p);
|
||||
p += 8;
|
||||
_flags = b.template at<uint64_t>(p);
|
||||
p += 8;
|
||||
_id = b.template at<uint32_t>(p);
|
||||
p += 4;
|
||||
_thingCount = b.template at<uint16_t>(p);
|
||||
p += 2;
|
||||
for(unsigned int i=0,j=_thingCount;i<j;++i) {
|
||||
if (i < ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS) {
|
||||
_thingTypes[i] = (uint8_t)b[p++];
|
||||
memcpy(_thingValues[i],b.field(p,ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE),ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE);
|
||||
p += ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
_issuedTo.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
|
||||
p += ZT_ADDRESS_LENGTH;
|
||||
_signedBy.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
|
||||
p += ZT_ADDRESS_LENGTH;
|
||||
if (b[p++] == 1) {
|
||||
if (b.template at<uint16_t>(p) != ZT_C25519_SIGNATURE_LEN) {
|
||||
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
|
||||
}
|
||||
p += 2;
|
||||
memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN);
|
||||
p += ZT_C25519_SIGNATURE_LEN;
|
||||
} else {
|
||||
p += 2 + b.template at<uint16_t>(p);
|
||||
}
|
||||
|
||||
p += 2 + b.template at<uint16_t>(p);
|
||||
if (p > b.size()) {
|
||||
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
|
||||
}
|
||||
|
||||
return (p - startAt);
|
||||
}
|
||||
|
||||
// Provides natural sort order by ID
|
||||
inline bool operator<(const CertificateOfOwnership &coo) const { return (_id < coo._id); }
|
||||
|
||||
inline bool operator==(const CertificateOfOwnership &coo) const { return (memcmp(this,&coo,sizeof(CertificateOfOwnership)) == 0); }
|
||||
inline bool operator!=(const CertificateOfOwnership &coo) const { return (memcmp(this,&coo,sizeof(CertificateOfOwnership)) != 0); }
|
||||
|
||||
private:
|
||||
bool _owns(const Thing &t,const void *v,unsigned int l) const;
|
||||
|
||||
uint64_t _networkId;
|
||||
int64_t _ts;
|
||||
uint64_t _flags;
|
||||
uint32_t _id;
|
||||
uint16_t _thingCount;
|
||||
uint8_t _thingTypes[ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS];
|
||||
uint8_t _thingValues[ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS][ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE];
|
||||
Address _issuedTo;
|
||||
Address _signedBy;
|
||||
C25519::Signature _signature;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,767 @@
|
||||
/*
|
||||
* Copyright (c)2013-2020 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_CONSTANTS_HPP
|
||||
#define ZT_CONSTANTS_HPP
|
||||
|
||||
#include "../include/ZeroTierOne.h"
|
||||
|
||||
//
|
||||
// This include file also auto-detects and canonicalizes some environment
|
||||
// information defines:
|
||||
//
|
||||
// __LINUX__
|
||||
// __APPLE__
|
||||
// __BSD__ (OSX also defines this)
|
||||
// __UNIX_LIKE__ (Linux, BSD, etc.)
|
||||
// __WINDOWS__
|
||||
//
|
||||
// Also makes sure __BYTE_ORDER is defined reasonably.
|
||||
//
|
||||
|
||||
#ifndef ZT_INLINE
|
||||
#define ZT_INLINE inline
|
||||
#endif
|
||||
|
||||
#define restrict
|
||||
|
||||
// Hack: make sure __GCC__ is defined on old GCC compilers
|
||||
#ifndef __GCC__
|
||||
#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
|
||||
#define __GCC__
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
|
||||
#ifndef __LINUX__
|
||||
#define __LINUX__
|
||||
#endif
|
||||
#ifndef __UNIX_LIKE__
|
||||
#define __UNIX_LIKE__
|
||||
#endif
|
||||
#include <endian.h>
|
||||
#if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64) || defined(__aarch64__))
|
||||
#ifdef ZT_SSO_SUPPORTED
|
||||
#define ZT_SSO_ENABLED 1
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#ifdef ZT_SSO_SUPPORTED
|
||||
#define ZT_SSO_ENABLED 1
|
||||
#endif
|
||||
#define likely(x) __builtin_expect((x),1)
|
||||
#define unlikely(x) __builtin_expect((x),0)
|
||||
#include <TargetConditionals.h>
|
||||
#ifndef __UNIX_LIKE__
|
||||
#define __UNIX_LIKE__
|
||||
#endif
|
||||
#ifndef __BSD__
|
||||
#define __BSD__
|
||||
#endif
|
||||
#include <machine/endian.h>
|
||||
#endif
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
#ifdef ZT_SSO_SUPPORTED
|
||||
#define ZT_SSO_ENABLED 0
|
||||
#endif
|
||||
#ifndef __UNIX_LIKE__
|
||||
#define __UNIX_LIKE__
|
||||
#endif
|
||||
#ifndef __BSD__
|
||||
#define __BSD__
|
||||
#endif
|
||||
#include <machine/endian.h>
|
||||
#ifndef __BYTE_ORDER
|
||||
#define __BYTE_ORDER _BYTE_ORDER
|
||||
#define __LITTLE_ENDIAN _LITTLE_ENDIAN
|
||||
#define __BIG_ENDIAN _BIG_ENDIAN
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#ifdef ZT_SSO_SUPPORTED
|
||||
#define ZT_SSO_ENABLED 1
|
||||
#endif
|
||||
#ifndef __WINDOWS__
|
||||
#define __WINDOWS__
|
||||
#endif
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#pragma warning(disable : 4290)
|
||||
#pragma warning(disable : 4996)
|
||||
#pragma warning(disable : 4101)
|
||||
#undef __UNIX_LIKE__
|
||||
#undef __BSD__
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef __NetBSD__
|
||||
#ifndef RTF_MULTICAST
|
||||
#define RTF_MULTICAST 0x20000000
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64))
|
||||
#define ZT_ARCH_X64 1
|
||||
#include <xmmintrin.h>
|
||||
#include <emmintrin.h>
|
||||
#include <immintrin.h>
|
||||
#endif
|
||||
|
||||
#if (defined(__ARM_NEON) || defined(__ARM_NEON__) || defined(ZT_ARCH_ARM_HAS_NEON))
|
||||
#if (defined(__APPLE__) && !defined(__LP64__)) || (defined(__ANDROID__) && defined(__arm__))
|
||||
#ifdef ZT_ARCH_ARM_HAS_NEON
|
||||
#undef ZT_ARCH_ARM_HAS_NEON
|
||||
#endif
|
||||
#else
|
||||
#ifndef ZT_ARCH_ARM_HAS_NEON
|
||||
#define ZT_ARCH_ARM_HAS_NEON 1
|
||||
#endif
|
||||
#include <arm_neon.h>
|
||||
/*#include <arm_acle.h>*/
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef ZT_ARCH_ARM_HAS_CRYPTO
|
||||
#if defined(__ARM_FEATURE_CRYPTO)
|
||||
// ARM Cryptography Extension
|
||||
#define ZT_ARCH_ARM_HAS_CRYPTO
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Define ZT_NO_TYPE_PUNNING to disable reckless casts on anything other than x86/x64.
|
||||
#if (!(defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64) || defined(_M_X64) || defined(i386) || defined(__i386) || defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(__X86__) || defined(_X86_) || defined(__I86__) || defined(__INTEL__) || defined(__386)))
|
||||
#ifndef ZT_NO_TYPE_PUNNING
|
||||
#define ZT_NO_TYPE_PUNNING 1
|
||||
#endif
|
||||
#endif
|
||||
#ifdef ZT_NO_TYPE_PUNNING
|
||||
#ifndef ZT_NO_UNALIGNED_ACCESS
|
||||
#define ZT_NO_UNALIGNED_ACCESS 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Assume little endian if not defined
|
||||
#if (defined(__APPLE__) || defined(__WINDOWS__)) && (!defined(__BYTE_ORDER))
|
||||
#undef __BYTE_ORDER
|
||||
#undef __LITTLE_ENDIAN
|
||||
#undef __BIG_ENDIAN
|
||||
#define __BIG_ENDIAN 4321
|
||||
#define __LITTLE_ENDIAN 1234
|
||||
#define __BYTE_ORDER 1234
|
||||
#endif
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
#define ZT_PATH_SEPARATOR '\\'
|
||||
#define ZT_PATH_SEPARATOR_S "\\"
|
||||
#define ZT_EOL_S "\r\n"
|
||||
#else
|
||||
#define ZT_PATH_SEPARATOR '/'
|
||||
#define ZT_PATH_SEPARATOR_S "/"
|
||||
#define ZT_EOL_S "\n"
|
||||
#endif
|
||||
|
||||
#ifndef __BYTE_ORDER
|
||||
#include <endian.h>
|
||||
#endif
|
||||
|
||||
#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__)
|
||||
#ifndef likely
|
||||
#define likely(x) __builtin_expect((x),1)
|
||||
#endif
|
||||
#ifndef unlikely
|
||||
#define unlikely(x) __builtin_expect((x),0)
|
||||
#endif
|
||||
#else
|
||||
#ifndef likely
|
||||
#define likely(x) (x)
|
||||
#endif
|
||||
#ifndef unlikely
|
||||
#define unlikely(x) (x)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
#define ZT_PACKED_STRUCT(D) __pragma(pack(push,1)) D __pragma(pack(pop))
|
||||
#else
|
||||
#define ZT_PACKED_STRUCT(D) D __attribute__((packed))
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define ZT_PLATFORM_NAME "windows" // Windows
|
||||
#elif defined(_WIN64)
|
||||
#define ZT_PLATFORM_NAME "windows" // Windows
|
||||
#elif defined(__CYGWIN__)
|
||||
#define ZT_PLATFORM_NAME "windows" // Windows (Cygwin POSIX under Microsoft Window)
|
||||
#elif defined(__ANDROID__)
|
||||
#define ZT_PLATFORM_NAME "android" // Android (implies Linux, so it must come first)
|
||||
#elif defined(__linux__)
|
||||
#define ZT_PLATFORM_NAME "linux" // Debian, Ubuntu, Gentoo, Fedora, openSUSE, RedHat, Centos and other
|
||||
#elif defined(__unix__) || !defined(__APPLE__) && defined(__MACH__)
|
||||
#include <sys/param.h>
|
||||
#if defined(BSD)
|
||||
#define ZT_PLATFORM_NAME "bsd" // FreeBSD, NetBSD, OpenBSD, DragonFly BSD
|
||||
#endif
|
||||
#elif defined(__hpux)
|
||||
#define ZT_PLATFORM_NAME "hp-ux" // HP-UX
|
||||
#elif defined(_AIX)
|
||||
#define ZT_PLATFORM_NAME "aix" // IBM AIX
|
||||
#elif defined(__APPLE__) && defined(__MACH__) // Apple OSX and iOS (Darwin)
|
||||
#include <TargetConditionals.h>
|
||||
#if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR == 1
|
||||
#define ZT_PLATFORM_NAME "ios_sim" // Apple iOS
|
||||
#elif defined(TARGET_OS_IPAD) && TARGET_OS_IPAD == 1
|
||||
#define ZT_PLATFORM_NAME "ios_ipad"
|
||||
#elif defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1
|
||||
#define ZT_PLATFORM_NAME "ios_iphone" // Apple iOS
|
||||
#elif defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1
|
||||
#define ZT_PLATFORM_NAME "macos" // Apple OSX
|
||||
#endif
|
||||
#elif defined(__sun) && defined(__SVR4)
|
||||
#define ZT_PLATFORM_NAME "solaris" // Oracle Solaris, Open Indiana
|
||||
#else
|
||||
#define ZT_PLATFORM_NAME "unknown"
|
||||
#endif
|
||||
#ifndef ZT_PLATFORM_NAME
|
||||
#define ZT_PLATFORM_NAME "unknown"
|
||||
#endif
|
||||
|
||||
#if defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64) || defined(_M_AMD64)
|
||||
#define ZT_ARCH_NAME "x86_64"
|
||||
#elif defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(_X86_) || defined(_M_IX86) || defined(__X86__) || defined(__I86__) || defined(_M_I86)
|
||||
#define ZT_ARCH_NAME "x86"
|
||||
#elif defined(__aarch64__) || defined(__AARCH64EL__) || defined(_M_ARM64)
|
||||
#define ZT_ARCH_NAME "arm64"
|
||||
#elif defined(__arm__) || defined(__TARGET_ARCH_ARM) || defined(_ARM) || defined(_M_ARM) || defined(_M_ARMT) || defined(__arm) || defined(__thumb__)
|
||||
#define ZT_ARCH_NAME "arm"
|
||||
#elif defined(__loongarch__) || defined(_LOONGARCH_ARCH)
|
||||
#define ZT_ARCH_NAME "loongarch"
|
||||
#elif defined(__mips__) || defined(__MIPS__)
|
||||
#define ZT_ARCH_NAME "mips"
|
||||
#elif defined(__riscv) || defined(__riscv_xlen)
|
||||
#define ZT_ARCH_NAME "riscv"
|
||||
#elif defined(__powerpc__) || defined(__powerpc64__) || defined(__ppc__) || defined(__ppc64__) || defined (_M_PPC)
|
||||
#define ZT_ARCH_NAME "powerpc"
|
||||
#elif defined(__s390__) || defined(__s390x__) || defined(__zarch__)
|
||||
#define ZT_ARCH_NAME "s390"
|
||||
#else
|
||||
#define ZT_ARCH_NAME "unknown"
|
||||
#endif
|
||||
#ifndef ZT_ARCH_NAME
|
||||
#define ZT_ARCH_NAME "unknown"
|
||||
#endif
|
||||
|
||||
#define ZT_TARGET_NAME (ZT_PLATFORM_NAME "/" ZT_ARCH_NAME)
|
||||
|
||||
/**
|
||||
* Length of a ZeroTier address in bytes
|
||||
*/
|
||||
#define ZT_ADDRESS_LENGTH 5
|
||||
|
||||
/**
|
||||
* Length of a hexadecimal ZeroTier address
|
||||
*/
|
||||
#define ZT_ADDRESS_LENGTH_HEX 10
|
||||
|
||||
/**
|
||||
* Size of symmetric key (only the first 32 bits are used for some ciphers)
|
||||
*/
|
||||
#define ZT_SYMMETRIC_KEY_SIZE 48
|
||||
|
||||
/**
|
||||
* Addresses beginning with this byte are reserved for the joy of in-band signaling
|
||||
*/
|
||||
#define ZT_ADDRESS_RESERVED_PREFIX 0xff
|
||||
|
||||
/**
|
||||
* Default MTU used for Ethernet tap device
|
||||
*/
|
||||
#define ZT_DEFAULT_MTU 2800
|
||||
|
||||
/**
|
||||
* Maximum number of packet fragments we'll support (protocol max: 16)
|
||||
*/
|
||||
#define ZT_MAX_PACKET_FRAGMENTS 7
|
||||
|
||||
/**
|
||||
* Size of RX queue
|
||||
*/
|
||||
#define ZT_RX_QUEUE_SIZE 32
|
||||
|
||||
/**
|
||||
* Size of TX queue
|
||||
*/
|
||||
#define ZT_TX_QUEUE_SIZE 32
|
||||
|
||||
/**
|
||||
* Minimum delay between timer task checks to prevent thrashing
|
||||
*/
|
||||
#define ZT_CORE_TIMER_TASK_GRANULARITY 60
|
||||
|
||||
/**
|
||||
* How often Topology::clean() and Network::clean() and similar are called, in ms
|
||||
*/
|
||||
#define ZT_HOUSEKEEPING_PERIOD 30000
|
||||
|
||||
/**
|
||||
* Delay between WHOIS retries in ms
|
||||
*/
|
||||
#define ZT_WHOIS_RETRY_DELAY 500
|
||||
|
||||
/**
|
||||
* Transmit queue entry timeout
|
||||
*/
|
||||
#define ZT_TRANSMIT_QUEUE_TIMEOUT 5000
|
||||
|
||||
/**
|
||||
* Receive queue entry timeout
|
||||
*/
|
||||
#define ZT_RECEIVE_QUEUE_TIMEOUT 5000
|
||||
|
||||
/**
|
||||
* Maximum number of ZT hops allowed (this is not IP hops/TTL)
|
||||
*
|
||||
* The protocol allows up to 7, but we limit it to something smaller.
|
||||
*/
|
||||
#define ZT_RELAY_MAX_HOPS 3
|
||||
|
||||
/**
|
||||
* Expire time for multicast 'likes' and indirect multicast memberships in ms
|
||||
*/
|
||||
#define ZT_MULTICAST_LIKE_EXPIRE 600000
|
||||
|
||||
/**
|
||||
* Period for multicast LIKE announcements
|
||||
*/
|
||||
#define ZT_MULTICAST_ANNOUNCE_PERIOD 60000
|
||||
|
||||
/**
|
||||
* Delay between explicit MULTICAST_GATHER requests for a given multicast channel
|
||||
*/
|
||||
#define ZT_MULTICAST_EXPLICIT_GATHER_DELAY (ZT_MULTICAST_LIKE_EXPIRE / 10)
|
||||
|
||||
/**
|
||||
* Timeout for outgoing multicasts
|
||||
*
|
||||
* This is how long we wait for explicit or implicit gather results.
|
||||
*/
|
||||
#define ZT_MULTICAST_TRANSMIT_TIMEOUT 5000
|
||||
|
||||
/**
|
||||
* Delay between checks of peer pings, etc., and also related housekeeping tasks
|
||||
*/
|
||||
#define ZT_PING_CHECK_INTERVAL 5000
|
||||
|
||||
/**
|
||||
* How often the local.conf file is checked for changes (service, should be moved there)
|
||||
*/
|
||||
#define ZT_LOCAL_CONF_FILE_CHECK_INTERVAL 10000
|
||||
|
||||
/**
|
||||
* How frequently to send heartbeats over in-use paths
|
||||
*/
|
||||
#define ZT_PATH_HEARTBEAT_PERIOD 14000
|
||||
|
||||
/**
|
||||
* Do not accept HELLOs over a given path more often than this
|
||||
*/
|
||||
#define ZT_PATH_HELLO_RATE_LIMIT 1000
|
||||
|
||||
/**
|
||||
* Delay between full-fledge pings of directly connected peers
|
||||
*/
|
||||
#define ZT_PEER_PING_PERIOD 60000
|
||||
|
||||
/**
|
||||
* Paths are considered expired if they have not sent us a real packet in this long
|
||||
*/
|
||||
#define ZT_PEER_PATH_EXPIRATION ((ZT_PEER_PING_PERIOD * 4) + 3000)
|
||||
|
||||
/**
|
||||
* How often to retry expired paths that we're still remembering
|
||||
*/
|
||||
#define ZT_PEER_EXPIRED_PATH_TRIAL_PERIOD (ZT_PEER_PING_PERIOD * 10)
|
||||
|
||||
/**
|
||||
* Outgoing packets are only used for QoS/ACK statistical sampling if their
|
||||
* packet ID is divisible by this integer. This is to provide a mechanism for
|
||||
* both peers to agree on which packets need special treatment without having
|
||||
* to exchange information. Changing this value would be a breaking change and
|
||||
* would necessitate a protocol version upgrade. Since each incoming and
|
||||
* outgoing packet ID is checked against this value its evaluation is of the
|
||||
* form:
|
||||
*
|
||||
* (id & (divisor - 1)) == 0, thus the divisor must be a power of 2.
|
||||
*
|
||||
* This value is set at (16) so that given a normally-distributed RNG output
|
||||
* we will sample 1/16th (or ~6.25%) of packets.
|
||||
*/
|
||||
#define ZT_QOS_ACK_DIVISOR 0x2
|
||||
|
||||
/**
|
||||
* Time horizon for VERB_QOS_MEASUREMENT and VERB_ACK packet processing cutoff
|
||||
*/
|
||||
#define ZT_QOS_ACK_CUTOFF_TIME 30000
|
||||
|
||||
/**
|
||||
* Maximum number of VERB_QOS_MEASUREMENT and VERB_ACK packets allowed to be
|
||||
* processed within cutoff time. Separate totals are kept for each type but
|
||||
* the limit is the same for both.
|
||||
*
|
||||
* This limits how often this peer will compute statistical estimates
|
||||
* of various QoS measures from a VERB_QOS_MEASUREMENT or VERB_ACK packets to
|
||||
* CUTOFF_LIMIT times per CUTOFF_TIME milliseconds per peer to prevent
|
||||
* this from being useful for DOS amplification attacks.
|
||||
*/
|
||||
#define ZT_QOS_ACK_CUTOFF_LIMIT 128
|
||||
|
||||
/**
|
||||
* Minimum acceptable size for a VERB_QOS_MEASUREMENT packet
|
||||
*/
|
||||
#define ZT_QOS_MIN_PACKET_SIZE (8 + 1)
|
||||
|
||||
/**
|
||||
* Maximum acceptable size for a VERB_QOS_MEASUREMENT packet
|
||||
*/
|
||||
#define ZT_QOS_MAX_PACKET_SIZE 1400
|
||||
|
||||
/**
|
||||
* How many ID:sojourn time pairs are in a single QoS packet
|
||||
*/
|
||||
#define ZT_QOS_TABLE_SIZE ((ZT_QOS_MAX_PACKET_SIZE * 8) / (64 + 16))
|
||||
|
||||
/**
|
||||
* Maximum number of packets we monitor for ACK information at any given time
|
||||
*/
|
||||
#define ZT_ACK_MAX_PENDING_RECORDS (32 * 1024)
|
||||
|
||||
/**
|
||||
* Maximum number of packets we monitor for QoS information at any given time
|
||||
*/
|
||||
#define ZT_QOS_MAX_PENDING_RECORDS (ZT_QOS_TABLE_SIZE * 3)
|
||||
|
||||
/**
|
||||
* Interval used for rate-limiting the computation of path quality estimates.
|
||||
*/
|
||||
#define ZT_QOS_COMPUTE_INTERVAL 1000
|
||||
|
||||
/**
|
||||
* Number of samples to consider when processing real-time path statistics
|
||||
*/
|
||||
#define ZT_QOS_SHORTTERM_SAMPLE_WIN_SIZE 64
|
||||
|
||||
/**
|
||||
* Number of samples required before statistics summaries are computed
|
||||
*/
|
||||
#define ZT_QOS_SHORTTERM_SAMPLE_WIN_MIN_REQ_SIZE 4
|
||||
|
||||
/**
|
||||
* Max allowable time spent in any queue (in ms)
|
||||
*/
|
||||
#define ZT_AQM_TARGET 5
|
||||
|
||||
/**
|
||||
* Time period where the time spent in the queue by a packet should fall below.
|
||||
* target at least once. (in ms)
|
||||
*/
|
||||
#define ZT_AQM_INTERVAL 100
|
||||
|
||||
/**
|
||||
* The number of bytes that each queue is allowed to send during each DRR cycle.
|
||||
* This approximates a single-byte-based fairness queuing scheme.
|
||||
*/
|
||||
#define ZT_AQM_QUANTUM ZT_DEFAULT_MTU
|
||||
|
||||
/**
|
||||
* The maximum total number of packets that can be queued among all
|
||||
* active/inactive, old/new queues.
|
||||
*/
|
||||
#define ZT_AQM_MAX_ENQUEUED_PACKETS 1024
|
||||
|
||||
/**
|
||||
* Number of QoS queues (buckets)
|
||||
*/
|
||||
#define ZT_AQM_NUM_BUCKETS 9
|
||||
|
||||
/**
|
||||
* All unspecified traffic is put in this bucket. Anything in a bucket with a
|
||||
* smaller value is de-prioritized. Anything in a bucket with a higher value is
|
||||
prioritized over other traffic.
|
||||
*/
|
||||
#define ZT_AQM_DEFAULT_BUCKET 0
|
||||
|
||||
/**
|
||||
* Timeout for overall peer activity (measured from last receive)
|
||||
*/
|
||||
#ifndef ZT_SDK
|
||||
#define ZT_PEER_ACTIVITY_TIMEOUT 500000
|
||||
#else
|
||||
#define ZT_PEER_ACTIVITY_TIMEOUT 30000
|
||||
#endif
|
||||
|
||||
/**
|
||||
* General rate limit timeout for multiple packet types (HELLO, etc.)
|
||||
*/
|
||||
#define ZT_PEER_GENERAL_INBOUND_RATE_LIMIT 500
|
||||
|
||||
/**
|
||||
* General limit for max RTT for requests over the network
|
||||
*/
|
||||
#define ZT_GENERAL_RTT_LIMIT 5000
|
||||
|
||||
/**
|
||||
* Delay between requests for updated network autoconf information
|
||||
*
|
||||
* Don't lengthen this as it affects things like QoS / uptime monitoring
|
||||
* via ZeroTier Central. This is the heartbeat, basically.
|
||||
*/
|
||||
#define ZT_NETWORK_AUTOCONF_DELAY 60000
|
||||
|
||||
/**
|
||||
* Minimum interval between attempts by relays to unite peers
|
||||
*
|
||||
* When a relay gets a packet destined for another peer, it sends both peers
|
||||
* a RENDEZVOUS message no more than this often. This instructs the peers
|
||||
* to attempt NAT-t and gives each the other's corresponding IP:port pair.
|
||||
*/
|
||||
#define ZT_MIN_UNITE_INTERVAL 30000
|
||||
|
||||
/**
|
||||
* How often should peers try memorized or statically defined paths?
|
||||
*/
|
||||
#define ZT_TRY_MEMORIZED_PATH_INTERVAL 30000
|
||||
|
||||
/**
|
||||
* Sanity limit on maximum bridge routes
|
||||
*
|
||||
* If the number of bridge routes exceeds this, we cull routes from the
|
||||
* bridges with the most MACs behind them until it doesn't. This is a
|
||||
* sanity limit to prevent memory-filling DOS attacks, nothing more. No
|
||||
* physical LAN has anywhere even close to this many nodes. Note that this
|
||||
* does not limit the size of ZT virtual LANs, only bridge routing.
|
||||
*/
|
||||
#define ZT_MAX_BRIDGE_ROUTES 67108864
|
||||
|
||||
/**
|
||||
* If there is no known L2 bridging route, spam to up to this many active bridges
|
||||
*/
|
||||
#define ZT_MAX_BRIDGE_SPAM 32
|
||||
|
||||
/**
|
||||
* Interval between direct path pushes in milliseconds
|
||||
*/
|
||||
#define ZT_DIRECT_PATH_PUSH_INTERVAL 15000
|
||||
|
||||
/**
|
||||
* Interval between direct path pushes in milliseconds if we already have a path
|
||||
*/
|
||||
#define ZT_DIRECT_PATH_PUSH_INTERVAL_HAVEPATH 120000
|
||||
|
||||
/**
|
||||
* Time horizon for push direct paths cutoff
|
||||
*/
|
||||
#define ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME 30000
|
||||
|
||||
/**
|
||||
* Drainage constants for VERB_ECHO rate-limiters
|
||||
*/
|
||||
#define ZT_ECHO_CUTOFF_LIMIT ((1000 / ZT_CORE_TIMER_TASK_GRANULARITY) * ZT_MAX_PEER_NETWORK_PATHS)
|
||||
#define ZT_ECHO_DRAINAGE_DIVISOR (1000 / ZT_ECHO_CUTOFF_LIMIT)
|
||||
|
||||
/**
|
||||
* Drainage constants for VERB_QOS rate-limiters
|
||||
*/
|
||||
#define ZT_QOS_CUTOFF_LIMIT ((1000 / ZT_CORE_TIMER_TASK_GRANULARITY) * ZT_MAX_PEER_NETWORK_PATHS)
|
||||
#define ZT_QOS_DRAINAGE_DIVISOR (1000 / ZT_QOS_CUTOFF_LIMIT)
|
||||
|
||||
/**
|
||||
* Drainage constants for VERB_ACK rate-limiters
|
||||
*/
|
||||
#define ZT_ACK_CUTOFF_LIMIT 128
|
||||
#define ZT_ACK_DRAINAGE_DIVISOR (1000 / ZT_ACK_CUTOFF_LIMIT)
|
||||
|
||||
#define ZT_BOND_DEFAULT_REFRACTORY_PERIOD 8000
|
||||
#define ZT_BOND_MAX_REFRACTORY_PERIOD 600000
|
||||
|
||||
/**
|
||||
* Maximum number of direct path pushes within cutoff time
|
||||
*
|
||||
* This limits response to PUSH_DIRECT_PATHS to CUTOFF_LIMIT responses
|
||||
* per CUTOFF_TIME milliseconds per peer to prevent this from being
|
||||
* useful for DOS amplification attacks.
|
||||
*/
|
||||
#define ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT 8
|
||||
|
||||
/**
|
||||
* Maximum number of paths per IP scope (e.g. global, link-local) and family (e.g. v4/v6)
|
||||
*/
|
||||
#define ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY 8
|
||||
|
||||
/**
|
||||
* Rate limit for network credential pushes from peer.
|
||||
*/
|
||||
#define ZT_PEER_CREDENTIALS_RATE_LIMIT 1000
|
||||
|
||||
/**
|
||||
* Rate limit for responding to peer credential requests
|
||||
*/
|
||||
#define ZT_PEER_CREDENTIALS_REQUEST_RATE_LIMIT 1000
|
||||
|
||||
/**
|
||||
* WHOIS rate limit (we allow these to be pretty fast)
|
||||
*/
|
||||
#define ZT_PEER_WHOIS_RATE_LIMIT 100
|
||||
|
||||
/**
|
||||
* General rate limit for other kinds of rate-limited packets (HELLO, credential request, etc.) both inbound and outbound
|
||||
*/
|
||||
#define ZT_PEER_GENERAL_RATE_LIMIT 1000
|
||||
|
||||
|
||||
/**
|
||||
* Minimum allowed amount of time between flow/path optimizations (anti-flapping)
|
||||
*/
|
||||
#define ZT_BOND_OPTIMIZE_INTERVAL 15000
|
||||
|
||||
/**
|
||||
* Maximum number of flows allowed before we start forcibly forgetting old ones
|
||||
*/
|
||||
#define ZT_FLOW_MAX_COUNT (1024 * 64)
|
||||
|
||||
/**
|
||||
* How often we emit a bond summary for each bond
|
||||
*/
|
||||
#define ZT_BOND_STATUS_INTERVAL 30000
|
||||
|
||||
/**
|
||||
* How long before we consider a path to be dead in the general sense. This is
|
||||
* used while searching for default or alternative paths to try in the absence
|
||||
* of direct guidance from the user or a selection policy.
|
||||
*/
|
||||
#define ZT_BOND_FAILOVER_DEFAULT_INTERVAL 5000
|
||||
|
||||
/**
|
||||
* Anything below this value gets into thrashing territory since we divide
|
||||
* this value by ZT_BOND_ECHOS_PER_FAILOVER_INTERVAL to send ECHOs often.
|
||||
*/
|
||||
#define ZT_BOND_FAILOVER_MIN_INTERVAL 500
|
||||
|
||||
/**
|
||||
* How many times per failover interval that an ECHO is sent. This should be
|
||||
* at least 2. Anything more then 4 starts to increase overhead significantly.
|
||||
*/
|
||||
#define ZT_BOND_ECHOS_PER_FAILOVER_INTERVAL 3
|
||||
|
||||
/**
|
||||
* A defensive timer to prevent path quality metrics from being
|
||||
* processed too often.
|
||||
*/
|
||||
#define ZT_BOND_BACKGROUND_TASK_MIN_INTERVAL ZT_CORE_TIMER_TASK_GRANULARITY
|
||||
|
||||
/**
|
||||
* How often a bonding policy's background tasks are processed,
|
||||
* some need more frequent attention than others.
|
||||
*/
|
||||
#define ZT_BOND_ACTIVE_BACKUP_CHECK_INTERVAL ZT_CORE_TIMER_TASK_GRANULARITY
|
||||
|
||||
/**
|
||||
* Time horizon for path negotiation paths cutoff
|
||||
*/
|
||||
#define ZT_PATH_NEGOTIATION_CUTOFF_TIME 60000
|
||||
|
||||
/**
|
||||
* Maximum number of path negotiations within cutoff time
|
||||
*
|
||||
* This limits response to PATH_NEGOTIATION to CUTOFF_LIMIT responses
|
||||
* per CUTOFF_TIME milliseconds per peer to prevent this from being
|
||||
* useful for DOS amplification attacks.
|
||||
*/
|
||||
#define ZT_PATH_NEGOTIATION_CUTOFF_LIMIT 8
|
||||
|
||||
/**
|
||||
* How many times a peer will attempt to petition another peer to synchronize its
|
||||
* traffic to the same path before giving up and surrendering to the other peer's preference.
|
||||
*/
|
||||
#define ZT_PATH_NEGOTIATION_TRY_COUNT 3
|
||||
|
||||
/**
|
||||
* How much greater the quality of a path should be before an
|
||||
* optimization procedure triggers a switch.
|
||||
*/
|
||||
#define ZT_BOND_ACTIVE_BACKUP_OPTIMIZE_MIN_THRESHOLD 0.10
|
||||
|
||||
/**
|
||||
* Artificially inflates the failover score for paths which meet
|
||||
* certain non-performance-related policy ranking criteria.
|
||||
*/
|
||||
#define ZT_BOND_FAILOVER_HANDICAP_PREFERRED 500
|
||||
#define ZT_BOND_FAILOVER_HANDICAP_PRIMARY 1000
|
||||
#define ZT_BOND_FAILOVER_HANDICAP_NEGOTIATED 5000
|
||||
|
||||
/**
|
||||
* An indicator that no flow is to be associated with the given packet
|
||||
*/
|
||||
#define ZT_QOS_NO_FLOW -1
|
||||
|
||||
/**
|
||||
* Don't do expensive identity validation more often than this
|
||||
*
|
||||
* IPv4 and IPv6 address prefixes are hashed down to 14-bit (0-16383) integers
|
||||
* using the first 24 bits for IPv4 or the first 48 bits for IPv6. These are
|
||||
* then rate limited to one identity validation per this often milliseconds.
|
||||
*/
|
||||
#if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64) || defined(_M_AMD64))
|
||||
// AMD64 machines can do anywhere from one every 50ms to one every 10ms. This provides plenty of margin.
|
||||
#define ZT_IDENTITY_VALIDATION_SOURCE_RATE_LIMIT 2000
|
||||
#else
|
||||
#if (defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(_X86_) || defined(__I86__))
|
||||
// 32-bit Intel machines usually average about one every 100ms
|
||||
#define ZT_IDENTITY_VALIDATION_SOURCE_RATE_LIMIT 5000
|
||||
#else
|
||||
// This provides a safe margin for ARM, MIPS, etc. that usually average one every 250-400ms
|
||||
#define ZT_IDENTITY_VALIDATION_SOURCE_RATE_LIMIT 10000
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* How long is a path or peer considered to have a trust relationship with us (for e.g. relay policy) since last trusted established packet?
|
||||
*/
|
||||
#define ZT_TRUST_EXPIRATION 600000
|
||||
|
||||
/**
|
||||
* Desired buffer size for UDP sockets (used in service and osdep but defined here)
|
||||
*/
|
||||
#define ZT_UDP_DESIRED_BUF_SIZE 1048576
|
||||
|
||||
/**
|
||||
* Desired / recommended min stack size for threads (used on some platforms to reset thread stack size)
|
||||
*/
|
||||
#define ZT_THREAD_MIN_STACK_SIZE 1048576
|
||||
|
||||
// Exceptions thrown in core ZT code
|
||||
#define ZT_EXCEPTION_OUT_OF_BOUNDS 100
|
||||
#define ZT_EXCEPTION_OUT_OF_MEMORY 101
|
||||
#define ZT_EXCEPTION_PRIVATE_KEY_REQUIRED 102
|
||||
#define ZT_EXCEPTION_INVALID_ARGUMENT 103
|
||||
#define ZT_EXCEPTION_INVALID_IDENTITY 104
|
||||
#define ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE 200
|
||||
#define ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW 201
|
||||
#define ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN 202
|
||||
#define ZT_EXCEPTION_INVALID_SERIALIZED_DATA_BAD_ENCODING 203
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_CREDENTIAL_HPP
|
||||
#define ZT_CREDENTIAL_HPP
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Constants.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* Base class for credentials
|
||||
*/
|
||||
class Credential
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Do not change type code IDs -- these are used in Revocation objects and elsewhere
|
||||
*/
|
||||
enum Type
|
||||
{
|
||||
CREDENTIAL_TYPE_NULL = 0,
|
||||
CREDENTIAL_TYPE_COM = 1, // CertificateOfMembership
|
||||
CREDENTIAL_TYPE_CAPABILITY = 2,
|
||||
CREDENTIAL_TYPE_TAG = 3,
|
||||
CREDENTIAL_TYPE_COO = 4, // CertificateOfOwnership
|
||||
CREDENTIAL_TYPE_REVOCATION = 6
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c)2020 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_DNS_HPP
|
||||
#define ZT_DNS_HPP
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Buffer.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "../include/ZeroTierOne.h"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* DNS data serialization methods
|
||||
*/
|
||||
class DNS {
|
||||
public:
|
||||
template<unsigned int C>
|
||||
static inline void serializeDNS(Buffer<C> &b, const ZT_VirtualNetworkDNS *dns)
|
||||
{
|
||||
b.append(dns->domain, 128);
|
||||
for(unsigned int j = 0; j < ZT_MAX_DNS_SERVERS; ++j) {
|
||||
InetAddress tmp(dns->server_addr[j]);
|
||||
tmp.serialize(b);
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned int C>
|
||||
static inline void deserializeDNS(const Buffer<C> &b, unsigned int &p, ZT_VirtualNetworkDNS *dns)
|
||||
{
|
||||
char *d = (char*)b.data()+p;
|
||||
memset(dns, 0, sizeof(ZT_VirtualNetworkDNS));
|
||||
memcpy(dns->domain, d, 128);
|
||||
dns->domain[127] = 0;
|
||||
p += 128;
|
||||
for (unsigned int j = 0; j < ZT_MAX_DNS_SERVERS; ++j) {
|
||||
p += reinterpret_cast<InetAddress *>(&(dns->server_addr[j]))->deserialize(b, p);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // ZT_DNS_HPP
|
||||
@@ -0,0 +1,479 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_DICTIONARY_HPP
|
||||
#define ZT_DICTIONARY_HPP
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "Buffer.hpp"
|
||||
#include "Address.hpp"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* A small (in code and data) packed key=value store
|
||||
*
|
||||
* This stores data in the form of a compact blob that is sort of human
|
||||
* readable (depending on whether you put binary data in it) and is backward
|
||||
* compatible with older versions. Binary data is escaped such that the
|
||||
* serialized form of a Dictionary is always a valid null-terminated C string.
|
||||
*
|
||||
* Keys are restricted: no binary data, no CR/LF, and no equals (=). If a key
|
||||
* contains these characters it may not be retrievable. This is not checked.
|
||||
*
|
||||
* Lookup is via linear search and will be slow with a lot of keys. It's
|
||||
* designed for small things.
|
||||
*
|
||||
* There is code to test and fuzz this in selftest.cpp. Fuzzing a blob of
|
||||
* pointer tricks like this is important after any modifications.
|
||||
*
|
||||
* This is used for network configurations and for saving some things on disk
|
||||
* in the ZeroTier One service code.
|
||||
*
|
||||
* @tparam C Dictionary max capacity in bytes
|
||||
*/
|
||||
template<unsigned int C>
|
||||
class Dictionary
|
||||
{
|
||||
public:
|
||||
Dictionary() { memset(_d,0,sizeof(_d)); }
|
||||
Dictionary(const char *s) { this->load(s); }
|
||||
Dictionary(const char *s,unsigned int len)
|
||||
{
|
||||
for(unsigned int i=0;i<C;++i) {
|
||||
if ((s)&&(i < len)) {
|
||||
if (!(_d[i] = *s)) {
|
||||
s = (const char *)0;
|
||||
} else {
|
||||
++s;
|
||||
}
|
||||
} else {
|
||||
_d[i] = (char)0;
|
||||
}
|
||||
}
|
||||
_d[C - 1] = (char)0;
|
||||
}
|
||||
Dictionary(const Dictionary &d) { memcpy(_d,d._d,C); }
|
||||
|
||||
inline Dictionary &operator=(const Dictionary &d)
|
||||
{
|
||||
memcpy(_d,d._d,C);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline operator bool() const { return (_d[0] != 0); }
|
||||
|
||||
/**
|
||||
* Load a dictionary from a C-string
|
||||
*
|
||||
* @param s Dictionary in string form
|
||||
* @return False if 's' was longer than our capacity
|
||||
*/
|
||||
inline bool load(const char *s)
|
||||
{
|
||||
for(unsigned int i=0;i<C;++i) {
|
||||
if (s) {
|
||||
if (!(_d[i] = *s)) {
|
||||
s = (const char *)0;
|
||||
} else {
|
||||
++s;
|
||||
}
|
||||
} else {
|
||||
_d[i] = (char)0;
|
||||
}
|
||||
}
|
||||
_d[C - 1] = (char)0;
|
||||
return (!s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all entries
|
||||
*/
|
||||
inline void clear()
|
||||
{
|
||||
memset(_d,0,sizeof(_d));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Size of dictionary in bytes not including terminating NULL
|
||||
*/
|
||||
inline unsigned int sizeBytes() const
|
||||
{
|
||||
for(unsigned int i=0;i<C;++i) {
|
||||
if (!_d[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return C-1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an entry
|
||||
*
|
||||
* Note that to get binary values, dest[] should be at least one more than
|
||||
* the maximum size of the value being retrieved. That's because even if
|
||||
* the data is binary a terminating 0 is still appended to dest[] after it.
|
||||
*
|
||||
* If the key is not found, dest[0] is set to 0 to make dest[] an empty
|
||||
* C string in that case. The dest[] array will *never* be unterminated
|
||||
* after this call.
|
||||
*
|
||||
* Security note: if 'key' is ever directly based on anything that is not
|
||||
* a hard-code or internally-generated name, it must be checked to ensure
|
||||
* that the buffer is NULL-terminated since key[] does not take a secondary
|
||||
* size parameter. In NetworkConfig all keys are hard-coded strings so this
|
||||
* isn't a problem in the core.
|
||||
*
|
||||
* @param key Key to look up
|
||||
* @param dest Destination buffer
|
||||
* @param destlen Size of destination buffer
|
||||
* @return -1 if not found, or actual number of bytes stored in dest[] minus trailing 0
|
||||
*/
|
||||
inline int get(const char *key,char *dest,unsigned int destlen) const
|
||||
{
|
||||
const char *p = _d;
|
||||
const char *const eof = p + C;
|
||||
const char *k;
|
||||
bool esc;
|
||||
int j;
|
||||
|
||||
if (!destlen) { // sanity check
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (*p) {
|
||||
k = key;
|
||||
while ((*k)&&(*p)) {
|
||||
if (*p != *k) {
|
||||
break;
|
||||
}
|
||||
++k;
|
||||
if (++p == eof) {
|
||||
dest[0] = (char)0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((!*k)&&(*p == '=')) {
|
||||
j = 0;
|
||||
esc = false;
|
||||
++p;
|
||||
while ((*p != 0)&&(*p != 13)&&(*p != 10)) {
|
||||
if (esc) {
|
||||
esc = false;
|
||||
switch(*p) {
|
||||
case 'r':
|
||||
dest[j++] = 13;
|
||||
break;
|
||||
case 'n':
|
||||
dest[j++] = 10;
|
||||
break;
|
||||
case '0':
|
||||
dest[j++] = (char)0;
|
||||
break;
|
||||
case 'e':
|
||||
dest[j++] = '=';
|
||||
break;
|
||||
default:
|
||||
dest[j++] = *p;
|
||||
break;
|
||||
}
|
||||
if (j == (int)destlen) {
|
||||
dest[j-1] = (char)0;
|
||||
return j-1;
|
||||
}
|
||||
} else if (*p == '\\') {
|
||||
esc = true;
|
||||
} else {
|
||||
dest[j++] = *p;
|
||||
if (j == (int)destlen) {
|
||||
dest[j-1] = (char)0;
|
||||
return j-1;
|
||||
}
|
||||
}
|
||||
if (++p == eof) {
|
||||
dest[0] = (char)0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
dest[j] = (char)0;
|
||||
return j;
|
||||
} else {
|
||||
while ((*p)&&(*p != 13)&&(*p != 10)) {
|
||||
if (++p == eof) {
|
||||
dest[0] = (char)0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (*p) {
|
||||
if (++p == eof) {
|
||||
dest[0] = (char)0;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dest[0] = (char)0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contents of a key into a buffer
|
||||
*
|
||||
* @param key Key to get
|
||||
* @param dest Destination buffer
|
||||
* @return True if key was found (if false, dest will be empty)
|
||||
* @tparam BC Buffer capacity (usually inferred)
|
||||
*/
|
||||
template<unsigned int BC>
|
||||
inline bool get(const char *key,Buffer<BC> &dest) const
|
||||
{
|
||||
const int r = this->get(key,const_cast<char *>(reinterpret_cast<const char *>(dest.data())),BC);
|
||||
if (r >= 0) {
|
||||
dest.setSize((unsigned int)r);
|
||||
return true;
|
||||
} else {
|
||||
dest.clear();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a boolean value
|
||||
*
|
||||
* @param key Key to look up
|
||||
* @param dfl Default value if not found in dictionary
|
||||
* @return Boolean value of key or 'dfl' if not found
|
||||
*/
|
||||
bool getB(const char *key,bool dfl = false) const
|
||||
{
|
||||
char tmp[4];
|
||||
if (this->get(key,tmp,sizeof(tmp)) >= 0) {
|
||||
return ((*tmp == '1')||(*tmp == 't')||(*tmp == 'T'));
|
||||
}
|
||||
return dfl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an unsigned int64 stored as hex in the dictionary
|
||||
*
|
||||
* @param key Key to look up
|
||||
* @param dfl Default value or 0 if unspecified
|
||||
* @return Decoded hex UInt value or 'dfl' if not found
|
||||
*/
|
||||
inline uint64_t getUI(const char *key,uint64_t dfl = 0) const
|
||||
{
|
||||
char tmp[128];
|
||||
if (this->get(key,tmp,sizeof(tmp)) >= 1) {
|
||||
return Utils::hexStrToU64(tmp);
|
||||
}
|
||||
return dfl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an unsigned int64 stored as hex in the dictionary
|
||||
*
|
||||
* @param key Key to look up
|
||||
* @param dfl Default value or 0 if unspecified
|
||||
* @return Decoded hex UInt value or 'dfl' if not found
|
||||
*/
|
||||
inline int64_t getI(const char *key,int64_t dfl = 0) const
|
||||
{
|
||||
char tmp[128];
|
||||
if (this->get(key,tmp,sizeof(tmp)) >= 1) {
|
||||
return Utils::hexStrTo64(tmp);
|
||||
}
|
||||
return dfl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new key=value pair
|
||||
*
|
||||
* If the key is already present this will append another, but the first
|
||||
* will always be returned by get(). This is not checked. If you want to
|
||||
* ensure a key is not present use erase() first.
|
||||
*
|
||||
* Use the vlen parameter to add binary values. Nulls will be escaped.
|
||||
*
|
||||
* @param key Key -- nulls, CR/LF, and equals (=) are illegal characters
|
||||
* @param value Value to set
|
||||
* @param vlen Length of value in bytes or -1 to treat value[] as a C-string and look for terminating 0
|
||||
* @return True if there was enough room to add this key=value pair
|
||||
*/
|
||||
inline bool add(const char *key,const char *value,int vlen = -1)
|
||||
{
|
||||
for(unsigned int i=0;i<C;++i) {
|
||||
if (!_d[i]) {
|
||||
unsigned int j = i;
|
||||
|
||||
if (j > 0) {
|
||||
_d[j++] = (char)10;
|
||||
if (j == C) {
|
||||
_d[i] = (char)0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const char *p = key;
|
||||
while (*p) {
|
||||
_d[j++] = *(p++);
|
||||
if (j == C) {
|
||||
_d[i] = (char)0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_d[j++] = '=';
|
||||
if (j == C) {
|
||||
_d[i] = (char)0;
|
||||
return false;
|
||||
}
|
||||
|
||||
p = value;
|
||||
int k = 0;
|
||||
while ( ((vlen < 0)&&(*p)) || (k < vlen) ) {
|
||||
switch(*p) {
|
||||
case 0:
|
||||
case 13:
|
||||
case 10:
|
||||
case '\\':
|
||||
case '=':
|
||||
_d[j++] = '\\';
|
||||
if (j == C) {
|
||||
_d[i] = (char)0;
|
||||
return false;
|
||||
}
|
||||
switch(*p) {
|
||||
case 0:
|
||||
_d[j++] = '0';
|
||||
break;
|
||||
case 13:
|
||||
_d[j++] = 'r';
|
||||
break;
|
||||
case 10:
|
||||
_d[j++] = 'n';
|
||||
break;
|
||||
case '\\':
|
||||
_d[j++] = '\\';
|
||||
break;
|
||||
case '=':
|
||||
_d[j++] = 'e';
|
||||
break;
|
||||
}
|
||||
if (j == C) {
|
||||
_d[i] = (char)0;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
_d[j++] = *p;
|
||||
if (j == C) {
|
||||
_d[i] = (char)0;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
++p;
|
||||
++k;
|
||||
}
|
||||
|
||||
_d[j] = (char)0;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a boolean as a '1' or a '0'
|
||||
*/
|
||||
inline bool add(const char *key,bool value)
|
||||
{
|
||||
return this->add(key,(value) ? "1" : "0",1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a 64-bit integer (unsigned) as a hex value
|
||||
*/
|
||||
inline bool add(const char *key,uint64_t value)
|
||||
{
|
||||
char tmp[32];
|
||||
return this->add(key,Utils::hex(value,tmp),-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a 64-bit integer (unsigned) as a hex value
|
||||
*/
|
||||
inline bool add(const char *key,int64_t value)
|
||||
{
|
||||
char tmp[32];
|
||||
if (value >= 0) {
|
||||
return this->add(key,Utils::hex((uint64_t)value,tmp),-1);
|
||||
} else {
|
||||
tmp[0] = '-';
|
||||
return this->add(key,Utils::hex((uint64_t)(value * -1),tmp+1),-1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a 64-bit integer (unsigned) as a hex value
|
||||
*/
|
||||
inline bool add(const char *key,const Address &a)
|
||||
{
|
||||
char tmp[32];
|
||||
return this->add(key,Utils::hex(a.toInt(),tmp),-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a binary buffer's contents as a value
|
||||
*
|
||||
* @tparam BC Buffer capacity (usually inferred)
|
||||
*/
|
||||
template<unsigned int BC>
|
||||
inline bool add(const char *key,const Buffer<BC> &value)
|
||||
{
|
||||
return this->add(key,(const char *)value.data(),(int)value.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key Key to check
|
||||
* @return True if key is present
|
||||
*/
|
||||
inline bool contains(const char *key) const
|
||||
{
|
||||
char tmp[2];
|
||||
return (this->get(key,tmp,2) >= 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Value of C template parameter
|
||||
*/
|
||||
inline unsigned int capacity() const { return C; }
|
||||
|
||||
inline const char *data() const { return _d; }
|
||||
inline char *unsafeData() { return _d; }
|
||||
|
||||
private:
|
||||
char _d[C];
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,435 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_HASHTABLE_HPP
|
||||
#define ZT_HASHTABLE_HPP
|
||||
|
||||
#include "Constants.hpp"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* A minimal hash table implementation for the ZeroTier core
|
||||
*/
|
||||
template<typename K,typename V>
|
||||
class Hashtable
|
||||
{
|
||||
private:
|
||||
struct _Bucket
|
||||
{
|
||||
_Bucket(const K &k,const V &v) : k(k),v(v) {}
|
||||
_Bucket(const K &k) : k(k),v() {}
|
||||
_Bucket(const _Bucket &b) : k(b.k),v(b.v) {}
|
||||
inline _Bucket &operator=(const _Bucket &b) { k = b.k; v = b.v; return *this; }
|
||||
K k;
|
||||
V v;
|
||||
_Bucket *next; // must be set manually for each _Bucket
|
||||
};
|
||||
|
||||
public:
|
||||
/**
|
||||
* A simple forward iterator (different from STL)
|
||||
*
|
||||
* It's safe to erase the last key, but not others. Don't use set() since that
|
||||
* may rehash and invalidate the iterator. Note the erasing the key will destroy
|
||||
* the targets of the pointers returned by next().
|
||||
*/
|
||||
class Iterator
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @param ht Hash table to iterate over
|
||||
*/
|
||||
Iterator(Hashtable &ht) :
|
||||
_idx(0),
|
||||
_ht(&ht),
|
||||
_b(ht._t[0])
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param kptr Pointer to set to point to next key
|
||||
* @param vptr Pointer to set to point to next value
|
||||
* @return True if kptr and vptr are set, false if no more entries
|
||||
*/
|
||||
inline bool next(K *&kptr,V *&vptr)
|
||||
{
|
||||
for(;;) {
|
||||
if (_b) {
|
||||
kptr = &(_b->k);
|
||||
vptr = &(_b->v);
|
||||
_b = _b->next;
|
||||
return true;
|
||||
}
|
||||
++_idx;
|
||||
if (_idx >= _ht->_bc) {
|
||||
return false;
|
||||
}
|
||||
_b = _ht->_t[_idx];
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned long _idx;
|
||||
Hashtable *_ht;
|
||||
_Bucket *_b;
|
||||
};
|
||||
//friend class Hashtable<K,V>::Iterator;
|
||||
|
||||
/**
|
||||
* @param bc Initial capacity in buckets (default: 64, must be nonzero)
|
||||
*/
|
||||
Hashtable(unsigned long bc = 64) :
|
||||
_t(reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * bc))),
|
||||
_bc(bc),
|
||||
_s(0)
|
||||
{
|
||||
if (!_t) {
|
||||
throw ZT_EXCEPTION_OUT_OF_MEMORY;
|
||||
}
|
||||
for(unsigned long i=0;i<bc;++i) {
|
||||
_t[i] = (_Bucket *)0;
|
||||
}
|
||||
}
|
||||
|
||||
Hashtable(const Hashtable<K,V> &ht) :
|
||||
_t(reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * ht._bc))),
|
||||
_bc(ht._bc),
|
||||
_s(ht._s)
|
||||
{
|
||||
if (!_t) {
|
||||
throw ZT_EXCEPTION_OUT_OF_MEMORY;
|
||||
}
|
||||
for(unsigned long i=0;i<_bc;++i) {
|
||||
_t[i] = (_Bucket *)0;
|
||||
}
|
||||
for(unsigned long i=0;i<_bc;++i) {
|
||||
const _Bucket *b = ht._t[i];
|
||||
while (b) {
|
||||
_Bucket *nb = new _Bucket(*b);
|
||||
nb->next = _t[i];
|
||||
_t[i] = nb;
|
||||
b = b->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~Hashtable()
|
||||
{
|
||||
this->clear();
|
||||
::free(_t);
|
||||
}
|
||||
|
||||
inline Hashtable &operator=(const Hashtable<K,V> &ht)
|
||||
{
|
||||
this->clear();
|
||||
if (ht._s) {
|
||||
for(unsigned long i=0;i<ht._bc;++i) {
|
||||
const _Bucket *b = ht._t[i];
|
||||
while (b) {
|
||||
this->set(b->k,b->v);
|
||||
b = b->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Erase all entries
|
||||
*/
|
||||
inline void clear()
|
||||
{
|
||||
if (_s) {
|
||||
for(unsigned long i=0;i<_bc;++i) {
|
||||
_Bucket *b = _t[i];
|
||||
while (b) {
|
||||
_Bucket *const nb = b->next;
|
||||
delete b;
|
||||
b = nb;
|
||||
}
|
||||
_t[i] = (_Bucket *)0;
|
||||
}
|
||||
_s = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Vector of all keys
|
||||
*/
|
||||
inline typename std::vector<K> keys() const
|
||||
{
|
||||
typename std::vector<K> k;
|
||||
if (_s) {
|
||||
k.reserve(_s);
|
||||
for(unsigned long i=0;i<_bc;++i) {
|
||||
_Bucket *b = _t[i];
|
||||
while (b) {
|
||||
k.push_back(b->k);
|
||||
b = b->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append all keys (in unspecified order) to the supplied vector or list
|
||||
*
|
||||
* @param v Vector, list, or other compliant container
|
||||
* @tparam Type of V (generally inferred)
|
||||
*/
|
||||
template<typename C>
|
||||
inline void appendKeys(C &v) const
|
||||
{
|
||||
if (_s) {
|
||||
for(unsigned long i=0;i<_bc;++i) {
|
||||
_Bucket *b = _t[i];
|
||||
while (b) {
|
||||
v.push_back(b->k);
|
||||
b = b->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Vector of all entries (pairs of K,V)
|
||||
*/
|
||||
inline typename std::vector< std::pair<K,V> > entries() const
|
||||
{
|
||||
typename std::vector< std::pair<K,V> > k;
|
||||
if (_s) {
|
||||
k.reserve(_s);
|
||||
for(unsigned long i=0;i<_bc;++i) {
|
||||
_Bucket *b = _t[i];
|
||||
while (b) {
|
||||
k.push_back(std::pair<K,V>(b->k,b->v));
|
||||
b = b->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param k Key
|
||||
* @return Pointer to value or NULL if not found
|
||||
*/
|
||||
inline V *get(const K &k)
|
||||
{
|
||||
_Bucket *b = _t[_hc(k) % _bc];
|
||||
while (b) {
|
||||
if (b->k == k) {
|
||||
return &(b->v);
|
||||
}
|
||||
b = b->next;
|
||||
}
|
||||
return (V *)0;
|
||||
}
|
||||
inline const V *get(const K &k) const { return const_cast<Hashtable *>(this)->get(k); }
|
||||
|
||||
/**
|
||||
* @param k Key
|
||||
* @param v Value to fill with result
|
||||
* @return True if value was found and set (if false, v is not modified)
|
||||
*/
|
||||
inline bool get(const K &k,V &v) const
|
||||
{
|
||||
_Bucket *b = _t[_hc(k) % _bc];
|
||||
while (b) {
|
||||
if (b->k == k) {
|
||||
v = b->v;
|
||||
return true;
|
||||
}
|
||||
b = b->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param k Key to check
|
||||
* @return True if key is present
|
||||
*/
|
||||
inline bool contains(const K &k) const
|
||||
{
|
||||
_Bucket *b = _t[_hc(k) % _bc];
|
||||
while (b) {
|
||||
if (b->k == k) {
|
||||
return true;
|
||||
}
|
||||
b = b->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param k Key
|
||||
* @return True if value was present
|
||||
*/
|
||||
inline bool erase(const K &k)
|
||||
{
|
||||
const unsigned long bidx = _hc(k) % _bc;
|
||||
_Bucket *lastb = (_Bucket *)0;
|
||||
_Bucket *b = _t[bidx];
|
||||
while (b) {
|
||||
if (b->k == k) {
|
||||
if (lastb) {
|
||||
lastb->next = b->next;
|
||||
} else {
|
||||
_t[bidx] = b->next;
|
||||
}
|
||||
delete b;
|
||||
--_s;
|
||||
return true;
|
||||
}
|
||||
lastb = b;
|
||||
b = b->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param k Key
|
||||
* @param v Value
|
||||
* @return Reference to value in table
|
||||
*/
|
||||
inline V &set(const K &k,const V &v)
|
||||
{
|
||||
const unsigned long h = _hc(k);
|
||||
unsigned long bidx = h % _bc;
|
||||
|
||||
_Bucket *b = _t[bidx];
|
||||
while (b) {
|
||||
if (b->k == k) {
|
||||
b->v = v;
|
||||
return b->v;
|
||||
}
|
||||
b = b->next;
|
||||
}
|
||||
|
||||
if (_s >= _bc) {
|
||||
_grow();
|
||||
bidx = h % _bc;
|
||||
}
|
||||
|
||||
b = new _Bucket(k,v);
|
||||
b->next = _t[bidx];
|
||||
_t[bidx] = b;
|
||||
++_s;
|
||||
return b->v;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param k Key
|
||||
* @return Value, possibly newly created
|
||||
*/
|
||||
inline V &operator[](const K &k)
|
||||
{
|
||||
const unsigned long h = _hc(k);
|
||||
unsigned long bidx = h % _bc;
|
||||
|
||||
_Bucket *b = _t[bidx];
|
||||
while (b) {
|
||||
if (b->k == k) {
|
||||
return b->v;
|
||||
}
|
||||
b = b->next;
|
||||
}
|
||||
|
||||
if (_s >= _bc) {
|
||||
_grow();
|
||||
bidx = h % _bc;
|
||||
}
|
||||
|
||||
b = new _Bucket(k);
|
||||
b->next = _t[bidx];
|
||||
_t[bidx] = b;
|
||||
++_s;
|
||||
return b->v;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Number of entries
|
||||
*/
|
||||
inline unsigned long size() const { return _s; }
|
||||
|
||||
/**
|
||||
* @return True if table is empty
|
||||
*/
|
||||
inline bool empty() const { return (_s == 0); }
|
||||
|
||||
private:
|
||||
template<typename O>
|
||||
static inline unsigned long _hc(const O &obj)
|
||||
{
|
||||
return (unsigned long)obj.hashCode();
|
||||
}
|
||||
static inline unsigned long _hc(const uint64_t i)
|
||||
{
|
||||
return (unsigned long)(i ^ (i >> 32)); // good for network IDs and addresses
|
||||
}
|
||||
static inline unsigned long _hc(const uint32_t i)
|
||||
{
|
||||
return ((unsigned long)i * (unsigned long)0x9e3779b1);
|
||||
}
|
||||
static inline unsigned long _hc(const uint16_t i)
|
||||
{
|
||||
return ((unsigned long)i * (unsigned long)0x9e3779b1);
|
||||
}
|
||||
static inline unsigned long _hc(const int i)
|
||||
{
|
||||
return ((unsigned long)i * (unsigned long)0x9e3379b1);
|
||||
}
|
||||
|
||||
inline void _grow()
|
||||
{
|
||||
const unsigned long nc = _bc * 2;
|
||||
_Bucket **nt = reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * nc));
|
||||
if (nt) {
|
||||
for(unsigned long i=0;i<nc;++i) {
|
||||
nt[i] = (_Bucket *)0;
|
||||
}
|
||||
for(unsigned long i=0;i<_bc;++i) {
|
||||
_Bucket *b = _t[i];
|
||||
while (b) {
|
||||
_Bucket *const nb = b->next;
|
||||
const unsigned long nidx = _hc(b->k) % nc;
|
||||
b->next = nt[nidx];
|
||||
nt[nidx] = b;
|
||||
b = nb;
|
||||
}
|
||||
}
|
||||
::free(_t);
|
||||
_t = nt;
|
||||
_bc = nc;
|
||||
}
|
||||
}
|
||||
|
||||
_Bucket **_t;
|
||||
unsigned long _bc;
|
||||
unsigned long _s;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "SHA512.hpp"
|
||||
#include "Salsa20.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
// These can't be changed without a new identity type. They define the
|
||||
// parameters of the hashcash hashing/searching algorithm.
|
||||
|
||||
#define ZT_IDENTITY_GEN_HASHCASH_FIRST_BYTE_LESS_THAN 17
|
||||
#define ZT_IDENTITY_GEN_MEMORY 2097152
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
// A memory-hard composition of SHA-512 and Salsa20 for hashcash hashing
|
||||
static inline void _computeMemoryHardHash(const void *publicKey,unsigned int publicKeyBytes,void *digest,void *genmem)
|
||||
{
|
||||
// Digest publicKey[] to obtain initial digest
|
||||
SHA512(digest,publicKey,publicKeyBytes);
|
||||
|
||||
// Initialize genmem[] using Salsa20 in a CBC-like configuration since
|
||||
// ordinary Salsa20 is randomly seek-able. This is good for a cipher
|
||||
// but is not what we want for sequential memory-hardness.
|
||||
memset(genmem,0,ZT_IDENTITY_GEN_MEMORY);
|
||||
Salsa20 s20(digest,(char *)digest + 32);
|
||||
s20.crypt20((char *)genmem,(char *)genmem,64);
|
||||
for(unsigned long i=64;i<ZT_IDENTITY_GEN_MEMORY;i+=64) {
|
||||
unsigned long k = i - 64;
|
||||
*((uint64_t *)((char *)genmem + i)) = *((uint64_t *)((char *)genmem + k));
|
||||
*((uint64_t *)((char *)genmem + i + 8)) = *((uint64_t *)((char *)genmem + k + 8));
|
||||
*((uint64_t *)((char *)genmem + i + 16)) = *((uint64_t *)((char *)genmem + k + 16));
|
||||
*((uint64_t *)((char *)genmem + i + 24)) = *((uint64_t *)((char *)genmem + k + 24));
|
||||
*((uint64_t *)((char *)genmem + i + 32)) = *((uint64_t *)((char *)genmem + k + 32));
|
||||
*((uint64_t *)((char *)genmem + i + 40)) = *((uint64_t *)((char *)genmem + k + 40));
|
||||
*((uint64_t *)((char *)genmem + i + 48)) = *((uint64_t *)((char *)genmem + k + 48));
|
||||
*((uint64_t *)((char *)genmem + i + 56)) = *((uint64_t *)((char *)genmem + k + 56));
|
||||
s20.crypt20((char *)genmem + i,(char *)genmem + i,64);
|
||||
}
|
||||
|
||||
// Render final digest using genmem as a lookup table
|
||||
for(unsigned long i=0;i<(ZT_IDENTITY_GEN_MEMORY / sizeof(uint64_t));) {
|
||||
unsigned long idx1 = (unsigned long)(Utils::ntoh(((uint64_t *)genmem)[i++]) % (64 / sizeof(uint64_t)));
|
||||
unsigned long idx2 = (unsigned long)(Utils::ntoh(((uint64_t *)genmem)[i++]) % (ZT_IDENTITY_GEN_MEMORY / sizeof(uint64_t)));
|
||||
uint64_t tmp = ((uint64_t *)genmem)[idx2];
|
||||
((uint64_t *)genmem)[idx2] = ((uint64_t *)digest)[idx1];
|
||||
((uint64_t *)digest)[idx1] = tmp;
|
||||
s20.crypt20(digest,digest,64);
|
||||
}
|
||||
}
|
||||
|
||||
// Hashcash generation halting condition -- halt when first byte is less than
|
||||
// threshold value.
|
||||
struct _Identity_generate_cond
|
||||
{
|
||||
_Identity_generate_cond() {}
|
||||
_Identity_generate_cond(unsigned char *sb,char *gm) : digest(sb),genmem(gm) {}
|
||||
inline bool operator()(const C25519::Pair &kp) const
|
||||
{
|
||||
_computeMemoryHardHash(kp.pub.data,ZT_C25519_PUBLIC_KEY_LEN,digest,genmem);
|
||||
return (digest[0] < ZT_IDENTITY_GEN_HASHCASH_FIRST_BYTE_LESS_THAN);
|
||||
}
|
||||
unsigned char *digest;
|
||||
char *genmem;
|
||||
};
|
||||
|
||||
void Identity::generate()
|
||||
{
|
||||
unsigned char digest[64];
|
||||
char *genmem = new char[ZT_IDENTITY_GEN_MEMORY];
|
||||
|
||||
C25519::Pair kp;
|
||||
do {
|
||||
kp = C25519::generateSatisfying(_Identity_generate_cond(digest,genmem));
|
||||
_address.setTo(digest + 59,ZT_ADDRESS_LENGTH); // last 5 bytes are address
|
||||
} while (_address.isReserved());
|
||||
|
||||
_publicKey = kp.pub;
|
||||
if (!_privateKey) {
|
||||
_privateKey = new C25519::Private();
|
||||
}
|
||||
*_privateKey = kp.priv;
|
||||
|
||||
delete [] genmem;
|
||||
}
|
||||
|
||||
bool Identity::locallyValidate() const
|
||||
{
|
||||
if (_address.isReserved()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char digest[64];
|
||||
char *genmem = new char[ZT_IDENTITY_GEN_MEMORY];
|
||||
_computeMemoryHardHash(_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN,digest,genmem);
|
||||
delete [] genmem;
|
||||
|
||||
unsigned char addrb[5];
|
||||
_address.copyTo(addrb,5);
|
||||
|
||||
return (
|
||||
(digest[0] < ZT_IDENTITY_GEN_HASHCASH_FIRST_BYTE_LESS_THAN)&&
|
||||
(digest[59] == addrb[0])&&
|
||||
(digest[60] == addrb[1])&&
|
||||
(digest[61] == addrb[2])&&
|
||||
(digest[62] == addrb[3])&&
|
||||
(digest[63] == addrb[4]));
|
||||
}
|
||||
|
||||
char *Identity::toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const
|
||||
{
|
||||
char *p = buf;
|
||||
Utils::hex10(_address.toInt(),p);
|
||||
p += 10;
|
||||
*(p++) = ':';
|
||||
*(p++) = '0';
|
||||
*(p++) = ':';
|
||||
Utils::hex(_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN,p);
|
||||
p += ZT_C25519_PUBLIC_KEY_LEN * 2;
|
||||
if ((_privateKey)&&(includePrivate)) {
|
||||
*(p++) = ':';
|
||||
Utils::hex(_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN,p);
|
||||
p += ZT_C25519_PRIVATE_KEY_LEN * 2;
|
||||
}
|
||||
*p = (char)0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
bool Identity::fromString(const char *str)
|
||||
{
|
||||
if (!str) {
|
||||
_address.zero();
|
||||
return false;
|
||||
}
|
||||
char tmp[ZT_IDENTITY_STRING_BUFFER_LENGTH];
|
||||
if (!Utils::scopy(tmp,sizeof(tmp),str)) {
|
||||
_address.zero();
|
||||
return false;
|
||||
}
|
||||
|
||||
delete _privateKey;
|
||||
_privateKey = (C25519::Private *)0;
|
||||
|
||||
int fno = 0;
|
||||
char *saveptr = (char *)0;
|
||||
for(char *f=Utils::stok(tmp,":",&saveptr);(f);f=Utils::stok((char *)0,":",&saveptr)) {
|
||||
switch(fno++) {
|
||||
case 0:
|
||||
_address = Address(Utils::hexStrToU64(f));
|
||||
if (_address.isReserved()) {
|
||||
_address.zero();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if ((f[0] != '0')||(f[1])) {
|
||||
_address.zero();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (Utils::unhex(f,_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN) != ZT_C25519_PUBLIC_KEY_LEN) {
|
||||
_address.zero();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
_privateKey = new C25519::Private();
|
||||
if (Utils::unhex(f,_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN) != ZT_C25519_PRIVATE_KEY_LEN) {
|
||||
_address.zero();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
_address.zero();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (fno < 3) {
|
||||
_address.zero();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_IDENTITY_HPP
|
||||
#define ZT_IDENTITY_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "C25519.hpp"
|
||||
#include "Buffer.hpp"
|
||||
#include "SHA512.hpp"
|
||||
|
||||
#define ZT_IDENTITY_STRING_BUFFER_LENGTH 384
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* A ZeroTier identity
|
||||
*
|
||||
* An identity consists of a public key, a 40-bit ZeroTier address computed
|
||||
* from that key in a collision-resistant fashion, and a self-signature.
|
||||
*
|
||||
* The address derivation algorithm makes it computationally very expensive to
|
||||
* search for a different public key that duplicates an existing address. (See
|
||||
* code for deriveAddress() for this algorithm.)
|
||||
*/
|
||||
class Identity
|
||||
{
|
||||
public:
|
||||
Identity() :
|
||||
_privateKey((C25519::Private *)0)
|
||||
{
|
||||
}
|
||||
|
||||
Identity(const Identity &id) :
|
||||
_address(id._address),
|
||||
_publicKey(id._publicKey),
|
||||
_privateKey((id._privateKey) ? new C25519::Private(*(id._privateKey)) : (C25519::Private *)0)
|
||||
{
|
||||
}
|
||||
|
||||
Identity(const char *str) :
|
||||
_privateKey((C25519::Private *)0)
|
||||
{
|
||||
if (!fromString(str)) {
|
||||
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned int C>
|
||||
Identity(const Buffer<C> &b,unsigned int startAt = 0) :
|
||||
_privateKey((C25519::Private *)0)
|
||||
{
|
||||
deserialize(b,startAt);
|
||||
}
|
||||
|
||||
~Identity()
|
||||
{
|
||||
if (_privateKey) {
|
||||
Utils::burn(_privateKey,sizeof(C25519::Private));
|
||||
delete _privateKey;
|
||||
}
|
||||
}
|
||||
|
||||
inline Identity &operator=(const Identity &id)
|
||||
{
|
||||
_address = id._address;
|
||||
_publicKey = id._publicKey;
|
||||
if (id._privateKey) {
|
||||
if (!_privateKey) {
|
||||
_privateKey = new C25519::Private();
|
||||
}
|
||||
*_privateKey = *(id._privateKey);
|
||||
} else {
|
||||
delete _privateKey;
|
||||
_privateKey = (C25519::Private *)0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new identity (address, key pair)
|
||||
*
|
||||
* This is a time consuming operation.
|
||||
*/
|
||||
void generate();
|
||||
|
||||
/**
|
||||
* Check the validity of this identity's pairing of key to address
|
||||
*
|
||||
* @return True if validation check passes
|
||||
*/
|
||||
bool locallyValidate() const;
|
||||
|
||||
/**
|
||||
* @return True if this identity contains a private key
|
||||
*/
|
||||
inline bool hasPrivate() const { return (_privateKey != (C25519::Private *)0); }
|
||||
|
||||
/**
|
||||
* Compute a SHA384 hash of this identity's address and public key(s).
|
||||
*
|
||||
* @param sha384buf Buffer with 48 bytes of space to receive hash
|
||||
*/
|
||||
inline void publicKeyHash(void *sha384buf) const
|
||||
{
|
||||
uint8_t address[ZT_ADDRESS_LENGTH];
|
||||
_address.copyTo(address, ZT_ADDRESS_LENGTH);
|
||||
SHA384(sha384buf, address, ZT_ADDRESS_LENGTH, _publicKey.data, ZT_C25519_PUBLIC_KEY_LEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the SHA512 hash of our private key (if we have one)
|
||||
*
|
||||
* @param sha Buffer to receive SHA512 (MUST be ZT_SHA512_DIGEST_LEN (64) bytes in length)
|
||||
* @return True on success, false if no private key
|
||||
*/
|
||||
inline bool sha512PrivateKey(void *sha) const
|
||||
{
|
||||
if (_privateKey) {
|
||||
SHA512(sha,_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign a message with this identity (private key required)
|
||||
*
|
||||
* @param data Data to sign
|
||||
* @param len Length of data
|
||||
*/
|
||||
inline C25519::Signature sign(const void *data,unsigned int len) const
|
||||
{
|
||||
if (_privateKey) {
|
||||
return C25519::sign(*_privateKey,_publicKey,data,len);
|
||||
}
|
||||
throw ZT_EXCEPTION_PRIVATE_KEY_REQUIRED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a message signature against this identity
|
||||
*
|
||||
* @param data Data to check
|
||||
* @param len Length of data
|
||||
* @param signature Signature bytes
|
||||
* @param siglen Length of signature in bytes
|
||||
* @return True if signature validates and data integrity checks
|
||||
*/
|
||||
inline bool verify(const void *data,unsigned int len,const void *signature,unsigned int siglen) const
|
||||
{
|
||||
if (siglen != ZT_C25519_SIGNATURE_LEN) {
|
||||
return false;
|
||||
}
|
||||
return C25519::verify(_publicKey,data,len,signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a message signature against this identity
|
||||
*
|
||||
* @param data Data to check
|
||||
* @param len Length of data
|
||||
* @param signature Signature
|
||||
* @return True if signature validates and data integrity checks
|
||||
*/
|
||||
inline bool verify(const void *data,unsigned int len,const C25519::Signature &signature) const
|
||||
{
|
||||
return C25519::verify(_publicKey,data,len,signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut method to perform key agreement with another identity
|
||||
*
|
||||
* This identity must have a private key. (Check hasPrivate())
|
||||
*
|
||||
* @param id Identity to agree with
|
||||
* @param key Result parameter to fill with key bytes
|
||||
* @return Was agreement successful?
|
||||
*/
|
||||
inline bool agree(const Identity &id,void *const key) const
|
||||
{
|
||||
if (_privateKey) {
|
||||
C25519::agree(*_privateKey,id._publicKey,key,ZT_SYMMETRIC_KEY_SIZE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return This identity's address
|
||||
*/
|
||||
inline const Address &address() const { return _address; }
|
||||
|
||||
/**
|
||||
* Serialize this identity (binary)
|
||||
*
|
||||
* @param b Destination buffer to append to
|
||||
* @param includePrivate If true, include private key component (if present) (default: false)
|
||||
* @throws std::out_of_range Buffer too small
|
||||
*/
|
||||
template<unsigned int C>
|
||||
inline void serialize(Buffer<C> &b,bool includePrivate = false) const
|
||||
{
|
||||
_address.appendTo(b);
|
||||
b.append((uint8_t)0); // C25519/Ed25519 identity type
|
||||
b.append(_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN);
|
||||
if ((_privateKey)&&(includePrivate)) {
|
||||
b.append((unsigned char)ZT_C25519_PRIVATE_KEY_LEN);
|
||||
b.append(_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN);
|
||||
} else {
|
||||
b.append((unsigned char)0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a binary serialized identity
|
||||
*
|
||||
* If an exception is thrown, the Identity object is left in an undefined
|
||||
* state and should not be used.
|
||||
*
|
||||
* @param b Buffer containing serialized data
|
||||
* @param startAt Index within buffer of serialized data (default: 0)
|
||||
* @return Length of serialized data read from buffer
|
||||
* @throws std::out_of_range Serialized data invalid
|
||||
* @throws std::invalid_argument Serialized data invalid
|
||||
*/
|
||||
template<unsigned int C>
|
||||
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
|
||||
{
|
||||
delete _privateKey;
|
||||
_privateKey = (C25519::Private *)0;
|
||||
|
||||
unsigned int p = startAt;
|
||||
|
||||
_address.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
|
||||
p += ZT_ADDRESS_LENGTH;
|
||||
|
||||
if (b[p++] != 0) {
|
||||
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
|
||||
}
|
||||
|
||||
memcpy(_publicKey.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN);
|
||||
p += ZT_C25519_PUBLIC_KEY_LEN;
|
||||
|
||||
unsigned int privateKeyLength = (unsigned int)b[p++];
|
||||
if (privateKeyLength) {
|
||||
if (privateKeyLength != ZT_C25519_PRIVATE_KEY_LEN) {
|
||||
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
|
||||
}
|
||||
_privateKey = new C25519::Private();
|
||||
memcpy(_privateKey->data,b.field(p,ZT_C25519_PRIVATE_KEY_LEN),ZT_C25519_PRIVATE_KEY_LEN);
|
||||
p += ZT_C25519_PRIVATE_KEY_LEN;
|
||||
}
|
||||
|
||||
return (p - startAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize to a more human-friendly string
|
||||
*
|
||||
* @param includePrivate If true, include private key (if it exists)
|
||||
* @param buf Buffer to store string
|
||||
* @return ASCII string representation of identity
|
||||
*/
|
||||
char *toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const;
|
||||
|
||||
/**
|
||||
* Deserialize a human-friendly string
|
||||
*
|
||||
* Note: validation is for the format only. The locallyValidate() method
|
||||
* must be used to check signature and address/key correspondence.
|
||||
*
|
||||
* @param str String to deserialize
|
||||
* @return True if deserialization appears successful
|
||||
*/
|
||||
bool fromString(const char *str);
|
||||
|
||||
/**
|
||||
* @return C25519 public key
|
||||
*/
|
||||
inline const C25519::Public &publicKey() const { return _publicKey; }
|
||||
|
||||
/**
|
||||
* @return C25519 key pair (only returns valid pair if private key is present in this Identity object)
|
||||
*/
|
||||
inline const C25519::Pair privateKeyPair() const
|
||||
{
|
||||
C25519::Pair pair;
|
||||
pair.pub = _publicKey;
|
||||
if (_privateKey) {
|
||||
pair.priv = *_privateKey;
|
||||
} else {
|
||||
memset(pair.priv.data,0,ZT_C25519_PRIVATE_KEY_LEN);
|
||||
}
|
||||
return pair;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if this identity contains something
|
||||
*/
|
||||
inline operator bool() const { return (_address); }
|
||||
|
||||
inline bool operator==(const Identity &id) const { return ((_address == id._address)&&(memcmp(_publicKey.data,id._publicKey.data,ZT_C25519_PUBLIC_KEY_LEN) == 0)); }
|
||||
inline bool operator<(const Identity &id) const { return ((_address < id._address)||((_address == id._address)&&(memcmp(_publicKey.data,id._publicKey.data,ZT_C25519_PUBLIC_KEY_LEN) < 0))); }
|
||||
inline bool operator!=(const Identity &id) const { return !(*this == id); }
|
||||
inline bool operator>(const Identity &id) const { return (id < *this); }
|
||||
inline bool operator<=(const Identity &id) const { return !(id < *this); }
|
||||
inline bool operator>=(const Identity &id) const { return !(*this < id); }
|
||||
|
||||
private:
|
||||
Address _address;
|
||||
C25519::Public _publicKey;
|
||||
C25519::Private *_privateKey;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (c)2013-2020 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_INCOMINGPACKET_HPP
|
||||
#define ZT_INCOMINGPACKET_HPP
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "Packet.hpp"
|
||||
#include "Path.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "MulticastGroup.hpp"
|
||||
#include "Peer.hpp"
|
||||
|
||||
/*
|
||||
* The big picture:
|
||||
*
|
||||
* tryDecode gets called for a given fully-assembled packet until it returns
|
||||
* true or the packet's time to live has been exceeded, in which case it is
|
||||
* discarded as failed decode. Any exception thrown by tryDecode also causes
|
||||
* the packet to be discarded.
|
||||
*
|
||||
* Thus a return of false from tryDecode() indicates that it should be called
|
||||
* again. Logic is very simple as to when, and it's in doAnythingWaitingForPeer
|
||||
* in Switch. This might be expanded to be more fine grained in the future.
|
||||
*
|
||||
* A return value of true indicates that the packet is done. tryDecode must
|
||||
* never be called again after that.
|
||||
*/
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class RuntimeEnvironment;
|
||||
class Network;
|
||||
|
||||
/**
|
||||
* Subclass of packet that handles the decoding of it
|
||||
*/
|
||||
class IncomingPacket : public Packet
|
||||
{
|
||||
public:
|
||||
IncomingPacket() :
|
||||
Packet(),
|
||||
_receiveTime(0),
|
||||
_path(),
|
||||
_authenticated(false)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new packet-in-decode
|
||||
*
|
||||
* @param data Packet data
|
||||
* @param len Packet length
|
||||
* @param path Path over which packet arrived
|
||||
* @param now Current time
|
||||
* @throws std::out_of_range Range error processing packet
|
||||
*/
|
||||
IncomingPacket(const void *data,unsigned int len,const SharedPtr<Path> &path,int64_t now) :
|
||||
Packet(data,len),
|
||||
_receiveTime(now),
|
||||
_path(path),
|
||||
_authenticated(false)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Init packet-in-decode in place
|
||||
*
|
||||
* @param data Packet data
|
||||
* @param len Packet length
|
||||
* @param path Path over which packet arrived
|
||||
* @param now Current time
|
||||
* @throws std::out_of_range Range error processing packet
|
||||
*/
|
||||
inline void init(const void *data,unsigned int len,const SharedPtr<Path> &path,int64_t now)
|
||||
{
|
||||
copyFrom(data,len);
|
||||
_receiveTime = now;
|
||||
_path = path;
|
||||
_authenticated = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to decode this packet
|
||||
*
|
||||
* Note that this returns 'true' if processing is complete. This says nothing
|
||||
* about whether the packet was valid. A rejection is 'complete.'
|
||||
*
|
||||
* Once true is returned, this must not be called again. The packet's state
|
||||
* may no longer be valid.
|
||||
*
|
||||
* @param RR Runtime environment
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @return True if decoding and processing is complete, false if caller should try again
|
||||
*/
|
||||
bool tryDecode(const RuntimeEnvironment *RR,void *tPtr,int32_t flowId);
|
||||
|
||||
/**
|
||||
* @return Time of packet receipt / start of decode
|
||||
*/
|
||||
inline uint64_t receiveTime() const { return _receiveTime; }
|
||||
|
||||
private:
|
||||
// These are called internally to handle packet contents once it has
|
||||
// been authenticated, decrypted, decompressed, and classified.
|
||||
bool _doERROR(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
|
||||
bool _doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool alreadyAuthenticated);
|
||||
bool _doACK(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
|
||||
bool _doQOS_MEASUREMENT(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
|
||||
bool _doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
|
||||
bool _doWHOIS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
|
||||
bool _doRENDEZVOUS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
|
||||
bool _doFRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer,int32_t flowId);
|
||||
bool _doEXT_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer,int32_t flowId);
|
||||
bool _doECHO(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
|
||||
bool _doMULTICAST_LIKE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
|
||||
bool _doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
|
||||
bool _doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
|
||||
bool _doNETWORK_CONFIG(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
|
||||
bool _doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
|
||||
bool _doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
|
||||
bool _doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
|
||||
bool _doUSER_MESSAGE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
|
||||
bool _doREMOTE_TRACE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
|
||||
bool _doPATH_NEGOTIATION_REQUEST(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
|
||||
|
||||
void _sendErrorNeedCredentials(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer,const uint64_t nwid);
|
||||
|
||||
uint64_t _receiveTime;
|
||||
SharedPtr<Path> _path;
|
||||
bool _authenticated;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,533 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
const InetAddress InetAddress::LO4((const void *)("\x7f\x00\x00\x01"),4,0);
|
||||
const InetAddress InetAddress::LO6((const void *)("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"),16,0);
|
||||
|
||||
InetAddress::IpScope InetAddress::ipScope() const
|
||||
{
|
||||
switch(ss_family) {
|
||||
|
||||
case AF_INET: {
|
||||
const uint32_t ip = Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr);
|
||||
switch(ip >> 24) {
|
||||
case 0x00:
|
||||
return IP_SCOPE_NONE; // 0.0.0.0/8 (reserved, never used)
|
||||
case 0x06:
|
||||
return IP_SCOPE_PSEUDOPRIVATE; // 6.0.0.0/8 (US Army)
|
||||
case 0x0a:
|
||||
return IP_SCOPE_PRIVATE; // 10.0.0.0/8
|
||||
case 0x0b:
|
||||
return IP_SCOPE_PSEUDOPRIVATE; // 11.0.0.0/8 (US DoD)
|
||||
case 0x15:
|
||||
return IP_SCOPE_PSEUDOPRIVATE; // 21.0.0.0/8 (US DDN-RVN)
|
||||
case 0x16:
|
||||
return IP_SCOPE_PSEUDOPRIVATE; // 22.0.0.0/8 (US DISA)
|
||||
case 0x19:
|
||||
return IP_SCOPE_PSEUDOPRIVATE; // 25.0.0.0/8 (UK Ministry of Defense)
|
||||
case 0x1a:
|
||||
return IP_SCOPE_PSEUDOPRIVATE; // 26.0.0.0/8 (US DISA)
|
||||
case 0x1c:
|
||||
return IP_SCOPE_PSEUDOPRIVATE; // 28.0.0.0/8 (US DSI-North)
|
||||
case 0x1d:
|
||||
return IP_SCOPE_PSEUDOPRIVATE; // 29.0.0.0/8 (US DISA)
|
||||
case 0x1e:
|
||||
return IP_SCOPE_PSEUDOPRIVATE; // 30.0.0.0/8 (US DISA)
|
||||
case 0x33:
|
||||
return IP_SCOPE_PSEUDOPRIVATE; // 51.0.0.0/8 (UK Department of Social Security)
|
||||
case 0x37:
|
||||
return IP_SCOPE_PSEUDOPRIVATE; // 55.0.0.0/8 (US DoD)
|
||||
case 0x38:
|
||||
return IP_SCOPE_PSEUDOPRIVATE; // 56.0.0.0/8 (US Postal Service)
|
||||
case 0x64:
|
||||
if ((ip & 0xffc00000) == 0x64400000) {
|
||||
return IP_SCOPE_PRIVATE; // 100.64.0.0/10
|
||||
}
|
||||
break;
|
||||
case 0x7f:
|
||||
return IP_SCOPE_LOOPBACK; // 127.0.0.0/8
|
||||
case 0xa9:
|
||||
if ((ip & 0xffff0000) == 0xa9fe0000) {
|
||||
return IP_SCOPE_LINK_LOCAL; // 169.254.0.0/16
|
||||
}
|
||||
break;
|
||||
case 0xac:
|
||||
if ((ip & 0xfff00000) == 0xac100000) {
|
||||
return IP_SCOPE_PRIVATE; // 172.16.0.0/12
|
||||
}
|
||||
break;
|
||||
case 0xc0:
|
||||
if ((ip & 0xffff0000) == 0xc0a80000) {
|
||||
return IP_SCOPE_PRIVATE; // 192.168.0.0/16
|
||||
}
|
||||
if ((ip & 0xffffff00) == 0xc0000200) {
|
||||
return IP_SCOPE_PRIVATE; // 192.0.2.0/24
|
||||
}
|
||||
break;
|
||||
case 0xc6:
|
||||
if ((ip & 0xfffe0000) == 0xc6120000) {
|
||||
return IP_SCOPE_PRIVATE; // 198.18.0.0/15
|
||||
}
|
||||
if ((ip & 0xffffff00) == 0xc6336400) {
|
||||
return IP_SCOPE_PRIVATE; // 198.51.100.0/24
|
||||
}
|
||||
break;
|
||||
case 0xcb:
|
||||
if ((ip & 0xffffff00) == 0xcb007100) {
|
||||
return IP_SCOPE_PRIVATE; // 203.0.113.0/24
|
||||
}
|
||||
break;
|
||||
case 0xff:
|
||||
return IP_SCOPE_NONE; // 255.0.0.0/8 (broadcast, or unused/unusable)
|
||||
}
|
||||
switch(ip >> 28) {
|
||||
case 0xe:
|
||||
return IP_SCOPE_MULTICAST; // 224.0.0.0/4
|
||||
case 0xf:
|
||||
return IP_SCOPE_PSEUDOPRIVATE; // 240.0.0.0/4 ("reserved," usually unusable)
|
||||
}
|
||||
return IP_SCOPE_GLOBAL;
|
||||
} break;
|
||||
|
||||
case AF_INET6: {
|
||||
const unsigned char *ip = reinterpret_cast<const unsigned char *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
|
||||
if ((ip[0] & 0xf0) == 0xf0) {
|
||||
if (ip[0] == 0xff) {
|
||||
return IP_SCOPE_MULTICAST; // ff00::/8
|
||||
}
|
||||
if ((ip[0] == 0xfe)&&((ip[1] & 0xc0) == 0x80)) {
|
||||
unsigned int k = 2;
|
||||
while ((!ip[k])&&(k < 15)) {
|
||||
++k;
|
||||
}
|
||||
if ((k == 15)&&(ip[15] == 0x01)) {
|
||||
return IP_SCOPE_LOOPBACK; // fe80::1/128
|
||||
} else {
|
||||
return IP_SCOPE_LINK_LOCAL; // fe80::/10
|
||||
}
|
||||
}
|
||||
if ((ip[0] & 0xfe) == 0xfc) {
|
||||
return IP_SCOPE_PRIVATE; // fc00::/7
|
||||
}
|
||||
}
|
||||
|
||||
// :::ffff:127.0.0.1
|
||||
// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x7f, 0, 0, 1
|
||||
unsigned int k = 0;
|
||||
while ((!ip[k])&&(k < 9)) {
|
||||
++k;
|
||||
}
|
||||
if (k == 9) {
|
||||
if (ip[10] == 0xff && ip[11] == 0xff && ip[12] == 0x7f) {
|
||||
return IP_SCOPE_LOOPBACK;
|
||||
}
|
||||
}
|
||||
|
||||
k = 0;
|
||||
while ((!ip[k])&&(k < 15)) {
|
||||
++k;
|
||||
}
|
||||
if (k == 15) { // all 0's except last byte
|
||||
if (ip[15] == 0x01) {
|
||||
return IP_SCOPE_LOOPBACK; // ::1/128
|
||||
}
|
||||
if (ip[15] == 0x00) {
|
||||
return IP_SCOPE_NONE; // ::/128
|
||||
}
|
||||
}
|
||||
return IP_SCOPE_GLOBAL;
|
||||
} break;
|
||||
|
||||
}
|
||||
|
||||
return IP_SCOPE_NONE;
|
||||
}
|
||||
|
||||
void InetAddress::set(const void *ipBytes,unsigned int ipLen,unsigned int port)
|
||||
{
|
||||
memset(this,0,sizeof(InetAddress));
|
||||
if (ipLen == 4) {
|
||||
uint32_t ipb[1];
|
||||
memcpy(ipb,ipBytes,4);
|
||||
ss_family = AF_INET;
|
||||
reinterpret_cast<struct sockaddr_in *>(this)->sin_addr.s_addr = ipb[0];
|
||||
reinterpret_cast<struct sockaddr_in *>(this)->sin_port = Utils::hton((uint16_t)port);
|
||||
} else if (ipLen == 16) {
|
||||
ss_family = AF_INET6;
|
||||
memcpy(reinterpret_cast<struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,ipBytes,16);
|
||||
reinterpret_cast<struct sockaddr_in6 *>(this)->sin6_port = Utils::hton((uint16_t)port);
|
||||
}
|
||||
}
|
||||
|
||||
char *InetAddress::toString(char buf[64]) const
|
||||
{
|
||||
char *p = toIpString(buf);
|
||||
if (*p) {
|
||||
while (*p) {
|
||||
++p;
|
||||
}
|
||||
*(p++) = '/';
|
||||
Utils::decimal(port(),p);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
char *InetAddress::toIpString(char buf[64]) const
|
||||
{
|
||||
buf[0] = (char)0;
|
||||
switch(ss_family) {
|
||||
case AF_INET: {
|
||||
#ifdef _WIN32
|
||||
inet_ntop(AF_INET, (void*)&reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr, buf, INET_ADDRSTRLEN);
|
||||
#else
|
||||
inet_ntop(AF_INET, &reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr, buf, INET_ADDRSTRLEN);
|
||||
#endif
|
||||
} break;
|
||||
|
||||
case AF_INET6: {
|
||||
#ifdef _WIN32
|
||||
inet_ntop(AF_INET6, (void*)reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr, buf, INET6_ADDRSTRLEN);
|
||||
#else
|
||||
inet_ntop(AF_INET6, reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr, buf, INET6_ADDRSTRLEN);
|
||||
#endif
|
||||
} break;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
bool InetAddress::fromString(const char *ipSlashPort)
|
||||
{
|
||||
char buf[64];
|
||||
|
||||
memset(this,0,sizeof(InetAddress));
|
||||
|
||||
if (!*ipSlashPort) {
|
||||
return true;
|
||||
}
|
||||
if (!Utils::scopy(buf,sizeof(buf),ipSlashPort)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char *portAt = buf;
|
||||
while ((*portAt)&&(*portAt != '/')) {
|
||||
++portAt;
|
||||
}
|
||||
unsigned int port = 0;
|
||||
if (*portAt) {
|
||||
*(portAt++) = (char)0;
|
||||
port = Utils::strToUInt(portAt) & 0xffff;
|
||||
}
|
||||
|
||||
if (strchr(buf,':')) {
|
||||
struct sockaddr_in6 *const in6 = reinterpret_cast<struct sockaddr_in6 *>(this);
|
||||
inet_pton(AF_INET6, buf, &in6->sin6_addr.s6_addr);
|
||||
in6->sin6_family = AF_INET6;
|
||||
in6->sin6_port = Utils::hton((uint16_t)port);
|
||||
return true;
|
||||
} else if (strchr(buf,'.')) {
|
||||
struct sockaddr_in *const in = reinterpret_cast<struct sockaddr_in *>(this);
|
||||
inet_pton(AF_INET, buf, &in->sin_addr.s_addr);
|
||||
in->sin_family = AF_INET;
|
||||
in->sin_port = Utils::hton((uint16_t)port);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
InetAddress InetAddress::netmask() const
|
||||
{
|
||||
InetAddress r(*this);
|
||||
switch(r.ss_family) {
|
||||
case AF_INET:
|
||||
reinterpret_cast<struct sockaddr_in *>(&r)->sin_addr.s_addr = Utils::hton((uint32_t)(0xffffffff << (32 - netmaskBits())));
|
||||
break;
|
||||
case AF_INET6: {
|
||||
uint64_t nm[2];
|
||||
const unsigned int bits = netmaskBits();
|
||||
if(bits) {
|
||||
nm[0] = Utils::hton((uint64_t)((bits >= 64) ? 0xffffffffffffffffULL : (0xffffffffffffffffULL << (64 - bits))));
|
||||
nm[1] = Utils::hton((uint64_t)((bits <= 64) ? 0ULL : (0xffffffffffffffffULL << (128 - bits))));
|
||||
} else {
|
||||
nm[0] = 0;
|
||||
nm[1] = 0;
|
||||
}
|
||||
memcpy(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,nm,16);
|
||||
} break;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
InetAddress InetAddress::broadcast() const
|
||||
{
|
||||
if (ss_family == AF_INET) {
|
||||
InetAddress r(*this);
|
||||
reinterpret_cast<struct sockaddr_in *>(&r)->sin_addr.s_addr |= Utils::hton((uint32_t)(0xffffffff >> netmaskBits()));
|
||||
return r;
|
||||
}
|
||||
return InetAddress();
|
||||
}
|
||||
|
||||
InetAddress InetAddress::network() const
|
||||
{
|
||||
InetAddress r(*this);
|
||||
switch(r.ss_family) {
|
||||
case AF_INET:
|
||||
reinterpret_cast<struct sockaddr_in *>(&r)->sin_addr.s_addr &= Utils::hton((uint32_t)(0xffffffff << (32 - netmaskBits())));
|
||||
break;
|
||||
case AF_INET6: {
|
||||
uint64_t nm[2];
|
||||
const unsigned int bits = netmaskBits();
|
||||
memcpy(nm,reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,16);
|
||||
nm[0] &= Utils::hton((uint64_t)((bits >= 64) ? 0xffffffffffffffffULL : (0xffffffffffffffffULL << (64 - bits))));
|
||||
nm[1] &= Utils::hton((uint64_t)((bits <= 64) ? 0ULL : (0xffffffffffffffffULL << (128 - bits))));
|
||||
memcpy(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,nm,16);
|
||||
} break;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
bool InetAddress::isEqualPrefix(const InetAddress &addr) const
|
||||
{
|
||||
if (addr.ss_family == ss_family) {
|
||||
switch(ss_family) {
|
||||
case AF_INET6: {
|
||||
const InetAddress mask(netmask());
|
||||
InetAddress addr_mask(addr.netmask());
|
||||
const uint8_t *n = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&addr_mask)->sin6_addr.s6_addr);
|
||||
const uint8_t *m = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&mask)->sin6_addr.s6_addr);
|
||||
const uint8_t *a = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_addr.s6_addr);
|
||||
const uint8_t *b = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
|
||||
for(unsigned int i=0;i<16;++i) {
|
||||
if ((a[i] & m[i]) != (b[i] & n[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InetAddress::containsAddress(const InetAddress &addr) const
|
||||
{
|
||||
if (addr.ss_family == ss_family) {
|
||||
switch(ss_family) {
|
||||
case AF_INET: {
|
||||
const unsigned int bits = netmaskBits();
|
||||
if (bits == 0) {
|
||||
return true;
|
||||
}
|
||||
return ( (Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_addr.s_addr) >> (32 - bits)) == (Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr) >> (32 - bits)) );
|
||||
}
|
||||
case AF_INET6: {
|
||||
const InetAddress mask(netmask());
|
||||
const uint8_t *m = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&mask)->sin6_addr.s6_addr);
|
||||
const uint8_t *a = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_addr.s6_addr);
|
||||
const uint8_t *b = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
|
||||
for(unsigned int i=0;i<16;++i) {
|
||||
if ((a[i] & m[i]) != b[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InetAddress::isNetwork() const
|
||||
{
|
||||
switch(ss_family) {
|
||||
case AF_INET: {
|
||||
unsigned int bits = netmaskBits();
|
||||
if (bits <= 0) {
|
||||
return false;
|
||||
}
|
||||
if (bits >= 32) {
|
||||
return false;
|
||||
}
|
||||
uint32_t ip = Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr);
|
||||
return ((ip & (0xffffffff >> bits)) == 0);
|
||||
}
|
||||
case AF_INET6: {
|
||||
unsigned int bits = netmaskBits();
|
||||
if (bits <= 0) {
|
||||
return false;
|
||||
}
|
||||
if (bits >= 128) {
|
||||
return false;
|
||||
}
|
||||
const unsigned char *ip = reinterpret_cast<const unsigned char *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
|
||||
unsigned int p = bits / 8;
|
||||
if ((ip[p++] & (0xff >> (bits % 8))) != 0) {
|
||||
return false;
|
||||
}
|
||||
while (p < 16) {
|
||||
if (ip[p++]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InetAddress::operator==(const InetAddress &a) const
|
||||
{
|
||||
if (ss_family == a.ss_family) {
|
||||
switch(ss_family) {
|
||||
case AF_INET:
|
||||
return (
|
||||
(reinterpret_cast<const struct sockaddr_in *>(this)->sin_port == reinterpret_cast<const struct sockaddr_in *>(&a)->sin_port)&&
|
||||
(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr == reinterpret_cast<const struct sockaddr_in *>(&a)->sin_addr.s_addr));
|
||||
break;
|
||||
case AF_INET6:
|
||||
return (
|
||||
(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port == reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_port)&&
|
||||
(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_flowinfo == reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_flowinfo)&&
|
||||
(memcmp(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_addr.s6_addr,16) == 0)&&
|
||||
(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_scope_id == reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_scope_id));
|
||||
break;
|
||||
default:
|
||||
return (memcmp(this,&a,sizeof(InetAddress)) == 0);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InetAddress::operator<(const InetAddress &a) const
|
||||
{
|
||||
if (ss_family < a.ss_family) {
|
||||
return true;
|
||||
} else if (ss_family == a.ss_family) {
|
||||
switch(ss_family) {
|
||||
case AF_INET:
|
||||
if (reinterpret_cast<const struct sockaddr_in *>(this)->sin_port < reinterpret_cast<const struct sockaddr_in *>(&a)->sin_port) {
|
||||
return true;
|
||||
} else if (reinterpret_cast<const struct sockaddr_in *>(this)->sin_port == reinterpret_cast<const struct sockaddr_in *>(&a)->sin_port) {
|
||||
if (reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr < reinterpret_cast<const struct sockaddr_in *>(&a)->sin_addr.s_addr) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AF_INET6:
|
||||
if (reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port < reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_port) {
|
||||
return true;
|
||||
} else if (reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port == reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_port) {
|
||||
if (reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_flowinfo < reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_flowinfo) {
|
||||
return true;
|
||||
} else if (reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_flowinfo == reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_flowinfo) {
|
||||
if (memcmp(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_addr.s6_addr,16) < 0) {
|
||||
return true;
|
||||
} else if (memcmp(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_addr.s6_addr,16) == 0) {
|
||||
if (reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_scope_id < reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_scope_id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return (memcmp(this,&a,sizeof(InetAddress)) < 0);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
InetAddress InetAddress::makeIpv6LinkLocal(const MAC &mac)
|
||||
{
|
||||
struct sockaddr_in6 sin6;
|
||||
sin6.sin6_family = AF_INET6;
|
||||
sin6.sin6_addr.s6_addr[0] = 0xfe;
|
||||
sin6.sin6_addr.s6_addr[1] = 0x80;
|
||||
sin6.sin6_addr.s6_addr[2] = 0x00;
|
||||
sin6.sin6_addr.s6_addr[3] = 0x00;
|
||||
sin6.sin6_addr.s6_addr[4] = 0x00;
|
||||
sin6.sin6_addr.s6_addr[5] = 0x00;
|
||||
sin6.sin6_addr.s6_addr[6] = 0x00;
|
||||
sin6.sin6_addr.s6_addr[7] = 0x00;
|
||||
sin6.sin6_addr.s6_addr[8] = mac[0] & 0xfd;
|
||||
sin6.sin6_addr.s6_addr[9] = mac[1];
|
||||
sin6.sin6_addr.s6_addr[10] = mac[2];
|
||||
sin6.sin6_addr.s6_addr[11] = 0xff;
|
||||
sin6.sin6_addr.s6_addr[12] = 0xfe;
|
||||
sin6.sin6_addr.s6_addr[13] = mac[3];
|
||||
sin6.sin6_addr.s6_addr[14] = mac[4];
|
||||
sin6.sin6_addr.s6_addr[15] = mac[5];
|
||||
sin6.sin6_port = Utils::hton((uint16_t)64);
|
||||
return InetAddress(sin6);
|
||||
}
|
||||
|
||||
InetAddress InetAddress::makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress)
|
||||
{
|
||||
InetAddress r;
|
||||
struct sockaddr_in6 *const sin6 = reinterpret_cast<struct sockaddr_in6 *>(&r);
|
||||
sin6->sin6_family = AF_INET6;
|
||||
sin6->sin6_addr.s6_addr[0] = 0xfd;
|
||||
sin6->sin6_addr.s6_addr[1] = (uint8_t)(nwid >> 56);
|
||||
sin6->sin6_addr.s6_addr[2] = (uint8_t)(nwid >> 48);
|
||||
sin6->sin6_addr.s6_addr[3] = (uint8_t)(nwid >> 40);
|
||||
sin6->sin6_addr.s6_addr[4] = (uint8_t)(nwid >> 32);
|
||||
sin6->sin6_addr.s6_addr[5] = (uint8_t)(nwid >> 24);
|
||||
sin6->sin6_addr.s6_addr[6] = (uint8_t)(nwid >> 16);
|
||||
sin6->sin6_addr.s6_addr[7] = (uint8_t)(nwid >> 8);
|
||||
sin6->sin6_addr.s6_addr[8] = (uint8_t)nwid;
|
||||
sin6->sin6_addr.s6_addr[9] = 0x99;
|
||||
sin6->sin6_addr.s6_addr[10] = 0x93;
|
||||
sin6->sin6_addr.s6_addr[11] = (uint8_t)(zeroTierAddress >> 32);
|
||||
sin6->sin6_addr.s6_addr[12] = (uint8_t)(zeroTierAddress >> 24);
|
||||
sin6->sin6_addr.s6_addr[13] = (uint8_t)(zeroTierAddress >> 16);
|
||||
sin6->sin6_addr.s6_addr[14] = (uint8_t)(zeroTierAddress >> 8);
|
||||
sin6->sin6_addr.s6_addr[15] = (uint8_t)zeroTierAddress;
|
||||
sin6->sin6_port = Utils::hton((uint16_t)88); // /88 includes 0xfd + network ID, discriminating by device ID below that
|
||||
return r;
|
||||
}
|
||||
|
||||
InetAddress InetAddress::makeIpv66plane(uint64_t nwid,uint64_t zeroTierAddress)
|
||||
{
|
||||
nwid ^= (nwid >> 32);
|
||||
InetAddress r;
|
||||
struct sockaddr_in6 *const sin6 = reinterpret_cast<struct sockaddr_in6 *>(&r);
|
||||
sin6->sin6_family = AF_INET6;
|
||||
sin6->sin6_addr.s6_addr[0] = 0xfc;
|
||||
sin6->sin6_addr.s6_addr[1] = (uint8_t)(nwid >> 24);
|
||||
sin6->sin6_addr.s6_addr[2] = (uint8_t)(nwid >> 16);
|
||||
sin6->sin6_addr.s6_addr[3] = (uint8_t)(nwid >> 8);
|
||||
sin6->sin6_addr.s6_addr[4] = (uint8_t)nwid;
|
||||
sin6->sin6_addr.s6_addr[5] = (uint8_t)(zeroTierAddress >> 32);
|
||||
sin6->sin6_addr.s6_addr[6] = (uint8_t)(zeroTierAddress >> 24);
|
||||
sin6->sin6_addr.s6_addr[7] = (uint8_t)(zeroTierAddress >> 16);
|
||||
sin6->sin6_addr.s6_addr[8] = (uint8_t)(zeroTierAddress >> 8);
|
||||
sin6->sin6_addr.s6_addr[9] = (uint8_t)zeroTierAddress;
|
||||
sin6->sin6_addr.s6_addr[15] = 0x01;
|
||||
sin6->sin6_port = Utils::hton((uint16_t)40);
|
||||
return r;
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
@@ -0,0 +1,681 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_INETADDRESS_HPP
|
||||
#define ZT_INETADDRESS_HPP
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "../include/ZeroTierOne.h"
|
||||
#include "Utils.hpp"
|
||||
#include "MAC.hpp"
|
||||
#include "Buffer.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* Maximum integer value of enum IpScope
|
||||
*/
|
||||
#define ZT_INETADDRESS_MAX_SCOPE 7
|
||||
|
||||
/**
|
||||
* Extends sockaddr_storage with friendly C++ methods
|
||||
*
|
||||
* This is basically a "mixin" for sockaddr_storage. It adds methods and
|
||||
* operators, but does not modify the structure. This can be cast to/from
|
||||
* sockaddr_storage and used interchangeably. DO NOT change this by e.g.
|
||||
* adding non-static fields, since much code depends on this identity.
|
||||
*/
|
||||
struct InetAddress : public sockaddr_storage
|
||||
{
|
||||
/**
|
||||
* Loopback IPv4 address (no port)
|
||||
*/
|
||||
static const InetAddress LO4;
|
||||
|
||||
/**
|
||||
* Loopback IPV6 address (no port)
|
||||
*/
|
||||
static const InetAddress LO6;
|
||||
|
||||
/**
|
||||
* IP address scope
|
||||
*
|
||||
* Note that these values are in ascending order of path preference and
|
||||
* MUST remain that way or Path must be changed to reflect. Also be sure
|
||||
* to change ZT_INETADDRESS_MAX_SCOPE if the max changes.
|
||||
*/
|
||||
enum IpScope
|
||||
{
|
||||
IP_SCOPE_NONE = 0, // NULL or not an IP address
|
||||
IP_SCOPE_MULTICAST = 1, // 224.0.0.0 and other V4/V6 multicast IPs
|
||||
IP_SCOPE_LOOPBACK = 2, // 127.0.0.1, ::1, etc.
|
||||
IP_SCOPE_PSEUDOPRIVATE = 3, // 28.x.x.x, etc. -- unofficially unrouted IPv4 blocks often "bogarted"
|
||||
IP_SCOPE_GLOBAL = 4, // globally routable IP address (all others)
|
||||
IP_SCOPE_LINK_LOCAL = 5, // 169.254.x.x, IPv6 LL
|
||||
IP_SCOPE_SHARED = 6, // currently unused, formerly used for carrier-grade NAT ranges
|
||||
IP_SCOPE_PRIVATE = 7 // 10.x.x.x, 192.168.x.x, etc.
|
||||
};
|
||||
|
||||
// Can be used with the unordered maps and sets in c++11. We don't use C++11 in the core
|
||||
// but this is safe to put here.
|
||||
struct Hasher
|
||||
{
|
||||
inline std::size_t operator()(const InetAddress &a) const { return (std::size_t)a.hashCode(); }
|
||||
};
|
||||
|
||||
InetAddress() { memset(this,0,sizeof(InetAddress)); }
|
||||
InetAddress(const InetAddress &a) { memcpy(this,&a,sizeof(InetAddress)); }
|
||||
InetAddress(const InetAddress *a) { memcpy(this,a,sizeof(InetAddress)); }
|
||||
InetAddress(const struct sockaddr_storage &ss) { *this = ss; }
|
||||
InetAddress(const struct sockaddr_storage *ss) { *this = ss; }
|
||||
InetAddress(const struct sockaddr &sa) { *this = sa; }
|
||||
InetAddress(const struct sockaddr *sa) { *this = sa; }
|
||||
InetAddress(const struct sockaddr_in &sa) { *this = sa; }
|
||||
InetAddress(const struct sockaddr_in *sa) { *this = sa; }
|
||||
InetAddress(const struct sockaddr_in6 &sa) { *this = sa; }
|
||||
InetAddress(const struct sockaddr_in6 *sa) { *this = sa; }
|
||||
InetAddress(const void *ipBytes,unsigned int ipLen,unsigned int port) { this->set(ipBytes,ipLen,port); }
|
||||
InetAddress(const uint32_t ipv4,unsigned int port) { this->set(&ipv4,4,port); }
|
||||
InetAddress(const char *ipSlashPort) { this->fromString(ipSlashPort); }
|
||||
|
||||
inline InetAddress &operator=(const InetAddress &a)
|
||||
{
|
||||
if (&a != this) {
|
||||
memcpy(this,&a,sizeof(InetAddress));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline InetAddress &operator=(const InetAddress *a)
|
||||
{
|
||||
if (a != this) {
|
||||
memcpy(this,a,sizeof(InetAddress));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline InetAddress &operator=(const struct sockaddr_storage &ss)
|
||||
{
|
||||
if (reinterpret_cast<const InetAddress *>(&ss) != this) {
|
||||
memcpy(this,&ss,sizeof(InetAddress));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline InetAddress &operator=(const struct sockaddr_storage *ss)
|
||||
{
|
||||
if (reinterpret_cast<const InetAddress *>(ss) != this) {
|
||||
memcpy(this,ss,sizeof(InetAddress));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline InetAddress &operator=(const struct sockaddr_in &sa)
|
||||
{
|
||||
if (reinterpret_cast<const InetAddress *>(&sa) != this) {
|
||||
memset(this,0,sizeof(InetAddress));
|
||||
memcpy(this,&sa,sizeof(struct sockaddr_in));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline InetAddress &operator=(const struct sockaddr_in *sa)
|
||||
{
|
||||
if (reinterpret_cast<const InetAddress *>(sa) != this) {
|
||||
memset(this,0,sizeof(InetAddress));
|
||||
memcpy(this,sa,sizeof(struct sockaddr_in));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline InetAddress &operator=(const struct sockaddr_in6 &sa)
|
||||
{
|
||||
if (reinterpret_cast<const InetAddress *>(&sa) != this) {
|
||||
memset(this,0,sizeof(InetAddress));
|
||||
memcpy(this,&sa,sizeof(struct sockaddr_in6));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline InetAddress &operator=(const struct sockaddr_in6 *sa)
|
||||
{
|
||||
if (reinterpret_cast<const InetAddress *>(sa) != this) {
|
||||
memset(this,0,sizeof(InetAddress));
|
||||
memcpy(this,sa,sizeof(struct sockaddr_in6));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline InetAddress &operator=(const struct sockaddr &sa)
|
||||
{
|
||||
if (reinterpret_cast<const InetAddress *>(&sa) != this) {
|
||||
memset(this,0,sizeof(InetAddress));
|
||||
switch(sa.sa_family) {
|
||||
case AF_INET:
|
||||
memcpy(this,&sa,sizeof(struct sockaddr_in));
|
||||
break;
|
||||
case AF_INET6:
|
||||
memcpy(this,&sa,sizeof(struct sockaddr_in6));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline InetAddress &operator=(const struct sockaddr *sa)
|
||||
{
|
||||
if (reinterpret_cast<const InetAddress *>(sa) != this) {
|
||||
memset(this,0,sizeof(InetAddress));
|
||||
switch(sa->sa_family) {
|
||||
case AF_INET:
|
||||
memcpy(this,sa,sizeof(struct sockaddr_in));
|
||||
break;
|
||||
case AF_INET6:
|
||||
memcpy(this,sa,sizeof(struct sockaddr_in6));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return IP scope classification (e.g. loopback, link-local, private, global)
|
||||
*/
|
||||
IpScope ipScope() const;
|
||||
|
||||
/**
|
||||
* Set from a raw IP and port number
|
||||
*
|
||||
* @param ipBytes Bytes of IP address in network byte order
|
||||
* @param ipLen Length of IP address: 4 or 16
|
||||
* @param port Port number or 0 for none
|
||||
*/
|
||||
void set(const void *ipBytes,unsigned int ipLen,unsigned int port);
|
||||
|
||||
/**
|
||||
* Set the port component
|
||||
*
|
||||
* @param port Port, 0 to 65535
|
||||
*/
|
||||
inline void setPort(unsigned int port)
|
||||
{
|
||||
switch(ss_family) {
|
||||
case AF_INET:
|
||||
reinterpret_cast<struct sockaddr_in *>(this)->sin_port = Utils::hton((uint16_t)port);
|
||||
break;
|
||||
case AF_INET6:
|
||||
reinterpret_cast<struct sockaddr_in6 *>(this)->sin6_port = Utils::hton((uint16_t)port);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if this network/netmask route describes a default route (e.g. 0.0.0.0/0)
|
||||
*/
|
||||
inline bool isDefaultRoute() const
|
||||
{
|
||||
switch(ss_family) {
|
||||
case AF_INET:
|
||||
return ( (reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr == 0) && (reinterpret_cast<const struct sockaddr_in *>(this)->sin_port == 0) );
|
||||
case AF_INET6:
|
||||
const uint8_t *ipb = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
|
||||
for(int i=0;i<16;++i) {
|
||||
if (ipb[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return (reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port == 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ASCII IP/port format representation
|
||||
*/
|
||||
char *toString(char buf[64]) const;
|
||||
|
||||
/**
|
||||
* @return IP portion only, in ASCII string format
|
||||
*/
|
||||
char *toIpString(char buf[64]) const;
|
||||
|
||||
/**
|
||||
* @param ipSlashPort IP/port (port is optional, will be 0 if not included)
|
||||
* @return True if address appeared to be valid
|
||||
*/
|
||||
bool fromString(const char *ipSlashPort);
|
||||
|
||||
/**
|
||||
* @return Port or 0 if no port component defined
|
||||
*/
|
||||
inline unsigned int port() const
|
||||
{
|
||||
switch(ss_family) {
|
||||
case AF_INET:
|
||||
return Utils::ntoh((uint16_t)(reinterpret_cast<const struct sockaddr_in *>(this)->sin_port));
|
||||
case AF_INET6:
|
||||
return Utils::ntoh((uint16_t)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port));
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for port()
|
||||
*
|
||||
* This just aliases port() to make code more readable when netmask bits
|
||||
* are stuffed there, as they are in Network, EthernetTap, and a few other
|
||||
* spots.
|
||||
*
|
||||
* @return Netmask bits
|
||||
*/
|
||||
inline unsigned int netmaskBits() const { return port(); }
|
||||
|
||||
/**
|
||||
* @return True if netmask bits is valid for the address type
|
||||
*/
|
||||
inline bool netmaskBitsValid() const
|
||||
{
|
||||
const unsigned int n = port();
|
||||
switch(ss_family) {
|
||||
case AF_INET:
|
||||
return (n <= 32);
|
||||
case AF_INET6:
|
||||
return (n <= 128);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for port()
|
||||
*
|
||||
* This just aliases port() because for gateways we use this field to
|
||||
* store the gateway metric.
|
||||
*
|
||||
* @return Gateway metric
|
||||
*/
|
||||
inline unsigned int metric() const { return port(); }
|
||||
|
||||
/**
|
||||
* Construct a full netmask as an InetAddress
|
||||
*
|
||||
* @return Netmask such as 255.255.255.0 if this address is /24 (port field will be unchanged)
|
||||
*/
|
||||
InetAddress netmask() const;
|
||||
|
||||
/**
|
||||
* Constructs a broadcast address from a network/netmask address
|
||||
*
|
||||
* This is only valid for IPv4 and will return a NULL InetAddress for other
|
||||
* address families.
|
||||
*
|
||||
* @return Broadcast address (only IP portion is meaningful)
|
||||
*/
|
||||
InetAddress broadcast() const;
|
||||
|
||||
/**
|
||||
* Return the network -- a.k.a. the IP ANDed with the netmask
|
||||
*
|
||||
* @return Network e.g. 10.0.1.0/24 from 10.0.1.200/24
|
||||
*/
|
||||
InetAddress network() const;
|
||||
|
||||
/**
|
||||
* Test whether this IPv6 prefix matches the prefix of a given IPv6 address
|
||||
*
|
||||
* @param addr Address to check
|
||||
* @return True if this IPv6 prefix matches the prefix of a given IPv6 address
|
||||
*/
|
||||
bool isEqualPrefix(const InetAddress &addr) const;
|
||||
|
||||
/**
|
||||
* Test whether this IP/netmask contains this address
|
||||
*
|
||||
* @param addr Address to check
|
||||
* @return True if this IP/netmask (route) contains this address
|
||||
*/
|
||||
bool containsAddress(const InetAddress &addr) const;
|
||||
|
||||
/**
|
||||
* @return True if this is an IPv4 address
|
||||
*/
|
||||
inline bool isV4() const { return (ss_family == AF_INET); }
|
||||
|
||||
/**
|
||||
* @return True if this is an IPv6 address
|
||||
*/
|
||||
inline bool isV6() const { return (ss_family == AF_INET6); }
|
||||
|
||||
/**
|
||||
* @return pointer to raw address bytes or NULL if not available
|
||||
*/
|
||||
inline const void *rawIpData() const
|
||||
{
|
||||
switch(ss_family) {
|
||||
case AF_INET:
|
||||
return (const void *)&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr);
|
||||
case AF_INET6:
|
||||
return (const void *)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return InetAddress containing only the IP portion of this address and a zero port, or NULL if not IPv4 or IPv6
|
||||
*/
|
||||
inline InetAddress ipOnly() const
|
||||
{
|
||||
InetAddress r;
|
||||
switch(ss_family) {
|
||||
case AF_INET:
|
||||
r.ss_family = AF_INET;
|
||||
reinterpret_cast<struct sockaddr_in *>(&r)->sin_addr.s_addr = reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr;
|
||||
break;
|
||||
case AF_INET6:
|
||||
r.ss_family = AF_INET6;
|
||||
memcpy(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,16);
|
||||
break;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an IP-only comparison or, if that is impossible, a memcmp()
|
||||
*
|
||||
* @param a InetAddress to compare again
|
||||
* @return True if only IP portions are equal (false for non-IP or null addresses)
|
||||
*/
|
||||
inline bool ipsEqual(const InetAddress &a) const
|
||||
{
|
||||
if (ss_family == a.ss_family) {
|
||||
if (ss_family == AF_INET) {
|
||||
return (reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr == reinterpret_cast<const struct sockaddr_in *>(&a)->sin_addr.s_addr);
|
||||
}
|
||||
if (ss_family == AF_INET6) {
|
||||
return (memcmp(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_addr.s6_addr,16) == 0);
|
||||
}
|
||||
return (memcmp(this,&a,sizeof(InetAddress)) == 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an IP-only comparison or, if that is impossible, a memcmp()
|
||||
*
|
||||
* This version compares only the first 64 bits of IPv6 addresses.
|
||||
*
|
||||
* @param a InetAddress to compare again
|
||||
* @return True if only IP portions are equal (false for non-IP or null addresses)
|
||||
*/
|
||||
inline bool ipsEqual2(const InetAddress &a) const
|
||||
{
|
||||
if (ss_family == a.ss_family) {
|
||||
if (ss_family == AF_INET) {
|
||||
return (reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr == reinterpret_cast<const struct sockaddr_in *>(&a)->sin_addr.s_addr);
|
||||
}
|
||||
if (ss_family == AF_INET6) {
|
||||
return (memcmp(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr, reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_addr.s6_addr, 8) == 0);
|
||||
}
|
||||
return (memcmp(this,&a,sizeof(InetAddress)) == 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline unsigned long hashCode() const
|
||||
{
|
||||
if (ss_family == AF_INET) {
|
||||
return ((unsigned long)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr + (unsigned long)reinterpret_cast<const struct sockaddr_in *>(this)->sin_port);
|
||||
} else if (ss_family == AF_INET6) {
|
||||
unsigned long tmp = reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port;
|
||||
const uint8_t *a = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
|
||||
for(long i=0;i<16;++i) {
|
||||
reinterpret_cast<uint8_t *>(&tmp)[i % sizeof(tmp)] ^= a[i];
|
||||
}
|
||||
return tmp;
|
||||
} else {
|
||||
unsigned long tmp = reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port;
|
||||
const uint8_t *a = reinterpret_cast<const uint8_t *>(this);
|
||||
for(long i=0;i<(long)sizeof(InetAddress);++i) {
|
||||
reinterpret_cast<uint8_t *>(&tmp)[i % sizeof(tmp)] ^= a[i];
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to null/zero
|
||||
*/
|
||||
inline void zero() { memset(this,0,sizeof(InetAddress)); }
|
||||
|
||||
/**
|
||||
* Check whether this is a network/route rather than an IP assignment
|
||||
*
|
||||
* A network is an IP/netmask where everything after the netmask is
|
||||
* zero e.g. 10.0.0.0/8.
|
||||
*
|
||||
* @return True if everything after netmask bits is zero
|
||||
*/
|
||||
bool isNetwork() const;
|
||||
|
||||
/**
|
||||
* Find the total number of prefix bits that match between this IP and another
|
||||
*
|
||||
* @param b Second IP to compare with
|
||||
* @return Number of matching prefix bits or 0 if none match or IPs are of different families (e.g. v4 and v6)
|
||||
*/
|
||||
inline unsigned int matchingPrefixBits(const InetAddress &b) const
|
||||
{
|
||||
unsigned int c = 0;
|
||||
if (ss_family == b.ss_family) {
|
||||
switch(ss_family) {
|
||||
case AF_INET: {
|
||||
uint32_t ip0 = Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr);
|
||||
uint32_t ip1 = Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(&b)->sin_addr.s_addr);
|
||||
while ((ip0 >> 31) == (ip1 >> 31)) {
|
||||
ip0 <<= 1;
|
||||
ip1 <<= 1;
|
||||
if (++c == 32) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case AF_INET6: {
|
||||
const uint8_t *ip0 = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
|
||||
const uint8_t *ip1 = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&b)->sin6_addr.s6_addr);
|
||||
for(unsigned int i=0;i<16;++i) {
|
||||
if (ip0[i] == ip1[i]) {
|
||||
c += 8;
|
||||
} else {
|
||||
uint8_t ip0b = ip0[i];
|
||||
uint8_t ip1b = ip1[i];
|
||||
uint8_t bit = 0x80;
|
||||
while (bit != 0) {
|
||||
if ((ip0b & bit) != (ip1b & bit)) {
|
||||
break;
|
||||
}
|
||||
++c;
|
||||
bit >>= 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 14-bit (0-16383) hash of this IP's first 24 or 48 bits (for V4 or V6) for rate limiting code, or 0 if non-IP
|
||||
*/
|
||||
inline unsigned long rateGateHash() const
|
||||
{
|
||||
unsigned long h = 0;
|
||||
switch(ss_family) {
|
||||
case AF_INET:
|
||||
h = (Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr) & 0xffffff00) >> 8;
|
||||
h ^= (h >> 14);
|
||||
break;
|
||||
case AF_INET6: {
|
||||
const uint8_t *ip = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
|
||||
h = ((unsigned long)ip[0]);
|
||||
h <<= 1;
|
||||
h += ((unsigned long)ip[1]);
|
||||
h <<= 1;
|
||||
h += ((unsigned long)ip[2]);
|
||||
h <<= 1;
|
||||
h += ((unsigned long)ip[3]);
|
||||
h <<= 1;
|
||||
h += ((unsigned long)ip[4]);
|
||||
h <<= 1;
|
||||
h += ((unsigned long)ip[5]);
|
||||
} break;
|
||||
}
|
||||
return (h & 0x3fff);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if address family is non-zero
|
||||
*/
|
||||
inline operator bool() const { return (ss_family != 0); }
|
||||
|
||||
template<unsigned int C>
|
||||
inline void serialize(Buffer<C> &b) const
|
||||
{
|
||||
// This is used in the protocol and must be the same as describe in places
|
||||
// like VERB_HELLO in Packet.hpp.
|
||||
switch(ss_family) {
|
||||
case AF_INET:
|
||||
b.append((uint8_t)0x04);
|
||||
b.append(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr),4);
|
||||
b.append((uint16_t)port()); // just in case sin_port != uint16_t
|
||||
return;
|
||||
case AF_INET6:
|
||||
b.append((uint8_t)0x06);
|
||||
b.append(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,16);
|
||||
b.append((uint16_t)port()); // just in case sin_port != uint16_t
|
||||
return;
|
||||
default:
|
||||
b.append((uint8_t)0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned int C>
|
||||
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
|
||||
{
|
||||
memset(this,0,sizeof(InetAddress));
|
||||
unsigned int p = startAt;
|
||||
switch(b[p++]) {
|
||||
case 0:
|
||||
return 1;
|
||||
case 0x01:
|
||||
// TODO: Ethernet address (but accept for forward compatibility)
|
||||
return 7;
|
||||
case 0x02:
|
||||
// TODO: Bluetooth address (but accept for forward compatibility)
|
||||
return 7;
|
||||
case 0x03:
|
||||
// TODO: Other address types (but accept for forward compatibility)
|
||||
// These could be extended/optional things like AF_UNIX, LTE Direct, shared memory, etc.
|
||||
return (unsigned int)(b.template at<uint16_t>(p) + 3); // other addresses begin with 16-bit non-inclusive length
|
||||
case 0x04:
|
||||
ss_family = AF_INET;
|
||||
memcpy(&(reinterpret_cast<struct sockaddr_in *>(this)->sin_addr.s_addr),b.field(p,4),4);
|
||||
p += 4;
|
||||
reinterpret_cast<struct sockaddr_in *>(this)->sin_port = Utils::hton(b.template at<uint16_t>(p));
|
||||
p += 2;
|
||||
break;
|
||||
case 0x06:
|
||||
ss_family = AF_INET6;
|
||||
memcpy(reinterpret_cast<struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,b.field(p,16),16);
|
||||
p += 16;
|
||||
reinterpret_cast<struct sockaddr_in *>(this)->sin_port = Utils::hton(b.template at<uint16_t>(p));
|
||||
p += 2;
|
||||
break;
|
||||
default:
|
||||
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_BAD_ENCODING;
|
||||
}
|
||||
return (p - startAt);
|
||||
}
|
||||
|
||||
bool operator==(const InetAddress &a) const;
|
||||
bool operator<(const InetAddress &a) const;
|
||||
inline bool operator!=(const InetAddress &a) const { return !(*this == a); }
|
||||
inline bool operator>(const InetAddress &a) const { return (a < *this); }
|
||||
inline bool operator<=(const InetAddress &a) const { return !(a < *this); }
|
||||
inline bool operator>=(const InetAddress &a) const { return !(*this < a); }
|
||||
|
||||
/**
|
||||
* @param mac MAC address seed
|
||||
* @return IPv6 link-local address
|
||||
*/
|
||||
static InetAddress makeIpv6LinkLocal(const MAC &mac);
|
||||
|
||||
/**
|
||||
* Compute private IPv6 unicast address from network ID and ZeroTier address
|
||||
*
|
||||
* This generates a private unicast IPv6 address that is mostly compliant
|
||||
* with the letter of RFC4193 and certainly compliant in spirit.
|
||||
*
|
||||
* RFC4193 specifies a format of:
|
||||
*
|
||||
* | 7 bits |1| 40 bits | 16 bits | 64 bits |
|
||||
* | Prefix |L| Global ID | Subnet ID | Interface ID |
|
||||
*
|
||||
* The 'L' bit is set to 1, yielding an address beginning with 0xfd. Then
|
||||
* the network ID is filled into the global ID, subnet ID, and first byte
|
||||
* of the "interface ID" field. Since the first 40 bits of the network ID
|
||||
* is the unique ZeroTier address of its controller, this makes a very
|
||||
* good random global ID. Since network IDs have 24 more bits, we let it
|
||||
* overflow into the interface ID.
|
||||
*
|
||||
* After that we pad with two bytes: 0x99, 0x93, namely the default ZeroTier
|
||||
* port in hex.
|
||||
*
|
||||
* Finally we fill the remaining 40 bits of the interface ID field with
|
||||
* the 40-bit unique ZeroTier device ID of the network member.
|
||||
*
|
||||
* This yields a valid RFC4193 address with a random global ID, a
|
||||
* meaningful subnet ID, and a unique interface ID, all mappable back onto
|
||||
* ZeroTier space.
|
||||
*
|
||||
* This in turn could allow us, on networks numbered this way, to emulate
|
||||
* IPv6 NDP and eliminate all multicast. This could be beneficial for
|
||||
* small devices and huge networks, e.g. IoT applications.
|
||||
*
|
||||
* The returned address is given an odd prefix length of /88, since within
|
||||
* a given network only the last 40 bits (device ID) are variable. This
|
||||
* is a bit unusual but as far as we know should not cause any problems with
|
||||
* any non-braindead IPv6 stack.
|
||||
*
|
||||
* @param nwid 64-bit network ID
|
||||
* @param zeroTierAddress 40-bit device address (in least significant 40 bits, highest 24 bits ignored)
|
||||
* @return IPv6 private unicast address with /88 netmask
|
||||
*/
|
||||
static InetAddress makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress);
|
||||
|
||||
/**
|
||||
* Compute a private IPv6 "6plane" unicast address from network ID and ZeroTier address
|
||||
*/
|
||||
static InetAddress makeIpv66plane(uint64_t nwid,uint64_t zeroTierAddress);
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_MAC_HPP
|
||||
#define ZT_MAC_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "Buffer.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* 48-byte Ethernet MAC address
|
||||
*/
|
||||
class MAC
|
||||
{
|
||||
public:
|
||||
MAC() : _m(0ULL) {}
|
||||
MAC(const MAC &m) : _m(m._m) {}
|
||||
|
||||
MAC(const unsigned char a,const unsigned char b,const unsigned char c,const unsigned char d,const unsigned char e,const unsigned char f) :
|
||||
_m( ((((uint64_t)a) & 0xffULL) << 40) |
|
||||
((((uint64_t)b) & 0xffULL) << 32) |
|
||||
((((uint64_t)c) & 0xffULL) << 24) |
|
||||
((((uint64_t)d) & 0xffULL) << 16) |
|
||||
((((uint64_t)e) & 0xffULL) << 8) |
|
||||
(((uint64_t)f) & 0xffULL) ) {}
|
||||
MAC(const void *bits,unsigned int len) { setTo(bits,len); }
|
||||
MAC(const Address &ztaddr,uint64_t nwid) { fromAddress(ztaddr,nwid); }
|
||||
MAC(const uint64_t m) : _m(m & 0xffffffffffffULL) {}
|
||||
|
||||
/**
|
||||
* @return MAC in 64-bit integer
|
||||
*/
|
||||
inline uint64_t toInt() const { return _m; }
|
||||
|
||||
/**
|
||||
* Set MAC to zero
|
||||
*/
|
||||
inline void zero() { _m = 0ULL; }
|
||||
|
||||
/**
|
||||
* @return True if MAC is non-zero
|
||||
*/
|
||||
inline operator bool() const { return (_m != 0ULL); }
|
||||
|
||||
/**
|
||||
* @param bits Raw MAC in big-endian byte order
|
||||
* @param len Length, must be >= 6 or result is zero
|
||||
*/
|
||||
inline void setTo(const void *bits,unsigned int len)
|
||||
{
|
||||
if (len < 6) {
|
||||
_m = 0ULL;
|
||||
return;
|
||||
}
|
||||
const unsigned char *b = (const unsigned char *)bits;
|
||||
_m = ((((uint64_t)*b) & 0xff) << 40);
|
||||
++b;
|
||||
_m |= ((((uint64_t)*b) & 0xff) << 32);
|
||||
++b;
|
||||
_m |= ((((uint64_t)*b) & 0xff) << 24);
|
||||
++b;
|
||||
_m |= ((((uint64_t)*b) & 0xff) << 16);
|
||||
++b;
|
||||
_m |= ((((uint64_t)*b) & 0xff) << 8);
|
||||
++b;
|
||||
_m |= (((uint64_t)*b) & 0xff);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param buf Destination buffer for MAC in big-endian byte order
|
||||
* @param len Length of buffer, must be >= 6 or nothing is copied
|
||||
*/
|
||||
inline void copyTo(void *buf,unsigned int len) const
|
||||
{
|
||||
if (len < 6) {
|
||||
return;
|
||||
}
|
||||
unsigned char *b = (unsigned char *)buf;
|
||||
*(b++) = (unsigned char)((_m >> 40) & 0xff);
|
||||
*(b++) = (unsigned char)((_m >> 32) & 0xff);
|
||||
*(b++) = (unsigned char)((_m >> 24) & 0xff);
|
||||
*(b++) = (unsigned char)((_m >> 16) & 0xff);
|
||||
*(b++) = (unsigned char)((_m >> 8) & 0xff);
|
||||
*b = (unsigned char)(_m & 0xff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append to a buffer in big-endian byte order
|
||||
*
|
||||
* @param b Buffer to append to
|
||||
*/
|
||||
template<unsigned int C>
|
||||
inline void appendTo(Buffer<C> &b) const
|
||||
{
|
||||
unsigned char *p = (unsigned char *)b.appendField(6);
|
||||
*(p++) = (unsigned char)((_m >> 40) & 0xff);
|
||||
*(p++) = (unsigned char)((_m >> 32) & 0xff);
|
||||
*(p++) = (unsigned char)((_m >> 24) & 0xff);
|
||||
*(p++) = (unsigned char)((_m >> 16) & 0xff);
|
||||
*(p++) = (unsigned char)((_m >> 8) & 0xff);
|
||||
*p = (unsigned char)(_m & 0xff);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if this is broadcast (all 0xff)
|
||||
*/
|
||||
inline bool isBroadcast() const { return (_m == 0xffffffffffffULL); }
|
||||
|
||||
/**
|
||||
* @return True if this is a multicast MAC
|
||||
*/
|
||||
inline bool isMulticast() const { return ((_m & 0x010000000000ULL) != 0ULL); }
|
||||
|
||||
/**
|
||||
* @param True if this is a locally-administered MAC
|
||||
*/
|
||||
inline bool isLocallyAdministered() const { return ((_m & 0x020000000000ULL) != 0ULL); }
|
||||
|
||||
/**
|
||||
* Set this MAC to a MAC derived from an address and a network ID
|
||||
*
|
||||
* @param ztaddr ZeroTier address
|
||||
* @param nwid 64-bit network ID
|
||||
*/
|
||||
inline void fromAddress(const Address &ztaddr,uint64_t nwid)
|
||||
{
|
||||
uint64_t m = ((uint64_t)firstOctetForNetwork(nwid)) << 40;
|
||||
m |= ztaddr.toInt(); // a is 40 bits
|
||||
m ^= ((nwid >> 8) & 0xff) << 32;
|
||||
m ^= ((nwid >> 16) & 0xff) << 24;
|
||||
m ^= ((nwid >> 24) & 0xff) << 16;
|
||||
m ^= ((nwid >> 32) & 0xff) << 8;
|
||||
m ^= (nwid >> 40) & 0xff;
|
||||
_m = m;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ZeroTier address for this MAC on this network (assuming no bridging of course, basic unicast)
|
||||
*
|
||||
* This just XORs the next-least-significant 5 bytes of the network ID again to unmask.
|
||||
*
|
||||
* @param nwid Network ID
|
||||
*/
|
||||
inline Address toAddress(uint64_t nwid) const
|
||||
{
|
||||
uint64_t a = _m & 0xffffffffffULL; // least significant 40 bits of MAC are formed from address
|
||||
a ^= ((nwid >> 8) & 0xff) << 32; // ... XORed with bits 8-48 of the nwid in little-endian byte order, so unmask it
|
||||
a ^= ((nwid >> 16) & 0xff) << 24;
|
||||
a ^= ((nwid >> 24) & 0xff) << 16;
|
||||
a ^= ((nwid >> 32) & 0xff) << 8;
|
||||
a ^= (nwid >> 40) & 0xff;
|
||||
return Address(a);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nwid Network ID
|
||||
* @return First octet of MAC for this network
|
||||
*/
|
||||
static inline unsigned char firstOctetForNetwork(uint64_t nwid)
|
||||
{
|
||||
unsigned char a = ((unsigned char)(nwid & 0xfe) | 0x02); // locally administered, not multicast, from LSB of network ID
|
||||
return ((a == 0x52) ? 0x32 : a); // blacklist 0x52 since it's used by KVM, libvirt, and other popular virtualization engines... seems de-facto standard on Linux
|
||||
}
|
||||
|
||||
/**
|
||||
* @param i Value from 0 to 5 (inclusive)
|
||||
* @return Byte at said position (address interpreted in big-endian order)
|
||||
*/
|
||||
inline unsigned char operator[](unsigned int i) const { return (unsigned char)((_m >> (40 - (i * 8))) & 0xff); }
|
||||
|
||||
/**
|
||||
* @return 6, which is the number of bytes in a MAC, for container compliance
|
||||
*/
|
||||
inline unsigned int size() const { return 6; }
|
||||
|
||||
inline unsigned long hashCode() const { return (unsigned long)_m; }
|
||||
|
||||
inline char *toString(char buf[18]) const
|
||||
{
|
||||
buf[0] = Utils::HEXCHARS[(_m >> 44) & 0xf];
|
||||
buf[1] = Utils::HEXCHARS[(_m >> 40) & 0xf];
|
||||
buf[2] = ':';
|
||||
buf[3] = Utils::HEXCHARS[(_m >> 36) & 0xf];
|
||||
buf[4] = Utils::HEXCHARS[(_m >> 32) & 0xf];
|
||||
buf[5] = ':';
|
||||
buf[6] = Utils::HEXCHARS[(_m >> 28) & 0xf];
|
||||
buf[7] = Utils::HEXCHARS[(_m >> 24) & 0xf];
|
||||
buf[8] = ':';
|
||||
buf[9] = Utils::HEXCHARS[(_m >> 20) & 0xf];
|
||||
buf[10] = Utils::HEXCHARS[(_m >> 16) & 0xf];
|
||||
buf[11] = ':';
|
||||
buf[12] = Utils::HEXCHARS[(_m >> 12) & 0xf];
|
||||
buf[13] = Utils::HEXCHARS[(_m >> 8) & 0xf];
|
||||
buf[14] = ':';
|
||||
buf[15] = Utils::HEXCHARS[(_m >> 4) & 0xf];
|
||||
buf[16] = Utils::HEXCHARS[_m & 0xf];
|
||||
buf[17] = (char)0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
inline MAC &operator=(const MAC &m)
|
||||
{
|
||||
_m = m._m;
|
||||
return *this;
|
||||
}
|
||||
inline MAC &operator=(const uint64_t m)
|
||||
{
|
||||
_m = m;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline bool operator==(const MAC &m) const { return (_m == m._m); }
|
||||
inline bool operator!=(const MAC &m) const { return (_m != m._m); }
|
||||
inline bool operator<(const MAC &m) const { return (_m < m._m); }
|
||||
inline bool operator<=(const MAC &m) const { return (_m <= m._m); }
|
||||
inline bool operator>(const MAC &m) const { return (_m > m._m); }
|
||||
inline bool operator>=(const MAC &m) const { return (_m >= m._m); }
|
||||
|
||||
private:
|
||||
uint64_t _m;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "Membership.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Peer.hpp"
|
||||
#include "Topology.hpp"
|
||||
#include "Switch.hpp"
|
||||
#include "Packet.hpp"
|
||||
#include "Node.hpp"
|
||||
#include "Trace.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
Membership::Membership() :
|
||||
_lastUpdatedMulticast(0),
|
||||
_comRevocationThreshold(0),
|
||||
_lastPushedCredentials(0),
|
||||
_revocations(4),
|
||||
_remoteTags(4),
|
||||
_remoteCaps(4),
|
||||
_remoteCoos(4)
|
||||
{
|
||||
}
|
||||
|
||||
void Membership::pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const int64_t now,const Address &peerAddress,const NetworkConfig &nconf)
|
||||
{
|
||||
const Capability *sendCaps[ZT_MAX_NETWORK_CAPABILITIES];
|
||||
unsigned int sendCapCount = 0;
|
||||
for(unsigned int c=0;c<nconf.capabilityCount;++c) {
|
||||
sendCaps[sendCapCount++] = &(nconf.capabilities[c]);
|
||||
}
|
||||
|
||||
const Tag *sendTags[ZT_MAX_NETWORK_TAGS];
|
||||
unsigned int sendTagCount = 0;
|
||||
for(unsigned int t=0;t<nconf.tagCount;++t) {
|
||||
sendTags[sendTagCount++] = &(nconf.tags[t]);
|
||||
}
|
||||
|
||||
const CertificateOfOwnership *sendCoos[ZT_MAX_CERTIFICATES_OF_OWNERSHIP];
|
||||
unsigned int sendCooCount = 0;
|
||||
for(unsigned int c=0;c<nconf.certificateOfOwnershipCount;++c) {
|
||||
sendCoos[sendCooCount++] = &(nconf.certificatesOfOwnership[c]);
|
||||
}
|
||||
|
||||
unsigned int capPtr = 0;
|
||||
unsigned int tagPtr = 0;
|
||||
unsigned int cooPtr = 0;
|
||||
bool sendCom = (bool)(nconf.com);
|
||||
while ((capPtr < sendCapCount)||(tagPtr < sendTagCount)||(cooPtr < sendCooCount)||(sendCom)) {
|
||||
Packet outp(peerAddress,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
|
||||
|
||||
if (sendCom) {
|
||||
sendCom = false;
|
||||
nconf.com.serialize(outp);
|
||||
}
|
||||
outp.append((uint8_t)0x00);
|
||||
|
||||
const unsigned int capCountAt = outp.size();
|
||||
outp.addSize(2);
|
||||
unsigned int thisPacketCapCount = 0;
|
||||
while ((capPtr < sendCapCount)&&((outp.size() + sizeof(Capability) + 16) < ZT_PROTO_MAX_PACKET_LENGTH)) {
|
||||
sendCaps[capPtr++]->serialize(outp);
|
||||
++thisPacketCapCount;
|
||||
}
|
||||
outp.setAt(capCountAt,(uint16_t)thisPacketCapCount);
|
||||
|
||||
const unsigned int tagCountAt = outp.size();
|
||||
outp.addSize(2);
|
||||
unsigned int thisPacketTagCount = 0;
|
||||
while ((tagPtr < sendTagCount)&&((outp.size() + sizeof(Tag) + 16) < ZT_PROTO_MAX_PACKET_LENGTH)) {
|
||||
sendTags[tagPtr++]->serialize(outp);
|
||||
++thisPacketTagCount;
|
||||
}
|
||||
outp.setAt(tagCountAt,(uint16_t)thisPacketTagCount);
|
||||
|
||||
// No revocations, these propagate differently
|
||||
outp.append((uint16_t)0);
|
||||
|
||||
const unsigned int cooCountAt = outp.size();
|
||||
outp.addSize(2);
|
||||
unsigned int thisPacketCooCount = 0;
|
||||
while ((cooPtr < sendCooCount)&&((outp.size() + sizeof(CertificateOfOwnership) + 16) < ZT_PROTO_MAX_PACKET_LENGTH)) {
|
||||
sendCoos[cooPtr++]->serialize(outp);
|
||||
++thisPacketCooCount;
|
||||
}
|
||||
outp.setAt(cooCountAt,(uint16_t)thisPacketCooCount);
|
||||
|
||||
outp.compress();
|
||||
RR->sw->send(tPtr,outp,true);
|
||||
Metrics::pkt_network_credentials_out++;
|
||||
}
|
||||
|
||||
_lastPushedCredentials = now;
|
||||
}
|
||||
|
||||
Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const CertificateOfMembership &com)
|
||||
{
|
||||
const int64_t newts = com.timestamp();
|
||||
if (newts <= _comRevocationThreshold) {
|
||||
RR->t->credentialRejected(tPtr,com,"revoked");
|
||||
return ADD_REJECTED;
|
||||
}
|
||||
|
||||
const int64_t oldts = _com.timestamp();
|
||||
if (newts < oldts) {
|
||||
RR->t->credentialRejected(tPtr,com,"old");
|
||||
return ADD_REJECTED;
|
||||
}
|
||||
if (_com == com) {
|
||||
return ADD_ACCEPTED_REDUNDANT;
|
||||
}
|
||||
|
||||
switch(com.verify(RR,tPtr)) {
|
||||
default:
|
||||
RR->t->credentialRejected(tPtr,com,"invalid");
|
||||
return ADD_REJECTED;
|
||||
case 0:
|
||||
//printf("%.16llx %.10llx replacing COM %lld with %lld\n", com.networkId(), com.issuedTo().toInt(), _com.timestamp(), com.timestamp()); fflush(stdout);
|
||||
_com = com;
|
||||
return ADD_ACCEPTED_NEW;
|
||||
case 1:
|
||||
return ADD_DEFERRED_FOR_WHOIS;
|
||||
}
|
||||
}
|
||||
|
||||
// Template out addCredential() for many cred types to avoid copypasta
|
||||
template<typename C>
|
||||
static Membership::AddCredentialResult _addCredImpl(Hashtable<uint32_t,C> &remoteCreds,const Hashtable<uint64_t,int64_t> &revocations,const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const C &cred)
|
||||
{
|
||||
C *rc = remoteCreds.get(cred.id());
|
||||
if (rc) {
|
||||
if (rc->timestamp() > cred.timestamp()) {
|
||||
RR->t->credentialRejected(tPtr,cred,"old");
|
||||
return Membership::ADD_REJECTED;
|
||||
}
|
||||
if (*rc == cred) {
|
||||
return Membership::ADD_ACCEPTED_REDUNDANT;
|
||||
}
|
||||
}
|
||||
|
||||
const int64_t *const rt = revocations.get(Membership::credentialKey(C::credentialType(),cred.id()));
|
||||
if ((rt)&&(*rt >= cred.timestamp())) {
|
||||
RR->t->credentialRejected(tPtr,cred,"revoked");
|
||||
return Membership::ADD_REJECTED;
|
||||
}
|
||||
|
||||
switch(cred.verify(RR,tPtr)) {
|
||||
default:
|
||||
RR->t->credentialRejected(tPtr,cred,"invalid");
|
||||
return Membership::ADD_REJECTED;
|
||||
case 0:
|
||||
if (!rc) {
|
||||
rc = &(remoteCreds[cred.id()]);
|
||||
}
|
||||
*rc = cred;
|
||||
return Membership::ADD_ACCEPTED_NEW;
|
||||
case 1:
|
||||
return Membership::ADD_DEFERRED_FOR_WHOIS;
|
||||
}
|
||||
}
|
||||
|
||||
Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Tag &tag) { return _addCredImpl<Tag>(_remoteTags,_revocations,RR,tPtr,nconf,tag); }
|
||||
Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Capability &cap) { return _addCredImpl<Capability>(_remoteCaps,_revocations,RR,tPtr,nconf,cap); }
|
||||
Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const CertificateOfOwnership &coo) { return _addCredImpl<CertificateOfOwnership>(_remoteCoos,_revocations,RR,tPtr,nconf,coo); }
|
||||
|
||||
Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Revocation &rev)
|
||||
{
|
||||
int64_t *rt;
|
||||
switch(rev.verify(RR,tPtr)) {
|
||||
default:
|
||||
RR->t->credentialRejected(tPtr,rev,"invalid");
|
||||
return ADD_REJECTED;
|
||||
case 0: {
|
||||
const Credential::Type ct = rev.type();
|
||||
switch(ct) {
|
||||
case Credential::CREDENTIAL_TYPE_COM:
|
||||
if (rev.threshold() > _comRevocationThreshold) {
|
||||
_comRevocationThreshold = rev.threshold();
|
||||
return ADD_ACCEPTED_NEW;
|
||||
}
|
||||
return ADD_ACCEPTED_REDUNDANT;
|
||||
case Credential::CREDENTIAL_TYPE_CAPABILITY:
|
||||
case Credential::CREDENTIAL_TYPE_TAG:
|
||||
case Credential::CREDENTIAL_TYPE_COO:
|
||||
rt = &(_revocations[credentialKey(ct,rev.credentialId())]);
|
||||
if (*rt < rev.threshold()) {
|
||||
*rt = rev.threshold();
|
||||
_comRevocationThreshold = rev.threshold();
|
||||
return ADD_ACCEPTED_NEW;
|
||||
}
|
||||
return ADD_ACCEPTED_REDUNDANT;
|
||||
default:
|
||||
RR->t->credentialRejected(tPtr,rev,"invalid");
|
||||
return ADD_REJECTED;
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
return ADD_DEFERRED_FOR_WHOIS;
|
||||
}
|
||||
}
|
||||
|
||||
void Membership::clean(const int64_t now,const NetworkConfig &nconf)
|
||||
{
|
||||
_cleanCredImpl<Tag>(nconf,_remoteTags);
|
||||
_cleanCredImpl<Capability>(nconf,_remoteCaps);
|
||||
_cleanCredImpl<CertificateOfOwnership>(nconf,_remoteCoos);
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
@@ -0,0 +1,296 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_MEMBERSHIP_HPP
|
||||
#define ZT_MEMBERSHIP_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "../include/ZeroTierOne.h"
|
||||
#include "Credential.hpp"
|
||||
#include "Hashtable.hpp"
|
||||
#include "CertificateOfMembership.hpp"
|
||||
#include "Capability.hpp"
|
||||
#include "Tag.hpp"
|
||||
#include "Revocation.hpp"
|
||||
#include "NetworkConfig.hpp"
|
||||
|
||||
#define ZT_MEMBERSHIP_CRED_ID_UNUSED 0xffffffffffffffffULL
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class RuntimeEnvironment;
|
||||
class Network;
|
||||
|
||||
/**
|
||||
* A container for certificates of membership and other network credentials
|
||||
*
|
||||
* This is essentially a relational join between Peer and Network.
|
||||
*
|
||||
* This class is not thread safe. It must be locked externally.
|
||||
*/
|
||||
class Membership
|
||||
{
|
||||
public:
|
||||
enum AddCredentialResult
|
||||
{
|
||||
ADD_REJECTED,
|
||||
ADD_ACCEPTED_NEW,
|
||||
ADD_ACCEPTED_REDUNDANT,
|
||||
ADD_DEFERRED_FOR_WHOIS
|
||||
};
|
||||
|
||||
Membership();
|
||||
|
||||
/**
|
||||
* Send COM and other credentials to this peer
|
||||
*
|
||||
* @param RR Runtime environment
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param now Current time
|
||||
* @param peerAddress Address of member peer (the one that this Membership describes)
|
||||
* @param nconf My network config
|
||||
*/
|
||||
void pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const int64_t now,const Address &peerAddress,const NetworkConfig &nconf);
|
||||
|
||||
inline int64_t lastPushedCredentials() { return _lastPushedCredentials; }
|
||||
inline int64_t comTimestamp() { return _com.timestamp(); }
|
||||
inline int64_t comRevocationThreshold() { return _comRevocationThreshold; }
|
||||
|
||||
/**
|
||||
* Check whether we should push MULTICAST_LIKEs to this peer, and update last sent time if true
|
||||
*
|
||||
* @param now Current time
|
||||
* @return True if we should update multicasts
|
||||
*/
|
||||
inline bool multicastLikeGate(const int64_t now)
|
||||
{
|
||||
if ((now - _lastUpdatedMulticast) >= ZT_MULTICAST_ANNOUNCE_PERIOD) {
|
||||
_lastUpdatedMulticast = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the peer represented by this Membership should be allowed on this network at all
|
||||
*
|
||||
* @param nconf Our network config
|
||||
* @param otherNodeIdentity Identity of remote node
|
||||
* @return True if this peer is allowed on this network at all
|
||||
*/
|
||||
inline bool isAllowedOnNetwork(const NetworkConfig &thisNodeNetworkConfig, const Identity &otherNodeIdentity) const
|
||||
{
|
||||
return thisNodeNetworkConfig.isPublic() || (((_com.timestamp() > _comRevocationThreshold) && (thisNodeNetworkConfig.com.agreesWith(_com, otherNodeIdentity))));
|
||||
}
|
||||
|
||||
inline bool recentlyAssociated(const int64_t now) const
|
||||
{
|
||||
return ((_com)&&((now - _com.timestamp()) < ZT_PEER_ACTIVITY_TIMEOUT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the peer represented by this Membership owns a given resource
|
||||
*
|
||||
* @tparam Type of resource: InetAddress or MAC
|
||||
* @param nconf Our network config
|
||||
* @param r Resource to check
|
||||
* @return True if this peer has a certificate of ownership for the given resource
|
||||
*/
|
||||
template<typename T>
|
||||
inline bool hasCertificateOfOwnershipFor(const NetworkConfig &nconf,const T &r) const
|
||||
{
|
||||
uint32_t *k = (uint32_t *)0;
|
||||
CertificateOfOwnership *v = (CertificateOfOwnership *)0;
|
||||
Hashtable< uint32_t,CertificateOfOwnership >::Iterator i(*(const_cast< Hashtable< uint32_t,CertificateOfOwnership> *>(&_remoteCoos)));
|
||||
while (i.next(k,v)) {
|
||||
if (_isCredentialTimestampValid(nconf,*v)&&(v->owns(r))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return _isV6NDPEmulated(nconf,r);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a remote member's tag (if we have it)
|
||||
*
|
||||
* @param nconf Network configuration
|
||||
* @param id Tag ID
|
||||
* @return Pointer to tag or NULL if not found
|
||||
*/
|
||||
inline const Tag *getTag(const NetworkConfig &nconf,const uint32_t id) const
|
||||
{
|
||||
const Tag *const t = _remoteTags.get(id);
|
||||
return (((t)&&(_isCredentialTimestampValid(nconf,*t))) ? t : (Tag *)0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and add a credential if signature is okay and it's otherwise good
|
||||
*/
|
||||
AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const CertificateOfMembership &com);
|
||||
|
||||
/**
|
||||
* Validate and add a credential if signature is okay and it's otherwise good
|
||||
*/
|
||||
AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Tag &tag);
|
||||
|
||||
/**
|
||||
* Validate and add a credential if signature is okay and it's otherwise good
|
||||
*/
|
||||
AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Capability &cap);
|
||||
|
||||
/**
|
||||
* Validate and add a credential if signature is okay and it's otherwise good
|
||||
*/
|
||||
AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const CertificateOfOwnership &coo);
|
||||
|
||||
/**
|
||||
* Validate and add a credential if signature is okay and it's otherwise good
|
||||
*/
|
||||
AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Revocation &rev);
|
||||
|
||||
/**
|
||||
* Clean internal databases of stale entries
|
||||
*
|
||||
* @param now Current time
|
||||
* @param nconf Current network configuration
|
||||
*/
|
||||
void clean(const int64_t now,const NetworkConfig &nconf);
|
||||
|
||||
/**
|
||||
* Generates a key for the internal use in indexing credentials by type and credential ID
|
||||
*/
|
||||
static uint64_t credentialKey(const Credential::Type &t,const uint32_t i) { return (((uint64_t)t << 32) | (uint64_t)i); }
|
||||
|
||||
private:
|
||||
inline bool _isV6NDPEmulated(const NetworkConfig &nconf,const MAC &m) const { return false; }
|
||||
inline bool _isV6NDPEmulated(const NetworkConfig &nconf,const InetAddress &ip) const
|
||||
{
|
||||
if ((ip.isV6())&&(nconf.ndpEmulation())) {
|
||||
const InetAddress sixpl(InetAddress::makeIpv66plane(nconf.networkId,nconf.issuedTo.toInt()));
|
||||
for(unsigned int i=0;i<nconf.staticIpCount;++i) {
|
||||
if (nconf.staticIps[i].ipsEqual(sixpl)) {
|
||||
bool prefixMatches = true;
|
||||
for(unsigned int j=0;j<5;++j) { // check for match on /40
|
||||
if ((((const struct sockaddr_in6 *)&ip)->sin6_addr.s6_addr)[j] != (((const struct sockaddr_in6 *)&sixpl)->sin6_addr.s6_addr)[j]) {
|
||||
prefixMatches = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (prefixMatches) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const InetAddress rfc4193(InetAddress::makeIpv6rfc4193(nconf.networkId,nconf.issuedTo.toInt()));
|
||||
for(unsigned int i=0;i<nconf.staticIpCount;++i) {
|
||||
if (nconf.staticIps[i].ipsEqual(rfc4193)) {
|
||||
bool prefixMatches = true;
|
||||
for(unsigned int j=0;j<11;++j) { // check for match on /88
|
||||
if ((((const struct sockaddr_in6 *)&ip)->sin6_addr.s6_addr)[j] != (((const struct sockaddr_in6 *)&rfc4193)->sin6_addr.s6_addr)[j]) {
|
||||
prefixMatches = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (prefixMatches) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
inline bool _isCredentialTimestampValid(const NetworkConfig &nconf,const C &remoteCredential) const
|
||||
{
|
||||
const int64_t ts = remoteCredential.timestamp();
|
||||
if (((ts >= nconf.timestamp) ? (ts - nconf.timestamp) : (nconf.timestamp - ts)) <= nconf.credentialTimeMaxDelta) {
|
||||
const int64_t *threshold = _revocations.get(credentialKey(C::credentialType(),remoteCredential.id()));
|
||||
return ((!threshold)||(ts > *threshold));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
inline void _cleanCredImpl(const NetworkConfig &nconf,Hashtable<uint32_t,C> &remoteCreds)
|
||||
{
|
||||
uint32_t *k = (uint32_t *)0;
|
||||
C *v = (C *)0;
|
||||
typename Hashtable<uint32_t,C>::Iterator i(remoteCreds);
|
||||
while (i.next(k,v)) {
|
||||
if (!_isCredentialTimestampValid(nconf,*v)) {
|
||||
remoteCreds.erase(*k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Last time we pushed MULTICAST_LIKE(s)
|
||||
int64_t _lastUpdatedMulticast;
|
||||
|
||||
// Revocation threshold for COM or 0 if none
|
||||
int64_t _comRevocationThreshold;
|
||||
|
||||
// Time we last pushed credentials
|
||||
int64_t _lastPushedCredentials;
|
||||
|
||||
// Remote member's latest network COM
|
||||
CertificateOfMembership _com;
|
||||
|
||||
// Revocations by credentialKey()
|
||||
Hashtable< uint64_t,int64_t > _revocations;
|
||||
|
||||
// Remote credentials that we have received from this member (and that are valid)
|
||||
Hashtable< uint32_t,Tag > _remoteTags;
|
||||
Hashtable< uint32_t,Capability > _remoteCaps;
|
||||
Hashtable< uint32_t,CertificateOfOwnership > _remoteCoos;
|
||||
|
||||
public:
|
||||
class CapabilityIterator
|
||||
{
|
||||
public:
|
||||
CapabilityIterator(Membership &m,const NetworkConfig &nconf) :
|
||||
_hti(m._remoteCaps),
|
||||
_k((uint32_t *)0),
|
||||
_c((Capability *)0),
|
||||
_m(m),
|
||||
_nconf(nconf)
|
||||
{
|
||||
}
|
||||
|
||||
inline Capability *next()
|
||||
{
|
||||
while (_hti.next(_k,_c)) {
|
||||
if (_m._isCredentialTimestampValid(_nconf,*_c)) {
|
||||
return _c;
|
||||
}
|
||||
}
|
||||
return (Capability *)0;
|
||||
}
|
||||
|
||||
private:
|
||||
Hashtable< uint32_t,Capability >::Iterator _hti;
|
||||
uint32_t *_k;
|
||||
Capability *_c;
|
||||
Membership &_m;
|
||||
const NetworkConfig &_nconf;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* Copyright (c)2013-2023 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
|
||||
#include <prometheus/simpleapi.h>
|
||||
#include <prometheus/histogram.h>
|
||||
|
||||
namespace prometheus {
|
||||
namespace simpleapi {
|
||||
std::shared_ptr<Registry> registry_ptr = std::make_shared<Registry>();
|
||||
Registry& registry = *registry_ptr;
|
||||
SaveToFile saver;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ZeroTier {
|
||||
namespace Metrics {
|
||||
// Packet Type Counts
|
||||
prometheus::simpleapi::counter_family_t packets
|
||||
{ "zt_packet", "ZeroTier packet type counts"};
|
||||
|
||||
// Incoming packets
|
||||
prometheus::simpleapi::counter_metric_t pkt_nop_in
|
||||
{ packets.Add({{"packet_type", "nop"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_error_in
|
||||
{ packets.Add({{"packet_type", "error"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_ack_in
|
||||
{ packets.Add({{"packet_type", "ack"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_qos_in
|
||||
{ packets.Add({{"packet_type", "qos"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_hello_in
|
||||
{ packets.Add({{"packet_type", "hello"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_ok_in
|
||||
{ packets.Add({{"packet_type", "ok"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_whois_in
|
||||
{ packets.Add({{"packet_type", "whois"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_rendezvous_in
|
||||
{ packets.Add({{"packet_type", "rendezvous"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_frame_in
|
||||
{ packets.Add({{"packet_type", "frame"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_ext_frame_in
|
||||
{ packets.Add({{"packet_type", "ext_frame"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_echo_in
|
||||
{ packets.Add({{"packet_type", "echo"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_multicast_like_in
|
||||
{ packets.Add({{"packet_type", "multicast_like"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_network_credentials_in
|
||||
{ packets.Add({{"packet_type", "network_credentials"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_network_config_request_in
|
||||
{ packets.Add({{"packet_type", "network_config_request"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_network_config_in
|
||||
{ packets.Add({{"packet_type", "network_config"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_multicast_gather_in
|
||||
{ packets.Add({{"packet_type", "multicast_gather"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_multicast_frame_in
|
||||
{ packets.Add({{"packet_type", "multicast_frame"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_push_direct_paths_in
|
||||
{ packets.Add({{"packet_type", "push_direct_paths"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_user_message_in
|
||||
{ packets.Add({{"packet_type", "user_message"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_remote_trace_in
|
||||
{ packets.Add({{"packet_type", "remote_trace"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_path_negotiation_request_in
|
||||
{ packets.Add({{"packet_type", "path_negotiation_request"}, {"direction", "rx"}}) };
|
||||
|
||||
// Outgoing packets
|
||||
prometheus::simpleapi::counter_metric_t pkt_nop_out
|
||||
{ packets.Add({{"packet_type", "nop"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_error_out
|
||||
{ packets.Add({{"packet_type", "error"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_ack_out
|
||||
{ packets.Add({{"packet_type", "ack"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_qos_out
|
||||
{ packets.Add({{"packet_type", "qos"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_hello_out
|
||||
{ packets.Add({{"packet_type", "hello"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_ok_out
|
||||
{ packets.Add({{"packet_type", "ok"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_whois_out
|
||||
{ packets.Add({{"packet_type", "whois"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_rendezvous_out
|
||||
{ packets.Add({{"packet_type", "rendezvous"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_frame_out
|
||||
{ packets.Add({{"packet_type", "frame"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_ext_frame_out
|
||||
{ packets.Add({{"packet_type", "ext_frame"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_echo_out
|
||||
{ packets.Add({{"packet_type", "echo"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_multicast_like_out
|
||||
{ packets.Add({{"packet_type", "multicast_like"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_network_credentials_out
|
||||
{ packets.Add({{"packet_type", "network_credentials"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_network_config_request_out
|
||||
{ packets.Add({{"packet_type", "network_config_request"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_network_config_out
|
||||
{ packets.Add({{"packet_type", "network_config"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_multicast_gather_out
|
||||
{ packets.Add({{"packet_type", "multicast_gather"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_multicast_frame_out
|
||||
{ packets.Add({{"packet_type", "multicast_frame"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_push_direct_paths_out
|
||||
{ packets.Add({{"packet_type", "push_direct_paths"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_user_message_out
|
||||
{ packets.Add({{"packet_type", "user_message"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_remote_trace_out
|
||||
{ packets.Add({{"packet_type", "remote_trace"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_path_negotiation_request_out
|
||||
{ packets.Add({{"packet_type", "path_negotiation_request"}, {"direction", "tx"}}) };
|
||||
|
||||
|
||||
// Packet Error Counts
|
||||
prometheus::simpleapi::counter_family_t packet_errors
|
||||
{ "zt_packet_error", "ZeroTier packet errors"};
|
||||
|
||||
// Incoming Error Counts
|
||||
prometheus::simpleapi::counter_metric_t pkt_error_obj_not_found_in
|
||||
{ packet_errors.Add({{"error_type", "obj_not_found"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_error_unsupported_op_in
|
||||
{ packet_errors.Add({{"error_type", "unsupported_operation"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_error_identity_collision_in
|
||||
{ packet_errors.Add({{"error_type", "identity_collision"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_error_need_membership_cert_in
|
||||
{ packet_errors.Add({{"error_type", "need_membership_certificate"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_error_network_access_denied_in
|
||||
{ packet_errors.Add({{"error_type", "network_access_denied"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_error_unwanted_multicast_in
|
||||
{ packet_errors.Add({{"error_type", "unwanted_multicast"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_error_authentication_required_in
|
||||
{ packet_errors.Add({{"error_type", "authentication_required"}, {"direction", "rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_error_internal_server_error_in
|
||||
{ packet_errors.Add({{"error_type", "internal_server_error"}, {"direction", "rx"}}) };
|
||||
|
||||
// Outgoing Error Counts
|
||||
prometheus::simpleapi::counter_metric_t pkt_error_obj_not_found_out
|
||||
{ packet_errors.Add({{"error_type", "obj_not_found"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_error_unsupported_op_out
|
||||
{ packet_errors.Add({{"error_type", "unsupported_operation"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_error_identity_collision_out
|
||||
{ packet_errors.Add({{"error_type", "identity_collision"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_error_need_membership_cert_out
|
||||
{ packet_errors.Add({{"error_type", "need_membership_certificate"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_error_network_access_denied_out
|
||||
{ packet_errors.Add({{"error_type", "network_access_denied"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_error_unwanted_multicast_out
|
||||
{ packet_errors.Add({{"error_type", "unwanted_multicast"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_error_authentication_required_out
|
||||
{ packet_errors.Add({{"error_type", "authentication_required"}, {"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t pkt_error_internal_server_error_out
|
||||
{ packet_errors.Add({{"error_type", "internal_server_error"}, {"direction", "tx"}}) };
|
||||
|
||||
// Data Sent/Received Metrics
|
||||
prometheus::simpleapi::counter_family_t data
|
||||
{ "zt_data", "number of bytes ZeroTier has transmitted or received" };
|
||||
prometheus::simpleapi::counter_metric_t udp_recv
|
||||
{ data.Add({{"protocol","udp"},{"direction","rx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t udp_send
|
||||
{ data.Add({{"protocol","udp"},{"direction","tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t tcp_send
|
||||
{ data.Add({{"protocol","tcp"},{"direction", "tx"}}) };
|
||||
prometheus::simpleapi::counter_metric_t tcp_recv
|
||||
{ data.Add({{"protocol","tcp"},{"direction", "rx"}}) };
|
||||
|
||||
// Network Metrics
|
||||
prometheus::simpleapi::gauge_metric_t network_num_joined
|
||||
{ "zt_num_networks", "number of networks this instance is joined to" };
|
||||
prometheus::simpleapi::gauge_family_t network_num_multicast_groups
|
||||
{ "zt_network_multicast_groups_subscribed", "number of multicast groups networks are subscribed to" };
|
||||
prometheus::simpleapi::counter_family_t network_packets
|
||||
{ "zt_network_packets", "number of incoming/outgoing packets per network" };
|
||||
|
||||
#ifndef ZT_NO_PEER_METRICS
|
||||
// PeerMetrics
|
||||
prometheus::CustomFamily<prometheus::Histogram<uint64_t>> &peer_latency =
|
||||
prometheus::Builder<prometheus::Histogram<uint64_t>>()
|
||||
.Name("zt_peer_latency")
|
||||
.Help("peer latency (ms)")
|
||||
.Register(prometheus::simpleapi::registry);
|
||||
|
||||
prometheus::simpleapi::gauge_family_t peer_path_count
|
||||
{ "zt_peer_path_count", "number of paths to peer" };
|
||||
prometheus::simpleapi::counter_family_t peer_packets
|
||||
{ "zt_peer_packets", "number of packets to/from a peer" };
|
||||
prometheus::simpleapi::counter_family_t peer_packet_errors
|
||||
{ "zt_peer_packet_errors" , "number of incoming packet errors from a peer" };
|
||||
#endif
|
||||
|
||||
// General Controller Metrics
|
||||
prometheus::simpleapi::gauge_metric_t network_count
|
||||
{"controller_network_count", "number of networks the controller is serving"};
|
||||
prometheus::simpleapi::gauge_metric_t member_count
|
||||
{"controller_member_count", "number of network members the controller is serving"};
|
||||
prometheus::simpleapi::counter_metric_t network_changes
|
||||
{"controller_network_change_count", "number of times a network configuration is changed"};
|
||||
prometheus::simpleapi::counter_metric_t member_changes
|
||||
{"controller_member_change_count", "number of times a network member configuration is changed"};
|
||||
prometheus::simpleapi::counter_metric_t member_auths
|
||||
{"controller_member_auth_count", "number of network member auths"};
|
||||
prometheus::simpleapi::counter_metric_t member_deauths
|
||||
{"controller_member_deauth_count", "number of network member deauths"};
|
||||
|
||||
prometheus::simpleapi::gauge_metric_t network_config_request_queue_size
|
||||
{ "controller_network_config_request_queue", "number of entries in the request queue for network configurations" };
|
||||
|
||||
prometheus::simpleapi::counter_metric_t sso_expiration_checks
|
||||
{ "controller_sso_expiration_checks", "number of sso expiration checks done" };
|
||||
|
||||
prometheus::simpleapi::counter_metric_t sso_member_deauth
|
||||
{ "controller_sso_timeouts", "number of sso timeouts" };
|
||||
|
||||
prometheus::simpleapi::counter_metric_t network_config_request
|
||||
{ "controller_network_config_request", "count of config requests handled" };
|
||||
prometheus::simpleapi::gauge_metric_t network_config_request_threads
|
||||
{ "controller_network_config_request_threads", "number of active network config handling threads" };
|
||||
prometheus::simpleapi::counter_metric_t db_get_network
|
||||
{ "controller_db_get_network", "counter" };
|
||||
prometheus::simpleapi::counter_metric_t db_get_network_and_member
|
||||
{ "controller_db_get_network_and_member", "counter" };
|
||||
prometheus::simpleapi::counter_metric_t db_get_network_and_member_and_summary
|
||||
{ "controller_db_get_networK_and_member_summary", "counter" };
|
||||
prometheus::simpleapi::counter_metric_t db_get_member_list
|
||||
{ "controller_db_get_member_list", "counter" };
|
||||
prometheus::simpleapi::counter_metric_t db_get_network_list
|
||||
{ "controller_db_get_network_list", "counter" };
|
||||
prometheus::simpleapi::counter_metric_t db_member_change
|
||||
{ "controller_db_member_change", "counter" };
|
||||
prometheus::simpleapi::counter_metric_t db_network_change
|
||||
{ "controller_db_network_change", "counter" };
|
||||
|
||||
#ifdef ZT_CONTROLLER_USE_LIBPQ
|
||||
// Central Controller Metrics
|
||||
prometheus::simpleapi::counter_metric_t pgsql_mem_notification
|
||||
{ "controller_pgsql_member_notifications_received", "number of member change notifications received via pgsql NOTIFY" };
|
||||
prometheus::simpleapi::counter_metric_t pgsql_net_notification
|
||||
{ "controller_pgsql_network_notifications_received", "number of network change notifications received via pgsql NOTIFY" };
|
||||
prometheus::simpleapi::counter_metric_t pgsql_node_checkin
|
||||
{ "controller_pgsql_node_checkin_count", "number of node check-ins (pgsql)" };
|
||||
prometheus::simpleapi::counter_metric_t pgsql_commit_ticks
|
||||
{ "controller_pgsql_commit_ticks", "number of commit ticks run (pgsql)" };
|
||||
prometheus::simpleapi::counter_metric_t db_get_sso_info
|
||||
{ "controller_db_get_sso_info", "counter" };
|
||||
|
||||
prometheus::simpleapi::counter_metric_t redis_mem_notification
|
||||
{ "controller_redis_member_notifications_received", "number of member change notifications received via redis" };
|
||||
prometheus::simpleapi::counter_metric_t redis_net_notification
|
||||
{ "controller_redis_network_notifications_received", "number of network change notifications received via redis" };
|
||||
prometheus::simpleapi::counter_metric_t redis_node_checkin
|
||||
{ "controller_redis_node_checkin_count", "number of node check-ins (redis)" };
|
||||
|
||||
// Central DB Pool Metrics
|
||||
prometheus::simpleapi::counter_metric_t conn_counter
|
||||
{ "controller_pgsql_connections_created", "number of pgsql connections created"};
|
||||
prometheus::simpleapi::counter_metric_t max_pool_size
|
||||
{ "controller_pgsql_max_conn_pool_size", "max connection pool size for postgres"};
|
||||
prometheus::simpleapi::counter_metric_t min_pool_size
|
||||
{ "controller_pgsql_min_conn_pool_size", "minimum connection pool size for postgres" };
|
||||
prometheus::simpleapi::gauge_metric_t pool_avail
|
||||
{ "controller_pgsql_available_conns", "number of available postgres connections" };
|
||||
prometheus::simpleapi::gauge_metric_t pool_in_use
|
||||
{ "controller_pgsql_in_use_conns", "number of postgres database connections in use" };
|
||||
prometheus::simpleapi::counter_metric_t pool_errors
|
||||
{ "controller_pgsql_connection_errors", "number of connection errors the connection pool has seen" };
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (c)2013-2023 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
#ifndef METRICS_H_
|
||||
#define METRICS_H_
|
||||
|
||||
#include <prometheus/simpleapi.h>
|
||||
#include <prometheus/histogram.h>
|
||||
|
||||
namespace prometheus {
|
||||
namespace simpleapi {
|
||||
extern std::shared_ptr<Registry> registry_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ZeroTier {
|
||||
namespace Metrics {
|
||||
// Packet Type Counts
|
||||
extern prometheus::simpleapi::counter_family_t packets;
|
||||
|
||||
// incoming packets
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_nop_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_error_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_ack_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_qos_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_hello_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_ok_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_whois_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_rendezvous_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_frame_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_ext_frame_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_echo_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_multicast_like_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_network_credentials_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_network_config_request_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_network_config_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_multicast_gather_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_multicast_frame_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_push_direct_paths_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_user_message_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_remote_trace_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_path_negotiation_request_in;
|
||||
|
||||
// outgoing packets
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_nop_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_error_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_ack_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_qos_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_hello_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_ok_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_whois_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_rendezvous_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_frame_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_ext_frame_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_echo_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_multicast_like_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_network_credentials_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_network_config_request_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_network_config_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_multicast_gather_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_multicast_frame_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_push_direct_paths_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_user_message_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_remote_trace_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_path_negotiation_request_out;
|
||||
|
||||
// Packet Error Counts
|
||||
extern prometheus::simpleapi::counter_family_t packet_errors;
|
||||
|
||||
// incoming errors
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_error_obj_not_found_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_error_unsupported_op_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_error_identity_collision_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_error_need_membership_cert_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_error_network_access_denied_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_error_unwanted_multicast_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_error_authentication_required_in;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_error_internal_server_error_in;
|
||||
|
||||
// outgoing errors
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_error_obj_not_found_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_error_unsupported_op_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_error_identity_collision_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_error_need_membership_cert_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_error_network_access_denied_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_error_unwanted_multicast_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_error_authentication_required_out;
|
||||
extern prometheus::simpleapi::counter_metric_t pkt_error_internal_server_error_out;
|
||||
|
||||
// Data Sent/Received Metrics
|
||||
extern prometheus::simpleapi::counter_family_t data;
|
||||
extern prometheus::simpleapi::counter_metric_t udp_send;
|
||||
extern prometheus::simpleapi::counter_metric_t udp_recv;
|
||||
extern prometheus::simpleapi::counter_metric_t tcp_send;
|
||||
extern prometheus::simpleapi::counter_metric_t tcp_recv;
|
||||
|
||||
// Network Metrics
|
||||
extern prometheus::simpleapi::gauge_metric_t network_num_joined;
|
||||
extern prometheus::simpleapi::gauge_family_t network_num_multicast_groups;
|
||||
extern prometheus::simpleapi::counter_family_t network_packets;
|
||||
|
||||
#ifndef ZT_NO_PEER_METRICS
|
||||
// Peer Metrics
|
||||
extern prometheus::CustomFamily<prometheus::Histogram<uint64_t>> &peer_latency;
|
||||
extern prometheus::simpleapi::gauge_family_t peer_path_count;
|
||||
extern prometheus::simpleapi::counter_family_t peer_packets;
|
||||
extern prometheus::simpleapi::counter_family_t peer_packet_errors;
|
||||
#endif
|
||||
|
||||
// General Controller Metrics
|
||||
extern prometheus::simpleapi::gauge_metric_t network_count;
|
||||
extern prometheus::simpleapi::gauge_metric_t member_count;
|
||||
extern prometheus::simpleapi::counter_metric_t network_changes;
|
||||
extern prometheus::simpleapi::counter_metric_t member_changes;
|
||||
extern prometheus::simpleapi::counter_metric_t member_auths;
|
||||
extern prometheus::simpleapi::counter_metric_t member_deauths;
|
||||
|
||||
extern prometheus::simpleapi::gauge_metric_t network_config_request_queue_size;
|
||||
extern prometheus::simpleapi::counter_metric_t sso_expiration_checks;
|
||||
extern prometheus::simpleapi::counter_metric_t sso_member_deauth;
|
||||
extern prometheus::simpleapi::counter_metric_t network_config_request;
|
||||
extern prometheus::simpleapi::gauge_metric_t network_config_request_threads;
|
||||
|
||||
extern prometheus::simpleapi::counter_metric_t db_get_network;
|
||||
extern prometheus::simpleapi::counter_metric_t db_get_network_and_member;
|
||||
extern prometheus::simpleapi::counter_metric_t db_get_network_and_member_and_summary;
|
||||
extern prometheus::simpleapi::counter_metric_t db_get_member_list;
|
||||
extern prometheus::simpleapi::counter_metric_t db_get_network_list;
|
||||
extern prometheus::simpleapi::counter_metric_t db_member_change;
|
||||
extern prometheus::simpleapi::counter_metric_t db_network_change;
|
||||
|
||||
|
||||
#ifdef ZT_CONTROLLER_USE_LIBPQ
|
||||
// Central Controller Metrics
|
||||
extern prometheus::simpleapi::counter_metric_t pgsql_mem_notification;
|
||||
extern prometheus::simpleapi::counter_metric_t pgsql_net_notification;
|
||||
extern prometheus::simpleapi::counter_metric_t pgsql_node_checkin;
|
||||
extern prometheus::simpleapi::counter_metric_t pgsql_commit_ticks;
|
||||
extern prometheus::simpleapi::counter_metric_t db_get_sso_info;
|
||||
|
||||
extern prometheus::simpleapi::counter_metric_t redis_mem_notification;
|
||||
extern prometheus::simpleapi::counter_metric_t redis_net_notification;
|
||||
extern prometheus::simpleapi::counter_metric_t redis_node_checkin;
|
||||
|
||||
|
||||
|
||||
// Central DB Pool Metrics
|
||||
extern prometheus::simpleapi::counter_metric_t conn_counter;
|
||||
extern prometheus::simpleapi::counter_metric_t max_pool_size;
|
||||
extern prometheus::simpleapi::counter_metric_t min_pool_size;
|
||||
extern prometheus::simpleapi::gauge_metric_t pool_avail;
|
||||
extern prometheus::simpleapi::gauge_metric_t pool_in_use;
|
||||
extern prometheus::simpleapi::counter_metric_t pool_errors;
|
||||
#endif
|
||||
} // namespace Metrics
|
||||
}// namespace ZeroTier
|
||||
|
||||
#endif // METRICS_H_
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_MULTICASTGROUP_HPP
|
||||
#define ZT_MULTICASTGROUP_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "MAC.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* A multicast group composed of a multicast MAC and a 32-bit ADI field
|
||||
*
|
||||
* ADI stands for additional distinguishing information. ADI is primarily for
|
||||
* adding additional information to broadcast (ff:ff:ff:ff:ff:ff) memberships,
|
||||
* since straight-up broadcast won't scale. Right now it's zero except for
|
||||
* IPv4 ARP, where it holds the IPv4 address itself to make ARP into a
|
||||
* selective multicast query that can scale.
|
||||
*
|
||||
* In the future we might add some kind of plugin architecture that can add
|
||||
* ADI for things like mDNS (multicast DNS) to improve the selectivity of
|
||||
* those protocols.
|
||||
*
|
||||
* MulticastGroup behaves as an immutable value object.
|
||||
*/
|
||||
class MulticastGroup
|
||||
{
|
||||
public:
|
||||
MulticastGroup() :
|
||||
_mac(),
|
||||
_adi(0)
|
||||
{
|
||||
}
|
||||
|
||||
MulticastGroup(const MAC &m,uint32_t a) :
|
||||
_mac(m),
|
||||
_adi(a)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive the multicast group used for address resolution (ARP/NDP) for an IP
|
||||
*
|
||||
* @param ip IP address (port field is ignored)
|
||||
* @return Multicast group for ARP/NDP
|
||||
*/
|
||||
static inline MulticastGroup deriveMulticastGroupForAddressResolution(const InetAddress &ip)
|
||||
{
|
||||
if (ip.isV4()) {
|
||||
// IPv4 wants broadcast MACs, so we shove the V4 address itself into
|
||||
// the Multicast Group ADI field. Making V4 ARP work is basically why
|
||||
// ADI was added, as well as handling other things that want mindless
|
||||
// Ethernet broadcast to all.
|
||||
return MulticastGroup(MAC(0xffffffffffffULL),Utils::ntoh(*((const uint32_t *)ip.rawIpData())));
|
||||
} else if (ip.isV6()) {
|
||||
// IPv6 is better designed in this respect. We can compute the IPv6
|
||||
// multicast address directly from the IP address, and it gives us
|
||||
// 24 bits of uniqueness. Collisions aren't likely to be common enough
|
||||
// to care about.
|
||||
const unsigned char *a = (const unsigned char *)ip.rawIpData();
|
||||
return MulticastGroup(MAC(0x33,0x33,0xff,a[13],a[14],a[15]),0);
|
||||
}
|
||||
return MulticastGroup();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Multicast address
|
||||
*/
|
||||
inline const MAC &mac() const { return _mac; }
|
||||
|
||||
/**
|
||||
* @return Additional distinguishing information
|
||||
*/
|
||||
inline uint32_t adi() const { return _adi; }
|
||||
|
||||
inline unsigned long hashCode() const { return (_mac.hashCode() ^ (unsigned long)_adi); }
|
||||
|
||||
inline bool operator==(const MulticastGroup &g) const { return ((_mac == g._mac)&&(_adi == g._adi)); }
|
||||
inline bool operator!=(const MulticastGroup &g) const { return ((_mac != g._mac)||(_adi != g._adi)); }
|
||||
inline bool operator<(const MulticastGroup &g) const
|
||||
{
|
||||
if (_mac < g._mac) {
|
||||
return true;
|
||||
} else if (_mac == g._mac) {
|
||||
return (_adi < g._adi);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
inline bool operator>(const MulticastGroup &g) const { return (g < *this); }
|
||||
inline bool operator<=(const MulticastGroup &g) const { return !(g < *this); }
|
||||
inline bool operator>=(const MulticastGroup &g) const { return !(*this < g); }
|
||||
|
||||
private:
|
||||
MAC _mac;
|
||||
uint32_t _adi;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,463 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Multicaster.hpp"
|
||||
#include "Topology.hpp"
|
||||
#include "Switch.hpp"
|
||||
#include "Packet.hpp"
|
||||
#include "Peer.hpp"
|
||||
#include "C25519.hpp"
|
||||
#include "CertificateOfMembership.hpp"
|
||||
#include "Node.hpp"
|
||||
#include "Network.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
Multicaster::Multicaster(const RuntimeEnvironment *renv) :
|
||||
RR(renv),
|
||||
_groups(32)
|
||||
{
|
||||
}
|
||||
|
||||
Multicaster::~Multicaster()
|
||||
{
|
||||
}
|
||||
|
||||
void Multicaster::addMultiple(void *tPtr,int64_t now,uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,unsigned int totalKnown)
|
||||
{
|
||||
const unsigned char *p = (const unsigned char *)addresses;
|
||||
const unsigned char *e = p + (5 * count);
|
||||
Mutex::Lock _l(_groups_m);
|
||||
MulticastGroupStatus &gs = _groups[Multicaster::Key(nwid,mg)];
|
||||
while (p != e) {
|
||||
_add(tPtr,now,nwid,mg,gs,Address(p,5));
|
||||
p += 5;
|
||||
}
|
||||
}
|
||||
|
||||
void Multicaster::remove(uint64_t nwid,const MulticastGroup &mg,const Address &member)
|
||||
{
|
||||
Mutex::Lock _l(_groups_m);
|
||||
MulticastGroupStatus *s = _groups.get(Multicaster::Key(nwid,mg));
|
||||
if (s) {
|
||||
for(std::vector<MulticastGroupMember>::iterator m(s->members.begin());m!=s->members.end();++m) {
|
||||
if (m->address == member) {
|
||||
s->members.erase(m);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int Multicaster::gather(const Address &queryingPeer,uint64_t nwid,const MulticastGroup &mg,Buffer<ZT_PROTO_MAX_PACKET_LENGTH> &appendTo,unsigned int limit) const
|
||||
{
|
||||
unsigned char *p;
|
||||
unsigned int added = 0,i,k,rptr,totalKnown = 0;
|
||||
uint64_t a,picked[(ZT_PROTO_MAX_PACKET_LENGTH / 5) + 2];
|
||||
|
||||
if (!limit) {
|
||||
return 0;
|
||||
} else if (limit > 0xffff) {
|
||||
limit = 0xffff;
|
||||
}
|
||||
|
||||
const unsigned int totalAt = appendTo.size();
|
||||
appendTo.addSize(4); // sizeof(uint32_t)
|
||||
const unsigned int addedAt = appendTo.size();
|
||||
appendTo.addSize(2); // sizeof(uint16_t)
|
||||
|
||||
{ // Return myself if I am a member of this group
|
||||
SharedPtr<Network> network(RR->node->network(nwid));
|
||||
if ((network)&&(network->subscribedToMulticastGroup(mg,true))) {
|
||||
RR->identity.address().appendTo(appendTo);
|
||||
++totalKnown;
|
||||
++added;
|
||||
}
|
||||
}
|
||||
|
||||
Mutex::Lock _l(_groups_m);
|
||||
|
||||
const MulticastGroupStatus *s = _groups.get(Multicaster::Key(nwid,mg));
|
||||
if ((s)&&(!s->members.empty())) {
|
||||
totalKnown += (unsigned int)s->members.size();
|
||||
|
||||
// Members are returned in random order so that repeated gather queries
|
||||
// will return different subsets of a large multicast group.
|
||||
k = 0;
|
||||
while ((added < limit)&&(k < s->members.size())&&((appendTo.size() + ZT_ADDRESS_LENGTH) <= ZT_PROTO_MAX_PACKET_LENGTH)) {
|
||||
rptr = (unsigned int)RR->node->prng();
|
||||
|
||||
restart_member_scan:
|
||||
a = s->members[rptr % (unsigned int)s->members.size()].address.toInt();
|
||||
for(i=0;i<k;++i) {
|
||||
if (picked[i] == a) {
|
||||
++rptr;
|
||||
goto restart_member_scan;
|
||||
}
|
||||
}
|
||||
picked[k++] = a;
|
||||
|
||||
if (queryingPeer.toInt() != a) { // do not return the peer that is making the request as a result
|
||||
p = (unsigned char *)appendTo.appendField(ZT_ADDRESS_LENGTH);
|
||||
*(p++) = (unsigned char)((a >> 32) & 0xff);
|
||||
*(p++) = (unsigned char)((a >> 24) & 0xff);
|
||||
*(p++) = (unsigned char)((a >> 16) & 0xff);
|
||||
*(p++) = (unsigned char)((a >> 8) & 0xff);
|
||||
*p = (unsigned char)(a & 0xff);
|
||||
++added;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
appendTo.setAt(totalAt,(uint32_t)totalKnown);
|
||||
appendTo.setAt(addedAt,(uint16_t)added);
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
std::vector<Address> Multicaster::getMembers(uint64_t nwid,const MulticastGroup &mg,unsigned int limit) const
|
||||
{
|
||||
std::vector<Address> ls;
|
||||
Mutex::Lock _l(_groups_m);
|
||||
const MulticastGroupStatus *s = _groups.get(Multicaster::Key(nwid,mg));
|
||||
if (!s) {
|
||||
return ls;
|
||||
}
|
||||
for(std::vector<MulticastGroupMember>::const_reverse_iterator m(s->members.rbegin());m!=s->members.rend();++m) {
|
||||
ls.push_back(m->address);
|
||||
if (ls.size() >= limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ls;
|
||||
}
|
||||
|
||||
void Multicaster::send(
|
||||
void *tPtr,
|
||||
int64_t now,
|
||||
const SharedPtr<Network> &network,
|
||||
const Address &origin,
|
||||
const MulticastGroup &mg,
|
||||
const MAC &src,
|
||||
unsigned int etherType,
|
||||
const void *data,
|
||||
unsigned int len)
|
||||
{
|
||||
unsigned long idxbuf[4096];
|
||||
unsigned long *indexes = idxbuf;
|
||||
|
||||
// If we're in hub-and-spoke designated multicast replication mode, see if we
|
||||
// have a multicast replicator active. If so, pick the best and send it
|
||||
// there. If we are a multicast replicator or if none are alive, fall back
|
||||
// to sender replication. Note that bridges do not do this since this would
|
||||
// break bridge route learning. This is sort of an edge case limitation of
|
||||
// the current protocol and could be fixed, but fixing it would add more
|
||||
// complexity than the fix is probably worth. Bridges are generally high
|
||||
// bandwidth nodes.
|
||||
if (!network->config().isActiveBridge(RR->identity.address())) {
|
||||
Address multicastReplicators[ZT_MAX_NETWORK_SPECIALISTS];
|
||||
const unsigned int multicastReplicatorCount = network->config().multicastReplicators(multicastReplicators);
|
||||
if (multicastReplicatorCount) {
|
||||
if (std::find(multicastReplicators,multicastReplicators + multicastReplicatorCount,RR->identity.address()) == (multicastReplicators + multicastReplicatorCount)) {
|
||||
SharedPtr<Peer> bestMulticastReplicator;
|
||||
SharedPtr<Path> bestMulticastReplicatorPath;
|
||||
unsigned int bestMulticastReplicatorLatency = 0xffff;
|
||||
for(unsigned int i=0;i<multicastReplicatorCount;++i) {
|
||||
const SharedPtr<Peer> p(RR->topology->getPeerNoCache(multicastReplicators[i]));
|
||||
if ((p)&&(p->isAlive(now))) {
|
||||
const SharedPtr<Path> pp(p->getAppropriatePath(now,false));
|
||||
if ((pp)&&(pp->latency() < bestMulticastReplicatorLatency)) {
|
||||
bestMulticastReplicatorLatency = pp->latency();
|
||||
bestMulticastReplicatorPath = pp;
|
||||
bestMulticastReplicator = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bestMulticastReplicator) {
|
||||
Packet outp(bestMulticastReplicator->address(),RR->identity.address(),Packet::VERB_MULTICAST_FRAME);
|
||||
outp.append((uint64_t)network->id());
|
||||
outp.append((uint8_t)0x0c); // includes source MAC | please replicate
|
||||
((src) ? src : MAC(RR->identity.address(),network->id())).appendTo(outp);
|
||||
mg.mac().appendTo(outp);
|
||||
outp.append((uint32_t)mg.adi());
|
||||
outp.append((uint16_t)etherType);
|
||||
outp.append(data,len);
|
||||
if (!network->config().disableCompression()) {
|
||||
outp.compress();
|
||||
}
|
||||
outp.armor(bestMulticastReplicator->key(),true,bestMulticastReplicator->aesKeysIfSupported());
|
||||
Metrics::pkt_multicast_frame_out++;
|
||||
bestMulticastReplicatorPath->send(RR,tPtr,outp.data(),outp.size(),now);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Mutex::Lock _l(_groups_m);
|
||||
MulticastGroupStatus &gs = _groups[Multicaster::Key(network->id(),mg)];
|
||||
|
||||
if (!gs.members.empty()) {
|
||||
// Allocate a memory buffer if group is monstrous
|
||||
if (gs.members.size() > (sizeof(idxbuf) / sizeof(unsigned long))) {
|
||||
indexes = new unsigned long[gs.members.size()];
|
||||
}
|
||||
|
||||
// Generate a random permutation of member indexes
|
||||
for(unsigned long i=0;i<gs.members.size();++i) {
|
||||
indexes[i] = i;
|
||||
}
|
||||
for(unsigned long i=(unsigned long)gs.members.size()-1;i>0;--i) {
|
||||
unsigned long j = (unsigned long)RR->node->prng() % (i + 1);
|
||||
unsigned long tmp = indexes[j];
|
||||
indexes[j] = indexes[i];
|
||||
indexes[i] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
Address activeBridges[ZT_MAX_NETWORK_SPECIALISTS];
|
||||
const unsigned int activeBridgeCount = network->config().activeBridges(activeBridges);
|
||||
const unsigned int limit = network->config().multicastLimit;
|
||||
|
||||
if (gs.members.size() >= limit) {
|
||||
// Skip queue if we already have enough members to complete the send operation
|
||||
OutboundMulticast out;
|
||||
|
||||
out.init(
|
||||
RR,
|
||||
now,
|
||||
network->id(),
|
||||
network->config().disableCompression(),
|
||||
limit,
|
||||
1, // we'll still gather a little from peers to keep multicast list fresh
|
||||
src,
|
||||
mg,
|
||||
etherType,
|
||||
data,
|
||||
len);
|
||||
|
||||
unsigned int count = 0;
|
||||
|
||||
for(unsigned int i=0;i<activeBridgeCount;++i) {
|
||||
if ((activeBridges[i] != RR->identity.address())&&(activeBridges[i] != origin)) {
|
||||
out.sendOnly(RR,tPtr,activeBridges[i]); // optimization: don't use dedup log if it's a one-pass send
|
||||
if (++count >= limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long idx = 0;
|
||||
while ((count < limit)&&(idx < gs.members.size())) {
|
||||
const Address ma(gs.members[indexes[idx++]].address);
|
||||
if ((std::find(activeBridges,activeBridges + activeBridgeCount,ma) == (activeBridges + activeBridgeCount))&&(ma != origin)) {
|
||||
out.sendOnly(RR,tPtr,ma); // optimization: don't use dedup log if it's a one-pass send
|
||||
++count;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (gs.txQueue.size() >= ZT_TX_QUEUE_SIZE) {
|
||||
gs.txQueue.pop_front();
|
||||
}
|
||||
|
||||
const unsigned int gatherLimit = (limit - (unsigned int)gs.members.size()) + 1;
|
||||
|
||||
int timerScale = RR->node->lowBandwidthModeEnabled() ? 3 : 1;
|
||||
if ((gs.members.empty())||((now - gs.lastExplicitGather) >= (ZT_MULTICAST_EXPLICIT_GATHER_DELAY * timerScale))) {
|
||||
gs.lastExplicitGather = now;
|
||||
|
||||
Address explicitGatherPeers[16];
|
||||
unsigned int numExplicitGatherPeers = 0;
|
||||
|
||||
SharedPtr<Peer> bestRoot(RR->topology->getUpstreamPeer());
|
||||
if (bestRoot) {
|
||||
explicitGatherPeers[numExplicitGatherPeers++] = bestRoot->address();
|
||||
}
|
||||
|
||||
explicitGatherPeers[numExplicitGatherPeers++] = network->controller();
|
||||
|
||||
Address ac[ZT_MAX_NETWORK_SPECIALISTS];
|
||||
const unsigned int accnt = network->config().alwaysContactAddresses(ac);
|
||||
unsigned int shuffled[ZT_MAX_NETWORK_SPECIALISTS];
|
||||
for(unsigned int i=0;i<accnt;++i) {
|
||||
shuffled[i] = i;
|
||||
}
|
||||
for(unsigned int i=0,k=accnt>>1;i<k;++i) {
|
||||
const uint64_t x = RR->node->prng();
|
||||
const unsigned int x1 = shuffled[(unsigned int)x % accnt];
|
||||
const unsigned int x2 = shuffled[(unsigned int)(x >> 32) % accnt];
|
||||
const unsigned int tmp = shuffled[x1];
|
||||
shuffled[x1] = shuffled[x2];
|
||||
shuffled[x2] = tmp;
|
||||
}
|
||||
for(unsigned int i=0;i<accnt;++i) {
|
||||
explicitGatherPeers[numExplicitGatherPeers++] = ac[shuffled[i]];
|
||||
if (numExplicitGatherPeers == 16) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Address> anchors(network->config().anchors());
|
||||
for(std::vector<Address>::const_iterator a(anchors.begin());a!=anchors.end();++a) {
|
||||
if (*a != RR->identity.address()) {
|
||||
explicitGatherPeers[numExplicitGatherPeers++] = *a;
|
||||
if (numExplicitGatherPeers == 16) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(unsigned int k=0;k<numExplicitGatherPeers;++k) {
|
||||
const CertificateOfMembership *com = (network) ? ((network->config().com) ? &(network->config().com) : (const CertificateOfMembership *)0) : (const CertificateOfMembership *)0;
|
||||
Packet outp(explicitGatherPeers[k],RR->identity.address(),Packet::VERB_MULTICAST_GATHER);
|
||||
outp.append(network->id());
|
||||
outp.append((uint8_t)((com) ? 0x01 : 0x00));
|
||||
mg.mac().appendTo(outp);
|
||||
outp.append((uint32_t)mg.adi());
|
||||
outp.append((uint32_t)gatherLimit);
|
||||
if (com) {
|
||||
com->serialize(outp);
|
||||
}
|
||||
RR->node->expectReplyTo(outp.packetId());
|
||||
RR->sw->send(tPtr,outp,true);
|
||||
Metrics::pkt_multicast_gather_out++;
|
||||
}
|
||||
}
|
||||
|
||||
gs.txQueue.push_back(OutboundMulticast());
|
||||
OutboundMulticast &out = gs.txQueue.back();
|
||||
|
||||
out.init(
|
||||
RR,
|
||||
now,
|
||||
network->id(),
|
||||
network->config().disableCompression(),
|
||||
limit,
|
||||
gatherLimit,
|
||||
src,
|
||||
mg,
|
||||
etherType,
|
||||
data,
|
||||
len);
|
||||
|
||||
if (origin) {
|
||||
out.logAsSent(origin);
|
||||
}
|
||||
|
||||
unsigned int count = 0;
|
||||
|
||||
for(unsigned int i=0;i<activeBridgeCount;++i) {
|
||||
if (activeBridges[i] != RR->identity.address()) {
|
||||
out.sendAndLog(RR,tPtr,activeBridges[i]);
|
||||
if (++count >= limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long idx = 0;
|
||||
while ((count < limit)&&(idx < gs.members.size())) {
|
||||
Address ma(gs.members[indexes[idx++]].address);
|
||||
if (std::find(activeBridges,activeBridges + activeBridgeCount,ma) == (activeBridges + activeBridgeCount)) {
|
||||
out.sendAndLog(RR,tPtr,ma);
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch ( ... ) {} // this is a sanity check to catch any failures and make sure indexes[] still gets deleted
|
||||
|
||||
// Free allocated memory buffer if any
|
||||
if (indexes != idxbuf) {
|
||||
delete [] indexes;
|
||||
}
|
||||
}
|
||||
|
||||
void Multicaster::clean(int64_t now)
|
||||
{
|
||||
Mutex::Lock _l(_groups_m);
|
||||
Multicaster::Key *k = (Multicaster::Key *)0;
|
||||
MulticastGroupStatus *s = (MulticastGroupStatus *)0;
|
||||
Hashtable<Multicaster::Key,MulticastGroupStatus>::Iterator mm(_groups);
|
||||
while (mm.next(k,s)) {
|
||||
for(std::list<OutboundMulticast>::iterator tx(s->txQueue.begin());tx!=s->txQueue.end();) {
|
||||
if ((tx->expired(now))||(tx->atLimit())) {
|
||||
s->txQueue.erase(tx++);
|
||||
} else {
|
||||
++tx;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long count = 0;
|
||||
{
|
||||
std::vector<MulticastGroupMember>::iterator reader(s->members.begin());
|
||||
std::vector<MulticastGroupMember>::iterator writer(reader);
|
||||
while (reader != s->members.end()) {
|
||||
if ((now - reader->timestamp) < ZT_MULTICAST_LIKE_EXPIRE) {
|
||||
*writer = *reader;
|
||||
++writer;
|
||||
++count;
|
||||
}
|
||||
++reader;
|
||||
}
|
||||
}
|
||||
|
||||
if (count) {
|
||||
s->members.resize(count);
|
||||
} else if (s->txQueue.empty()) {
|
||||
_groups.erase(*k);
|
||||
} else {
|
||||
s->members.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Multicaster::_add(void *tPtr,int64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member)
|
||||
{
|
||||
// assumes _groups_m is locked
|
||||
|
||||
// Do not add self -- even if someone else returns it
|
||||
if (member == RR->identity.address()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<MulticastGroupMember>::iterator m(std::lower_bound(gs.members.begin(),gs.members.end(),member));
|
||||
if (m != gs.members.end()) {
|
||||
if (m->address == member) {
|
||||
m->timestamp = now;
|
||||
return;
|
||||
}
|
||||
gs.members.insert(m,MulticastGroupMember(member,now));
|
||||
} else {
|
||||
gs.members.push_back(MulticastGroupMember(member,now));
|
||||
}
|
||||
|
||||
for(std::list<OutboundMulticast>::iterator tx(gs.txQueue.begin());tx!=gs.txQueue.end();) {
|
||||
if (tx->atLimit()) {
|
||||
gs.txQueue.erase(tx++);
|
||||
} else {
|
||||
tx->sendIfNew(RR,tPtr,member);
|
||||
if (tx->atLimit()) {
|
||||
gs.txQueue.erase(tx++);
|
||||
} else {
|
||||
++tx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_MULTICASTER_HPP
|
||||
#define ZT_MULTICASTER_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Hashtable.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "MAC.hpp"
|
||||
#include "MulticastGroup.hpp"
|
||||
#include "OutboundMulticast.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "Mutex.hpp"
|
||||
#include "SharedPtr.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class RuntimeEnvironment;
|
||||
class CertificateOfMembership;
|
||||
class Packet;
|
||||
class Network;
|
||||
|
||||
/**
|
||||
* Database of known multicast peers within a network
|
||||
*/
|
||||
class Multicaster
|
||||
{
|
||||
public:
|
||||
Multicaster(const RuntimeEnvironment *renv);
|
||||
~Multicaster();
|
||||
|
||||
/**
|
||||
* Add or update a member in a multicast group
|
||||
*
|
||||
* @param now Current time
|
||||
* @param nwid Network ID
|
||||
* @param mg Multicast group
|
||||
* @param member New member address
|
||||
*/
|
||||
inline void add(void *tPtr,int64_t now,uint64_t nwid,const MulticastGroup &mg,const Address &member)
|
||||
{
|
||||
Mutex::Lock _l(_groups_m);
|
||||
_add(tPtr,now,nwid,mg,_groups[Multicaster::Key(nwid,mg)],member);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add multiple addresses from a binary array of 5-byte address fields
|
||||
*
|
||||
* It's up to the caller to check bounds on the array before calling this.
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param now Current time
|
||||
* @param nwid Network ID
|
||||
* @param mg Multicast group
|
||||
* @param addresses Raw binary addresses in big-endian format, as a series of 5-byte fields
|
||||
* @param count Number of addresses
|
||||
* @param totalKnown Total number of known addresses as reported by peer
|
||||
*/
|
||||
void addMultiple(void *tPtr,int64_t now,uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,unsigned int totalKnown);
|
||||
|
||||
/**
|
||||
* Remove a multicast group member (if present)
|
||||
*
|
||||
* @param nwid Network ID
|
||||
* @param mg Multicast group
|
||||
* @param member Member to unsubscribe
|
||||
*/
|
||||
void remove(uint64_t nwid,const MulticastGroup &mg,const Address &member);
|
||||
|
||||
/**
|
||||
* Append gather results to a packet by choosing registered multicast recipients at random
|
||||
*
|
||||
* This appends the following fields to the packet:
|
||||
* <[4] 32-bit total number of known members in this multicast group>
|
||||
* <[2] 16-bit number of members enumerated in this packet>
|
||||
* <[...] series of 5-byte ZeroTier addresses of enumerated members>
|
||||
*
|
||||
* If zero is returned, the first two fields will still have been appended.
|
||||
*
|
||||
* @param queryingPeer Peer asking for gather (to skip in results)
|
||||
* @param nwid Network ID
|
||||
* @param mg Multicast group
|
||||
* @param appendTo Packet to append to
|
||||
* @param limit Maximum number of 5-byte addresses to append
|
||||
* @return Number of addresses appended
|
||||
* @throws std::out_of_range Buffer overflow writing to packet
|
||||
*/
|
||||
unsigned int gather(const Address &queryingPeer,uint64_t nwid,const MulticastGroup &mg,Buffer<ZT_PROTO_MAX_PACKET_LENGTH> &appendTo,unsigned int limit) const;
|
||||
|
||||
/**
|
||||
* Get subscribers to a multicast group
|
||||
*
|
||||
* @param nwid Network ID
|
||||
* @param mg Multicast group
|
||||
*/
|
||||
std::vector<Address> getMembers(uint64_t nwid,const MulticastGroup &mg,unsigned int limit) const;
|
||||
|
||||
/**
|
||||
* Send a multicast
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param now Current time
|
||||
* @param network Network
|
||||
* @param origin Origin of multicast (to not return to sender) or NULL if none
|
||||
* @param mg Multicast group
|
||||
* @param src Source Ethernet MAC address or NULL to skip in packet and compute from ZT address (non-bridged mode)
|
||||
* @param etherType Ethernet frame type
|
||||
* @param data Packet data
|
||||
* @param len Length of packet data
|
||||
*/
|
||||
void send(
|
||||
void *tPtr,
|
||||
int64_t now,
|
||||
const SharedPtr<Network> &network,
|
||||
const Address &origin,
|
||||
const MulticastGroup &mg,
|
||||
const MAC &src,
|
||||
unsigned int etherType,
|
||||
const void *data,
|
||||
unsigned int len);
|
||||
|
||||
/**
|
||||
* Clean database
|
||||
*
|
||||
* @param RR Runtime environment
|
||||
* @param now Current time
|
||||
*/
|
||||
void clean(int64_t now);
|
||||
|
||||
private:
|
||||
struct Key
|
||||
{
|
||||
Key() : nwid(0),mg() {}
|
||||
Key(uint64_t n,const MulticastGroup &g) : nwid(n),mg(g) {}
|
||||
|
||||
uint64_t nwid;
|
||||
MulticastGroup mg;
|
||||
|
||||
inline bool operator==(const Key &k) const { return ((nwid == k.nwid)&&(mg == k.mg)); }
|
||||
inline bool operator!=(const Key &k) const { return ((nwid != k.nwid)||(mg != k.mg)); }
|
||||
inline unsigned long hashCode() const { return (mg.hashCode() ^ (unsigned long)(nwid ^ (nwid >> 32))); }
|
||||
};
|
||||
|
||||
struct MulticastGroupMember
|
||||
{
|
||||
MulticastGroupMember() {}
|
||||
MulticastGroupMember(const Address &a,uint64_t ts) : address(a),timestamp(ts) {}
|
||||
|
||||
inline bool operator<(const MulticastGroupMember &a) const { return (address < a.address); }
|
||||
inline bool operator==(const MulticastGroupMember &a) const { return (address == a.address); }
|
||||
inline bool operator!=(const MulticastGroupMember &a) const { return (address != a.address); }
|
||||
inline bool operator<(const Address &a) const { return (address < a); }
|
||||
inline bool operator==(const Address &a) const { return (address == a); }
|
||||
inline bool operator!=(const Address &a) const { return (address != a); }
|
||||
|
||||
Address address;
|
||||
int64_t timestamp; // time of last notification
|
||||
};
|
||||
|
||||
struct MulticastGroupStatus
|
||||
{
|
||||
MulticastGroupStatus() : lastExplicitGather(0) {}
|
||||
|
||||
int64_t lastExplicitGather;
|
||||
std::list<OutboundMulticast> txQueue; // pending outbound multicasts
|
||||
std::vector<MulticastGroupMember> members; // members of this group
|
||||
};
|
||||
|
||||
void _add(void *tPtr,int64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member);
|
||||
|
||||
const RuntimeEnvironment *const RR;
|
||||
|
||||
Hashtable<Multicaster::Key,MulticastGroupStatus> _groups;
|
||||
Mutex _groups_m;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_MUTEX_HPP
|
||||
#define ZT_MUTEX_HPP
|
||||
|
||||
#include "Constants.hpp"
|
||||
|
||||
#ifdef __UNIX_LIKE__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
// libpthread based mutex lock
|
||||
class Mutex
|
||||
{
|
||||
public:
|
||||
Mutex()
|
||||
{
|
||||
pthread_mutex_init(&_mh,(const pthread_mutexattr_t *)0);
|
||||
}
|
||||
|
||||
~Mutex()
|
||||
{
|
||||
pthread_mutex_destroy(&_mh);
|
||||
}
|
||||
|
||||
inline void lock() const
|
||||
{
|
||||
pthread_mutex_lock(&((const_cast <Mutex *> (this))->_mh));
|
||||
}
|
||||
|
||||
inline void unlock() const
|
||||
{
|
||||
pthread_mutex_unlock(&((const_cast <Mutex *> (this))->_mh));
|
||||
}
|
||||
|
||||
class Lock
|
||||
{
|
||||
public:
|
||||
Lock(Mutex &m) :
|
||||
_m(&m)
|
||||
{
|
||||
m.lock();
|
||||
}
|
||||
|
||||
Lock(const Mutex &m) :
|
||||
_m(const_cast<Mutex *>(&m))
|
||||
{
|
||||
_m->lock();
|
||||
}
|
||||
|
||||
~Lock()
|
||||
{
|
||||
_m->unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
Mutex *const _m;
|
||||
};
|
||||
|
||||
private:
|
||||
Mutex(const Mutex &) {}
|
||||
const Mutex &operator=(const Mutex &) { return *this; }
|
||||
|
||||
pthread_mutex_t _mh;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
// Windows critical section based lock
|
||||
class Mutex
|
||||
{
|
||||
public:
|
||||
Mutex()
|
||||
{
|
||||
InitializeCriticalSection(&_cs);
|
||||
}
|
||||
|
||||
~Mutex()
|
||||
{
|
||||
DeleteCriticalSection(&_cs);
|
||||
}
|
||||
|
||||
inline void lock()
|
||||
{
|
||||
EnterCriticalSection(&_cs);
|
||||
}
|
||||
|
||||
inline void unlock()
|
||||
{
|
||||
LeaveCriticalSection(&_cs);
|
||||
}
|
||||
|
||||
inline void lock() const
|
||||
{
|
||||
(const_cast <Mutex *> (this))->lock();
|
||||
}
|
||||
|
||||
inline void unlock() const
|
||||
{
|
||||
(const_cast <Mutex *> (this))->unlock();
|
||||
}
|
||||
|
||||
class Lock
|
||||
{
|
||||
public:
|
||||
Lock(Mutex &m) :
|
||||
_m(&m)
|
||||
{
|
||||
m.lock();
|
||||
}
|
||||
|
||||
Lock(const Mutex &m) :
|
||||
_m(const_cast<Mutex *>(&m))
|
||||
{
|
||||
_m->lock();
|
||||
}
|
||||
|
||||
~Lock()
|
||||
{
|
||||
_m->unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
Mutex *const _m;
|
||||
};
|
||||
|
||||
private:
|
||||
Mutex(const Mutex &) {}
|
||||
const Mutex &operator=(const Mutex &) { return *this; }
|
||||
|
||||
CRITICAL_SECTION _cs;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
#endif
|
||||
+1723
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,494 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_NETWORK_HPP
|
||||
#define ZT_NETWORK_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../include/ZeroTierOne.h"
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Hashtable.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "Mutex.hpp"
|
||||
#include "SharedPtr.hpp"
|
||||
#include "AtomicCounter.hpp"
|
||||
#include "MulticastGroup.hpp"
|
||||
#include "MAC.hpp"
|
||||
#include "Dictionary.hpp"
|
||||
#include "Multicaster.hpp"
|
||||
#include "Membership.hpp"
|
||||
#include "NetworkConfig.hpp"
|
||||
#include "CertificateOfMembership.hpp"
|
||||
#include "Metrics.hpp"
|
||||
|
||||
#define ZT_NETWORK_MAX_INCOMING_UPDATES 3
|
||||
#define ZT_NETWORK_MAX_UPDATE_CHUNKS ((ZT_NETWORKCONFIG_DICT_CAPACITY / 1024) + 1)
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class RuntimeEnvironment;
|
||||
class Peer;
|
||||
|
||||
/**
|
||||
* A virtual LAN
|
||||
*/
|
||||
class Network
|
||||
{
|
||||
friend class SharedPtr<Network>;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Broadcast multicast group: ff:ff:ff:ff:ff:ff / 0
|
||||
*/
|
||||
static const MulticastGroup BROADCAST;
|
||||
|
||||
/**
|
||||
* Compute primary controller device ID from network ID
|
||||
*/
|
||||
static inline Address controllerFor(uint64_t nwid) { return Address(nwid >> 24); }
|
||||
|
||||
/**
|
||||
* Construct a new network
|
||||
*
|
||||
* Note that init() should be called immediately after the network is
|
||||
* constructed to actually configure the port.
|
||||
*
|
||||
* @param renv Runtime environment
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param nwid Network ID
|
||||
* @param uptr Arbitrary pointer used by externally-facing API (for user use)
|
||||
* @param nconf Network config, if known
|
||||
*/
|
||||
Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *uptr,const NetworkConfig *nconf);
|
||||
|
||||
~Network();
|
||||
|
||||
inline uint64_t id() const { return _id; }
|
||||
inline Address controller() const { return Address(_id >> 24); }
|
||||
inline bool multicastEnabled() const { return (_config.multicastLimit > 0); }
|
||||
inline bool hasConfig() const { return (_config); }
|
||||
inline uint64_t lastConfigUpdate() const { return _lastConfigUpdate; }
|
||||
inline ZT_VirtualNetworkStatus status() const { Mutex::Lock _l(_lock); return _status(); }
|
||||
inline const NetworkConfig &config() const { return _config; }
|
||||
inline const MAC &mac() const { return _mac; }
|
||||
|
||||
/**
|
||||
* Apply filters to an outgoing packet
|
||||
*
|
||||
* This applies filters from our network config and, if that doesn't match,
|
||||
* our capabilities in ascending order of capability ID. Additional actions
|
||||
* such as TEE may be taken, and credentials may be pushed, so this is not
|
||||
* side-effect-free. It's basically step one in sending something over VL2.
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param noTee If true, do not TEE anything anywhere (for two-pass filtering as done with multicast and bridging)
|
||||
* @param ztSource Source ZeroTier address
|
||||
* @param ztDest Destination ZeroTier address
|
||||
* @param macSource Ethernet layer source address
|
||||
* @param macDest Ethernet layer destination address
|
||||
* @param frameData Ethernet frame data
|
||||
* @param frameLen Ethernet frame payload length
|
||||
* @param etherType 16-bit ethernet type ID
|
||||
* @param vlanId 16-bit VLAN ID
|
||||
* @return True if packet should be sent, false if dropped or redirected
|
||||
*/
|
||||
bool filterOutgoingPacket(
|
||||
void *tPtr,
|
||||
const bool noTee,
|
||||
const Address &ztSource,
|
||||
const Address &ztDest,
|
||||
const MAC &macSource,
|
||||
const MAC &macDest,
|
||||
const uint8_t *frameData,
|
||||
const unsigned int frameLen,
|
||||
const unsigned int etherType,
|
||||
const unsigned int vlanId,
|
||||
uint8_t &qosBucket);
|
||||
|
||||
/**
|
||||
* Apply filters to an incoming packet
|
||||
*
|
||||
* This applies filters from our network config and, if that doesn't match,
|
||||
* the peer's capabilities in ascending order of capability ID. If there is
|
||||
* a match certain actions may be taken such as sending a copy of the packet
|
||||
* to a TEE or REDIRECT target.
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param sourcePeer Source Peer
|
||||
* @param ztDest Destination ZeroTier address
|
||||
* @param macSource Ethernet layer source address
|
||||
* @param macDest Ethernet layer destination address
|
||||
* @param frameData Ethernet frame data
|
||||
* @param frameLen Ethernet frame payload length
|
||||
* @param etherType 16-bit ethernet type ID
|
||||
* @param vlanId 16-bit VLAN ID
|
||||
* @return 0 == drop, 1 == accept, 2 == accept even if bridged
|
||||
*/
|
||||
int filterIncomingPacket(
|
||||
void *tPtr,
|
||||
const SharedPtr<Peer> &sourcePeer,
|
||||
const Address &ztDest,
|
||||
const MAC &macSource,
|
||||
const MAC &macDest,
|
||||
const uint8_t *frameData,
|
||||
const unsigned int frameLen,
|
||||
const unsigned int etherType,
|
||||
const unsigned int vlanId);
|
||||
|
||||
/**
|
||||
* Check whether we are subscribed to a multicast group
|
||||
*
|
||||
* @param mg Multicast group
|
||||
* @param includeBridgedGroups If true, also check groups we've learned via bridging
|
||||
* @return True if this network endpoint / peer is a member
|
||||
*/
|
||||
bool subscribedToMulticastGroup(const MulticastGroup &mg,bool includeBridgedGroups) const;
|
||||
|
||||
/**
|
||||
* Subscribe to a multicast group
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param mg New multicast group
|
||||
*/
|
||||
void multicastSubscribe(void *tPtr,const MulticastGroup &mg);
|
||||
|
||||
/**
|
||||
* Unsubscribe from a multicast group
|
||||
*
|
||||
* @param mg Multicast group
|
||||
*/
|
||||
void multicastUnsubscribe(const MulticastGroup &mg);
|
||||
|
||||
/**
|
||||
* Handle an inbound network config chunk
|
||||
*
|
||||
* This is called from IncomingPacket to handle incoming network config
|
||||
* chunks via OK(NETWORK_CONFIG_REQUEST) or NETWORK_CONFIG. It verifies
|
||||
* each chunk and once assembled applies the configuration.
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param packetId Packet ID or 0 if none (e.g. via cluster path)
|
||||
* @param source Address of sender of chunk or NULL if none (e.g. via cluster path)
|
||||
* @param chunk Buffer containing chunk
|
||||
* @param ptr Index of chunk and related fields in packet
|
||||
* @return Update ID if update was fully assembled and accepted or 0 otherwise
|
||||
*/
|
||||
uint64_t handleConfigChunk(void *tPtr,const uint64_t packetId,const Address &source,const Buffer<ZT_PROTO_MAX_PACKET_LENGTH> &chunk,unsigned int ptr);
|
||||
|
||||
/**
|
||||
* Set network configuration
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param nconf Network configuration
|
||||
* @param saveToDisk Save to disk? Used during loading, should usually be true otherwise.
|
||||
* @return 0 == bad, 1 == accepted but duplicate/unchanged, 2 == accepted and new
|
||||
*/
|
||||
int setConfiguration(void *tPtr,const NetworkConfig &nconf,bool saveToDisk);
|
||||
|
||||
/**
|
||||
* Set netconf failure to 'access denied' -- called in IncomingPacket when controller reports this
|
||||
*/
|
||||
inline void setAccessDenied(void *tPtr)
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
_netconfFailure = NETCONF_FAILURE_ACCESS_DENIED;
|
||||
|
||||
_sendUpdateEvent(tPtr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set netconf failure to 'not found' -- called by IncomingPacket when controller reports this
|
||||
*/
|
||||
inline void setNotFound(void *tPtr)
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
_netconfFailure = NETCONF_FAILURE_NOT_FOUND;
|
||||
|
||||
_sendUpdateEvent(tPtr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set netconf failure to 'authentication required' possibly with an authorization URL
|
||||
*/
|
||||
inline void setAuthenticationRequired(void *tPtr, const char *url)
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
_netconfFailure = NETCONF_FAILURE_AUTHENTICATION_REQUIRED;
|
||||
_authenticationURL = (url) ? url : "";
|
||||
_config.ssoEnabled = true;
|
||||
_config.ssoVersion = 0;
|
||||
_sendUpdateEvent(tPtr);
|
||||
}
|
||||
|
||||
/**
|
||||
* set netconf failure to 'authentication required' along with info needed
|
||||
* for sso full flow authentication.
|
||||
*/
|
||||
void setAuthenticationRequired(void *tPtr, const char* issuerURL, const char* centralEndpoint, const char* clientID, const char *ssoProvider, const char* nonce, const char* state);
|
||||
|
||||
/**
|
||||
* Causes this network to request an updated configuration from its master node now
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
*/
|
||||
void requestConfiguration(void *tPtr);
|
||||
|
||||
/**
|
||||
* Determine whether this peer is permitted to communicate on this network
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param peer Peer to check
|
||||
*/
|
||||
bool gate(void *tPtr,const SharedPtr<Peer> &peer);
|
||||
|
||||
/**
|
||||
* Check whether a given peer has recently had an association with this network
|
||||
*
|
||||
* This checks whether a peer has communicated with us recently about this
|
||||
* network and has possessed a valid certificate of membership. This may return
|
||||
* true even if the peer has been offline for a while or no longer has a valid
|
||||
* certificate of membership but had one recently.
|
||||
*
|
||||
* @param addr Peer address
|
||||
* @return True if peer has recently associated
|
||||
*/
|
||||
bool recentlyAssociatedWith(const Address &addr);
|
||||
|
||||
/**
|
||||
* Do periodic cleanup and housekeeping tasks
|
||||
*/
|
||||
void clean();
|
||||
|
||||
/**
|
||||
* Push state to members such as multicast group memberships and latest COM (if needed)
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
*/
|
||||
inline void sendUpdatesToMembers(void *tPtr)
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
_sendUpdatesToMembers(tPtr,(const MulticastGroup *)0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the node on this network that has this MAC behind it (if any)
|
||||
*
|
||||
* @param mac MAC address
|
||||
* @return ZeroTier address of bridge to this MAC
|
||||
*/
|
||||
inline Address findBridgeTo(const MAC &mac) const
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
const Address *const br = _remoteBridgeRoutes.get(mac);
|
||||
return ((br) ? *br : Address());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if QoS is in effect for this network
|
||||
*/
|
||||
inline bool qosEnabled() { return false; }
|
||||
|
||||
/**
|
||||
* Set a bridge route
|
||||
*
|
||||
* @param mac MAC address of destination
|
||||
* @param addr Bridge this MAC is reachable behind
|
||||
*/
|
||||
void learnBridgeRoute(const MAC &mac,const Address &addr);
|
||||
|
||||
/**
|
||||
* Learn a multicast group that is bridged to our tap device
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param mg Multicast group
|
||||
* @param now Current time
|
||||
*/
|
||||
void learnBridgedMulticastGroup(void *tPtr,const MulticastGroup &mg,int64_t now);
|
||||
|
||||
/**
|
||||
* Validate a credential and learn it if it passes certificate and other checks
|
||||
*/
|
||||
Membership::AddCredentialResult addCredential(void *tPtr,const CertificateOfMembership &com);
|
||||
|
||||
/**
|
||||
* Validate a credential and learn it if it passes certificate and other checks
|
||||
*/
|
||||
inline Membership::AddCredentialResult addCredential(void *tPtr,const Capability &cap)
|
||||
{
|
||||
if (cap.networkId() != _id) {
|
||||
return Membership::ADD_REJECTED;
|
||||
}
|
||||
Mutex::Lock _l(_lock);
|
||||
return _membership(cap.issuedTo()).addCredential(RR,tPtr,_config,cap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a credential and learn it if it passes certificate and other checks
|
||||
*/
|
||||
inline Membership::AddCredentialResult addCredential(void *tPtr,const Tag &tag)
|
||||
{
|
||||
if (tag.networkId() != _id) {
|
||||
return Membership::ADD_REJECTED;
|
||||
}
|
||||
Mutex::Lock _l(_lock);
|
||||
return _membership(tag.issuedTo()).addCredential(RR,tPtr,_config,tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a credential and learn it if it passes certificate and other checks
|
||||
*/
|
||||
Membership::AddCredentialResult addCredential(void *tPtr,const Address &sentFrom,const Revocation &rev);
|
||||
|
||||
/**
|
||||
* Validate a credential and learn it if it passes certificate and other checks
|
||||
*/
|
||||
inline Membership::AddCredentialResult addCredential(void *tPtr,const CertificateOfOwnership &coo)
|
||||
{
|
||||
if (coo.networkId() != _id) {
|
||||
return Membership::ADD_REJECTED;
|
||||
}
|
||||
Mutex::Lock _l(_lock);
|
||||
return _membership(coo.issuedTo()).addCredential(RR,tPtr,_config,coo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Force push credentials (COM, etc.) to a peer now
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param to Destination peer address
|
||||
* @param now Current time
|
||||
*/
|
||||
inline void peerRequestedCredentials(void *tPtr,const Address &to,const int64_t now)
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
Membership &m = _membership(to);
|
||||
const int64_t lastPushed = m.lastPushedCredentials();
|
||||
if ((lastPushed < _lastConfigUpdate)||((now - lastPushed) > ZT_PEER_CREDENTIALS_REQUEST_RATE_LIMIT)) {
|
||||
m.pushCredentials(RR,tPtr,now,to,_config);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Push credentials if we haven't done so in a very long time
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param to Destination peer address
|
||||
* @param now Current time
|
||||
*/
|
||||
inline void pushCredentialsIfNeeded(void *tPtr,const Address &to,const int64_t now)
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
Membership &m = _membership(to);
|
||||
const int64_t lastPushed = m.lastPushedCredentials();
|
||||
if ((lastPushed < _lastConfigUpdate)||((now - lastPushed) > ZT_PEER_ACTIVITY_TIMEOUT)) {
|
||||
m.pushCredentials(RR,tPtr,now,to,_config);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy this network
|
||||
*
|
||||
* This sets the network to completely remove itself on delete. This also prevents the
|
||||
* call of the normal port shutdown event on delete.
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* Get this network's config for export via the ZT core API
|
||||
*
|
||||
* @param ec Buffer to fill with externally-visible network configuration
|
||||
*/
|
||||
inline void externalConfig(ZT_VirtualNetworkConfig *ec) const
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
_externalConfig(ec);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Externally usable pointer-to-pointer exported via the core API
|
||||
*/
|
||||
inline void **userPtr() { return &_uPtr; }
|
||||
|
||||
private:
|
||||
ZT_VirtualNetworkStatus _status() const;
|
||||
void _externalConfig(ZT_VirtualNetworkConfig *ec) const; // assumes _lock is locked
|
||||
bool _gate(const SharedPtr<Peer> &peer);
|
||||
void _sendUpdatesToMembers(void *tPtr,const MulticastGroup *const newMulticastGroup);
|
||||
void _announceMulticastGroupsTo(void *tPtr,const Address &peer,const std::vector<MulticastGroup> &allMulticastGroups);
|
||||
std::vector<MulticastGroup> _allMulticastGroups() const;
|
||||
Membership &_membership(const Address &a);
|
||||
void _sendUpdateEvent(void *tPtr);
|
||||
|
||||
const RuntimeEnvironment *const RR;
|
||||
void *_uPtr;
|
||||
const uint64_t _id;
|
||||
std::string _nwidStr;
|
||||
uint64_t _lastAnnouncedMulticastGroupsUpstream;
|
||||
MAC _mac; // local MAC address
|
||||
bool _portInitialized;
|
||||
|
||||
std::vector< MulticastGroup > _myMulticastGroups; // multicast groups that we belong to (according to tap)
|
||||
Hashtable< MulticastGroup,uint64_t > _multicastGroupsBehindMe; // multicast groups that seem to be behind us and when we last saw them (if we are a bridge)
|
||||
Hashtable< MAC,Address > _remoteBridgeRoutes; // remote addresses where given MACs are reachable (for tracking devices behind remote bridges)
|
||||
|
||||
NetworkConfig _config;
|
||||
int64_t _lastConfigUpdate;
|
||||
|
||||
struct _IncomingConfigChunk
|
||||
{
|
||||
_IncomingConfigChunk() { memset(this,0,sizeof(_IncomingConfigChunk)); }
|
||||
uint64_t ts;
|
||||
uint64_t updateId;
|
||||
uint64_t haveChunkIds[ZT_NETWORK_MAX_UPDATE_CHUNKS];
|
||||
unsigned long haveChunks;
|
||||
unsigned long haveBytes;
|
||||
Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> data;
|
||||
};
|
||||
_IncomingConfigChunk _incomingConfigChunks[ZT_NETWORK_MAX_INCOMING_UPDATES];
|
||||
|
||||
bool _destroyed;
|
||||
|
||||
enum {
|
||||
NETCONF_FAILURE_NONE,
|
||||
NETCONF_FAILURE_ACCESS_DENIED,
|
||||
NETCONF_FAILURE_NOT_FOUND,
|
||||
NETCONF_FAILURE_INIT_FAILED,
|
||||
NETCONF_FAILURE_AUTHENTICATION_REQUIRED
|
||||
} _netconfFailure;
|
||||
int _portError; // return value from port config callback
|
||||
std::string _authenticationURL;
|
||||
|
||||
Hashtable<Address,Membership> _memberships;
|
||||
|
||||
Mutex _lock;
|
||||
|
||||
AtomicCounter __refCount;
|
||||
|
||||
prometheus::simpleapi::gauge_metric_t _num_multicast_groups;
|
||||
prometheus::simpleapi::counter_metric_t _incoming_packets_accepted;
|
||||
prometheus::simpleapi::counter_metric_t _incoming_packets_dropped;
|
||||
prometheus::simpleapi::counter_metric_t _outgoing_packets_accepted;
|
||||
prometheus::simpleapi::counter_metric_t _outgoing_packets_dropped;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,577 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "NetworkConfig.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,bool includeLegacy) const
|
||||
{
|
||||
Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> *tmp = new Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY>();
|
||||
char tmp2[128] = {0};
|
||||
|
||||
try {
|
||||
d.clear();
|
||||
|
||||
// Try to put the more human-readable fields first
|
||||
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_VERSION,(uint64_t)ZT_NETWORKCONFIG_VERSION)) {
|
||||
delete tmp;
|
||||
return false;
|
||||
}
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,this->networkId)) {
|
||||
delete tmp;
|
||||
return false;
|
||||
}
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,this->timestamp)) {
|
||||
delete tmp;
|
||||
return false;
|
||||
}
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA,this->credentialTimeMaxDelta)) {
|
||||
delete tmp;
|
||||
return false;
|
||||
}
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_REVISION,this->revision)) {
|
||||
delete tmp;
|
||||
return false;
|
||||
}
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,this->issuedTo.toString(tmp2))) {
|
||||
delete tmp;
|
||||
return false;
|
||||
}
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_REMOTE_TRACE_TARGET,this->remoteTraceTarget.toString(tmp2))) {
|
||||
delete tmp;
|
||||
return false;
|
||||
}
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_REMOTE_TRACE_LEVEL,(uint64_t)this->remoteTraceLevel)) {
|
||||
delete tmp;
|
||||
return false;
|
||||
}
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,this->flags)) {
|
||||
delete tmp;
|
||||
return false;
|
||||
}
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,(uint64_t)this->multicastLimit)) {
|
||||
delete tmp;
|
||||
return false;
|
||||
}
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TYPE,(uint64_t)this->type)) {
|
||||
delete tmp;
|
||||
return false;
|
||||
}
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NAME,this->name)) {
|
||||
delete tmp;
|
||||
return false;
|
||||
}
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_MTU,(uint64_t)this->mtu)) {
|
||||
delete tmp;
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
if (includeLegacy) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST_OLD,this->enableBroadcast())) {
|
||||
return false;
|
||||
}
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_PRIVATE_OLD,this->isPrivate())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string v4s;
|
||||
for(unsigned int i=0;i<staticIpCount;++i) {
|
||||
if (this->staticIps[i].ss_family == AF_INET) {
|
||||
if (v4s.length() > 0) {
|
||||
v4s.push_back(',');
|
||||
}
|
||||
char buf[64];
|
||||
v4s.append(this->staticIps[i].toString(buf));
|
||||
}
|
||||
}
|
||||
if (v4s.length() > 0) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC_OLD,v4s.c_str())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
std::string v6s;
|
||||
for(unsigned int i=0;i<staticIpCount;++i) {
|
||||
if (this->staticIps[i].ss_family == AF_INET6) {
|
||||
if (v6s.length() > 0) {
|
||||
v6s.push_back(',');
|
||||
}
|
||||
char buf[64];
|
||||
v6s.append(this->staticIps[i].toString(buf));
|
||||
}
|
||||
}
|
||||
if (v6s.length() > 0) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC_OLD,v6s.c_str())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string ets;
|
||||
unsigned int et = 0;
|
||||
ZT_VirtualNetworkRuleType lastrt = ZT_NETWORK_RULE_ACTION_ACCEPT;
|
||||
for(unsigned int i=0;i<ruleCount;++i) {
|
||||
ZT_VirtualNetworkRuleType rt = (ZT_VirtualNetworkRuleType)(rules[i].t & 0x7f);
|
||||
if (rt == ZT_NETWORK_RULE_MATCH_ETHERTYPE) {
|
||||
et = rules[i].v.etherType;
|
||||
} else if (rt == ZT_NETWORK_RULE_ACTION_ACCEPT) {
|
||||
if (((int)lastrt < 32)||(lastrt == ZT_NETWORK_RULE_MATCH_ETHERTYPE)) {
|
||||
if (ets.length() > 0) {
|
||||
ets.push_back(',');
|
||||
}
|
||||
char tmp2[16] = {0};
|
||||
ets.append(Utils::hex((uint16_t)et,tmp2));
|
||||
}
|
||||
et = 0;
|
||||
}
|
||||
lastrt = rt;
|
||||
}
|
||||
if (ets.length() > 0) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES_OLD,ets.c_str())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->com) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP_OLD,this->com.toString().c_str())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string ab;
|
||||
for(unsigned int i=0;i<this->specialistCount;++i) {
|
||||
if ((this->specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0) {
|
||||
if (ab.length() > 0) {
|
||||
ab.push_back(',');
|
||||
}
|
||||
char tmp2[16] = {0};
|
||||
ab.append(Address(this->specialists[i]).toString(tmp2));
|
||||
}
|
||||
}
|
||||
if (ab.length() > 0) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES_OLD,ab.c_str())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
|
||||
// Then add binary blobs
|
||||
|
||||
if (this->com) {
|
||||
tmp->clear();
|
||||
this->com.serialize(*tmp);
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_COM,*tmp)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
tmp->clear();
|
||||
for(unsigned int i=0;i<this->capabilityCount;++i) {
|
||||
this->capabilities[i].serialize(*tmp);
|
||||
}
|
||||
if (tmp->size()) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES,*tmp)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
tmp->clear();
|
||||
for(unsigned int i=0;i<this->tagCount;++i) {
|
||||
this->tags[i].serialize(*tmp);
|
||||
}
|
||||
if (tmp->size()) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TAGS,*tmp)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
tmp->clear();
|
||||
for(unsigned int i=0;i<this->certificateOfOwnershipCount;++i) {
|
||||
this->certificatesOfOwnership[i].serialize(*tmp);
|
||||
}
|
||||
if (tmp->size()) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP,*tmp)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
tmp->clear();
|
||||
for(unsigned int i=0;i<this->specialistCount;++i) {
|
||||
tmp->append((uint64_t)this->specialists[i]);
|
||||
}
|
||||
if (tmp->size()) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS,*tmp)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
tmp->clear();
|
||||
for(unsigned int i=0;i<this->routeCount;++i) {
|
||||
reinterpret_cast<const InetAddress *>(&(this->routes[i].target))->serialize(*tmp);
|
||||
reinterpret_cast<const InetAddress *>(&(this->routes[i].via))->serialize(*tmp);
|
||||
tmp->append((uint16_t)this->routes[i].flags);
|
||||
tmp->append((uint16_t)this->routes[i].metric);
|
||||
}
|
||||
if (tmp->size()) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ROUTES,*tmp)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
tmp->clear();
|
||||
for(unsigned int i=0;i<this->staticIpCount;++i) {
|
||||
this->staticIps[i].serialize(*tmp);
|
||||
}
|
||||
if (tmp->size()) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS,*tmp)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->ruleCount) {
|
||||
tmp->clear();
|
||||
Capability::serializeRules(*tmp,rules,ruleCount);
|
||||
if (tmp->size()) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_RULES,*tmp)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tmp->clear();
|
||||
DNS::serializeDNS(*tmp, &dns);
|
||||
if (tmp->size()) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_DNS,*tmp)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->ssoVersion == 0) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SSO_VERSION, this->ssoVersion)) {
|
||||
return false;
|
||||
}
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SSO_ENABLED, this->ssoEnabled)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this->ssoEnabled) {
|
||||
if (this->authenticationURL[0]) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_URL, this->authenticationURL)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_EXPIRY_TIME, this->authenticationExpiryTime)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if(this->ssoVersion == 1) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SSO_VERSION, this->ssoVersion)) {
|
||||
return false;
|
||||
}
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SSO_ENABLED, this->ssoEnabled)) {
|
||||
return false;
|
||||
}
|
||||
//if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_URL, this->authenticationURL)) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ISSUER_URL, this->issuerURL)) {
|
||||
return false;
|
||||
}
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CENTRAL_ENDPOINT_URL, this->centralAuthURL)) {
|
||||
return false;
|
||||
}
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NONCE, this->ssoNonce)) {
|
||||
return false;
|
||||
}
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_STATE, this->ssoState)) {
|
||||
return false;
|
||||
}
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CLIENT_ID, this->ssoClientID)) {
|
||||
return false;
|
||||
}
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SSO_PROVIDER, this->ssoProvider)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
delete tmp;
|
||||
} catch ( ... ) {
|
||||
delete tmp;
|
||||
throw;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NetworkConfig::fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d)
|
||||
{
|
||||
static const NetworkConfig NIL_NC;
|
||||
Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> *tmp = new Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY>();
|
||||
|
||||
try {
|
||||
*this = NIL_NC;
|
||||
|
||||
// Fields that are always present, new or old
|
||||
this->networkId = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,0);
|
||||
if (!this->networkId) {
|
||||
delete tmp;
|
||||
return false;
|
||||
}
|
||||
this->timestamp = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,0);
|
||||
this->credentialTimeMaxDelta = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA,0);
|
||||
this->revision = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_REVISION,0);
|
||||
this->issuedTo = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,0);
|
||||
if (!this->issuedTo) {
|
||||
delete tmp;
|
||||
return false;
|
||||
}
|
||||
this->remoteTraceTarget = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_REMOTE_TRACE_TARGET);
|
||||
this->remoteTraceLevel = (Trace::Level)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_REMOTE_TRACE_LEVEL);
|
||||
this->multicastLimit = (unsigned int)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,0);
|
||||
d.get(ZT_NETWORKCONFIG_DICT_KEY_NAME,this->name,sizeof(this->name));
|
||||
|
||||
this->mtu = (unsigned int)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_MTU,ZT_DEFAULT_MTU);
|
||||
if (this->mtu < 1280) {
|
||||
this->mtu = 1280; // minimum MTU allowed by IPv6 standard and others
|
||||
} else if (this->mtu > ZT_MAX_MTU) {
|
||||
this->mtu = ZT_MAX_MTU;
|
||||
}
|
||||
|
||||
if (d.getUI(ZT_NETWORKCONFIG_DICT_KEY_VERSION,0) < 6) {
|
||||
#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
char tmp2[1024] = {0};
|
||||
|
||||
// Decode legacy fields if version is old
|
||||
if (d.getB(ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST_OLD)) {
|
||||
this->flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST;
|
||||
}
|
||||
this->flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION; // always enable for old-style netconf
|
||||
this->type = (d.getB(ZT_NETWORKCONFIG_DICT_KEY_PRIVATE_OLD,true)) ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC;
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC_OLD,tmp2,sizeof(tmp2)) > 0) {
|
||||
char *saveptr = (char *)0;
|
||||
for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
|
||||
if (this->staticIpCount >= ZT_MAX_ZT_ASSIGNED_ADDRESSES) {
|
||||
break;
|
||||
}
|
||||
InetAddress ip(f);
|
||||
if (!ip.isNetwork()) {
|
||||
this->staticIps[this->staticIpCount++] = ip;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC_OLD,tmp2,sizeof(tmp2)) > 0) {
|
||||
char *saveptr = (char *)0;
|
||||
for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
|
||||
if (this->staticIpCount >= ZT_MAX_ZT_ASSIGNED_ADDRESSES) {
|
||||
break;
|
||||
}
|
||||
InetAddress ip(f);
|
||||
if (!ip.isNetwork()) {
|
||||
this->staticIps[this->staticIpCount++] = ip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP_OLD,tmp2,sizeof(tmp2)) > 0) {
|
||||
this->com.fromString(tmp2);
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES_OLD,tmp2,sizeof(tmp2)) > 0) {
|
||||
char *saveptr = (char *)0;
|
||||
for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
|
||||
unsigned int et = Utils::hexStrToUInt(f) & 0xffff;
|
||||
if ((this->ruleCount + 2) > ZT_MAX_NETWORK_RULES) {
|
||||
break;
|
||||
}
|
||||
if (et > 0) {
|
||||
this->rules[this->ruleCount].t = (uint8_t)ZT_NETWORK_RULE_MATCH_ETHERTYPE;
|
||||
this->rules[this->ruleCount].v.etherType = (uint16_t)et;
|
||||
++this->ruleCount;
|
||||
}
|
||||
this->rules[this->ruleCount++].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
|
||||
}
|
||||
} else {
|
||||
this->rules[0].t = ZT_NETWORK_RULE_ACTION_ACCEPT;
|
||||
this->ruleCount = 1;
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES_OLD,tmp2,sizeof(tmp2)) > 0) {
|
||||
char *saveptr = (char *)0;
|
||||
for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
|
||||
this->addSpecialist(Address(Utils::hexStrToU64(f)),ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE);
|
||||
}
|
||||
}
|
||||
#else
|
||||
delete tmp;
|
||||
return false;
|
||||
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
} else {
|
||||
// Otherwise we can use the new fields
|
||||
this->flags = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,0);
|
||||
this->type = (ZT_VirtualNetworkType)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_TYPE,(uint64_t)ZT_NETWORK_TYPE_PRIVATE);
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_COM,*tmp)) {
|
||||
this->com.deserialize(*tmp,0);
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES,*tmp)) {
|
||||
try {
|
||||
unsigned int p = 0;
|
||||
while (p < tmp->size()) {
|
||||
Capability cap;
|
||||
p += cap.deserialize(*tmp,p);
|
||||
this->capabilities[this->capabilityCount++] = cap;
|
||||
}
|
||||
} catch ( ... ) {}
|
||||
std::sort(&(this->capabilities[0]),&(this->capabilities[this->capabilityCount]));
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_TAGS,*tmp)) {
|
||||
try {
|
||||
unsigned int p = 0;
|
||||
while (p < tmp->size()) {
|
||||
Tag tag;
|
||||
p += tag.deserialize(*tmp,p);
|
||||
this->tags[this->tagCount++] = tag;
|
||||
}
|
||||
} catch ( ... ) {}
|
||||
std::sort(&(this->tags[0]),&(this->tags[this->tagCount]));
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP,*tmp)) {
|
||||
unsigned int p = 0;
|
||||
while (p < tmp->size()) {
|
||||
if (certificateOfOwnershipCount < ZT_MAX_CERTIFICATES_OF_OWNERSHIP) {
|
||||
p += certificatesOfOwnership[certificateOfOwnershipCount++].deserialize(*tmp,p);
|
||||
} else {
|
||||
CertificateOfOwnership foo;
|
||||
p += foo.deserialize(*tmp,p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS,*tmp)) {
|
||||
unsigned int p = 0;
|
||||
while ((p + 8) <= tmp->size()) {
|
||||
if (specialistCount < ZT_MAX_NETWORK_SPECIALISTS) {
|
||||
this->specialists[this->specialistCount++] = tmp->at<uint64_t>(p);
|
||||
}
|
||||
p += 8;
|
||||
}
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ROUTES,*tmp)) {
|
||||
unsigned int p = 0;
|
||||
while ((p < tmp->size())&&(routeCount < ZT_MAX_NETWORK_ROUTES)) {
|
||||
p += reinterpret_cast<InetAddress *>(&(this->routes[this->routeCount].target))->deserialize(*tmp,p);
|
||||
p += reinterpret_cast<InetAddress *>(&(this->routes[this->routeCount].via))->deserialize(*tmp,p);
|
||||
this->routes[this->routeCount].flags = tmp->at<uint16_t>(p);
|
||||
p += 2;
|
||||
this->routes[this->routeCount].metric = tmp->at<uint16_t>(p);
|
||||
p += 2;
|
||||
++this->routeCount;
|
||||
}
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS,*tmp)) {
|
||||
unsigned int p = 0;
|
||||
while ((p < tmp->size())&&(staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
|
||||
p += this->staticIps[this->staticIpCount++].deserialize(*tmp,p);
|
||||
}
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_RULES,*tmp)) {
|
||||
this->ruleCount = 0;
|
||||
unsigned int p = 0;
|
||||
Capability::deserializeRules(*tmp,p,this->rules,this->ruleCount,ZT_MAX_NETWORK_RULES);
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_DNS, *tmp)) {
|
||||
unsigned int p = 0;
|
||||
DNS::deserializeDNS(*tmp, p, &dns);
|
||||
}
|
||||
|
||||
this->ssoVersion = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_SSO_VERSION, 0ULL);
|
||||
this->ssoEnabled = d.getB(ZT_NETWORKCONFIG_DICT_KEY_SSO_ENABLED, false);
|
||||
|
||||
if (this->ssoVersion == 0) {
|
||||
// implicit flow
|
||||
if (this->ssoEnabled) {
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_URL, this->authenticationURL, (unsigned int)sizeof(this->authenticationURL)) > 0) {
|
||||
this->authenticationURL[sizeof(this->authenticationURL) - 1] = 0; // ensure null terminated
|
||||
} else {
|
||||
this->authenticationURL[0] = 0;
|
||||
}
|
||||
this->authenticationExpiryTime = d.getI(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_EXPIRY_TIME, 0);
|
||||
} else {
|
||||
this->authenticationURL[0] = 0;
|
||||
this->authenticationExpiryTime = 0;
|
||||
}
|
||||
} else if (this->ssoVersion == 1) {
|
||||
// full flow
|
||||
if (this->ssoEnabled) {
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_URL, this->authenticationURL, (unsigned int)sizeof(this->authenticationURL)) > 0) {
|
||||
this->authenticationURL[sizeof(this->authenticationURL) - 1] = 0;
|
||||
}
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ISSUER_URL, this->issuerURL, (unsigned int)sizeof(this->issuerURL)) > 0) {
|
||||
this->issuerURL[sizeof(this->issuerURL) - 1] = 0;
|
||||
}
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CENTRAL_ENDPOINT_URL, this->centralAuthURL, (unsigned int)sizeof(this->centralAuthURL)) > 0) {
|
||||
this->centralAuthURL[sizeof(this->centralAuthURL) - 1] = 0;
|
||||
}
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_NONCE, this->ssoNonce, (unsigned int)sizeof(this->ssoNonce)) > 0) {
|
||||
this->ssoNonce[sizeof(this->ssoNonce) - 1] = 0;
|
||||
}
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_STATE, this->ssoState, (unsigned int)sizeof(this->ssoState)) > 0) {
|
||||
this->ssoState[sizeof(this->ssoState) - 1] = 0;
|
||||
}
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CLIENT_ID, this->ssoClientID, (unsigned int)sizeof(this->ssoClientID)) > 0) {
|
||||
this->ssoClientID[sizeof(this->ssoClientID) - 1] = 0;
|
||||
}
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_SSO_PROVIDER, this->ssoProvider, (unsigned int)(sizeof(this->ssoProvider))) > 0) {
|
||||
this->ssoProvider[sizeof(this->ssoProvider) - 1] = 0;
|
||||
} else {
|
||||
strncpy(this->ssoProvider, "default", sizeof(this->ssoProvider));
|
||||
this->ssoProvider[sizeof(this->ssoProvider) - 1] = 0;
|
||||
}
|
||||
} else {
|
||||
this->authenticationURL[0] = 0;
|
||||
this->authenticationExpiryTime = 0;
|
||||
this->centralAuthURL[0] = 0;
|
||||
this->ssoNonce[0] = 0;
|
||||
this->ssoState[0] = 0;
|
||||
this->ssoClientID[0] = 0;
|
||||
this->issuerURL[0] = 0;
|
||||
this->ssoProvider[0] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//printf("~~~\n%s\n~~~\n",d.data());
|
||||
//dump();
|
||||
//printf("~~~\n");
|
||||
|
||||
delete tmp;
|
||||
return true;
|
||||
} catch ( ... ) {
|
||||
delete tmp;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
@@ -0,0 +1,734 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_NETWORKCONFIG_HPP
|
||||
#define ZT_NETWORKCONFIG_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
|
||||
#include "../include/ZeroTierOne.h"
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Buffer.hpp"
|
||||
#include "DNS.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "MulticastGroup.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "CertificateOfMembership.hpp"
|
||||
#include "CertificateOfOwnership.hpp"
|
||||
#include "Capability.hpp"
|
||||
#include "Tag.hpp"
|
||||
#include "Dictionary.hpp"
|
||||
#include "Hashtable.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "Trace.hpp"
|
||||
|
||||
/**
|
||||
* Default time delta for COMs, tags, and capabilities
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_DFL_MAX_DELTA ((int64_t)(1000 * 60 * 30))
|
||||
|
||||
/**
|
||||
* Maximum time delta for COMs, tags, and capabilities
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA ((int64_t)(1000 * 60 * 60 * 2))
|
||||
|
||||
/**
|
||||
* Minimum credential TTL and maxDelta for COM timestamps
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MIN_MAX_DELTA ((int64_t)(1000 * 60 * 5))
|
||||
|
||||
/**
|
||||
* Flag: enable broadcast
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST 0x0000000000000002ULL
|
||||
|
||||
/**
|
||||
* Flag: enable IPv6 NDP emulation for certain V6 address patterns
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION 0x0000000000000004ULL
|
||||
|
||||
/**
|
||||
* Flag: result of unrecognized MATCH entries in a rules table: match if set, no-match if clear
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_FLAG_RULES_RESULT_OF_UNSUPPORTED_MATCH 0x0000000000000008ULL
|
||||
|
||||
/**
|
||||
* Flag: disable frame compression
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_FLAG_DISABLE_COMPRESSION 0x0000000000000010ULL
|
||||
|
||||
/**
|
||||
* Device can bridge to other Ethernet networks and gets unknown recipient multicasts
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE 0x0000020000000000ULL
|
||||
|
||||
/**
|
||||
* Anchors are stable devices on this network that can act like roots when none are up
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR 0x0000040000000000ULL
|
||||
|
||||
/**
|
||||
* Designated multicast replicators replicate multicast in place of sender-side replication
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR 0x0000080000000000ULL
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
// Dictionary capacity needed for max size network config
|
||||
#define ZT_NETWORKCONFIG_DICT_CAPACITY (4096 + (sizeof(ZT_VirtualNetworkConfig)) + (sizeof(ZT_VirtualNetworkRule) * ZT_MAX_NETWORK_RULES) + (sizeof(Capability) * ZT_MAX_NETWORK_CAPABILITIES) + (sizeof(Tag) * ZT_MAX_NETWORK_TAGS) + (sizeof(CertificateOfOwnership) * ZT_MAX_CERTIFICATES_OF_OWNERSHIP))
|
||||
|
||||
// Dictionary capacity needed for max size network meta-data
|
||||
#define ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY 1024
|
||||
|
||||
// Network config version
|
||||
#define ZT_NETWORKCONFIG_VERSION 7
|
||||
|
||||
// Fields for meta-data sent with network config requests
|
||||
|
||||
// Network config version
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION "v"
|
||||
// Network config version
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_OS_ARCH "o"
|
||||
// Protocol version (see Packet.hpp)
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_PROTOCOL_VERSION "pv"
|
||||
// Software vendor
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_VENDOR "vend"
|
||||
// Software major version
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION "majv"
|
||||
// Software minor version
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION "minv"
|
||||
// Software revision
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION "revv"
|
||||
// Rules engine revision
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_RULES_ENGINE_REV "revr"
|
||||
// Maximum number of rules per network this node can accept
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_NETWORK_RULES "mr"
|
||||
// Maximum number of capabilities this node can accept
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_NETWORK_CAPABILITIES "mc"
|
||||
// Maximum number of rules per capability this node can accept
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_CAPABILITY_RULES "mcr"
|
||||
// Maximum number of tags this node can accept
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_NETWORK_TAGS "mt"
|
||||
// Network join authorization token (if any)
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_AUTH "a"
|
||||
// Network configuration meta-data flags
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_FLAGS "f"
|
||||
|
||||
// These dictionary keys are short so they don't take up much room.
|
||||
// By convention we use upper case for binary blobs, but it doesn't really matter.
|
||||
|
||||
// network config version
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_VERSION "v"
|
||||
// network ID
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID "nwid"
|
||||
// integer(hex)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP "ts"
|
||||
// integer(hex)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_REVISION "r"
|
||||
// address of member
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO "id"
|
||||
// remote trace target
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_REMOTE_TRACE_TARGET "tt"
|
||||
// remote trace level
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_REMOTE_TRACE_LEVEL "tl"
|
||||
// flags(hex)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_FLAGS "f"
|
||||
// integer(hex)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT "ml"
|
||||
// network type (hex)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_TYPE "t"
|
||||
// text
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_NAME "n"
|
||||
// network MTU
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_MTU "mtu"
|
||||
// credential time max delta in ms
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA "ctmd"
|
||||
// binary serialized certificate of membership
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_COM "C"
|
||||
// specialists (binary array of uint64_t)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS "S"
|
||||
// routes (binary blob)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_ROUTES "RT"
|
||||
// static IPs (binary blob)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS "I"
|
||||
// rules (binary blob)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_RULES "R"
|
||||
// capabilities (binary blobs)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES "CAP"
|
||||
// tags (binary blobs)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_TAGS "TAG"
|
||||
// tags (binary blobs)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP "COO"
|
||||
// dns (binary blobs)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_DNS "DNS"
|
||||
// sso enabled
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_SSO_ENABLED "ssoe"
|
||||
// so version
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_SSO_VERSION "ssov"
|
||||
// authentication URL
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_URL "aurl"
|
||||
// authentication expiry
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_AUTHENTICATION_EXPIRY_TIME "aexpt"
|
||||
// oidc issuer URL
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_ISSUER_URL "iurl"
|
||||
// central endpoint
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_CENTRAL_ENDPOINT_URL "ssoce"
|
||||
// nonce
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_NONCE "sson"
|
||||
// state
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_STATE "ssos"
|
||||
// client ID
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_CLIENT_ID "ssocid"
|
||||
// SSO Provider
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_SSO_PROVIDER "ssop"
|
||||
|
||||
// AuthInfo fields -- used by ncSendError for sso
|
||||
|
||||
// AuthInfo Version
|
||||
#define ZT_AUTHINFO_DICT_KEY_VERSION "aV"
|
||||
// authentication URL
|
||||
#define ZT_AUTHINFO_DICT_KEY_AUTHENTICATION_URL "aU"
|
||||
// issuer URL
|
||||
#define ZT_AUTHINFO_DICT_KEY_ISSUER_URL "iU"
|
||||
// Central endpoint URL
|
||||
#define ZT_AUTHINFO_DICT_KEY_CENTRAL_ENDPOINT_URL "aCU"
|
||||
// Nonce
|
||||
#define ZT_AUTHINFO_DICT_KEY_NONCE "aN"
|
||||
// State
|
||||
#define ZT_AUTHINFO_DICT_KEY_STATE "aS"
|
||||
// Client ID
|
||||
#define ZT_AUTHINFO_DICT_KEY_CLIENT_ID "aCID"
|
||||
// SSO Provider
|
||||
#define ZT_AUTHINFO_DICT_KEY_SSO_PROVIDER "aSSOp"
|
||||
|
||||
// Legacy fields -- these are obsoleted but are included when older clients query
|
||||
|
||||
// boolean (now a flag)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST_OLD "eb"
|
||||
// IP/bits[,IP/bits,...]
|
||||
// Note that IPs that end in all zeroes are routes with no assignment in them.
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC_OLD "v4s"
|
||||
// IP/bits[,IP/bits,...]
|
||||
// Note that IPs that end in all zeroes are routes with no assignment in them.
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC_OLD "v6s"
|
||||
// 0/1
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_PRIVATE_OLD "p"
|
||||
// integer(hex)[,integer(hex),...]
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES_OLD "et"
|
||||
// string-serialized CertificateOfMembership
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP_OLD "com"
|
||||
// node[,node,...]
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES_OLD "ab"
|
||||
// node;IP/port[,node;IP/port]
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_RELAYS_OLD "rl"
|
||||
|
||||
// End legacy fields
|
||||
|
||||
/**
|
||||
* Network configuration received from network controller nodes
|
||||
*
|
||||
* This is a memcpy()'able structure and is safe (in a crash sense) to modify
|
||||
* without locks.
|
||||
*/
|
||||
class NetworkConfig
|
||||
{
|
||||
public:
|
||||
NetworkConfig() :
|
||||
networkId(0),
|
||||
timestamp(0),
|
||||
credentialTimeMaxDelta(0),
|
||||
revision(0),
|
||||
issuedTo(),
|
||||
remoteTraceTarget(),
|
||||
flags(0),
|
||||
remoteTraceLevel(Trace::LEVEL_NORMAL),
|
||||
mtu(0),
|
||||
multicastLimit(0),
|
||||
specialistCount(0),
|
||||
routeCount(0),
|
||||
staticIpCount(0),
|
||||
ruleCount(0),
|
||||
capabilityCount(0),
|
||||
tagCount(0),
|
||||
certificateOfOwnershipCount(0),
|
||||
capabilities(),
|
||||
tags(),
|
||||
certificatesOfOwnership(),
|
||||
type(ZT_NETWORK_TYPE_PRIVATE),
|
||||
dnsCount(0),
|
||||
ssoEnabled(false),
|
||||
authenticationURL(),
|
||||
authenticationExpiryTime(0),
|
||||
issuerURL(),
|
||||
centralAuthURL(),
|
||||
ssoNonce(),
|
||||
ssoState(),
|
||||
ssoClientID()
|
||||
{
|
||||
name[0] = 0;
|
||||
memset(specialists, 0, sizeof(uint64_t)*ZT_MAX_NETWORK_SPECIALISTS);
|
||||
memset(routes, 0, sizeof(ZT_VirtualNetworkRoute)*ZT_MAX_NETWORK_ROUTES);
|
||||
memset(staticIps, 0, sizeof(InetAddress)*ZT_MAX_ZT_ASSIGNED_ADDRESSES);
|
||||
memset(rules, 0, sizeof(ZT_VirtualNetworkRule)*ZT_MAX_NETWORK_RULES);
|
||||
memset(&dns, 0, sizeof(ZT_VirtualNetworkDNS));
|
||||
memset(authenticationURL, 0, sizeof(authenticationURL));
|
||||
memset(issuerURL, 0, sizeof(issuerURL));
|
||||
memset(centralAuthURL, 0, sizeof(centralAuthURL));
|
||||
memset(ssoNonce, 0, sizeof(ssoNonce));
|
||||
memset(ssoState, 0, sizeof(ssoState));
|
||||
memset(ssoClientID, 0, sizeof(ssoClientID));
|
||||
strncpy(ssoProvider, "default", sizeof(ssoProvider));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write this network config to a dictionary for transport
|
||||
*
|
||||
* @param d Dictionary
|
||||
* @param includeLegacy If true, include legacy fields for old node versions
|
||||
* @return True if dictionary was successfully created, false if e.g. overflow
|
||||
*/
|
||||
bool toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,bool includeLegacy) const;
|
||||
|
||||
/**
|
||||
* Read this network config from a dictionary
|
||||
*
|
||||
* @param d Dictionary (non-const since it might be modified during parse, should not be used after call)
|
||||
* @return True if dictionary was valid and network config successfully initialized
|
||||
*/
|
||||
bool fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d);
|
||||
|
||||
/**
|
||||
* @return True if broadcast (ff:ff:ff:ff:ff:ff) address should work on this network
|
||||
*/
|
||||
inline bool enableBroadcast() const { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST) != 0); }
|
||||
|
||||
/**
|
||||
* @return True if IPv6 NDP emulation should be allowed for certain "magic" IPv6 address patterns
|
||||
*/
|
||||
inline bool ndpEmulation() const { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION) != 0); }
|
||||
|
||||
/**
|
||||
* @return True if frames should not be compressed
|
||||
*/
|
||||
inline bool disableCompression() const
|
||||
{
|
||||
#ifndef ZT_DISABLE_COMPRESSION
|
||||
return ((this->flags & ZT_NETWORKCONFIG_FLAG_DISABLE_COMPRESSION) != 0);
|
||||
#else
|
||||
/* Compression is disabled for libzt builds since it causes non-obvious chaotic
|
||||
interference with lwIP's TCP congestion algorithm. Compression is also disabled
|
||||
for some NAS builds due to the usage of low-performance processors in certain
|
||||
older and budget models. */
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Network type is public (no access control)
|
||||
*/
|
||||
inline bool isPublic() const { return (this->type == ZT_NETWORK_TYPE_PUBLIC); }
|
||||
|
||||
/**
|
||||
* @return Network type is private (certificate access control)
|
||||
*/
|
||||
inline bool isPrivate() const { return (this->type == ZT_NETWORK_TYPE_PRIVATE); }
|
||||
|
||||
/**
|
||||
* @return ZeroTier addresses of devices on this network designated as active bridges
|
||||
*/
|
||||
inline std::vector<Address> activeBridges() const
|
||||
{
|
||||
std::vector<Address> r;
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0) {
|
||||
r.push_back(Address(specialists[i]));
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
inline unsigned int activeBridges(Address ab[ZT_MAX_NETWORK_SPECIALISTS]) const
|
||||
{
|
||||
unsigned int c = 0;
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0) {
|
||||
ab[c++] = specialists[i];
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
inline bool isActiveBridge(const Address &a) const
|
||||
{
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
if (((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0)&&(a == specialists[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline std::vector<Address> anchors() const
|
||||
{
|
||||
std::vector<Address> r;
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR) != 0) {
|
||||
r.push_back(Address(specialists[i]));
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
inline std::vector<Address> multicastReplicators() const
|
||||
{
|
||||
std::vector<Address> r;
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR) != 0) {
|
||||
r.push_back(Address(specialists[i]));
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
inline unsigned int multicastReplicators(Address mr[ZT_MAX_NETWORK_SPECIALISTS]) const
|
||||
{
|
||||
unsigned int c = 0;
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR) != 0) {
|
||||
mr[c++] = specialists[i];
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
inline bool isMulticastReplicator(const Address &a) const
|
||||
{
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
if (((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR) != 0)&&(a == specialists[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline std::vector<Address> alwaysContactAddresses() const
|
||||
{
|
||||
std::vector<Address> r;
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
if ((specialists[i] & (ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR | ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR)) != 0) {
|
||||
r.push_back(Address(specialists[i]));
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
inline unsigned int alwaysContactAddresses(Address ac[ZT_MAX_NETWORK_SPECIALISTS]) const
|
||||
{
|
||||
unsigned int c = 0;
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
if ((specialists[i] & (ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR | ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR)) != 0) {
|
||||
ac[c++] = specialists[i];
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
inline void alwaysContactAddresses(Hashtable< Address,std::vector<InetAddress> > &a) const
|
||||
{
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
if ((specialists[i] & (ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR | ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR)) != 0) {
|
||||
a[Address(specialists[i])];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fromPeer Peer attempting to bridge other Ethernet peers onto network
|
||||
* @return True if this network allows bridging
|
||||
*/
|
||||
inline bool permitsBridging(const Address &fromPeer) const
|
||||
{
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
if ((fromPeer == specialists[i])&&((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline operator bool() const { return (networkId != 0); }
|
||||
inline bool operator==(const NetworkConfig &nc) const { return (memcmp(this,&nc,sizeof(NetworkConfig)) == 0); }
|
||||
inline bool operator!=(const NetworkConfig &nc) const { return (!(*this == nc)); }
|
||||
|
||||
/**
|
||||
* Add a specialist or mask flags if already present
|
||||
*
|
||||
* This masks the existing flags if the specialist is already here or adds
|
||||
* it otherwise.
|
||||
*
|
||||
* @param a Address of specialist
|
||||
* @param f Flags (OR of specialist role/type flags)
|
||||
* @return True if successfully masked or added
|
||||
*/
|
||||
inline bool addSpecialist(const Address &a,const uint64_t f)
|
||||
{
|
||||
const uint64_t aint = a.toInt();
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
if ((specialists[i] & 0xffffffffffULL) == aint) {
|
||||
specialists[i] |= f;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (specialistCount < ZT_MAX_NETWORK_SPECIALISTS) {
|
||||
specialists[specialistCount++] = f | aint;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const Capability *capability(const uint32_t id) const
|
||||
{
|
||||
for(unsigned int i=0;i<capabilityCount;++i) {
|
||||
if (capabilities[i].id() == id) {
|
||||
return &(capabilities[i]);
|
||||
}
|
||||
}
|
||||
return (Capability *)0;
|
||||
}
|
||||
|
||||
const Tag *tag(const uint32_t id) const
|
||||
{
|
||||
for(unsigned int i=0;i<tagCount;++i) {
|
||||
if (tags[i].id() == id) {
|
||||
return &(tags[i]);
|
||||
}
|
||||
}
|
||||
return (Tag *)0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Network ID that this configuration applies to
|
||||
*/
|
||||
uint64_t networkId;
|
||||
|
||||
/**
|
||||
* Controller-side time of config generation/issue
|
||||
*/
|
||||
int64_t timestamp;
|
||||
|
||||
/**
|
||||
* Max difference between timestamp and tag/capability timestamp
|
||||
*/
|
||||
int64_t credentialTimeMaxDelta;
|
||||
|
||||
/**
|
||||
* Controller-side revision counter for this configuration
|
||||
*/
|
||||
uint64_t revision;
|
||||
|
||||
/**
|
||||
* Address of device to which this config is issued
|
||||
*/
|
||||
Address issuedTo;
|
||||
|
||||
/**
|
||||
* If non-NULL, remote traces related to this network are sent here
|
||||
*/
|
||||
Address remoteTraceTarget;
|
||||
|
||||
/**
|
||||
* Flags (64-bit)
|
||||
*/
|
||||
uint64_t flags;
|
||||
|
||||
/**
|
||||
* Remote trace level
|
||||
*/
|
||||
Trace::Level remoteTraceLevel;
|
||||
|
||||
/**
|
||||
* Network MTU
|
||||
*/
|
||||
unsigned int mtu;
|
||||
|
||||
/**
|
||||
* Maximum number of recipients per multicast (not including active bridges)
|
||||
*/
|
||||
unsigned int multicastLimit;
|
||||
|
||||
/**
|
||||
* Number of specialists
|
||||
*/
|
||||
unsigned int specialistCount;
|
||||
|
||||
/**
|
||||
* Number of routes
|
||||
*/
|
||||
unsigned int routeCount;
|
||||
|
||||
/**
|
||||
* Number of ZT-managed static IP assignments
|
||||
*/
|
||||
unsigned int staticIpCount;
|
||||
|
||||
/**
|
||||
* Number of rule table entries
|
||||
*/
|
||||
unsigned int ruleCount;
|
||||
|
||||
/**
|
||||
* Number of capabilities
|
||||
*/
|
||||
unsigned int capabilityCount;
|
||||
|
||||
/**
|
||||
* Number of tags
|
||||
*/
|
||||
unsigned int tagCount;
|
||||
|
||||
/**
|
||||
* Number of certificates of ownership
|
||||
*/
|
||||
unsigned int certificateOfOwnershipCount;
|
||||
|
||||
/**
|
||||
* Specialist devices
|
||||
*
|
||||
* For each entry the least significant 40 bits are the device's ZeroTier
|
||||
* address and the most significant 24 bits are flags indicating its role.
|
||||
*/
|
||||
uint64_t specialists[ZT_MAX_NETWORK_SPECIALISTS];
|
||||
|
||||
/**
|
||||
* Statically defined "pushed" routes (including default gateways)
|
||||
*/
|
||||
ZT_VirtualNetworkRoute routes[ZT_MAX_NETWORK_ROUTES];
|
||||
|
||||
/**
|
||||
* Static IP assignments
|
||||
*/
|
||||
InetAddress staticIps[ZT_MAX_ZT_ASSIGNED_ADDRESSES];
|
||||
|
||||
/**
|
||||
* Base network rules
|
||||
*/
|
||||
ZT_VirtualNetworkRule rules[ZT_MAX_NETWORK_RULES];
|
||||
|
||||
/**
|
||||
* Capabilities for this node on this network, in ascending order of capability ID
|
||||
*/
|
||||
Capability capabilities[ZT_MAX_NETWORK_CAPABILITIES];
|
||||
|
||||
/**
|
||||
* Tags for this node on this network, in ascending order of tag ID
|
||||
*/
|
||||
Tag tags[ZT_MAX_NETWORK_TAGS];
|
||||
|
||||
/**
|
||||
* Certificates of ownership for this network member
|
||||
*/
|
||||
CertificateOfOwnership certificatesOfOwnership[ZT_MAX_CERTIFICATES_OF_OWNERSHIP];
|
||||
|
||||
/**
|
||||
* Network type (currently just public or private)
|
||||
*/
|
||||
ZT_VirtualNetworkType type;
|
||||
|
||||
/**
|
||||
* Network short name or empty string if not defined
|
||||
*/
|
||||
char name[ZT_MAX_NETWORK_SHORT_NAME_LENGTH + 1];
|
||||
|
||||
/**
|
||||
* Certificate of membership (for private networks)
|
||||
*/
|
||||
CertificateOfMembership com;
|
||||
|
||||
/**
|
||||
* Number of ZT-pushed DNS configurations
|
||||
*/
|
||||
unsigned int dnsCount;
|
||||
|
||||
/**
|
||||
* ZT pushed DNS configuration
|
||||
*/
|
||||
ZT_VirtualNetworkDNS dns;
|
||||
|
||||
/**
|
||||
* SSO enabled flag.
|
||||
*/
|
||||
bool ssoEnabled;
|
||||
|
||||
/**
|
||||
* SSO version
|
||||
*/
|
||||
uint64_t ssoVersion;
|
||||
|
||||
/**
|
||||
* Authentication URL if authentication is required
|
||||
*/
|
||||
char authenticationURL[2048];
|
||||
|
||||
/**
|
||||
* Time current authentication expires or 0 if external authentication is disabled
|
||||
*
|
||||
* Not used if authVersion >= 1
|
||||
*/
|
||||
uint64_t authenticationExpiryTime;
|
||||
|
||||
/**
|
||||
* OIDC issuer URL
|
||||
*/
|
||||
char issuerURL[2048];
|
||||
|
||||
/**
|
||||
* central base URL.
|
||||
*/
|
||||
char centralAuthURL[2048];
|
||||
|
||||
/**
|
||||
* sso nonce
|
||||
*/
|
||||
char ssoNonce[128];
|
||||
|
||||
/**
|
||||
* sso state
|
||||
*/
|
||||
char ssoState[256];
|
||||
|
||||
/**
|
||||
* oidc client id
|
||||
*/
|
||||
char ssoClientID[256];
|
||||
|
||||
/**
|
||||
* oidc provider
|
||||
*
|
||||
* because certain providers require specific scopes to be requested
|
||||
* and others to be not requested in order to make everything work
|
||||
* correctly
|
||||
**/
|
||||
char ssoProvider[64];
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_NETWORKCONFIGMASTER_HPP
|
||||
#define ZT_NETWORKCONFIGMASTER_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Dictionary.hpp"
|
||||
#include "NetworkConfig.hpp"
|
||||
#include "Revocation.hpp"
|
||||
#include "Address.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class Identity;
|
||||
struct InetAddress;
|
||||
|
||||
/**
|
||||
* Interface for network controller implementations
|
||||
*/
|
||||
class NetworkController
|
||||
{
|
||||
public:
|
||||
enum ErrorCode
|
||||
{
|
||||
NC_ERROR_NONE = 0,
|
||||
NC_ERROR_OBJECT_NOT_FOUND = 1,
|
||||
NC_ERROR_ACCESS_DENIED = 2,
|
||||
NC_ERROR_INTERNAL_SERVER_ERROR = 3,
|
||||
NC_ERROR_AUTHENTICATION_REQUIRED = 4
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface for sender used to send pushes and replies
|
||||
*/
|
||||
class Sender
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Send a configuration to a remote peer
|
||||
*
|
||||
* @param nwid Network ID
|
||||
* @param requestPacketId Request packet ID to send OK(NETWORK_CONFIG_REQUEST) or 0 to send NETWORK_CONFIG (push)
|
||||
* @param destination Destination peer Address
|
||||
* @param nc Network configuration to send
|
||||
* @param sendLegacyFormatConfig If true, send an old-format network config
|
||||
*/
|
||||
virtual void ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &destination,const NetworkConfig &nc,bool sendLegacyFormatConfig) = 0;
|
||||
|
||||
/**
|
||||
* Send revocation to a node
|
||||
*
|
||||
* @param destination Destination node address
|
||||
* @param rev Revocation to send
|
||||
*/
|
||||
virtual void ncSendRevocation(const Address &destination,const Revocation &rev) = 0;
|
||||
|
||||
/**
|
||||
* Send a network configuration request error
|
||||
*
|
||||
* If errorData/errorDataSize are provided they must point to a valid serialized
|
||||
* Dictionary containing error data. They can be null/zero if not specified.
|
||||
*
|
||||
* @param nwid Network ID
|
||||
* @param requestPacketId Request packet ID or 0 if none
|
||||
* @param destination Destination peer Address
|
||||
* @param errorCode Error code
|
||||
* @param errorData Data associated with error or NULL if none
|
||||
* @param errorDataSize Size of errorData in bytes
|
||||
*/
|
||||
virtual void ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode, const void *errorData, unsigned int errorDataSize) = 0;
|
||||
};
|
||||
|
||||
NetworkController() {}
|
||||
virtual ~NetworkController() {}
|
||||
|
||||
/**
|
||||
* Called when this is added to a Node to initialize and supply info
|
||||
*
|
||||
* @param signingId Identity for signing of network configurations, certs, etc.
|
||||
* @param sender Sender implementation for sending replies or config pushes
|
||||
*/
|
||||
virtual void init(const Identity &signingId,Sender *sender) = 0;
|
||||
|
||||
/**
|
||||
* Handle a network configuration request
|
||||
*
|
||||
* @param nwid 64-bit network ID
|
||||
* @param fromAddr Originating wire address or null address if packet is not direct (or from self)
|
||||
* @param requestPacketId Packet ID of request packet or 0 if not initiated by remote request
|
||||
* @param identity ZeroTier identity of originating peer
|
||||
* @param metaData Meta-data bundled with request (if any)
|
||||
* @return Returns NETCONF_QUERY_OK if result 'nc' is valid, or an error code on error
|
||||
*/
|
||||
virtual void request(
|
||||
uint64_t nwid,
|
||||
const InetAddress &fromAddr,
|
||||
uint64_t requestPacketId,
|
||||
const Identity &identity,
|
||||
const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> &metaData) = 0;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
+1152
File diff suppressed because it is too large
Load Diff
+341
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* Copyright (c)2013-2020 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_NODE_HPP
|
||||
#define ZT_NODE_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "Constants.hpp"
|
||||
|
||||
#include "../include/ZeroTierOne.h"
|
||||
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "Mutex.hpp"
|
||||
#include "MAC.hpp"
|
||||
#include "Network.hpp"
|
||||
#include "Path.hpp"
|
||||
#include "Salsa20.hpp"
|
||||
#include "NetworkController.hpp"
|
||||
#include "Hashtable.hpp"
|
||||
#include "Bond.hpp"
|
||||
#include "SelfAwareness.hpp"
|
||||
|
||||
// Bit mask for "expecting reply" hash
|
||||
#define ZT_EXPECTING_REPLIES_BUCKET_MASK1 255
|
||||
#define ZT_EXPECTING_REPLIES_BUCKET_MASK2 31
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class World;
|
||||
|
||||
/**
|
||||
* Implementation of Node object as defined in CAPI
|
||||
*
|
||||
* The pointer returned by ZT_Node_new() is an instance of this class.
|
||||
*/
|
||||
class Node : public NetworkController::Sender
|
||||
{
|
||||
public:
|
||||
Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,int64_t now);
|
||||
virtual ~Node();
|
||||
|
||||
// Get rid of alignment warnings on 32-bit Windows and possibly improve performance
|
||||
#ifdef __WINDOWS__
|
||||
void * operator new(size_t i) { return _mm_malloc(i,16); }
|
||||
void operator delete(void* p) { _mm_free(p); }
|
||||
#endif
|
||||
|
||||
// Public API Functions ----------------------------------------------------
|
||||
|
||||
ZT_ResultCode processWirePacket(
|
||||
void *tptr,
|
||||
int64_t now,
|
||||
int64_t localSocket,
|
||||
const struct sockaddr_storage *remoteAddress,
|
||||
const void *packetData,
|
||||
unsigned int packetLength,
|
||||
volatile int64_t *nextBackgroundTaskDeadline);
|
||||
ZT_ResultCode processVirtualNetworkFrame(
|
||||
void *tptr,
|
||||
int64_t now,
|
||||
uint64_t nwid,
|
||||
uint64_t sourceMac,
|
||||
uint64_t destMac,
|
||||
unsigned int etherType,
|
||||
unsigned int vlanId,
|
||||
const void *frameData,
|
||||
unsigned int frameLength,
|
||||
volatile int64_t *nextBackgroundTaskDeadline);
|
||||
ZT_ResultCode processBackgroundTasks(void *tptr,int64_t now,volatile int64_t *nextBackgroundTaskDeadline);
|
||||
ZT_ResultCode join(uint64_t nwid,void *uptr,void *tptr);
|
||||
ZT_ResultCode leave(uint64_t nwid,void **uptr,void *tptr);
|
||||
ZT_ResultCode multicastSubscribe(void *tptr,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
|
||||
ZT_ResultCode multicastUnsubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
|
||||
ZT_ResultCode orbit(void *tptr,uint64_t moonWorldId,uint64_t moonSeed);
|
||||
ZT_ResultCode deorbit(void *tptr,uint64_t moonWorldId);
|
||||
uint64_t address() const;
|
||||
void status(ZT_NodeStatus *status) const;
|
||||
ZT_PeerList *peers() const;
|
||||
ZT_VirtualNetworkConfig *networkConfig(uint64_t nwid) const;
|
||||
ZT_VirtualNetworkList *networks() const;
|
||||
void freeQueryResult(void *qr);
|
||||
int addLocalInterfaceAddress(const struct sockaddr_storage *addr);
|
||||
void clearLocalInterfaceAddresses();
|
||||
int sendUserMessage(void *tptr,uint64_t dest,uint64_t typeId,const void *data,unsigned int len);
|
||||
void setNetconfMaster(void *networkControllerInstance);
|
||||
|
||||
// Internal functions ------------------------------------------------------
|
||||
|
||||
inline int64_t now() const { return _now; }
|
||||
|
||||
inline bool putPacket(void *tPtr,const int64_t localSocket,const InetAddress &addr,const void *data,unsigned int len,unsigned int ttl = 0)
|
||||
{
|
||||
return (_cb.wirePacketSendFunction(
|
||||
reinterpret_cast<ZT_Node *>(this),
|
||||
_uPtr,
|
||||
tPtr,
|
||||
localSocket,
|
||||
reinterpret_cast<const struct sockaddr_storage *>(&addr),
|
||||
data,
|
||||
len,
|
||||
ttl) == 0);
|
||||
}
|
||||
|
||||
inline void putFrame(void *tPtr,uint64_t nwid,void **nuptr,const MAC &source,const MAC &dest,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
|
||||
{
|
||||
_cb.virtualNetworkFrameFunction(
|
||||
reinterpret_cast<ZT_Node *>(this),
|
||||
_uPtr,
|
||||
tPtr,
|
||||
nwid,
|
||||
nuptr,
|
||||
source.toInt(),
|
||||
dest.toInt(),
|
||||
etherType,
|
||||
vlanId,
|
||||
data,
|
||||
len);
|
||||
}
|
||||
|
||||
inline SharedPtr<Network> network(uint64_t nwid) const
|
||||
{
|
||||
Mutex::Lock _l(_networks_m);
|
||||
const SharedPtr<Network> *n = _networks.get(nwid);
|
||||
if (n) {
|
||||
return *n;
|
||||
}
|
||||
return SharedPtr<Network>();
|
||||
}
|
||||
|
||||
inline bool belongsToNetwork(uint64_t nwid) const
|
||||
{
|
||||
Mutex::Lock _l(_networks_m);
|
||||
return _networks.contains(nwid);
|
||||
}
|
||||
|
||||
inline std::vector< SharedPtr<Network> > allNetworks() const
|
||||
{
|
||||
std::vector< SharedPtr<Network> > nw;
|
||||
Mutex::Lock _l(_networks_m);
|
||||
Hashtable< uint64_t,SharedPtr<Network> >::Iterator i(*const_cast< Hashtable< uint64_t,SharedPtr<Network> > * >(&_networks));
|
||||
uint64_t *k = (uint64_t *)0;
|
||||
SharedPtr<Network> *v = (SharedPtr<Network> *)0;
|
||||
while (i.next(k,v)) {
|
||||
nw.push_back(*v);
|
||||
}
|
||||
return nw;
|
||||
}
|
||||
|
||||
inline std::vector<InetAddress> directPaths() const
|
||||
{
|
||||
Mutex::Lock _l(_directPaths_m);
|
||||
return _directPaths;
|
||||
}
|
||||
|
||||
inline void postEvent(void *tPtr,ZT_Event ev,const void *md = (const void *)0) { _cb.eventCallback(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,ev,md); }
|
||||
|
||||
inline int configureVirtualNetworkPort(void *tPtr,uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) { return _cb.virtualNetworkConfigFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,nwid,nuptr,op,nc); }
|
||||
|
||||
inline bool online() const { return _online; }
|
||||
|
||||
inline int stateObjectGet(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2],void *const data,const unsigned int maxlen) { return _cb.stateGetFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,type,id,data,maxlen); }
|
||||
inline void stateObjectPut(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2],const void *const data,const unsigned int len) { _cb.statePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,type,id,data,(int)len); }
|
||||
inline void stateObjectDelete(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2]) { _cb.statePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,type,id,(const void *)0,-1); }
|
||||
|
||||
bool shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,const int64_t localSocket,const InetAddress &remoteAddress);
|
||||
inline bool externalPathLookup(void *tPtr,const Address &ztaddr,int family,InetAddress &addr) { return ( (_cb.pathLookupFunction) ? (_cb.pathLookupFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,ztaddr.toInt(),family,reinterpret_cast<struct sockaddr_storage *>(&addr)) != 0) : false ); }
|
||||
|
||||
uint64_t prng();
|
||||
ZT_ResultCode setPhysicalPathConfiguration(const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig);
|
||||
|
||||
World planet() const;
|
||||
std::vector<World> moons() const;
|
||||
|
||||
inline const Identity &identity() const { return _RR.identity; }
|
||||
|
||||
inline const std::vector<InetAddress> SurfaceAddresses() const { return _RR.sa->whoami(); }
|
||||
|
||||
inline Bond *bondController() const { return _RR.bc; }
|
||||
|
||||
/**
|
||||
* Register that we are expecting a reply to a packet ID
|
||||
*
|
||||
* This only uses the most significant bits of the packet ID, both to save space
|
||||
* and to avoid using the higher bits that can be modified during armor() to
|
||||
* mask against the packet send counter used for QoS detection.
|
||||
*
|
||||
* @param packetId Packet ID to expect reply to
|
||||
*/
|
||||
inline void expectReplyTo(const uint64_t packetId)
|
||||
{
|
||||
const unsigned long pid2 = (unsigned long)(packetId >> 32);
|
||||
const unsigned long bucket = (unsigned long)(pid2 & ZT_EXPECTING_REPLIES_BUCKET_MASK1);
|
||||
_expectingRepliesTo[bucket][_expectingRepliesToBucketPtr[bucket]++ & ZT_EXPECTING_REPLIES_BUCKET_MASK2] = (uint32_t)pid2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a given packet ID is something we are expecting a reply to
|
||||
*
|
||||
* This only uses the most significant bits of the packet ID, both to save space
|
||||
* and to avoid using the higher bits that can be modified during armor() to
|
||||
* mask against the packet send counter used for QoS detection.
|
||||
*
|
||||
* @param packetId Packet ID to check
|
||||
* @return True if we're expecting a reply
|
||||
*/
|
||||
inline bool expectingReplyTo(const uint64_t packetId) const
|
||||
{
|
||||
const uint32_t pid2 = (uint32_t)(packetId >> 32);
|
||||
const unsigned long bucket = (unsigned long)(pid2 & ZT_EXPECTING_REPLIES_BUCKET_MASK1);
|
||||
for(unsigned long i=0;i<=ZT_EXPECTING_REPLIES_BUCKET_MASK2;++i) {
|
||||
if (_expectingRepliesTo[bucket][i] == pid2) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether we should do potentially expensive identity verification (rate limit)
|
||||
*
|
||||
* @param now Current time
|
||||
* @param from Source address of packet
|
||||
* @return True if within rate limits
|
||||
*/
|
||||
inline bool rateGateIdentityVerification(const int64_t now,const InetAddress &from)
|
||||
{
|
||||
unsigned long iph = from.rateGateHash();
|
||||
if ((now - _lastIdentityVerification[iph]) >= ZT_IDENTITY_VALIDATION_SOURCE_RATE_LIMIT) {
|
||||
_lastIdentityVerification[iph] = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &destination,const NetworkConfig &nc,bool sendLegacyFormatConfig);
|
||||
virtual void ncSendRevocation(const Address &destination,const Revocation &rev);
|
||||
virtual void ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode, const void *errorData, unsigned int errorDataSize);
|
||||
|
||||
inline const Address &remoteTraceTarget() const { return _remoteTraceTarget; }
|
||||
inline Trace::Level remoteTraceLevel() const { return _remoteTraceLevel; }
|
||||
|
||||
inline bool localControllerHasAuthorized(const int64_t now,const uint64_t nwid,const Address &addr) const
|
||||
{
|
||||
_localControllerAuthorizations_m.lock();
|
||||
const int64_t *const at = _localControllerAuthorizations.get(_LocalControllerAuth(nwid,addr));
|
||||
_localControllerAuthorizations_m.unlock();
|
||||
if (at) {
|
||||
return ((now - *at) < (ZT_NETWORK_AUTOCONF_DELAY * 3));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void statsLogVerb(const unsigned int v,const unsigned int bytes)
|
||||
{
|
||||
++_stats.inVerbCounts[v];
|
||||
_stats.inVerbBytes[v] += (uint64_t)bytes;
|
||||
}
|
||||
|
||||
inline void setLowBandwidthMode(bool isEnabled)
|
||||
{
|
||||
_lowBandwidthMode = isEnabled;
|
||||
}
|
||||
|
||||
inline bool lowBandwidthModeEnabled()
|
||||
{
|
||||
return _lowBandwidthMode;
|
||||
}
|
||||
|
||||
void initMultithreading(unsigned int concurrency, bool cpuPinningEnabled);
|
||||
|
||||
|
||||
public:
|
||||
RuntimeEnvironment _RR;
|
||||
RuntimeEnvironment *RR;
|
||||
void *_uPtr; // _uptr (lower case) is reserved in Visual Studio :P
|
||||
ZT_Node_Callbacks _cb;
|
||||
|
||||
// For tracking packet IDs to filter out OK/ERROR replies to packets we did not send
|
||||
uint8_t _expectingRepliesToBucketPtr[ZT_EXPECTING_REPLIES_BUCKET_MASK1 + 1];
|
||||
uint32_t _expectingRepliesTo[ZT_EXPECTING_REPLIES_BUCKET_MASK1 + 1][ZT_EXPECTING_REPLIES_BUCKET_MASK2 + 1];
|
||||
|
||||
// Time of last identity verification indexed by InetAddress.rateGateHash() -- used in IncomingPacket::_doHELLO() via rateGateIdentityVerification()
|
||||
int64_t _lastIdentityVerification[16384];
|
||||
|
||||
// Statistics about stuff happening
|
||||
volatile ZT_NodeStatistics _stats;
|
||||
|
||||
// Map that remembers if we have recently sent a network config to someone
|
||||
// querying us as a controller.
|
||||
struct _LocalControllerAuth
|
||||
{
|
||||
uint64_t nwid,address;
|
||||
_LocalControllerAuth(const uint64_t nwid_,const Address &address_) : nwid(nwid_),address(address_.toInt()) {}
|
||||
inline unsigned long hashCode() const { return (unsigned long)(nwid ^ address); }
|
||||
inline bool operator==(const _LocalControllerAuth &a) const { return ((a.nwid == nwid)&&(a.address == address)); }
|
||||
inline bool operator!=(const _LocalControllerAuth &a) const { return ((a.nwid != nwid)||(a.address != address)); }
|
||||
};
|
||||
Hashtable< _LocalControllerAuth,int64_t > _localControllerAuthorizations;
|
||||
Mutex _localControllerAuthorizations_m;
|
||||
|
||||
Hashtable< uint64_t,SharedPtr<Network> > _networks;
|
||||
Mutex _networks_m;
|
||||
|
||||
std::vector<InetAddress> _directPaths;
|
||||
Mutex _directPaths_m;
|
||||
|
||||
Mutex _backgroundTasksLock;
|
||||
|
||||
Address _remoteTraceTarget;
|
||||
enum Trace::Level _remoteTraceLevel;
|
||||
|
||||
volatile int64_t _now;
|
||||
int64_t _lastPingCheck;
|
||||
int64_t _lastGratuitousPingCheck;
|
||||
int64_t _lastHousekeepingRun;
|
||||
int64_t _lastMemoizedTraceSettings;
|
||||
volatile int64_t _prngState[2];
|
||||
bool _online;
|
||||
bool _lowBandwidthMode;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "OutboundMulticast.hpp"
|
||||
#include "Switch.hpp"
|
||||
#include "Network.hpp"
|
||||
#include "Node.hpp"
|
||||
#include "Peer.hpp"
|
||||
#include "Topology.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
void OutboundMulticast::init(
|
||||
const RuntimeEnvironment *RR,
|
||||
uint64_t timestamp,
|
||||
uint64_t nwid,
|
||||
bool disableCompression,
|
||||
unsigned int limit,
|
||||
unsigned int gatherLimit,
|
||||
const MAC &src,
|
||||
const MulticastGroup &dest,
|
||||
unsigned int etherType,
|
||||
const void *payload,
|
||||
unsigned int len)
|
||||
{
|
||||
uint8_t flags = 0;
|
||||
|
||||
_timestamp = timestamp;
|
||||
_nwid = nwid;
|
||||
if (src) {
|
||||
_macSrc = src;
|
||||
flags |= 0x04;
|
||||
} else {
|
||||
_macSrc.fromAddress(RR->identity.address(),nwid);
|
||||
}
|
||||
_macDest = dest.mac();
|
||||
_limit = limit;
|
||||
_frameLen = (len < ZT_MAX_MTU) ? len : ZT_MAX_MTU;
|
||||
_etherType = etherType;
|
||||
|
||||
if (gatherLimit) {
|
||||
flags |= 0x02;
|
||||
}
|
||||
|
||||
_packet.setSource(RR->identity.address());
|
||||
_packet.setVerb(Packet::VERB_MULTICAST_FRAME);
|
||||
_packet.append((uint64_t)nwid);
|
||||
_packet.append(flags);
|
||||
if (gatherLimit) {
|
||||
_packet.append((uint32_t)gatherLimit);
|
||||
}
|
||||
if (src) {
|
||||
src.appendTo(_packet);
|
||||
}
|
||||
dest.mac().appendTo(_packet);
|
||||
_packet.append((uint32_t)dest.adi());
|
||||
_packet.append((uint16_t)etherType);
|
||||
_packet.append(payload,_frameLen);
|
||||
if (!disableCompression) {
|
||||
_packet.compress();
|
||||
}
|
||||
|
||||
memcpy(_frameData,payload,_frameLen);
|
||||
}
|
||||
|
||||
void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,void *tPtr,const Address &toAddr)
|
||||
{
|
||||
const SharedPtr<Network> nw(RR->node->network(_nwid));
|
||||
uint8_t QoSBucket = 255; // Dummy value
|
||||
if ((nw)&&(nw->filterOutgoingPacket(tPtr,true,RR->identity.address(),toAddr,_macSrc,_macDest,_frameData,_frameLen,_etherType,0,QoSBucket))) {
|
||||
nw->pushCredentialsIfNeeded(tPtr,toAddr,RR->node->now());
|
||||
_packet.newInitializationVector();
|
||||
_packet.setDestination(toAddr);
|
||||
RR->node->expectReplyTo(_packet.packetId());
|
||||
_tmp = _packet;
|
||||
RR->sw->send(tPtr,_tmp,true);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_OUTBOUNDMULTICAST_HPP
|
||||
#define ZT_OUTBOUNDMULTICAST_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "MAC.hpp"
|
||||
#include "MulticastGroup.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "Packet.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class CertificateOfMembership;
|
||||
class RuntimeEnvironment;
|
||||
|
||||
/**
|
||||
* An outbound multicast packet
|
||||
*
|
||||
* This object isn't guarded by a mutex; caller must synchronize access.
|
||||
*/
|
||||
class OutboundMulticast
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Create an uninitialized outbound multicast
|
||||
*
|
||||
* It must be initialized with init().
|
||||
*/
|
||||
OutboundMulticast() {}
|
||||
|
||||
/**
|
||||
* Initialize outbound multicast
|
||||
*
|
||||
* @param RR Runtime environment
|
||||
* @param timestamp Creation time
|
||||
* @param nwid Network ID
|
||||
* @param disableCompression Disable compression of frame payload
|
||||
* @param limit Multicast limit for desired number of packets to send
|
||||
* @param gatherLimit Number to lazily/implicitly gather with this frame or 0 for none
|
||||
* @param src Source MAC address of frame or NULL to imply compute from sender ZT address
|
||||
* @param dest Destination multicast group (MAC + ADI)
|
||||
* @param etherType 16-bit Ethernet type ID
|
||||
* @param payload Data
|
||||
* @param len Length of data
|
||||
* @throws std::out_of_range Data too large to fit in a MULTICAST_FRAME
|
||||
*/
|
||||
void init(
|
||||
const RuntimeEnvironment *RR,
|
||||
uint64_t timestamp,
|
||||
uint64_t nwid,
|
||||
bool disableCompression,
|
||||
unsigned int limit,
|
||||
unsigned int gatherLimit,
|
||||
const MAC &src,
|
||||
const MulticastGroup &dest,
|
||||
unsigned int etherType,
|
||||
const void *payload,
|
||||
unsigned int len);
|
||||
|
||||
/**
|
||||
* @return Multicast creation time
|
||||
*/
|
||||
inline uint64_t timestamp() const { return _timestamp; }
|
||||
|
||||
/**
|
||||
* @param now Current time
|
||||
* @return True if this multicast is expired (has exceeded transmit timeout)
|
||||
*/
|
||||
inline bool expired(int64_t now) const { return ((now - _timestamp) >= ZT_MULTICAST_TRANSMIT_TIMEOUT); }
|
||||
|
||||
/**
|
||||
* @return True if this outbound multicast has been sent to enough peers
|
||||
*/
|
||||
inline bool atLimit() const { return (_alreadySentTo.size() >= _limit); }
|
||||
|
||||
/**
|
||||
* Just send without checking log
|
||||
*
|
||||
* @param RR Runtime environment
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param toAddr Destination address
|
||||
*/
|
||||
void sendOnly(const RuntimeEnvironment *RR,void *tPtr,const Address &toAddr);
|
||||
|
||||
/**
|
||||
* Just send and log but do not check sent log
|
||||
*
|
||||
* @param RR Runtime environment
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param toAddr Destination address
|
||||
*/
|
||||
inline void sendAndLog(const RuntimeEnvironment *RR,void *tPtr,const Address &toAddr)
|
||||
{
|
||||
_alreadySentTo.push_back(toAddr);
|
||||
sendOnly(RR,tPtr,toAddr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an address as having been used so we will not send there in the future
|
||||
*
|
||||
* @param toAddr Address to log as sent
|
||||
*/
|
||||
inline void logAsSent(const Address &toAddr)
|
||||
{
|
||||
_alreadySentTo.push_back(toAddr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to send this to a given peer if it hasn't been sent to them already
|
||||
*
|
||||
* @param RR Runtime environment
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param toAddr Destination address
|
||||
* @return True if address is new and packet was sent to switch, false if duplicate
|
||||
*/
|
||||
inline bool sendIfNew(const RuntimeEnvironment *RR,void *tPtr,const Address &toAddr)
|
||||
{
|
||||
if (std::find(_alreadySentTo.begin(),_alreadySentTo.end(),toAddr) == _alreadySentTo.end()) {
|
||||
sendAndLog(RR,tPtr,toAddr);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t _timestamp;
|
||||
uint64_t _nwid;
|
||||
MAC _macSrc;
|
||||
MAC _macDest;
|
||||
unsigned int _limit;
|
||||
unsigned int _frameLen;
|
||||
unsigned int _etherType;
|
||||
Packet _packet,_tmp;
|
||||
std::vector<Address> _alreadySentTo;
|
||||
uint8_t _frameData[ZT_MAX_MTU];
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
+1196
File diff suppressed because it is too large
Load Diff
+1436
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (c)2013-2021 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#include "PacketMultiplexer.hpp"
|
||||
|
||||
#include "Node.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Constants.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
PacketMultiplexer::PacketMultiplexer(const RuntimeEnvironment* renv)
|
||||
{
|
||||
RR = renv;
|
||||
};
|
||||
|
||||
void PacketMultiplexer::putFrame(void* tPtr, uint64_t nwid, void** nuptr, const MAC& source, const MAC& dest, unsigned int etherType, unsigned int vlanId, const void* data, unsigned int len, unsigned int flowId)
|
||||
{
|
||||
#if defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__WINDOWS__)
|
||||
RR->node->putFrame(tPtr,nwid,nuptr,source,dest,etherType,vlanId,(const void *)data,len);
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (!_enabled) {
|
||||
RR->node->putFrame(tPtr,nwid,nuptr,source,dest,etherType,vlanId,(const void *)data,len);
|
||||
return;
|
||||
}
|
||||
|
||||
PacketRecord* packet;
|
||||
_rxPacketVector_m.lock();
|
||||
if (_rxPacketVector.empty()) {
|
||||
packet = new PacketRecord;
|
||||
}
|
||||
else {
|
||||
packet = _rxPacketVector.back();
|
||||
_rxPacketVector.pop_back();
|
||||
}
|
||||
_rxPacketVector_m.unlock();
|
||||
|
||||
packet->tPtr = tPtr;
|
||||
packet->nwid = nwid;
|
||||
packet->nuptr = nuptr;
|
||||
packet->source = source.toInt();
|
||||
packet->dest = dest.toInt();
|
||||
packet->etherType = etherType;
|
||||
packet->vlanId = vlanId;
|
||||
packet->len = len;
|
||||
packet->flowId = flowId;
|
||||
memcpy(packet->data, data, len);
|
||||
|
||||
int bucket = flowId % _concurrency;
|
||||
_rxPacketQueues[bucket]->postLimit(packet, 2048);
|
||||
}
|
||||
|
||||
void PacketMultiplexer::setUpPostDecodeReceiveThreads(unsigned int concurrency, bool cpuPinningEnabled)
|
||||
{
|
||||
#if defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__WINDOWS__)
|
||||
return;
|
||||
#endif
|
||||
_enabled = true;
|
||||
_concurrency = concurrency;
|
||||
bool _enablePinning = cpuPinningEnabled;
|
||||
|
||||
for (unsigned int i = 0; i < _concurrency; ++i) {
|
||||
fprintf(stderr, "Reserved queue for thread %d\n", i);
|
||||
_rxPacketQueues.push_back(new BlockingQueue<PacketRecord*>());
|
||||
}
|
||||
|
||||
// Each thread picks from its own queue to feed into the core
|
||||
for (unsigned int i = 0; i < _concurrency; ++i) {
|
||||
_rxThreads.push_back(std::thread([this, i, _enablePinning]() {
|
||||
fprintf(stderr, "Created post-decode packet ingestion thread %d\n", i);
|
||||
|
||||
PacketRecord* packet = nullptr;
|
||||
for (;;) {
|
||||
if (! _rxPacketQueues[i]->get(packet)) {
|
||||
break;
|
||||
}
|
||||
if (! packet) {
|
||||
break;
|
||||
}
|
||||
|
||||
// fprintf(stderr, "popped packet from queue %d\n", i);
|
||||
|
||||
MAC sourceMac = MAC(packet->source);
|
||||
MAC destMac = MAC(packet->dest);
|
||||
|
||||
RR->node->putFrame(packet->tPtr, packet->nwid, packet->nuptr, sourceMac, destMac, packet->etherType, 0, (const void*)packet->data, packet->len);
|
||||
{
|
||||
Mutex::Lock l(_rxPacketVector_m);
|
||||
_rxPacketVector.push_back(packet);
|
||||
}
|
||||
/*
|
||||
if (ZT_ResultCode_isFatal(err)) {
|
||||
char tmp[256];
|
||||
OSUtils::ztsnprintf(tmp, sizeof(tmp), "error processing packet: %d", (int)err);
|
||||
Mutex::Lock _l(_termReason_m);
|
||||
_termReason = ONE_UNRECOVERABLE_ERROR;
|
||||
_fatalErrorMessage = tmp;
|
||||
this->terminate();
|
||||
break;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c)2013-2021 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_PACKET_MULTIPLEXER_HPP
|
||||
#define ZT_PACKET_MULTIPLEXER_HPP
|
||||
|
||||
#include "../osdep/BlockingQueue.hpp"
|
||||
#include "MAC.hpp"
|
||||
#include "Mutex.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
struct PacketRecord {
|
||||
void* tPtr;
|
||||
uint64_t nwid;
|
||||
void** nuptr;
|
||||
uint64_t source;
|
||||
uint64_t dest;
|
||||
unsigned int etherType;
|
||||
unsigned int vlanId;
|
||||
uint8_t data[ZT_MAX_MTU];
|
||||
unsigned int len;
|
||||
unsigned int flowId;
|
||||
};
|
||||
|
||||
class PacketMultiplexer {
|
||||
public:
|
||||
const RuntimeEnvironment* RR;
|
||||
|
||||
PacketMultiplexer(const RuntimeEnvironment* renv);
|
||||
|
||||
void setUpPostDecodeReceiveThreads(unsigned int concurrency, bool cpuPinningEnabled);
|
||||
|
||||
void putFrame(void* tPtr, uint64_t nwid, void** nuptr, const MAC& source, const MAC& dest, unsigned int etherType, unsigned int vlanId, const void* data, unsigned int len, unsigned int flowId);
|
||||
|
||||
std::vector<BlockingQueue<PacketRecord*>*> _rxPacketQueues;
|
||||
|
||||
unsigned int _concurrency;
|
||||
// pool
|
||||
std::vector<PacketRecord*> _rxPacketVector;
|
||||
std::vector<std::thread> _rxPacketThreads;
|
||||
Mutex _rxPacketVector_m, _rxPacketThreads_m;
|
||||
|
||||
std::vector<std::thread> _rxThreads;
|
||||
unsigned int _rxThreadCount;
|
||||
bool _enabled;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif // ZT_PACKET_MULTIPLEXER_HPP
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#include "Path.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Node.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
bool Path::send(const RuntimeEnvironment *RR,void *tPtr,const void *data,unsigned int len,int64_t now)
|
||||
{
|
||||
if (RR->node->putPacket(tPtr,_localSocket,_addr,data,len)) {
|
||||
_lastOut = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
+408
@@ -0,0 +1,408 @@
|
||||
/*
|
||||
* Copyright (c)2013-2020 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_PATH_HPP
|
||||
#define ZT_PATH_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "SharedPtr.hpp"
|
||||
#include "AtomicCounter.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "Packet.hpp"
|
||||
#include "RingBuffer.hpp"
|
||||
|
||||
/**
|
||||
* Maximum return value of preferenceRank()
|
||||
*/
|
||||
#define ZT_PATH_MAX_PREFERENCE_RANK ((ZT_INETADDRESS_MAX_SCOPE << 1) | 1)
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class RuntimeEnvironment;
|
||||
|
||||
/**
|
||||
* A path across the physical network
|
||||
*/
|
||||
class Path
|
||||
{
|
||||
friend class SharedPtr<Path>;
|
||||
friend class Bond;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Efficient unique key for paths in a Hashtable
|
||||
*/
|
||||
class HashKey
|
||||
{
|
||||
public:
|
||||
HashKey() {}
|
||||
|
||||
HashKey(const int64_t l,const InetAddress &r)
|
||||
{
|
||||
if (r.ss_family == AF_INET) {
|
||||
_k[0] = (uint64_t)reinterpret_cast<const struct sockaddr_in *>(&r)->sin_addr.s_addr;
|
||||
_k[1] = (uint64_t)reinterpret_cast<const struct sockaddr_in *>(&r)->sin_port;
|
||||
_k[2] = (uint64_t)l;
|
||||
} else if (r.ss_family == AF_INET6) {
|
||||
memcpy(_k,reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,16);
|
||||
_k[2] = ((uint64_t)reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_port << 32) ^ (uint64_t)l;
|
||||
} else {
|
||||
memcpy(_k,&r,std::min(sizeof(_k),sizeof(InetAddress)));
|
||||
_k[2] += (uint64_t)l;
|
||||
}
|
||||
}
|
||||
|
||||
inline unsigned long hashCode() const { return (unsigned long)(_k[0] + _k[1] + _k[2]); }
|
||||
|
||||
inline bool operator==(const HashKey &k) const { return ( (_k[0] == k._k[0]) && (_k[1] == k._k[1]) && (_k[2] == k._k[2]) ); }
|
||||
inline bool operator!=(const HashKey &k) const { return (!(*this == k)); }
|
||||
|
||||
private:
|
||||
uint64_t _k[3];
|
||||
};
|
||||
|
||||
Path() :
|
||||
_lastOut(0),
|
||||
_lastIn(0),
|
||||
_lastTrustEstablishedPacketReceived(0),
|
||||
_lastEchoRequestReceived(0),
|
||||
_localPort(0),
|
||||
_localSocket(-1),
|
||||
_latencyMean(0.0),
|
||||
_latencyVariance(0.0),
|
||||
_packetLossRatio(0.0),
|
||||
_packetErrorRatio(0.0),
|
||||
_assignedFlowCount(0),
|
||||
_valid(true),
|
||||
_eligible(false),
|
||||
_bonded(false),
|
||||
_mtu(0),
|
||||
_givenLinkSpeed(0),
|
||||
_relativeQuality(0),
|
||||
_latency(0xffff),
|
||||
_addr(),
|
||||
_ipScope(InetAddress::IP_SCOPE_NONE)
|
||||
{}
|
||||
|
||||
Path(const int64_t localSocket,const InetAddress &addr) :
|
||||
_lastOut(0),
|
||||
_lastIn(0),
|
||||
_lastTrustEstablishedPacketReceived(0),
|
||||
_lastEchoRequestReceived(0),
|
||||
_localPort(0),
|
||||
_localSocket(localSocket),
|
||||
_latencyMean(0.0),
|
||||
_latencyVariance(0.0),
|
||||
_packetLossRatio(0.0),
|
||||
_packetErrorRatio(0.0),
|
||||
_assignedFlowCount(0),
|
||||
_valid(true),
|
||||
_eligible(false),
|
||||
_bonded(false),
|
||||
_mtu(0),
|
||||
_givenLinkSpeed(0),
|
||||
_relativeQuality(0),
|
||||
_latency(0xffff),
|
||||
_addr(addr),
|
||||
_ipScope(addr.ipScope())
|
||||
{}
|
||||
|
||||
/**
|
||||
* Called when a packet is received from this remote path, regardless of content
|
||||
*
|
||||
* @param t Time of receive
|
||||
*/
|
||||
inline void received(const uint64_t t)
|
||||
{
|
||||
_lastIn = t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set time last trusted packet was received (done in Peer::received())
|
||||
*/
|
||||
inline void trustedPacketReceived(const uint64_t t) { _lastTrustEstablishedPacketReceived = t; }
|
||||
|
||||
/**
|
||||
* Send a packet via this path (last out time is also updated)
|
||||
*
|
||||
* @param RR Runtime environment
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param data Packet data
|
||||
* @param len Packet length
|
||||
* @param now Current time
|
||||
* @return True if transport reported success
|
||||
*/
|
||||
bool send(const RuntimeEnvironment *RR,void *tPtr,const void *data,unsigned int len,int64_t now);
|
||||
|
||||
/**
|
||||
* Manually update last sent time
|
||||
*
|
||||
* @param t Time of send
|
||||
*/
|
||||
inline void sent(const int64_t t) { _lastOut = t; }
|
||||
|
||||
/**
|
||||
* Update path latency with a new measurement
|
||||
*
|
||||
* @param l Measured latency
|
||||
*/
|
||||
inline void updateLatency(const unsigned int l, int64_t now)
|
||||
{
|
||||
unsigned int pl = _latency;
|
||||
if (pl < 0xffff) {
|
||||
_latency = (pl + l) / 2;
|
||||
} else {
|
||||
_latency = l;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Local socket as specified by external code
|
||||
*/
|
||||
inline int64_t localSocket() const { return _localSocket; }
|
||||
|
||||
/**
|
||||
* @return Local port corresponding to the localSocket
|
||||
*/
|
||||
inline int64_t localPort() const { return _localPort; }
|
||||
|
||||
/**
|
||||
* @return Physical address
|
||||
*/
|
||||
inline const InetAddress &address() const { return _addr; }
|
||||
|
||||
/**
|
||||
* @return IP scope -- faster shortcut for address().ipScope()
|
||||
*/
|
||||
inline InetAddress::IpScope ipScope() const { return _ipScope; }
|
||||
|
||||
/**
|
||||
* @return True if path has received a trust established packet (e.g. common network membership) in the past ZT_TRUST_EXPIRATION ms
|
||||
*/
|
||||
inline bool trustEstablished(const int64_t now) const { return ((now - _lastTrustEstablishedPacketReceived) < ZT_TRUST_EXPIRATION); }
|
||||
|
||||
/**
|
||||
* @return Preference rank, higher == better
|
||||
*/
|
||||
inline unsigned int preferenceRank() const
|
||||
{
|
||||
// This causes us to rank paths in order of IP scope rank (see InetAddress.hpp) but
|
||||
// within each IP scope class to prefer IPv6 over IPv4.
|
||||
return ( ((unsigned int)_ipScope << 1) | (unsigned int)(_addr.ss_family == AF_INET6) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this address is valid for a ZeroTier path
|
||||
*
|
||||
* This checks the address type and scope against address types and scopes
|
||||
* that we currently support for ZeroTier communication.
|
||||
*
|
||||
* @param a Address to check
|
||||
* @return True if address is good for ZeroTier path use
|
||||
*/
|
||||
static inline bool isAddressValidForPath(const InetAddress &a)
|
||||
{
|
||||
if ((a.ss_family == AF_INET)||(a.ss_family == AF_INET6)) {
|
||||
switch(a.ipScope()) {
|
||||
/* Note: we don't do link-local at the moment. Unfortunately these
|
||||
* cause several issues. The first is that they usually require a
|
||||
* device qualifier, which we don't handle yet and can't portably
|
||||
* push in PUSH_DIRECT_PATHS. The second is that some OSes assign
|
||||
* these very ephemerally or otherwise strangely. So we'll use
|
||||
* private, pseudo-private, shared (e.g. carrier grade NAT), or
|
||||
* global IP addresses. */
|
||||
case InetAddress::IP_SCOPE_PRIVATE:
|
||||
case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
|
||||
case InetAddress::IP_SCOPE_SHARED:
|
||||
case InetAddress::IP_SCOPE_GLOBAL:
|
||||
if (a.ss_family == AF_INET6) {
|
||||
// TEMPORARY HACK: for now, we are going to blacklist he.net IPv6
|
||||
// tunnels due to very spotty performance and low MTU issues over
|
||||
// these IPv6 tunnel links.
|
||||
const uint8_t *ipd = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_addr.s6_addr);
|
||||
if ((ipd[0] == 0x20)&&(ipd[1] == 0x01)&&(ipd[2] == 0x04)&&(ipd[3] == 0x70)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Latency or 0xffff if unknown
|
||||
*/
|
||||
inline unsigned int latency() const { return _latency; }
|
||||
|
||||
/**
|
||||
* @return Path quality -- lower is better
|
||||
*/
|
||||
inline long quality(const int64_t now) const
|
||||
{
|
||||
const int l = (long)_latency;
|
||||
const int age = (long)std::min((now - _lastIn),(int64_t)(ZT_PATH_HEARTBEAT_PERIOD * 10)); // set an upper sanity limit to avoid overflow
|
||||
return (((age < (ZT_PATH_HEARTBEAT_PERIOD + 5000)) ? l : (l + 0xffff + age)) * (long)((ZT_INETADDRESS_MAX_SCOPE - _ipScope) + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if this path is alive (receiving heartbeats)
|
||||
*/
|
||||
inline bool alive(const int64_t now) const {
|
||||
return (now - _lastIn) < (ZT_PATH_HEARTBEAT_PERIOD + 5000);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if this path needs a heartbeat
|
||||
*/
|
||||
inline bool needsHeartbeat(const int64_t now) const { return ((now - _lastOut) >= ZT_PATH_HEARTBEAT_PERIOD); }
|
||||
|
||||
/**
|
||||
* @return Last time we sent something
|
||||
*/
|
||||
inline int64_t lastOut() const { return _lastOut; }
|
||||
|
||||
/**
|
||||
* @return Last time we received anything
|
||||
*/
|
||||
inline int64_t lastIn() const { return _lastIn; }
|
||||
|
||||
/**
|
||||
* @return the age of the path in terms of receiving packets
|
||||
*/
|
||||
inline int64_t age(int64_t now) { return (now - _lastIn); }
|
||||
|
||||
/**
|
||||
* @return Time last trust-established packet was received
|
||||
*/
|
||||
inline int64_t lastTrustEstablishedPacketReceived() const { return _lastTrustEstablishedPacketReceived; }
|
||||
|
||||
/**
|
||||
* Rate limit gate for inbound ECHO requests
|
||||
*/
|
||||
inline bool rateGateEchoRequest(const int64_t now)
|
||||
{
|
||||
if ((now - _lastEchoRequestReceived) >= (ZT_PEER_GENERAL_RATE_LIMIT / 6)) {
|
||||
_lastEchoRequestReceived = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Mean latency as reported by the bonding layer
|
||||
*/
|
||||
inline float latencyMean() const { return _latencyMean; }
|
||||
|
||||
/**
|
||||
* @return Latency variance as reported by the bonding layer
|
||||
*/
|
||||
inline float latencyVariance() const { return _latencyVariance; }
|
||||
|
||||
/**
|
||||
* @return Packet Loss Ratio as reported by the bonding layer
|
||||
*/
|
||||
inline float packetLossRatio() const { return _packetLossRatio; }
|
||||
|
||||
/**
|
||||
* @return Packet Error Ratio as reported by the bonding layer
|
||||
*/
|
||||
inline float packetErrorRatio() const { return _packetErrorRatio; }
|
||||
|
||||
/**
|
||||
* @return Number of flows assigned to this path
|
||||
*/
|
||||
inline unsigned int assignedFlowCount() const { return _assignedFlowCount; }
|
||||
|
||||
/**
|
||||
* @return Whether this path is valid as reported by the bonding layer. The bonding layer
|
||||
* actually checks with Phy to see if the interface is still up
|
||||
*/
|
||||
inline bool valid() const { return _valid; }
|
||||
|
||||
/**
|
||||
* @return Whether this path is eligible for use in a bond as reported by the bonding layer
|
||||
*/
|
||||
inline bool eligible() const { return _eligible; }
|
||||
|
||||
/**
|
||||
* @return Whether this path is bonded as reported by the bonding layer
|
||||
*/
|
||||
inline bool bonded() const { return _bonded; }
|
||||
|
||||
/**
|
||||
* @return Whether the user-specified MTU for this path (determined by MTU for parent link)
|
||||
*/
|
||||
inline uint16_t mtu() const { return _mtu; }
|
||||
|
||||
/**
|
||||
* @return Given link capacity as reported by the bonding layer
|
||||
*/
|
||||
inline uint32_t givenLinkSpeed() const { return _givenLinkSpeed; }
|
||||
|
||||
/**
|
||||
* @return Path's quality as reported by the bonding layer
|
||||
*/
|
||||
inline float relativeQuality() const { return _relativeQuality; }
|
||||
|
||||
/**
|
||||
* @return Physical interface name that this path lives on
|
||||
*/
|
||||
char *ifname() {
|
||||
return _ifname;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
char _ifname[ZT_MAX_PHYSIFNAME] = { };
|
||||
|
||||
volatile int64_t _lastOut;
|
||||
volatile int64_t _lastIn;
|
||||
volatile int64_t _lastTrustEstablishedPacketReceived;
|
||||
|
||||
int64_t _lastEchoRequestReceived;
|
||||
|
||||
uint16_t _localPort;
|
||||
int64_t _localSocket;
|
||||
|
||||
volatile float _latencyMean;
|
||||
volatile float _latencyVariance;
|
||||
volatile float _packetLossRatio;
|
||||
volatile float _packetErrorRatio;
|
||||
volatile uint16_t _assignedFlowCount;
|
||||
volatile bool _valid;
|
||||
volatile bool _eligible;
|
||||
volatile bool _bonded;
|
||||
volatile uint16_t _mtu;
|
||||
volatile uint32_t _givenLinkSpeed;
|
||||
volatile float _relativeQuality;
|
||||
|
||||
volatile unsigned int _latency;
|
||||
InetAddress _addr;
|
||||
InetAddress::IpScope _ipScope; // memoize this since it's a computed value checked often
|
||||
AtomicCounter __refCount;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
+702
@@ -0,0 +1,702 @@
|
||||
/*
|
||||
* Copyright (c)2013-2020 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#include "../version.h"
|
||||
#include "Constants.hpp"
|
||||
#include "Peer.hpp"
|
||||
#include "Switch.hpp"
|
||||
#include "Network.hpp"
|
||||
#include "SelfAwareness.hpp"
|
||||
#include "Packet.hpp"
|
||||
#include "Trace.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "RingBuffer.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "Metrics.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
static unsigned char s_freeRandomByteCounter = 0;
|
||||
|
||||
Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity)
|
||||
: RR(renv)
|
||||
, _lastReceive(0)
|
||||
, _lastNontrivialReceive(0)
|
||||
, _lastTriedMemorizedPath(0)
|
||||
, _lastDirectPathPushSent(0)
|
||||
, _lastDirectPathPushReceive(0)
|
||||
, _lastCredentialRequestSent(0)
|
||||
, _lastWhoisRequestReceived(0)
|
||||
, _lastCredentialsReceived(0)
|
||||
, _lastTrustEstablishedPacketReceived(0)
|
||||
, _lastSentFullHello(0)
|
||||
, _lastEchoCheck(0)
|
||||
, _freeRandomByte((unsigned char)((uintptr_t)this >> 4) ^ ++s_freeRandomByteCounter)
|
||||
, _vProto(0)
|
||||
, _vMajor(0)
|
||||
, _vMinor(0)
|
||||
, _vRevision(0)
|
||||
, _id(peerIdentity)
|
||||
, _directPathPushCutoffCount(0)
|
||||
, _echoRequestCutoffCount(0)
|
||||
, _localMultipathSupported(false)
|
||||
, _lastComputedAggregateMeanLatency(0)
|
||||
#ifndef ZT_NO_PEER_METRICS
|
||||
, _peer_latency{Metrics::peer_latency.Add({{"node_id", OSUtils::nodeIDStr(peerIdentity.address().toInt())}}, std::vector<uint64_t>{1,3,6,10,30,60,100,300,600,1000})}
|
||||
, _alive_path_count{Metrics::peer_path_count.Add({{"node_id", OSUtils::nodeIDStr(peerIdentity.address().toInt())},{"status","alive"}})}
|
||||
, _dead_path_count{Metrics::peer_path_count.Add({{"node_id", OSUtils::nodeIDStr(peerIdentity.address().toInt())},{"status","dead"}})}
|
||||
, _incoming_packet{Metrics::peer_packets.Add({{"direction", "rx"},{"node_id", OSUtils::nodeIDStr(peerIdentity.address().toInt())}})}
|
||||
, _outgoing_packet{Metrics::peer_packets.Add({{"direction", "tx"},{"node_id", OSUtils::nodeIDStr(peerIdentity.address().toInt())}})}
|
||||
, _packet_errors{Metrics::peer_packet_errors.Add({{"node_id", OSUtils::nodeIDStr(peerIdentity.address().toInt())}})}
|
||||
#endif
|
||||
{
|
||||
if (!myIdentity.agree(peerIdentity,_key)) {
|
||||
throw ZT_EXCEPTION_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
uint8_t ktmp[ZT_SYMMETRIC_KEY_SIZE];
|
||||
KBKDFHMACSHA384(_key,ZT_KBKDF_LABEL_AES_GMAC_SIV_K0,0,0,ktmp);
|
||||
_aesKeys[0].init(ktmp);
|
||||
KBKDFHMACSHA384(_key,ZT_KBKDF_LABEL_AES_GMAC_SIV_K1,0,0,ktmp);
|
||||
_aesKeys[1].init(ktmp);
|
||||
Utils::burn(ktmp,ZT_SYMMETRIC_KEY_SIZE);
|
||||
}
|
||||
|
||||
void Peer::received(
|
||||
void *tPtr,
|
||||
const SharedPtr<Path> &path,
|
||||
const unsigned int hops,
|
||||
const uint64_t packetId,
|
||||
const unsigned int payloadLength,
|
||||
const Packet::Verb verb,
|
||||
const uint64_t inRePacketId,
|
||||
const Packet::Verb inReVerb,
|
||||
const bool trustEstablished,
|
||||
const uint64_t networkId,
|
||||
const int32_t flowId)
|
||||
{
|
||||
const int64_t now = RR->node->now();
|
||||
|
||||
_lastReceive = now;
|
||||
switch (verb) {
|
||||
case Packet::VERB_FRAME:
|
||||
case Packet::VERB_EXT_FRAME:
|
||||
case Packet::VERB_NETWORK_CONFIG_REQUEST:
|
||||
case Packet::VERB_NETWORK_CONFIG:
|
||||
case Packet::VERB_MULTICAST_FRAME:
|
||||
_lastNontrivialReceive = now;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#ifndef ZT_NO_PEER_METRICS
|
||||
_incoming_packet++;
|
||||
#endif
|
||||
recordIncomingPacket(path, packetId, payloadLength, verb, flowId, now);
|
||||
|
||||
if (trustEstablished) {
|
||||
_lastTrustEstablishedPacketReceived = now;
|
||||
path->trustedPacketReceived(now);
|
||||
}
|
||||
|
||||
if (hops == 0) {
|
||||
// If this is a direct packet (no hops), update existing paths or learn new ones
|
||||
bool havePath = false;
|
||||
{
|
||||
Mutex::Lock _l(_paths_m);
|
||||
for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
|
||||
if (_paths[i].p) {
|
||||
if (_paths[i].p == path) {
|
||||
_paths[i].lr = now;
|
||||
havePath = true;
|
||||
break;
|
||||
}
|
||||
// If same address on same interface then don't learn unless existing path isn't alive (prevents learning loop)
|
||||
if (_paths[i].p->address().ipsEqual(path->address()) && _paths[i].p->localSocket() == path->localSocket()) {
|
||||
if (_paths[i].p->alive(now) && !_bond) {
|
||||
havePath = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( (!havePath) && RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id.address(),path->localSocket(),path->address()) ) {
|
||||
if (verb == Packet::VERB_OK) {
|
||||
Mutex::Lock _l(_paths_m);
|
||||
unsigned int oldestPathIdx = ZT_MAX_PEER_NETWORK_PATHS;
|
||||
unsigned int oldestPathAge = 0;
|
||||
unsigned int replacePath = ZT_MAX_PEER_NETWORK_PATHS;
|
||||
|
||||
for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
|
||||
if (_paths[i].p) {
|
||||
// Keep track of oldest path as a last resort option
|
||||
unsigned int currAge = _paths[i].p->age(now);
|
||||
if (currAge > oldestPathAge) {
|
||||
oldestPathAge = currAge;
|
||||
oldestPathIdx = i;
|
||||
}
|
||||
if (_paths[i].p->address().ipsEqual(path->address())) {
|
||||
if (_paths[i].p->localSocket() == path->localSocket()) {
|
||||
if (!_paths[i].p->alive(now)) {
|
||||
replacePath = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
replacePath = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't find a good candidate then resort to replacing oldest path
|
||||
replacePath = (replacePath == ZT_MAX_PEER_NETWORK_PATHS) ? oldestPathIdx : replacePath;
|
||||
if (replacePath != ZT_MAX_PEER_NETWORK_PATHS) {
|
||||
RR->t->peerLearnedNewPath(tPtr, networkId, *this, path, packetId);
|
||||
_paths[replacePath].lr = now;
|
||||
_paths[replacePath].p = path;
|
||||
_paths[replacePath].priority = 1;
|
||||
Mutex::Lock _l(_bond_m);
|
||||
if(_bond) {
|
||||
_bond->nominatePathToBond(_paths[replacePath].p, now);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Mutex::Lock ltl(_lastTriedPath_m);
|
||||
|
||||
bool triedTooRecently = false;
|
||||
for(std::list< std::pair< Path *, int64_t > >::iterator i(_lastTriedPath.begin());i!=_lastTriedPath.end();) {
|
||||
if ((now - i->second) > 1000) {
|
||||
_lastTriedPath.erase(i++);
|
||||
} else if (i->first == path.ptr()) {
|
||||
++i;
|
||||
triedTooRecently = true;
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
if (!triedTooRecently) {
|
||||
_lastTriedPath.push_back(std::pair< Path *, int64_t >(path.ptr(), now));
|
||||
attemptToContactAt(tPtr,path->localSocket(),path->address(),now,true);
|
||||
path->sent(now);
|
||||
RR->t->peerConfirmingUnknownPath(tPtr,networkId,*this,path,packetId,verb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we have a trust relationship periodically push a message enumerating
|
||||
// all known external addresses for ourselves. If we already have a path this
|
||||
// is done less frequently.
|
||||
if (this->trustEstablished(now)) {
|
||||
const int64_t sinceLastPush = now - _lastDirectPathPushSent;
|
||||
bool lowBandwidth = RR->node->lowBandwidthModeEnabled();
|
||||
int timerScale = lowBandwidth ? 16 : 1;
|
||||
if (sinceLastPush >= ((hops == 0) ? ZT_DIRECT_PATH_PUSH_INTERVAL_HAVEPATH * timerScale : ZT_DIRECT_PATH_PUSH_INTERVAL)) {
|
||||
_lastDirectPathPushSent = now;
|
||||
std::vector<InetAddress> pathsToPush(RR->node->directPaths());
|
||||
std::vector<InetAddress> ma = RR->sa->whoami();
|
||||
pathsToPush.insert(pathsToPush.end(), ma.begin(), ma.end());
|
||||
if (!pathsToPush.empty()) {
|
||||
std::vector<InetAddress>::const_iterator p(pathsToPush.begin());
|
||||
while (p != pathsToPush.end()) {
|
||||
Packet *const outp = new Packet(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS);
|
||||
outp->addSize(2); // leave room for count
|
||||
unsigned int count = 0;
|
||||
while ((p != pathsToPush.end())&&((outp->size() + 24) < 1200)) {
|
||||
uint8_t addressType = 4;
|
||||
switch(p->ss_family) {
|
||||
case AF_INET:
|
||||
break;
|
||||
case AF_INET6:
|
||||
addressType = 6;
|
||||
break;
|
||||
default: // we currently only push IP addresses
|
||||
++p;
|
||||
continue;
|
||||
}
|
||||
|
||||
outp->append((uint8_t)0); // no flags
|
||||
outp->append((uint16_t)0); // no extensions
|
||||
outp->append(addressType);
|
||||
outp->append((uint8_t)((addressType == 4) ? 6 : 18));
|
||||
outp->append(p->rawIpData(),((addressType == 4) ? 4 : 16));
|
||||
outp->append((uint16_t)p->port());
|
||||
|
||||
++count;
|
||||
++p;
|
||||
}
|
||||
if (count) {
|
||||
Metrics::pkt_push_direct_paths_out++;
|
||||
outp->setAt(ZT_PACKET_IDX_PAYLOAD,(uint16_t)count);
|
||||
outp->compress();
|
||||
outp->armor(_key,true,aesKeysIfSupported());
|
||||
Metrics::pkt_push_direct_paths_out++;
|
||||
path->send(RR,tPtr,outp->data(),outp->size(),now);
|
||||
}
|
||||
delete outp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SharedPtr<Path> Peer::getAppropriatePath(int64_t now, bool includeExpired, int32_t flowId)
|
||||
{
|
||||
Mutex::Lock _l(_paths_m);
|
||||
Mutex::Lock _lb(_bond_m);
|
||||
if(_bond && _bond->isReady()) {
|
||||
return _bond->getAppropriatePath(now, flowId);
|
||||
}
|
||||
unsigned int bestPath = ZT_MAX_PEER_NETWORK_PATHS;
|
||||
/**
|
||||
* Send traffic across the highest quality path only. This algorithm will still
|
||||
* use the old path quality metric from protocol version 9.
|
||||
*/
|
||||
long bestPathQuality = 2147483647;
|
||||
for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
|
||||
if (_paths[i].p) {
|
||||
if ((includeExpired)||((now - _paths[i].lr) < ZT_PEER_PATH_EXPIRATION)) {
|
||||
const long q = _paths[i].p->quality(now) / _paths[i].priority;
|
||||
if (q <= bestPathQuality) {
|
||||
bestPathQuality = q;
|
||||
bestPath = i;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bestPath != ZT_MAX_PEER_NETWORK_PATHS) {
|
||||
return _paths[bestPath].p;
|
||||
}
|
||||
return SharedPtr<Path>();
|
||||
}
|
||||
|
||||
void Peer::introduce(void *const tPtr,const int64_t now,const SharedPtr<Peer> &other) const
|
||||
{
|
||||
unsigned int myBestV4ByScope[ZT_INETADDRESS_MAX_SCOPE+1];
|
||||
unsigned int myBestV6ByScope[ZT_INETADDRESS_MAX_SCOPE+1];
|
||||
long myBestV4QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1];
|
||||
long myBestV6QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1];
|
||||
unsigned int theirBestV4ByScope[ZT_INETADDRESS_MAX_SCOPE+1];
|
||||
unsigned int theirBestV6ByScope[ZT_INETADDRESS_MAX_SCOPE+1];
|
||||
long theirBestV4QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1];
|
||||
long theirBestV6QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1];
|
||||
for(int i=0;i<=ZT_INETADDRESS_MAX_SCOPE;++i) {
|
||||
myBestV4ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS;
|
||||
myBestV6ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS;
|
||||
myBestV4QualityByScope[i] = 2147483647;
|
||||
myBestV6QualityByScope[i] = 2147483647;
|
||||
theirBestV4ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS;
|
||||
theirBestV6ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS;
|
||||
theirBestV4QualityByScope[i] = 2147483647;
|
||||
theirBestV6QualityByScope[i] = 2147483647;
|
||||
}
|
||||
|
||||
Mutex::Lock _l1(_paths_m);
|
||||
|
||||
for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
|
||||
if (_paths[i].p) {
|
||||
const long q = _paths[i].p->quality(now) / _paths[i].priority;
|
||||
const unsigned int s = (unsigned int)_paths[i].p->ipScope();
|
||||
switch(_paths[i].p->address().ss_family) {
|
||||
case AF_INET:
|
||||
if (q <= myBestV4QualityByScope[s]) {
|
||||
myBestV4QualityByScope[s] = q;
|
||||
myBestV4ByScope[s] = i;
|
||||
}
|
||||
break;
|
||||
case AF_INET6:
|
||||
if (q <= myBestV6QualityByScope[s]) {
|
||||
myBestV6QualityByScope[s] = q;
|
||||
myBestV6ByScope[s] = i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Mutex::Lock _l2(other->_paths_m);
|
||||
|
||||
for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
|
||||
if (other->_paths[i].p) {
|
||||
const long q = other->_paths[i].p->quality(now) / other->_paths[i].priority;
|
||||
const unsigned int s = (unsigned int)other->_paths[i].p->ipScope();
|
||||
switch(other->_paths[i].p->address().ss_family) {
|
||||
case AF_INET:
|
||||
if (q <= theirBestV4QualityByScope[s]) {
|
||||
theirBestV4QualityByScope[s] = q;
|
||||
theirBestV4ByScope[s] = i;
|
||||
}
|
||||
break;
|
||||
case AF_INET6:
|
||||
if (q <= theirBestV6QualityByScope[s]) {
|
||||
theirBestV6QualityByScope[s] = q;
|
||||
theirBestV6ByScope[s] = i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int mine = ZT_MAX_PEER_NETWORK_PATHS;
|
||||
unsigned int theirs = ZT_MAX_PEER_NETWORK_PATHS;
|
||||
|
||||
for(int s=ZT_INETADDRESS_MAX_SCOPE;s>=0;--s) {
|
||||
if ((myBestV6ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)&&(theirBestV6ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)) {
|
||||
mine = myBestV6ByScope[s];
|
||||
theirs = theirBestV6ByScope[s];
|
||||
break;
|
||||
}
|
||||
if ((myBestV4ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)&&(theirBestV4ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)) {
|
||||
mine = myBestV4ByScope[s];
|
||||
theirs = theirBestV4ByScope[s];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mine != ZT_MAX_PEER_NETWORK_PATHS) {
|
||||
unsigned int alt = (unsigned int)RR->node->prng() & 1; // randomize which hint we send first for black magickal NAT-t reasons
|
||||
const unsigned int completed = alt + 2;
|
||||
while (alt != completed) {
|
||||
if ((alt & 1) == 0) {
|
||||
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS);
|
||||
outp.append((uint8_t)0);
|
||||
other->_id.address().appendTo(outp);
|
||||
outp.append((uint16_t)other->_paths[theirs].p->address().port());
|
||||
if (other->_paths[theirs].p->address().ss_family == AF_INET6) {
|
||||
outp.append((uint8_t)16);
|
||||
outp.append(other->_paths[theirs].p->address().rawIpData(),16);
|
||||
} else {
|
||||
outp.append((uint8_t)4);
|
||||
outp.append(other->_paths[theirs].p->address().rawIpData(),4);
|
||||
}
|
||||
outp.armor(_key,true,aesKeysIfSupported());
|
||||
Metrics::pkt_rendezvous_out++;
|
||||
_paths[mine].p->send(RR,tPtr,outp.data(),outp.size(),now);
|
||||
} else {
|
||||
Packet outp(other->_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS);
|
||||
outp.append((uint8_t)0);
|
||||
_id.address().appendTo(outp);
|
||||
outp.append((uint16_t)_paths[mine].p->address().port());
|
||||
if (_paths[mine].p->address().ss_family == AF_INET6) {
|
||||
outp.append((uint8_t)16);
|
||||
outp.append(_paths[mine].p->address().rawIpData(),16);
|
||||
} else {
|
||||
outp.append((uint8_t)4);
|
||||
outp.append(_paths[mine].p->address().rawIpData(),4);
|
||||
}
|
||||
outp.armor(other->_key,true,other->aesKeysIfSupported());
|
||||
Metrics::pkt_rendezvous_out++;
|
||||
other->_paths[theirs].p->send(RR,tPtr,outp.data(),outp.size(),now);
|
||||
}
|
||||
++alt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Peer::sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now)
|
||||
{
|
||||
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_HELLO);
|
||||
|
||||
outp.append((unsigned char)ZT_PROTO_VERSION);
|
||||
outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR);
|
||||
outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
|
||||
outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
|
||||
outp.append(now);
|
||||
RR->identity.serialize(outp,false);
|
||||
atAddress.serialize(outp);
|
||||
|
||||
outp.append((uint64_t)RR->topology->planetWorldId());
|
||||
outp.append((uint64_t)RR->topology->planetWorldTimestamp());
|
||||
|
||||
const unsigned int startCryptedPortionAt = outp.size();
|
||||
|
||||
std::vector<World> moons(RR->topology->moons());
|
||||
std::vector<uint64_t> moonsWanted(RR->topology->moonsWanted());
|
||||
outp.append((uint16_t)(moons.size() + moonsWanted.size()));
|
||||
for(std::vector<World>::const_iterator m(moons.begin());m!=moons.end();++m) {
|
||||
outp.append((uint8_t)m->type());
|
||||
outp.append((uint64_t)m->id());
|
||||
outp.append((uint64_t)m->timestamp());
|
||||
}
|
||||
for(std::vector<uint64_t>::const_iterator m(moonsWanted.begin());m!=moonsWanted.end();++m) {
|
||||
outp.append((uint8_t)World::TYPE_MOON);
|
||||
outp.append(*m);
|
||||
outp.append((uint64_t)0);
|
||||
}
|
||||
|
||||
outp.cryptField(_key,startCryptedPortionAt,outp.size() - startCryptedPortionAt);
|
||||
|
||||
Metrics::pkt_hello_out++;
|
||||
|
||||
if (atAddress) {
|
||||
outp.armor(_key,false,nullptr); // false == don't encrypt full payload, but add MAC
|
||||
RR->node->expectReplyTo(outp.packetId());
|
||||
RR->node->putPacket(tPtr,RR->node->lowBandwidthModeEnabled() ? localSocket : -1,atAddress,outp.data(),outp.size());
|
||||
} else {
|
||||
RR->node->expectReplyTo(outp.packetId());
|
||||
RR->sw->send(tPtr,outp,false); // false == don't encrypt full payload, but add MAC
|
||||
}
|
||||
}
|
||||
|
||||
void Peer::attemptToContactAt(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now,bool sendFullHello)
|
||||
{
|
||||
if ( (!sendFullHello) && (_vProto >= 5) && (!((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0))) ) {
|
||||
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO);
|
||||
outp.armor(_key,true,aesKeysIfSupported());
|
||||
Metrics::pkt_echo_out++;
|
||||
RR->node->expectReplyTo(outp.packetId());
|
||||
RR->node->putPacket(tPtr,localSocket,atAddress,outp.data(),outp.size());
|
||||
} else {
|
||||
sendHELLO(tPtr,localSocket,atAddress,now);
|
||||
}
|
||||
}
|
||||
|
||||
void Peer::tryMemorizedPath(void *tPtr,int64_t now)
|
||||
{
|
||||
if ((now - _lastTriedMemorizedPath) >= ZT_TRY_MEMORIZED_PATH_INTERVAL) {
|
||||
_lastTriedMemorizedPath = now;
|
||||
InetAddress mp;
|
||||
if (RR->node->externalPathLookup(tPtr,_id.address(),-1,mp)) {
|
||||
attemptToContactAt(tPtr,-1,mp,now,true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Peer::performMultipathStateCheck(void *tPtr, int64_t now)
|
||||
{
|
||||
Mutex::Lock _l(_bond_m);
|
||||
/**
|
||||
* Check for conditions required for multipath bonding and create a bond
|
||||
* if allowed.
|
||||
*/
|
||||
int numAlivePaths = 0;
|
||||
bool atLeastOneNonExpired = false;
|
||||
for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
|
||||
if (_paths[i].p) {
|
||||
if(_paths[i].p->alive(now)) {
|
||||
numAlivePaths++;
|
||||
}
|
||||
if ((now - _paths[i].lr) < ZT_PEER_PATH_EXPIRATION) {
|
||||
atLeastOneNonExpired = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_bond) {
|
||||
if (numAlivePaths == 0 && !atLeastOneNonExpired) {
|
||||
_bond = SharedPtr<Bond>();
|
||||
RR->bc->destroyBond(_id.address().toInt());
|
||||
}
|
||||
return;
|
||||
}
|
||||
_localMultipathSupported = ((numAlivePaths >= 1) && (RR->bc->inUse()) && (ZT_PROTO_VERSION > 9));
|
||||
if (_localMultipathSupported && !_bond) {
|
||||
if (RR->bc) {
|
||||
_bond = RR->bc->createBond(RR, this);
|
||||
/**
|
||||
* Allow new bond to retroactively learn all paths known to this peer
|
||||
*/
|
||||
if (_bond) {
|
||||
for (unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
|
||||
if (_paths[i].p) {
|
||||
_bond->nominatePathToBond(_paths[i].p, now);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int Peer::doPingAndKeepalive(void *tPtr,int64_t now)
|
||||
{
|
||||
unsigned int sent = 0;
|
||||
{
|
||||
Mutex::Lock _l(_paths_m);
|
||||
|
||||
performMultipathStateCheck(tPtr, now);
|
||||
|
||||
const bool sendFullHello = ((now - _lastSentFullHello) >= ZT_PEER_PING_PERIOD);
|
||||
if (sendFullHello) {
|
||||
_lastSentFullHello = now;
|
||||
}
|
||||
|
||||
// Right now we only keep pinging links that have the maximum priority. The
|
||||
// priority is used to track cluster redirections, meaning that when a cluster
|
||||
// redirects us its redirect target links override all other links and we
|
||||
// let those old links expire.
|
||||
long maxPriority = 0;
|
||||
for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
|
||||
if (_paths[i].p) {
|
||||
maxPriority = std::max(_paths[i].priority,maxPriority);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool deletionOccurred = false;
|
||||
for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
|
||||
if (_paths[i].p) {
|
||||
// Clean expired and reduced priority paths
|
||||
if ( ((now - _paths[i].lr) < ZT_PEER_PATH_EXPIRATION) && (_paths[i].priority == maxPriority) ) {
|
||||
if ((sendFullHello)||(_paths[i].p->needsHeartbeat(now))) {
|
||||
attemptToContactAt(tPtr,_paths[i].p->localSocket(),_paths[i].p->address(),now,sendFullHello);
|
||||
_paths[i].p->sent(now);
|
||||
sent |= (_paths[i].p->address().ss_family == AF_INET) ? 0x1 : 0x2;
|
||||
}
|
||||
} else {
|
||||
_paths[i] = _PeerPath();
|
||||
deletionOccurred = true;
|
||||
}
|
||||
}
|
||||
if (!_paths[i].p || deletionOccurred) {
|
||||
for(unsigned int j=i;j<ZT_MAX_PEER_NETWORK_PATHS;++j) {
|
||||
if (_paths[j].p && i != j) {
|
||||
_paths[i] = _paths[j];
|
||||
_paths[j] = _PeerPath();
|
||||
break;
|
||||
}
|
||||
}
|
||||
deletionOccurred = false;
|
||||
}
|
||||
}
|
||||
#ifndef ZT_NO_PEER_METRICS
|
||||
uint16_t alive_path_count_tmp = 0, dead_path_count_tmp = 0;
|
||||
for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
|
||||
if (_paths[i].p) {
|
||||
if (_paths[i].p->alive(now)) {
|
||||
alive_path_count_tmp++;
|
||||
}
|
||||
else {
|
||||
dead_path_count_tmp++;
|
||||
}
|
||||
}
|
||||
}
|
||||
_alive_path_count = alive_path_count_tmp;
|
||||
_dead_path_count = dead_path_count_tmp;
|
||||
#endif
|
||||
}
|
||||
#ifndef ZT_NO_PEER_METRICS
|
||||
_peer_latency.Observe(latency(now));
|
||||
#endif
|
||||
return sent;
|
||||
}
|
||||
|
||||
void Peer::clusterRedirect(void *tPtr,const SharedPtr<Path> &originatingPath,const InetAddress &remoteAddress,const int64_t now)
|
||||
{
|
||||
SharedPtr<Path> np(RR->topology->getPath(originatingPath->localSocket(),remoteAddress));
|
||||
RR->t->peerRedirected(tPtr,0,*this,np);
|
||||
|
||||
attemptToContactAt(tPtr,originatingPath->localSocket(),remoteAddress,now,true);
|
||||
|
||||
{
|
||||
Mutex::Lock _l(_paths_m);
|
||||
|
||||
// New priority is higher than the priority of the originating path (if known)
|
||||
long newPriority = 1;
|
||||
for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
|
||||
if (_paths[i].p) {
|
||||
if (_paths[i].p == originatingPath) {
|
||||
newPriority = _paths[i].priority;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
newPriority += 2;
|
||||
|
||||
// Erase any paths with lower priority than this one or that are duplicate
|
||||
// IPs and add this path.
|
||||
unsigned int j = 0;
|
||||
for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
|
||||
if (_paths[i].p) {
|
||||
if ((_paths[i].priority >= newPriority)&&(!_paths[i].p->address().ipsEqual2(remoteAddress))) {
|
||||
if (i != j) {
|
||||
_paths[j] = _paths[i];
|
||||
}
|
||||
++j;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (j < ZT_MAX_PEER_NETWORK_PATHS) {
|
||||
_paths[j].lr = now;
|
||||
_paths[j].p = np;
|
||||
_paths[j].priority = newPriority;
|
||||
++j;
|
||||
while (j < ZT_MAX_PEER_NETWORK_PATHS) {
|
||||
_paths[j].lr = 0;
|
||||
_paths[j].p.zero();
|
||||
_paths[j].priority = 1;
|
||||
++j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Peer::resetWithinScope(void *tPtr,InetAddress::IpScope scope,int inetAddressFamily,int64_t now)
|
||||
{
|
||||
Mutex::Lock _l(_paths_m);
|
||||
for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
|
||||
if (_paths[i].p) {
|
||||
if ((_paths[i].p->address().ss_family == inetAddressFamily)&&(_paths[i].p->ipScope() == scope)) {
|
||||
attemptToContactAt(tPtr,_paths[i].p->localSocket(),_paths[i].p->address(),now,false);
|
||||
_paths[i].p->sent(now);
|
||||
_paths[i].lr = 0; // path will not be used unless it speaks again
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Peer::recordOutgoingPacket(const SharedPtr<Path> &path, const uint64_t packetId,
|
||||
uint16_t payloadLength, const Packet::Verb verb, const int32_t flowId, int64_t now)
|
||||
{
|
||||
#ifndef ZT_NO_PEER_METRICS
|
||||
_outgoing_packet++;
|
||||
#endif
|
||||
if (_localMultipathSupported && _bond) {
|
||||
_bond->recordOutgoingPacket(path, packetId, payloadLength, verb, flowId, now);
|
||||
}
|
||||
}
|
||||
|
||||
void Peer::recordIncomingInvalidPacket(const SharedPtr<Path>& path)
|
||||
{
|
||||
#ifndef ZT_NO_PEER_METRICS
|
||||
_packet_errors++;
|
||||
#endif
|
||||
if (_localMultipathSupported && _bond) {
|
||||
_bond->recordIncomingInvalidPacket(path);
|
||||
}
|
||||
}
|
||||
|
||||
void Peer::recordIncomingPacket(const SharedPtr<Path> &path, const uint64_t packetId,
|
||||
uint16_t payloadLength, const Packet::Verb verb, const int32_t flowId, int64_t now)
|
||||
{
|
||||
if (_localMultipathSupported && _bond) {
|
||||
_bond->recordIncomingPacket(path, packetId, payloadLength, verb, flowId, now);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
+702
@@ -0,0 +1,702 @@
|
||||
/*
|
||||
* Copyright (c)2013-2020 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_PEER_HPP
|
||||
#define ZT_PEER_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
#include "../include/ZeroTierOne.h"
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Node.hpp"
|
||||
#include "Path.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "Packet.hpp"
|
||||
#include "SharedPtr.hpp"
|
||||
#include "AtomicCounter.hpp"
|
||||
#include "Hashtable.hpp"
|
||||
#include "Mutex.hpp"
|
||||
#include "Bond.hpp"
|
||||
#include "AES.hpp"
|
||||
#include "Metrics.hpp"
|
||||
|
||||
#define ZT_PEER_MAX_SERIALIZED_STATE_SIZE (sizeof(Peer) + 32 + (sizeof(Path) * 2))
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* Peer on P2P Network (virtual layer 1)
|
||||
*/
|
||||
class Peer
|
||||
{
|
||||
friend class SharedPtr<Peer>;
|
||||
friend class SharedPtr<Bond>;
|
||||
friend class Switch;
|
||||
friend class Bond;
|
||||
|
||||
private:
|
||||
Peer() = delete; // disabled to prevent bugs -- should not be constructed uninitialized
|
||||
|
||||
public:
|
||||
~Peer() {
|
||||
Utils::burn(_key,sizeof(_key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new peer
|
||||
*
|
||||
* @param renv Runtime environment
|
||||
* @param myIdentity Identity of THIS node (for key agreement)
|
||||
* @param peerIdentity Identity of peer
|
||||
* @throws std::runtime_error Key agreement with peer's identity failed
|
||||
*/
|
||||
Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity);
|
||||
|
||||
/**
|
||||
* @return This peer's ZT address (short for identity().address())
|
||||
*/
|
||||
inline const Address &address() const { return _id.address(); }
|
||||
|
||||
/**
|
||||
* @return This peer's identity
|
||||
*/
|
||||
inline const Identity &identity() const { return _id; }
|
||||
|
||||
/**
|
||||
* Log receipt of an authenticated packet
|
||||
*
|
||||
* This is called by the decode pipe when a packet is proven to be authentic
|
||||
* and appears to be valid.
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param path Path over which packet was received
|
||||
* @param hops ZeroTier (not IP) hops
|
||||
* @param packetId Packet ID
|
||||
* @param verb Packet verb
|
||||
* @param inRePacketId Packet ID in reply to (default: none)
|
||||
* @param inReVerb Verb in reply to (for OK/ERROR, default: VERB_NOP)
|
||||
* @param trustEstablished If true, some form of non-trivial trust (like allowed in network) has been established
|
||||
* @param networkId Network ID if this pertains to a network, or 0 otherwise
|
||||
*/
|
||||
void received(
|
||||
void *tPtr,
|
||||
const SharedPtr<Path> &path,
|
||||
const unsigned int hops,
|
||||
const uint64_t packetId,
|
||||
const unsigned int payloadLength,
|
||||
const Packet::Verb verb,
|
||||
const uint64_t inRePacketId,
|
||||
const Packet::Verb inReVerb,
|
||||
const bool trustEstablished,
|
||||
const uint64_t networkId,
|
||||
const int32_t flowId);
|
||||
|
||||
/**
|
||||
* Check whether we have an active path to this peer via the given address
|
||||
*
|
||||
* @param now Current time
|
||||
* @param addr Remote address
|
||||
* @return True if we have an active path to this destination
|
||||
*/
|
||||
inline bool hasActivePathTo(int64_t now,const InetAddress &addr) const
|
||||
{
|
||||
Mutex::Lock _l(_paths_m);
|
||||
for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
|
||||
if (_paths[i].p) {
|
||||
if (((now - _paths[i].lr) < ZT_PEER_PATH_EXPIRATION)&&(_paths[i].p->address() == addr)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send via best direct path
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param data Packet data
|
||||
* @param len Packet length
|
||||
* @param now Current time
|
||||
* @param force If true, send even if path is not alive
|
||||
* @return True if we actually sent something
|
||||
*/
|
||||
inline bool sendDirect(void *tPtr,const void *data,unsigned int len,int64_t now,bool force)
|
||||
{
|
||||
SharedPtr<Path> bp(getAppropriatePath(now,force));
|
||||
if (bp) {
|
||||
return bp->send(RR,tPtr,data,len,now);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record incoming packets to
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param path Path over which packet was received
|
||||
* @param packetId Packet ID
|
||||
* @param payloadLength Length of packet data payload
|
||||
* @param verb Packet verb
|
||||
* @param flowId Flow ID
|
||||
* @param now Current time
|
||||
*/
|
||||
void recordIncomingPacket(const SharedPtr<Path> &path, const uint64_t packetId,
|
||||
uint16_t payloadLength, const Packet::Verb verb, const int32_t flowId, int64_t now);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param path Path over which packet is being sent
|
||||
* @param packetId Packet ID
|
||||
* @param payloadLength Length of packet data payload
|
||||
* @param verb Packet verb
|
||||
* @param flowId Flow ID
|
||||
* @param now Current time
|
||||
*/
|
||||
void recordOutgoingPacket(const SharedPtr<Path> &path, const uint64_t packetId,
|
||||
uint16_t payloadLength, const Packet::Verb verb, const int32_t flowId, int64_t now);
|
||||
|
||||
/**
|
||||
* Record an invalid incoming packet. This packet failed
|
||||
* MAC/compression/cipher checks and will now contribute to a
|
||||
* Packet Error Ratio (PER).
|
||||
*
|
||||
* @param path Path over which packet was received
|
||||
*/
|
||||
void recordIncomingInvalidPacket(const SharedPtr<Path>& path);
|
||||
|
||||
/**
|
||||
* Get the most appropriate direct path based on current multipath and QoS configuration
|
||||
*
|
||||
* @param now Current time
|
||||
* @param includeExpired If true, include even expired paths
|
||||
* @return Best current path or NULL if none
|
||||
*/
|
||||
SharedPtr<Path> getAppropriatePath(int64_t now, bool includeExpired, int32_t flowId = -1);
|
||||
|
||||
/**
|
||||
* Send VERB_RENDEZVOUS to this and another peer via the best common IP scope and path
|
||||
*/
|
||||
void introduce(void *const tPtr,const int64_t now,const SharedPtr<Peer> &other) const;
|
||||
|
||||
/**
|
||||
* Send a HELLO to this peer at a specified physical address
|
||||
*
|
||||
* No statistics or sent times are updated here.
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param localSocket Local source socket
|
||||
* @param atAddress Destination address
|
||||
* @param now Current time
|
||||
*/
|
||||
void sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now);
|
||||
|
||||
/**
|
||||
* Send ECHO (or HELLO for older peers) to this peer at the given address
|
||||
*
|
||||
* No statistics or sent times are updated here.
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param localSocket Local source socket
|
||||
* @param atAddress Destination address
|
||||
* @param now Current time
|
||||
* @param sendFullHello If true, always send a full HELLO instead of just an ECHO
|
||||
*/
|
||||
void attemptToContactAt(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now,bool sendFullHello);
|
||||
|
||||
/**
|
||||
* Try a memorized or statically defined path if any are known
|
||||
*
|
||||
* Under the hood this is done periodically based on ZT_TRY_MEMORIZED_PATH_INTERVAL.
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param now Current time
|
||||
*/
|
||||
void tryMemorizedPath(void *tPtr,int64_t now);
|
||||
|
||||
/**
|
||||
* A check to be performed periodically which determines whether multipath communication is
|
||||
* possible with this peer. This check should be performed early in the life-cycle of the peer
|
||||
* as well as during the process of learning new paths.
|
||||
*/
|
||||
void performMultipathStateCheck(void *tPtr, int64_t now);
|
||||
|
||||
/**
|
||||
* Send pings or keepalives depending on configured timeouts
|
||||
*
|
||||
* This also cleans up some internal data structures. It's called periodically from Node.
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param now Current time
|
||||
* @param inetAddressFamily Keep this address family alive, or -1 for any
|
||||
* @return 0 if nothing sent or bit mask: bit 0x1 if IPv4 sent, bit 0x2 if IPv6 sent (0x3 means both sent)
|
||||
*/
|
||||
unsigned int doPingAndKeepalive(void *tPtr,int64_t now);
|
||||
|
||||
/**
|
||||
* Process a cluster redirect sent by this peer
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param originatingPath Path from which redirect originated
|
||||
* @param remoteAddress Remote address
|
||||
* @param now Current time
|
||||
*/
|
||||
void clusterRedirect(void *tPtr,const SharedPtr<Path> &originatingPath,const InetAddress &remoteAddress,const int64_t now);
|
||||
|
||||
/**
|
||||
* Reset paths within a given IP scope and address family
|
||||
*
|
||||
* Resetting a path involves sending an ECHO to it and then deactivating
|
||||
* it until or unless it responds. This is done when we detect a change
|
||||
* to our external IP or another system change that might invalidate
|
||||
* many or all current paths.
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param scope IP scope
|
||||
* @param inetAddressFamily Family e.g. AF_INET
|
||||
* @param now Current time
|
||||
*/
|
||||
void resetWithinScope(void *tPtr,InetAddress::IpScope scope,int inetAddressFamily,int64_t now);
|
||||
|
||||
/**
|
||||
* @param now Current time
|
||||
* @return All known paths to this peer
|
||||
*/
|
||||
inline std::vector< SharedPtr<Path> > paths(const int64_t now) const
|
||||
{
|
||||
std::vector< SharedPtr<Path> > pp;
|
||||
Mutex::Lock _l(_paths_m);
|
||||
for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
|
||||
if (!_paths[i].p) {
|
||||
break;
|
||||
}
|
||||
pp.push_back(_paths[i].p);
|
||||
}
|
||||
return pp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Time of last receive of anything, whether direct or relayed
|
||||
*/
|
||||
inline int64_t lastReceive() const { return _lastReceive; }
|
||||
|
||||
/**
|
||||
* @return True if we've heard from this peer in less than ZT_PEER_ACTIVITY_TIMEOUT
|
||||
*/
|
||||
inline bool isAlive(const int64_t now) const { return ((now - _lastReceive) < ZT_PEER_ACTIVITY_TIMEOUT); }
|
||||
|
||||
/**
|
||||
* @return True if this peer has sent us real network traffic recently
|
||||
*/
|
||||
inline int64_t isActive(int64_t now) const { return ((now - _lastNontrivialReceive) < ZT_PEER_ACTIVITY_TIMEOUT); }
|
||||
|
||||
inline int64_t lastSentFullHello() { return _lastSentFullHello; }
|
||||
|
||||
/**
|
||||
* @return Latency in milliseconds of best/aggregate path or 0xffff if unknown / no paths
|
||||
*/
|
||||
inline unsigned int latency(const int64_t now)
|
||||
{
|
||||
if (_localMultipathSupported) {
|
||||
return (int)_lastComputedAggregateMeanLatency;
|
||||
} else {
|
||||
SharedPtr<Path> bp(getAppropriatePath(now,false));
|
||||
if (bp) {
|
||||
return (unsigned int)bp->latency();
|
||||
}
|
||||
return 0xffff;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This computes a quality score for relays and root servers
|
||||
*
|
||||
* If we haven't heard anything from these in ZT_PEER_ACTIVITY_TIMEOUT, they
|
||||
* receive the worst possible quality (max unsigned int). Otherwise the
|
||||
* quality is a product of latency and the number of potential missed
|
||||
* pings. This causes roots and relays to switch over a bit faster if they
|
||||
* fail.
|
||||
*
|
||||
* @return Relay quality score computed from latency and other factors, lower is better
|
||||
*/
|
||||
inline unsigned int relayQuality(const int64_t now)
|
||||
{
|
||||
const uint64_t tsr = now - _lastReceive;
|
||||
if (tsr >= ZT_PEER_ACTIVITY_TIMEOUT) {
|
||||
return (~(unsigned int)0);
|
||||
}
|
||||
unsigned int l = latency(now);
|
||||
if (!l) {
|
||||
l = 0xffff;
|
||||
}
|
||||
return (l * (((unsigned int)tsr / (ZT_PEER_PING_PERIOD + 1000)) + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 256-bit secret symmetric encryption key
|
||||
*/
|
||||
inline const unsigned char *key() const { return _key; }
|
||||
|
||||
/**
|
||||
* Set the currently known remote version of this peer's client
|
||||
*
|
||||
* @param vproto Protocol version
|
||||
* @param vmaj Major version
|
||||
* @param vmin Minor version
|
||||
* @param vrev Revision
|
||||
*/
|
||||
inline void setRemoteVersion(unsigned int vproto,unsigned int vmaj,unsigned int vmin,unsigned int vrev)
|
||||
{
|
||||
_vProto = (uint16_t)vproto;
|
||||
_vMajor = (uint16_t)vmaj;
|
||||
_vMinor = (uint16_t)vmin;
|
||||
_vRevision = (uint16_t)vrev;
|
||||
}
|
||||
|
||||
inline unsigned int remoteVersionProtocol() const { return _vProto; }
|
||||
inline unsigned int remoteVersionMajor() const { return _vMajor; }
|
||||
inline unsigned int remoteVersionMinor() const { return _vMinor; }
|
||||
inline unsigned int remoteVersionRevision() const { return _vRevision; }
|
||||
|
||||
inline bool remoteVersionKnown() const { return ((_vMajor > 0)||(_vMinor > 0)||(_vRevision > 0)); }
|
||||
|
||||
/**
|
||||
* @return True if peer has received a trust established packet (e.g. common network membership) in the past ZT_TRUST_EXPIRATION ms
|
||||
*/
|
||||
inline bool trustEstablished(const int64_t now) const { return ((now - _lastTrustEstablishedPacketReceived) < ZT_TRUST_EXPIRATION); }
|
||||
|
||||
/**
|
||||
* Rate limit gate for VERB_PUSH_DIRECT_PATHS
|
||||
*/
|
||||
inline bool rateGatePushDirectPaths(const int64_t now)
|
||||
{
|
||||
if ((now - _lastDirectPathPushReceive) <= ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME) {
|
||||
++_directPathPushCutoffCount;
|
||||
} else {
|
||||
_directPathPushCutoffCount = 0;
|
||||
}
|
||||
_lastDirectPathPushReceive = now;
|
||||
return (_directPathPushCutoffCount < ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rate limit gate for VERB_NETWORK_CREDENTIALS
|
||||
*/
|
||||
inline bool rateGateCredentialsReceived(const int64_t now)
|
||||
{
|
||||
if ((now - _lastCredentialsReceived) >= ZT_PEER_CREDENTIALS_RATE_LIMIT) {
|
||||
_lastCredentialsReceived = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rate limit gate for sending of ERROR_NEED_MEMBERSHIP_CERTIFICATE
|
||||
*/
|
||||
inline bool rateGateRequestCredentials(const int64_t now)
|
||||
{
|
||||
if ((now - _lastCredentialRequestSent) >= ZT_PEER_GENERAL_RATE_LIMIT) {
|
||||
_lastCredentialRequestSent = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rate limit gate for inbound WHOIS requests
|
||||
*/
|
||||
inline bool rateGateInboundWhoisRequest(const int64_t now)
|
||||
{
|
||||
if ((now - _lastWhoisRequestReceived) >= ZT_PEER_WHOIS_RATE_LIMIT) {
|
||||
_lastWhoisRequestReceived = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* See definition in Bond
|
||||
*/
|
||||
inline bool rateGateQoS(int64_t now, SharedPtr<Path>& path)
|
||||
{
|
||||
Mutex::Lock _l(_bond_m);
|
||||
if(_bond) {
|
||||
return _bond->rateGateQoS(now, path);
|
||||
}
|
||||
return false; // Default behavior. If there is no bond, we drop these
|
||||
}
|
||||
|
||||
/**
|
||||
* See definition in Bond
|
||||
*/
|
||||
void receivedQoS(const SharedPtr<Path>& path, int64_t now, int count, uint64_t* rx_id, uint16_t* rx_ts)
|
||||
{
|
||||
Mutex::Lock _l(_bond_m);
|
||||
if(_bond) {
|
||||
_bond->receivedQoS(path, now, count, rx_id, rx_ts);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See definition in Bond
|
||||
*/
|
||||
void processIncomingPathNegotiationRequest(uint64_t now, SharedPtr<Path>& path, int16_t remoteUtility)
|
||||
{
|
||||
Mutex::Lock _l(_bond_m);
|
||||
if(_bond) {
|
||||
_bond->processIncomingPathNegotiationRequest(now, path, remoteUtility);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See definition in Bond
|
||||
*/
|
||||
inline bool rateGatePathNegotiation(int64_t now, SharedPtr<Path>& path)
|
||||
{
|
||||
Mutex::Lock _l(_bond_m);
|
||||
if(_bond) {
|
||||
return _bond->rateGatePathNegotiation(now, path);
|
||||
}
|
||||
return false; // Default behavior. If there is no bond, we drop these
|
||||
}
|
||||
|
||||
/**
|
||||
* See definition in Bond
|
||||
*/
|
||||
bool flowHashingSupported()
|
||||
{
|
||||
Mutex::Lock _l(_bond_m);
|
||||
if(_bond) {
|
||||
return _bond->flowHashingSupported();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize a peer for storage in local cache
|
||||
*
|
||||
* This does not serialize everything, just non-ephemeral information.
|
||||
*/
|
||||
template<unsigned int C>
|
||||
inline void serializeForCache(Buffer<C> &b) const
|
||||
{
|
||||
b.append((uint8_t)2);
|
||||
|
||||
_id.serialize(b);
|
||||
|
||||
b.append((uint16_t)_vProto);
|
||||
b.append((uint16_t)_vMajor);
|
||||
b.append((uint16_t)_vMinor);
|
||||
b.append((uint16_t)_vRevision);
|
||||
|
||||
{
|
||||
Mutex::Lock _l(_paths_m);
|
||||
unsigned int pc = 0;
|
||||
for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
|
||||
if (_paths[i].p) {
|
||||
++pc;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
b.append((uint16_t)pc);
|
||||
for(unsigned int i=0;i<pc;++i) {
|
||||
_paths[i].p->address().serialize(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned int C>
|
||||
inline static SharedPtr<Peer> deserializeFromCache(int64_t now,void *tPtr,Buffer<C> &b,const RuntimeEnvironment *renv)
|
||||
{
|
||||
try {
|
||||
unsigned int ptr = 0;
|
||||
if (b[ptr++] != 2) {
|
||||
return SharedPtr<Peer>();
|
||||
}
|
||||
|
||||
Identity id;
|
||||
ptr += id.deserialize(b,ptr);
|
||||
if (!id) {
|
||||
return SharedPtr<Peer>();
|
||||
}
|
||||
|
||||
SharedPtr<Peer> p(new Peer(renv,renv->identity,id));
|
||||
|
||||
p->_vProto = b.template at<uint16_t>(ptr);
|
||||
ptr += 2;
|
||||
p->_vMajor = b.template at<uint16_t>(ptr);
|
||||
ptr += 2;
|
||||
p->_vMinor = b.template at<uint16_t>(ptr);
|
||||
ptr += 2;
|
||||
p->_vRevision = b.template at<uint16_t>(ptr);
|
||||
ptr += 2;
|
||||
|
||||
// When we deserialize from the cache we don't actually restore paths. We
|
||||
// just try them and then re-learn them if they happen to still be up.
|
||||
// Paths are fairly ephemeral in the real world in most cases.
|
||||
const unsigned int tryPathCount = b.template at<uint16_t>(ptr);
|
||||
ptr += 2;
|
||||
for(unsigned int i=0;i<tryPathCount;++i) {
|
||||
InetAddress inaddr;
|
||||
try {
|
||||
ptr += inaddr.deserialize(b,ptr);
|
||||
if (inaddr) {
|
||||
p->attemptToContactAt(tPtr,-1,inaddr,now,true);
|
||||
}
|
||||
} catch ( ... ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
} catch ( ... ) {
|
||||
return SharedPtr<Peer>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The bonding policy used to reach this peer
|
||||
*/
|
||||
SharedPtr<Bond> bond() { return _bond; }
|
||||
|
||||
/**
|
||||
* @return The bonding policy used to reach this peer
|
||||
*/
|
||||
inline int8_t bondingPolicy() {
|
||||
Mutex::Lock _l(_bond_m);
|
||||
if (_bond) {
|
||||
return _bond->policy();
|
||||
}
|
||||
return ZT_BOND_POLICY_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of links in this bond which are considered alive
|
||||
*/
|
||||
inline uint8_t getNumAliveLinks() {
|
||||
Mutex::Lock _l(_paths_m);
|
||||
if (_bond) {
|
||||
return _bond->getNumAliveLinks();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of links in this bond
|
||||
*/
|
||||
inline uint8_t getNumTotalLinks() {
|
||||
Mutex::Lock _l(_paths_m);
|
||||
if (_bond) {
|
||||
return _bond->getNumTotalLinks();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//inline const AES *aesKeysIfSupported() const
|
||||
//{ return (const AES *)0; }
|
||||
|
||||
inline const AES *aesKeysIfSupported() const
|
||||
{ return (_vProto >= 12) ? _aesKeys : (const AES *)0; }
|
||||
|
||||
inline const AES *aesKeys() const
|
||||
{ return _aesKeys; }
|
||||
|
||||
private:
|
||||
struct _PeerPath
|
||||
{
|
||||
_PeerPath() : lr(0),p(),priority(1) {}
|
||||
int64_t lr; // time of last valid ZeroTier packet
|
||||
SharedPtr<Path> p;
|
||||
long priority; // >= 1, higher is better
|
||||
};
|
||||
|
||||
uint8_t _key[ZT_SYMMETRIC_KEY_SIZE];
|
||||
AES _aesKeys[2];
|
||||
|
||||
const RuntimeEnvironment *RR;
|
||||
|
||||
int64_t _lastReceive; // direct or indirect
|
||||
int64_t _lastNontrivialReceive; // frames, things like netconf, etc.
|
||||
int64_t _lastTriedMemorizedPath;
|
||||
int64_t _lastDirectPathPushSent;
|
||||
int64_t _lastDirectPathPushReceive;
|
||||
int64_t _lastCredentialRequestSent;
|
||||
int64_t _lastWhoisRequestReceived;
|
||||
int64_t _lastCredentialsReceived;
|
||||
int64_t _lastTrustEstablishedPacketReceived;
|
||||
int64_t _lastSentFullHello;
|
||||
int64_t _lastEchoCheck;
|
||||
|
||||
unsigned char _freeRandomByte;
|
||||
|
||||
uint16_t _vProto;
|
||||
uint16_t _vMajor;
|
||||
uint16_t _vMinor;
|
||||
uint16_t _vRevision;
|
||||
|
||||
std::list< std::pair< Path *, int64_t > > _lastTriedPath;
|
||||
Mutex _lastTriedPath_m;
|
||||
|
||||
_PeerPath _paths[ZT_MAX_PEER_NETWORK_PATHS];
|
||||
Mutex _paths_m;
|
||||
Mutex _bond_m;
|
||||
|
||||
bool _isLeaf;
|
||||
|
||||
Identity _id;
|
||||
|
||||
unsigned int _directPathPushCutoffCount;
|
||||
unsigned int _echoRequestCutoffCount;
|
||||
|
||||
AtomicCounter __refCount;
|
||||
|
||||
bool _localMultipathSupported;
|
||||
|
||||
volatile bool _shouldCollectPathStatistics;
|
||||
|
||||
int32_t _lastComputedAggregateMeanLatency;
|
||||
|
||||
SharedPtr<Bond> _bond;
|
||||
|
||||
#ifndef ZT_NO_PEER_METRICS
|
||||
prometheus::Histogram<uint64_t> &_peer_latency;
|
||||
prometheus::simpleapi::gauge_metric_t _alive_path_count;
|
||||
prometheus::simpleapi::gauge_metric_t _dead_path_count;
|
||||
prometheus::simpleapi::counter_metric_t _incoming_packet;
|
||||
prometheus::simpleapi::counter_metric_t _outgoing_packet;
|
||||
prometheus::simpleapi::counter_metric_t _packet_errors;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
// Add a swap() for shared ptr's to peers to speed up peer sorts
|
||||
namespace std {
|
||||
template<>
|
||||
inline void swap(ZeroTier::SharedPtr<ZeroTier::Peer> &a,ZeroTier::SharedPtr<ZeroTier::Peer> &b)
|
||||
{
|
||||
a.swap(b);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,525 @@
|
||||
/*
|
||||
20080912
|
||||
D. J. Bernstein
|
||||
Public domain.
|
||||
*/
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Poly1305.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
#pragma warning(disable: 4146)
|
||||
#endif
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
namespace {
|
||||
|
||||
typedef struct poly1305_context {
|
||||
size_t aligner;
|
||||
unsigned char opaque[136];
|
||||
} poly1305_context;
|
||||
|
||||
#if (defined(_MSC_VER) || defined(__GNUC__)) && (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64))
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// 128-bit implementation for MSC and GCC from Poly1305-donna
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <intrin.h>
|
||||
|
||||
typedef struct uint128_t {
|
||||
unsigned long long lo;
|
||||
unsigned long long hi;
|
||||
} uint128_t;
|
||||
|
||||
#define MUL(out, x, y) out.lo = _umul128((x), (y), &out.hi)
|
||||
#define ADD(out, in) { unsigned long long t = out.lo; out.lo += in.lo; out.hi += (out.lo < t) + in.hi; }
|
||||
#define ADDLO(out, in) { unsigned long long t = out.lo; out.lo += in; out.hi += (out.lo < t); }
|
||||
#define SHR(in, shift) (__shiftright128(in.lo, in.hi, (shift)))
|
||||
#define LO(in) (in.lo)
|
||||
|
||||
// #define POLY1305_NOINLINE __declspec(noinline)
|
||||
#elif defined(__GNUC__)
|
||||
#if defined(__SIZEOF_INT128__)
|
||||
typedef unsigned __int128 uint128_t;
|
||||
#else
|
||||
typedef unsigned uint128_t __attribute__((mode(TI)));
|
||||
#endif
|
||||
|
||||
#define MUL(out, x, y) out = ((uint128_t)x * y)
|
||||
#define ADD(out, in) out += in
|
||||
#define ADDLO(out, in) out += in
|
||||
#define SHR(in, shift) (unsigned long long)(in >> (shift))
|
||||
#define LO(in) (unsigned long long)(in)
|
||||
|
||||
// #define POLY1305_NOINLINE __attribute__((noinline))
|
||||
#endif
|
||||
|
||||
#define poly1305_block_size 16
|
||||
|
||||
/* 17 + sizeof(size_t) + 8*sizeof(unsigned long long) */
|
||||
typedef struct poly1305_state_internal_t {
|
||||
unsigned long long r[3];
|
||||
unsigned long long h[3];
|
||||
unsigned long long pad[2];
|
||||
size_t leftover;
|
||||
unsigned char buffer[poly1305_block_size];
|
||||
unsigned char final;
|
||||
} poly1305_state_internal_t;
|
||||
|
||||
#if defined(ZT_NO_TYPE_PUNNING) || (__BYTE_ORDER != __LITTLE_ENDIAN)
|
||||
static inline unsigned long long U8TO64(const unsigned char *p)
|
||||
{
|
||||
return
|
||||
(((unsigned long long)(p[0] & 0xff) ) |
|
||||
((unsigned long long)(p[1] & 0xff) << 8) |
|
||||
((unsigned long long)(p[2] & 0xff) << 16) |
|
||||
((unsigned long long)(p[3] & 0xff) << 24) |
|
||||
((unsigned long long)(p[4] & 0xff) << 32) |
|
||||
((unsigned long long)(p[5] & 0xff) << 40) |
|
||||
((unsigned long long)(p[6] & 0xff) << 48) |
|
||||
((unsigned long long)(p[7] & 0xff) << 56));
|
||||
}
|
||||
#else
|
||||
#define U8TO64(p) (*reinterpret_cast<const unsigned long long *>(p))
|
||||
#endif
|
||||
|
||||
#if defined(ZT_NO_TYPE_PUNNING) || (__BYTE_ORDER != __LITTLE_ENDIAN)
|
||||
static inline void U64TO8(unsigned char *p, unsigned long long v)
|
||||
{
|
||||
p[0] = (v ) & 0xff;
|
||||
p[1] = (v >> 8) & 0xff;
|
||||
p[2] = (v >> 16) & 0xff;
|
||||
p[3] = (v >> 24) & 0xff;
|
||||
p[4] = (v >> 32) & 0xff;
|
||||
p[5] = (v >> 40) & 0xff;
|
||||
p[6] = (v >> 48) & 0xff;
|
||||
p[7] = (v >> 56) & 0xff;
|
||||
}
|
||||
#else
|
||||
#define U64TO8(p,v) ((*reinterpret_cast<unsigned long long *>(p)) = (v))
|
||||
#endif
|
||||
|
||||
static inline void poly1305_init(poly1305_context *ctx, const unsigned char key[32]) {
|
||||
poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;
|
||||
unsigned long long t0,t1;
|
||||
|
||||
/* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
|
||||
t0 = U8TO64(&key[0]);
|
||||
t1 = U8TO64(&key[8]);
|
||||
|
||||
st->r[0] = ( t0 ) & 0xffc0fffffff;
|
||||
st->r[1] = ((t0 >> 44) | (t1 << 20)) & 0xfffffc0ffff;
|
||||
st->r[2] = ((t1 >> 24) ) & 0x00ffffffc0f;
|
||||
|
||||
/* h = 0 */
|
||||
st->h[0] = 0;
|
||||
st->h[1] = 0;
|
||||
st->h[2] = 0;
|
||||
|
||||
/* save pad for later */
|
||||
st->pad[0] = U8TO64(&key[16]);
|
||||
st->pad[1] = U8TO64(&key[24]);
|
||||
|
||||
st->leftover = 0;
|
||||
st->final = 0;
|
||||
}
|
||||
|
||||
static inline void poly1305_blocks(poly1305_state_internal_t *st, const unsigned char *m, size_t bytes) {
|
||||
const unsigned long long hibit = (st->final) ? 0 : ((unsigned long long)1 << 40); /* 1 << 128 */
|
||||
unsigned long long r0,r1,r2;
|
||||
unsigned long long s1,s2;
|
||||
unsigned long long h0,h1,h2;
|
||||
unsigned long long c;
|
||||
uint128_t d0,d1,d2,d;
|
||||
|
||||
r0 = st->r[0];
|
||||
r1 = st->r[1];
|
||||
r2 = st->r[2];
|
||||
|
||||
h0 = st->h[0];
|
||||
h1 = st->h[1];
|
||||
h2 = st->h[2];
|
||||
|
||||
s1 = r1 * (5 << 2);
|
||||
s2 = r2 * (5 << 2);
|
||||
|
||||
while (bytes >= poly1305_block_size) {
|
||||
unsigned long long t0,t1;
|
||||
|
||||
/* h += m[i] */
|
||||
t0 = U8TO64(&m[0]);
|
||||
t1 = U8TO64(&m[8]);
|
||||
|
||||
h0 += (( t0 ) & 0xfffffffffff);
|
||||
h1 += (((t0 >> 44) | (t1 << 20)) & 0xfffffffffff);
|
||||
h2 += (((t1 >> 24) ) & 0x3ffffffffff) | hibit;
|
||||
|
||||
/* h *= r */
|
||||
MUL(d0, h0, r0); MUL(d, h1, s2); ADD(d0, d); MUL(d, h2, s1); ADD(d0, d);
|
||||
MUL(d1, h0, r1); MUL(d, h1, r0); ADD(d1, d); MUL(d, h2, s2); ADD(d1, d);
|
||||
MUL(d2, h0, r2); MUL(d, h1, r1); ADD(d2, d); MUL(d, h2, r0); ADD(d2, d);
|
||||
|
||||
/* (partial) h %= p */
|
||||
c = SHR(d0, 44); h0 = LO(d0) & 0xfffffffffff;
|
||||
ADDLO(d1, c); c = SHR(d1, 44); h1 = LO(d1) & 0xfffffffffff;
|
||||
ADDLO(d2, c); c = SHR(d2, 42); h2 = LO(d2) & 0x3ffffffffff;
|
||||
h0 += c * 5; c = (h0 >> 44); h0 = h0 & 0xfffffffffff;
|
||||
h1 += c;
|
||||
|
||||
m += poly1305_block_size;
|
||||
bytes -= poly1305_block_size;
|
||||
}
|
||||
|
||||
st->h[0] = h0;
|
||||
st->h[1] = h1;
|
||||
st->h[2] = h2;
|
||||
}
|
||||
|
||||
static inline void poly1305_finish(poly1305_context *ctx, unsigned char mac[16]) {
|
||||
poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;
|
||||
unsigned long long h0,h1,h2,c;
|
||||
unsigned long long g0,g1,g2;
|
||||
unsigned long long t0,t1;
|
||||
|
||||
/* process the remaining block */
|
||||
if (st->leftover) {
|
||||
size_t i = st->leftover;
|
||||
st->buffer[i] = 1;
|
||||
for (i = i + 1; i < poly1305_block_size; i++) {
|
||||
st->buffer[i] = 0;
|
||||
}
|
||||
st->final = 1;
|
||||
poly1305_blocks(st, st->buffer, poly1305_block_size);
|
||||
}
|
||||
|
||||
/* fully carry h */
|
||||
h0 = st->h[0];
|
||||
h1 = st->h[1];
|
||||
h2 = st->h[2];
|
||||
|
||||
c = (h1 >> 44); h1 &= 0xfffffffffff;
|
||||
h2 += c; c = (h2 >> 42); h2 &= 0x3ffffffffff;
|
||||
h0 += c * 5; c = (h0 >> 44); h0 &= 0xfffffffffff;
|
||||
h1 += c; c = (h1 >> 44); h1 &= 0xfffffffffff;
|
||||
h2 += c; c = (h2 >> 42); h2 &= 0x3ffffffffff;
|
||||
h0 += c * 5; c = (h0 >> 44); h0 &= 0xfffffffffff;
|
||||
h1 += c;
|
||||
|
||||
/* compute h + -p */
|
||||
g0 = h0 + 5; c = (g0 >> 44); g0 &= 0xfffffffffff;
|
||||
g1 = h1 + c; c = (g1 >> 44); g1 &= 0xfffffffffff;
|
||||
g2 = h2 + c - ((unsigned long long)1 << 42);
|
||||
|
||||
/* select h if h < p, or h + -p if h >= p */
|
||||
c = (g2 >> ((sizeof(unsigned long long) * 8) - 1)) - 1;
|
||||
g0 &= c;
|
||||
g1 &= c;
|
||||
g2 &= c;
|
||||
c = ~c;
|
||||
h0 = (h0 & c) | g0;
|
||||
h1 = (h1 & c) | g1;
|
||||
h2 = (h2 & c) | g2;
|
||||
|
||||
/* h = (h + pad) */
|
||||
t0 = st->pad[0];
|
||||
t1 = st->pad[1];
|
||||
|
||||
h0 += (( t0 ) & 0xfffffffffff) ; c = (h0 >> 44); h0 &= 0xfffffffffff;
|
||||
h1 += (((t0 >> 44) | (t1 << 20)) & 0xfffffffffff) + c; c = (h1 >> 44); h1 &= 0xfffffffffff;
|
||||
h2 += (((t1 >> 24) ) & 0x3ffffffffff) + c; h2 &= 0x3ffffffffff;
|
||||
|
||||
/* mac = h % (2^128) */
|
||||
h0 = ((h0 ) | (h1 << 44));
|
||||
h1 = ((h1 >> 20) | (h2 << 24));
|
||||
|
||||
U64TO8(&mac[0], h0);
|
||||
U64TO8(&mac[8], h1);
|
||||
|
||||
/* zero out the state */
|
||||
st->h[0] = 0;
|
||||
st->h[1] = 0;
|
||||
st->h[2] = 0;
|
||||
st->r[0] = 0;
|
||||
st->r[1] = 0;
|
||||
st->r[2] = 0;
|
||||
st->pad[0] = 0;
|
||||
st->pad[1] = 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#else
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// More portable 64-bit implementation
|
||||
|
||||
#define poly1305_block_size 16
|
||||
|
||||
/* 17 + sizeof(size_t) + 14*sizeof(unsigned long) */
|
||||
typedef struct poly1305_state_internal_t {
|
||||
unsigned long r[5];
|
||||
unsigned long h[5];
|
||||
unsigned long pad[4];
|
||||
size_t leftover;
|
||||
unsigned char buffer[poly1305_block_size];
|
||||
unsigned char final;
|
||||
} poly1305_state_internal_t;
|
||||
|
||||
/* interpret four 8 bit unsigned integers as a 32 bit unsigned integer in little endian */
|
||||
static unsigned long
|
||||
U8TO32(const unsigned char *p) {
|
||||
return
|
||||
(((unsigned long)(p[0] & 0xff) ) |
|
||||
((unsigned long)(p[1] & 0xff) << 8) |
|
||||
((unsigned long)(p[2] & 0xff) << 16) |
|
||||
((unsigned long)(p[3] & 0xff) << 24));
|
||||
}
|
||||
|
||||
/* store a 32 bit unsigned integer as four 8 bit unsigned integers in little endian */
|
||||
static void
|
||||
U32TO8(unsigned char *p, unsigned long v) {
|
||||
p[0] = (v ) & 0xff;
|
||||
p[1] = (v >> 8) & 0xff;
|
||||
p[2] = (v >> 16) & 0xff;
|
||||
p[3] = (v >> 24) & 0xff;
|
||||
}
|
||||
|
||||
static inline void
|
||||
poly1305_init(poly1305_context *ctx, const unsigned char key[32]) {
|
||||
poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;
|
||||
|
||||
/* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
|
||||
st->r[0] = (U8TO32(&key[ 0]) ) & 0x3ffffff;
|
||||
st->r[1] = (U8TO32(&key[ 3]) >> 2) & 0x3ffff03;
|
||||
st->r[2] = (U8TO32(&key[ 6]) >> 4) & 0x3ffc0ff;
|
||||
st->r[3] = (U8TO32(&key[ 9]) >> 6) & 0x3f03fff;
|
||||
st->r[4] = (U8TO32(&key[12]) >> 8) & 0x00fffff;
|
||||
|
||||
/* h = 0 */
|
||||
st->h[0] = 0;
|
||||
st->h[1] = 0;
|
||||
st->h[2] = 0;
|
||||
st->h[3] = 0;
|
||||
st->h[4] = 0;
|
||||
|
||||
/* save pad for later */
|
||||
st->pad[0] = U8TO32(&key[16]);
|
||||
st->pad[1] = U8TO32(&key[20]);
|
||||
st->pad[2] = U8TO32(&key[24]);
|
||||
st->pad[3] = U8TO32(&key[28]);
|
||||
|
||||
st->leftover = 0;
|
||||
st->final = 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
poly1305_blocks(poly1305_state_internal_t *st, const unsigned char *m, size_t bytes) {
|
||||
const unsigned long hibit = (st->final) ? 0 : (1 << 24); /* 1 << 128 */
|
||||
unsigned long r0,r1,r2,r3,r4;
|
||||
unsigned long s1,s2,s3,s4;
|
||||
unsigned long h0,h1,h2,h3,h4;
|
||||
unsigned long long d0,d1,d2,d3,d4;
|
||||
unsigned long c;
|
||||
|
||||
r0 = st->r[0];
|
||||
r1 = st->r[1];
|
||||
r2 = st->r[2];
|
||||
r3 = st->r[3];
|
||||
r4 = st->r[4];
|
||||
|
||||
s1 = r1 * 5;
|
||||
s2 = r2 * 5;
|
||||
s3 = r3 * 5;
|
||||
s4 = r4 * 5;
|
||||
|
||||
h0 = st->h[0];
|
||||
h1 = st->h[1];
|
||||
h2 = st->h[2];
|
||||
h3 = st->h[3];
|
||||
h4 = st->h[4];
|
||||
|
||||
while (bytes >= poly1305_block_size) {
|
||||
/* h += m[i] */
|
||||
h0 += (U8TO32(m+ 0) ) & 0x3ffffff;
|
||||
h1 += (U8TO32(m+ 3) >> 2) & 0x3ffffff;
|
||||
h2 += (U8TO32(m+ 6) >> 4) & 0x3ffffff;
|
||||
h3 += (U8TO32(m+ 9) >> 6) & 0x3ffffff;
|
||||
h4 += (U8TO32(m+12) >> 8) | hibit;
|
||||
|
||||
/* h *= r */
|
||||
d0 = ((unsigned long long)h0 * r0) + ((unsigned long long)h1 * s4) + ((unsigned long long)h2 * s3) + ((unsigned long long)h3 * s2) + ((unsigned long long)h4 * s1);
|
||||
d1 = ((unsigned long long)h0 * r1) + ((unsigned long long)h1 * r0) + ((unsigned long long)h2 * s4) + ((unsigned long long)h3 * s3) + ((unsigned long long)h4 * s2);
|
||||
d2 = ((unsigned long long)h0 * r2) + ((unsigned long long)h1 * r1) + ((unsigned long long)h2 * r0) + ((unsigned long long)h3 * s4) + ((unsigned long long)h4 * s3);
|
||||
d3 = ((unsigned long long)h0 * r3) + ((unsigned long long)h1 * r2) + ((unsigned long long)h2 * r1) + ((unsigned long long)h3 * r0) + ((unsigned long long)h4 * s4);
|
||||
d4 = ((unsigned long long)h0 * r4) + ((unsigned long long)h1 * r3) + ((unsigned long long)h2 * r2) + ((unsigned long long)h3 * r1) + ((unsigned long long)h4 * r0);
|
||||
|
||||
/* (partial) h %= p */
|
||||
c = (unsigned long)(d0 >> 26); h0 = (unsigned long)d0 & 0x3ffffff;
|
||||
d1 += c; c = (unsigned long)(d1 >> 26); h1 = (unsigned long)d1 & 0x3ffffff;
|
||||
d2 += c; c = (unsigned long)(d2 >> 26); h2 = (unsigned long)d2 & 0x3ffffff;
|
||||
d3 += c; c = (unsigned long)(d3 >> 26); h3 = (unsigned long)d3 & 0x3ffffff;
|
||||
d4 += c; c = (unsigned long)(d4 >> 26); h4 = (unsigned long)d4 & 0x3ffffff;
|
||||
h0 += c * 5; c = (h0 >> 26); h0 = h0 & 0x3ffffff;
|
||||
h1 += c;
|
||||
|
||||
m += poly1305_block_size;
|
||||
bytes -= poly1305_block_size;
|
||||
}
|
||||
|
||||
st->h[0] = h0;
|
||||
st->h[1] = h1;
|
||||
st->h[2] = h2;
|
||||
st->h[3] = h3;
|
||||
st->h[4] = h4;
|
||||
}
|
||||
|
||||
static inline void
|
||||
poly1305_finish(poly1305_context *ctx, unsigned char mac[16]) {
|
||||
poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;
|
||||
unsigned long h0,h1,h2,h3,h4,c;
|
||||
unsigned long g0,g1,g2,g3,g4;
|
||||
unsigned long long f;
|
||||
unsigned long mask;
|
||||
|
||||
/* process the remaining block */
|
||||
if (st->leftover) {
|
||||
size_t i = st->leftover;
|
||||
st->buffer[i++] = 1;
|
||||
for (; i < poly1305_block_size; i++) {
|
||||
st->buffer[i] = 0;
|
||||
}
|
||||
st->final = 1;
|
||||
poly1305_blocks(st, st->buffer, poly1305_block_size);
|
||||
}
|
||||
|
||||
/* fully carry h */
|
||||
h0 = st->h[0];
|
||||
h1 = st->h[1];
|
||||
h2 = st->h[2];
|
||||
h3 = st->h[3];
|
||||
h4 = st->h[4];
|
||||
|
||||
c = h1 >> 26; h1 = h1 & 0x3ffffff;
|
||||
h2 += c; c = h2 >> 26; h2 = h2 & 0x3ffffff;
|
||||
h3 += c; c = h3 >> 26; h3 = h3 & 0x3ffffff;
|
||||
h4 += c; c = h4 >> 26; h4 = h4 & 0x3ffffff;
|
||||
h0 += c * 5; c = h0 >> 26; h0 = h0 & 0x3ffffff;
|
||||
h1 += c;
|
||||
|
||||
/* compute h + -p */
|
||||
g0 = h0 + 5; c = g0 >> 26; g0 &= 0x3ffffff;
|
||||
g1 = h1 + c; c = g1 >> 26; g1 &= 0x3ffffff;
|
||||
g2 = h2 + c; c = g2 >> 26; g2 &= 0x3ffffff;
|
||||
g3 = h3 + c; c = g3 >> 26; g3 &= 0x3ffffff;
|
||||
g4 = h4 + c - (1 << 26);
|
||||
|
||||
/* select h if h < p, or h + -p if h >= p */
|
||||
mask = (g4 >> ((sizeof(unsigned long) * 8) - 1)) - 1;
|
||||
g0 &= mask;
|
||||
g1 &= mask;
|
||||
g2 &= mask;
|
||||
g3 &= mask;
|
||||
g4 &= mask;
|
||||
mask = ~mask;
|
||||
h0 = (h0 & mask) | g0;
|
||||
h1 = (h1 & mask) | g1;
|
||||
h2 = (h2 & mask) | g2;
|
||||
h3 = (h3 & mask) | g3;
|
||||
h4 = (h4 & mask) | g4;
|
||||
|
||||
/* h = h % (2^128) */
|
||||
h0 = ((h0 ) | (h1 << 26)) & 0xffffffff;
|
||||
h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff;
|
||||
h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff;
|
||||
h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff;
|
||||
|
||||
/* mac = (h + pad) % (2^128) */
|
||||
f = (unsigned long long)h0 + st->pad[0] ; h0 = (unsigned long)f;
|
||||
f = (unsigned long long)h1 + st->pad[1] + (f >> 32); h1 = (unsigned long)f;
|
||||
f = (unsigned long long)h2 + st->pad[2] + (f >> 32); h2 = (unsigned long)f;
|
||||
f = (unsigned long long)h3 + st->pad[3] + (f >> 32); h3 = (unsigned long)f;
|
||||
|
||||
U32TO8(mac + 0, h0);
|
||||
U32TO8(mac + 4, h1);
|
||||
U32TO8(mac + 8, h2);
|
||||
U32TO8(mac + 12, h3);
|
||||
|
||||
/* zero out the state */
|
||||
st->h[0] = 0;
|
||||
st->h[1] = 0;
|
||||
st->h[2] = 0;
|
||||
st->h[3] = 0;
|
||||
st->h[4] = 0;
|
||||
st->r[0] = 0;
|
||||
st->r[1] = 0;
|
||||
st->r[2] = 0;
|
||||
st->r[3] = 0;
|
||||
st->r[4] = 0;
|
||||
st->pad[0] = 0;
|
||||
st->pad[1] = 0;
|
||||
st->pad[2] = 0;
|
||||
st->pad[3] = 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif // MSC/GCC or not
|
||||
|
||||
static inline void poly1305_update(poly1305_context *ctx, const unsigned char *m, size_t bytes) {
|
||||
poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;
|
||||
size_t i;
|
||||
|
||||
/* handle leftover */
|
||||
if (st->leftover) {
|
||||
size_t want = (poly1305_block_size - st->leftover);
|
||||
if (want > bytes) {
|
||||
want = bytes;
|
||||
}
|
||||
for (i = 0; i < want; i++) {
|
||||
st->buffer[st->leftover + i] = m[i];
|
||||
}
|
||||
bytes -= want;
|
||||
m += want;
|
||||
st->leftover += want;
|
||||
if (st->leftover < poly1305_block_size) {
|
||||
return;
|
||||
}
|
||||
poly1305_blocks(st, st->buffer, poly1305_block_size);
|
||||
st->leftover = 0;
|
||||
}
|
||||
|
||||
/* process full blocks */
|
||||
if (bytes >= poly1305_block_size) {
|
||||
size_t want = (bytes & ~(poly1305_block_size - 1));
|
||||
poly1305_blocks(st, m, want);
|
||||
m += want;
|
||||
bytes -= want;
|
||||
}
|
||||
|
||||
/* store leftover */
|
||||
if (bytes) {
|
||||
for (i = 0; i < bytes; i++) {
|
||||
st->buffer[st->leftover + i] = m[i];
|
||||
}
|
||||
st->leftover += bytes;
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void Poly1305::compute(void *auth,const void *data,unsigned int len,const void *key)
|
||||
{
|
||||
poly1305_context ctx;
|
||||
poly1305_init(&ctx,reinterpret_cast<const unsigned char *>(key));
|
||||
poly1305_update(&ctx,reinterpret_cast<const unsigned char *>(data),(size_t)len);
|
||||
poly1305_finish(&ctx,reinterpret_cast<unsigned char *>(auth));
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_POLY1305_HPP
|
||||
#define ZT_POLY1305_HPP
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
#define ZT_POLY1305_KEY_LEN 32
|
||||
#define ZT_POLY1305_MAC_LEN 16
|
||||
|
||||
/**
|
||||
* Poly1305 one-time authentication code
|
||||
*
|
||||
* This takes a one-time-use 32-byte key and generates a 16-byte message
|
||||
* authentication code. The key must never be re-used for a different
|
||||
* message.
|
||||
*
|
||||
* In Packet this is done by using the first 32 bytes of the stream cipher
|
||||
* keystream as a one-time-use key. These 32 bytes are then discarded and
|
||||
* the packet is encrypted with the next N bytes.
|
||||
*/
|
||||
class Poly1305
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Compute a one-time authentication code
|
||||
*
|
||||
* @param auth Buffer to receive code -- MUST be 16 bytes in length
|
||||
* @param data Data to authenticate
|
||||
* @param len Length of data to authenticate in bytes
|
||||
* @param key 32-byte one-time use key to authenticate data (must not be reused)
|
||||
*/
|
||||
static void compute(void *auth,const void *data,unsigned int len,const void *key);
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
ZeroTier Network Hypervisor Core
|
||||
======
|
||||
|
||||
This directory contains the *real* ZeroTier: a completely OS-independent global virtual Ethernet switch engine. This is where the magic happens.
|
||||
|
||||
Give it wire packets and it gives you Ethernet packets, and vice versa. The core contains absolutely no actual I/O, port configuration, or other OS-specific code (except Utils::getSecureRandom()). It provides a simple C API via [/include/ZeroTierOne.h](../include/ZeroTierOne.h). It's designed to be small and maximally portable for future use on small embedded and special purpose systems.
|
||||
|
||||
Code in here follows these guidelines:
|
||||
|
||||
- Keep it minimal, especially in terms of code footprint and memory use.
|
||||
- There should be no OS-dependent code here unless absolutely necessary (e.g. getSecureRandom).
|
||||
- If it's not part of the core virtual Ethernet switch it does not belong here.
|
||||
- No C++11 or C++14 since older and embedded compilers don't support it yet and this should be maximally portable.
|
||||
- Minimize the use of complex C++ features since at some point we might end up "minus-minus'ing" this code if doing so proves necessary to port to tiny embedded systems.
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#include "Revocation.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "Topology.hpp"
|
||||
#include "Switch.hpp"
|
||||
#include "Network.hpp"
|
||||
#include "Node.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
int Revocation::verify(const RuntimeEnvironment *RR,void *tPtr) const
|
||||
{
|
||||
if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId))) {
|
||||
return -1;
|
||||
}
|
||||
const Identity id(RR->topology->getIdentity(tPtr,_signedBy));
|
||||
if (!id) {
|
||||
RR->sw->requestWhois(tPtr,RR->node->now(),_signedBy);
|
||||
return 1;
|
||||
}
|
||||
try {
|
||||
Buffer<sizeof(Revocation) + 64> tmp;
|
||||
this->serialize(tmp,true);
|
||||
return (id.verify(tmp.data(),tmp.size(),_signature) ? 0 : -1);
|
||||
} catch ( ... ) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_REVOCATION_HPP
|
||||
#define ZT_REVOCATION_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "../include/ZeroTierOne.h"
|
||||
#include "Credential.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "C25519.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "Buffer.hpp"
|
||||
#include "Identity.hpp"
|
||||
|
||||
/**
|
||||
* Flag: fast propagation via rumor mill algorithm
|
||||
*/
|
||||
#define ZT_REVOCATION_FLAG_FAST_PROPAGATE 0x1ULL
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class RuntimeEnvironment;
|
||||
|
||||
/**
|
||||
* Revocation certificate to instantaneously revoke a COM, capability, or tag
|
||||
*/
|
||||
class Revocation : public Credential
|
||||
{
|
||||
public:
|
||||
static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_REVOCATION; }
|
||||
|
||||
Revocation() :
|
||||
_id(0),
|
||||
_credentialId(0),
|
||||
_networkId(0),
|
||||
_threshold(0),
|
||||
_flags(0),
|
||||
_target(),
|
||||
_signedBy(),
|
||||
_type(Credential::CREDENTIAL_TYPE_NULL)
|
||||
{
|
||||
memset(_signature.data,0,sizeof(_signature.data));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param i ID (arbitrary for revocations, currently random)
|
||||
* @param nwid Network ID
|
||||
* @param cid Credential ID being revoked (0 for all or for COMs, which lack IDs)
|
||||
* @param thr Revocation time threshold before which credentials will be revoked
|
||||
* @param fl Flags
|
||||
* @param tgt Target node whose credential(s) are being revoked
|
||||
* @param ct Credential type being revoked
|
||||
*/
|
||||
Revocation(const uint32_t i,const uint64_t nwid,const uint32_t cid,const int64_t thr,const uint64_t fl,const Address &tgt,const Credential::Type ct) :
|
||||
_id(i),
|
||||
_credentialId(cid),
|
||||
_networkId(nwid),
|
||||
_threshold(thr),
|
||||
_flags(fl),
|
||||
_target(tgt),
|
||||
_signedBy(),
|
||||
_type(ct)
|
||||
{
|
||||
memset(_signature.data,0,sizeof(_signature.data));
|
||||
}
|
||||
|
||||
inline uint32_t id() const { return _id; }
|
||||
inline uint32_t credentialId() const { return _credentialId; }
|
||||
inline uint64_t networkId() const { return _networkId; }
|
||||
inline int64_t threshold() const { return _threshold; }
|
||||
inline const Address &target() const { return _target; }
|
||||
inline const Address &signer() const { return _signedBy; }
|
||||
inline Credential::Type type() const { return _type; }
|
||||
|
||||
inline bool fastPropagate() const { return ((_flags & ZT_REVOCATION_FLAG_FAST_PROPAGATE) != 0); }
|
||||
|
||||
/**
|
||||
* @param signer Signing identity, must have private key
|
||||
* @return True if signature was successful
|
||||
*/
|
||||
inline bool sign(const Identity &signer)
|
||||
{
|
||||
if (signer.hasPrivate()) {
|
||||
Buffer<sizeof(Revocation) + 64> tmp;
|
||||
_signedBy = signer.address();
|
||||
this->serialize(tmp,true);
|
||||
_signature = signer.sign(tmp.data(),tmp.size());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify this revocation's signature
|
||||
*
|
||||
* @param RR Runtime environment to provide for peer lookup, etc.
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or chain
|
||||
*/
|
||||
int verify(const RuntimeEnvironment *RR,void *tPtr) const;
|
||||
|
||||
template<unsigned int C>
|
||||
inline void serialize(Buffer<C> &b,const bool forSign = false) const
|
||||
{
|
||||
if (forSign) {
|
||||
b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
|
||||
}
|
||||
|
||||
b.append((uint32_t)0); // 4 unused bytes, currently set to 0
|
||||
b.append(_id);
|
||||
b.append(_networkId);
|
||||
b.append((uint32_t)0); // 4 unused bytes, currently set to 0
|
||||
b.append(_credentialId);
|
||||
b.append(_threshold);
|
||||
b.append(_flags);
|
||||
_target.appendTo(b);
|
||||
_signedBy.appendTo(b);
|
||||
b.append((uint8_t)_type);
|
||||
|
||||
if (!forSign) {
|
||||
b.append((uint8_t)1); // 1 == Ed25519 signature
|
||||
b.append((uint16_t)ZT_C25519_SIGNATURE_LEN);
|
||||
b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
|
||||
}
|
||||
|
||||
// This is the size of any additional fields, currently 0.
|
||||
b.append((uint16_t)0);
|
||||
|
||||
if (forSign) {
|
||||
b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned int C>
|
||||
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
|
||||
{
|
||||
*this = Revocation();
|
||||
|
||||
unsigned int p = startAt;
|
||||
|
||||
p += 4; // 4 bytes, currently unused
|
||||
_id = b.template at<uint32_t>(p);
|
||||
p += 4;
|
||||
_networkId = b.template at<uint64_t>(p);
|
||||
p += 8;
|
||||
p += 4; // 4 bytes, currently unused
|
||||
_credentialId = b.template at<uint32_t>(p);
|
||||
p += 4;
|
||||
_threshold = (int64_t)b.template at<uint64_t>(p);
|
||||
p += 8;
|
||||
_flags = b.template at<uint64_t>(p);
|
||||
p += 8;
|
||||
_target.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
|
||||
p += ZT_ADDRESS_LENGTH;
|
||||
_signedBy.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
|
||||
p += ZT_ADDRESS_LENGTH;
|
||||
_type = (Credential::Type)b[p++];
|
||||
|
||||
if (b[p++] == 1) {
|
||||
if (b.template at<uint16_t>(p) == ZT_C25519_SIGNATURE_LEN) {
|
||||
p += 2;
|
||||
memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN);
|
||||
p += ZT_C25519_SIGNATURE_LEN;
|
||||
} else {
|
||||
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
|
||||
}
|
||||
} else {
|
||||
p += 2 + b.template at<uint16_t>(p);
|
||||
}
|
||||
|
||||
p += 2 + b.template at<uint16_t>(p);
|
||||
if (p > b.size()) {
|
||||
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
|
||||
}
|
||||
|
||||
return (p - startAt);
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t _id;
|
||||
uint32_t _credentialId;
|
||||
uint64_t _networkId;
|
||||
int64_t _threshold;
|
||||
uint64_t _flags;
|
||||
Address _target;
|
||||
Address _signedBy;
|
||||
Credential::Type _type;
|
||||
C25519::Signature _signature;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
* Copyright (c)2013-2020 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_RINGBUFFER_H
|
||||
#define ZT_RINGBUFFER_H
|
||||
|
||||
#include <typeinfo>
|
||||
#include <cstdint>
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
#include <algorithm>
|
||||
#include <math.h>
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* A circular buffer
|
||||
*
|
||||
* For fast handling of continuously-evolving variables (such as path quality metrics).
|
||||
* Using this, we can maintain longer sliding historical windows for important path
|
||||
* metrics without the need for potentially expensive calls to memcpy/memmove.
|
||||
*
|
||||
* Some basic statistical functionality is implemented here in an attempt
|
||||
* to reduce the complexity of code needed to interact with this type of buffer.
|
||||
*/
|
||||
|
||||
template <class T,size_t S>
|
||||
class RingBuffer
|
||||
{
|
||||
private:
|
||||
T buf[S];
|
||||
size_t begin;
|
||||
size_t end;
|
||||
bool wrap;
|
||||
|
||||
public:
|
||||
RingBuffer() :
|
||||
begin(0),
|
||||
end(0),
|
||||
wrap(false)
|
||||
{
|
||||
memset(buf,0,sizeof(T)*S);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A pointer to the underlying buffer
|
||||
*/
|
||||
inline T *get_buf()
|
||||
{
|
||||
return buf + begin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust buffer index pointer as if we copied data in
|
||||
* @param n Number of elements to copy in
|
||||
* @return Number of elements we copied in
|
||||
*/
|
||||
inline size_t produce(size_t n)
|
||||
{
|
||||
n = std::min(n, getFree());
|
||||
if (n == 0) {
|
||||
return n;
|
||||
}
|
||||
const size_t first_chunk = std::min(n, S - end);
|
||||
end = (end + first_chunk) % S;
|
||||
if (first_chunk < n) {
|
||||
const size_t second_chunk = n - first_chunk;
|
||||
end = (end + second_chunk) % S;
|
||||
}
|
||||
if (begin == end) {
|
||||
wrap = true;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fast erase, O(1).
|
||||
* Merely reset the buffer pointer, doesn't erase contents
|
||||
*/
|
||||
inline void reset() { consume(count()); }
|
||||
|
||||
/**
|
||||
* adjust buffer index pointer as if we copied data out
|
||||
* @param n Number of elements we copied from the buffer
|
||||
* @return Number of elements actually available from the buffer
|
||||
*/
|
||||
inline size_t consume(size_t n)
|
||||
{
|
||||
n = std::min(n, count());
|
||||
if (n == 0) {
|
||||
return n;
|
||||
}
|
||||
if (wrap) {
|
||||
wrap = false;
|
||||
}
|
||||
const size_t first_chunk = std::min(n, S - begin);
|
||||
begin = (begin + first_chunk) % S;
|
||||
if (first_chunk < n) {
|
||||
const size_t second_chunk = n - first_chunk;
|
||||
begin = (begin + second_chunk) % S;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param data Buffer that is to be written to the ring
|
||||
* @param n Number of elements to write to the buffer
|
||||
*/
|
||||
inline size_t write(const T * data, size_t n)
|
||||
{
|
||||
n = std::min(n, getFree());
|
||||
if (n == 0) {
|
||||
return n;
|
||||
}
|
||||
const size_t first_chunk = std::min(n, S - end);
|
||||
memcpy(buf + end, data, first_chunk * sizeof(T));
|
||||
end = (end + first_chunk) % S;
|
||||
if (first_chunk < n) {
|
||||
const size_t second_chunk = n - first_chunk;
|
||||
memcpy(buf + end, data + first_chunk, second_chunk * sizeof(T));
|
||||
end = (end + second_chunk) % S;
|
||||
}
|
||||
if (begin == end) {
|
||||
wrap = true;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Place a single value on the buffer. If the buffer is full, consume a value first.
|
||||
*
|
||||
* @param value A single value to be placed in the buffer
|
||||
*/
|
||||
inline void push(const T value)
|
||||
{
|
||||
if (count() == S) {
|
||||
consume(1);
|
||||
}
|
||||
const size_t first_chunk = std::min((size_t)1, S - end);
|
||||
*(buf + end) = value;
|
||||
end = (end + first_chunk) % S;
|
||||
if (begin == end) {
|
||||
wrap = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The most recently pushed element on the buffer
|
||||
*/
|
||||
inline T get_most_recent() { return *(buf + end); }
|
||||
|
||||
/**
|
||||
* @param dest Destination buffer
|
||||
* @param n Size (in terms of number of elements) of the destination buffer
|
||||
* @return Number of elements read from the buffer
|
||||
*/
|
||||
inline size_t read(T *dest,size_t n)
|
||||
{
|
||||
n = std::min(n, count());
|
||||
if (n == 0) {
|
||||
return n;
|
||||
}
|
||||
if (wrap) {
|
||||
wrap = false;
|
||||
}
|
||||
const size_t first_chunk = std::min(n, S - begin);
|
||||
memcpy(dest, buf + begin, first_chunk * sizeof(T));
|
||||
begin = (begin + first_chunk) % S;
|
||||
if (first_chunk < n) {
|
||||
const size_t second_chunk = n - first_chunk;
|
||||
memcpy(dest + first_chunk, buf + begin, second_chunk * sizeof(T));
|
||||
begin = (begin + second_chunk) % S;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return how many elements are in the buffer, O(1).
|
||||
*
|
||||
* @return The number of elements in the buffer
|
||||
*/
|
||||
inline size_t count()
|
||||
{
|
||||
if (end == begin) {
|
||||
return wrap ? S : 0;
|
||||
} else if (end > begin) {
|
||||
return end - begin;
|
||||
} else {
|
||||
return S + end - begin;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The number of slots that are unused in the buffer
|
||||
*/
|
||||
inline size_t getFree() { return S - count(); }
|
||||
|
||||
/**
|
||||
* @return The arithmetic mean of the contents of the buffer
|
||||
*/
|
||||
inline float mean()
|
||||
{
|
||||
size_t iterator = begin;
|
||||
float subtotal = 0;
|
||||
size_t curr_cnt = count();
|
||||
for (size_t i=0; i<curr_cnt; i++) {
|
||||
iterator = (iterator + S - 1) % curr_cnt;
|
||||
subtotal += (float)*(buf + iterator);
|
||||
}
|
||||
return curr_cnt ? subtotal / (float)curr_cnt : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The arithmetic mean of the most recent 'n' elements of the buffer
|
||||
*/
|
||||
inline float mean(size_t n)
|
||||
{
|
||||
n = n < S ? n : S;
|
||||
size_t iterator = begin;
|
||||
float subtotal = 0;
|
||||
size_t curr_cnt = count();
|
||||
for (size_t i=0; i<n; i++) {
|
||||
iterator = (iterator + S - 1) % curr_cnt;
|
||||
subtotal += (float)*(buf + iterator);
|
||||
}
|
||||
return curr_cnt ? subtotal / (float)curr_cnt : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The sum of the contents of the buffer
|
||||
*/
|
||||
inline float sum()
|
||||
{
|
||||
size_t iterator = begin;
|
||||
float total = 0;
|
||||
size_t curr_cnt = count();
|
||||
for (size_t i=0; i<curr_cnt; i++) {
|
||||
iterator = (iterator + S - 1) % curr_cnt;
|
||||
total += (float)*(buf + iterator);
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The sample standard deviation of element values
|
||||
*/
|
||||
inline float stddev() { return sqrt(variance()); }
|
||||
|
||||
/**
|
||||
* @return The variance of element values
|
||||
*/
|
||||
inline float variance()
|
||||
{
|
||||
size_t iterator = begin;
|
||||
float cached_mean = mean();
|
||||
size_t curr_cnt = count();
|
||||
T sum_of_squared_deviations = 0;
|
||||
for (size_t i=0; i<curr_cnt; i++) {
|
||||
iterator = (iterator + S - 1) % curr_cnt;
|
||||
float deviation = (buf[i] - cached_mean);
|
||||
sum_of_squared_deviations += (T)(deviation*deviation);
|
||||
}
|
||||
float variance = (float)sum_of_squared_deviations / (float)(S - 1);
|
||||
return variance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The number of elements of zero value
|
||||
*/
|
||||
inline size_t zeroCount()
|
||||
{
|
||||
size_t iterator = begin;
|
||||
size_t zeros = 0;
|
||||
size_t curr_cnt = count();
|
||||
for (size_t i=0; i<curr_cnt; i++) {
|
||||
iterator = (iterator + S - 1) % curr_cnt;
|
||||
if (*(buf + iterator) == 0) {
|
||||
zeros++;
|
||||
}
|
||||
}
|
||||
return zeros;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value Value to match against in buffer
|
||||
* @return The number of values held in the ring buffer which match a given value
|
||||
*/
|
||||
inline size_t countValue(T value)
|
||||
{
|
||||
size_t iterator = begin;
|
||||
size_t cnt = 0;
|
||||
size_t curr_cnt = count();
|
||||
for (size_t i=0; i<curr_cnt; i++) {
|
||||
iterator = (iterator + S - 1) % curr_cnt;
|
||||
if (*(buf + iterator) == value) {
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the contents of the buffer
|
||||
*/
|
||||
/*
|
||||
inline void dump()
|
||||
{
|
||||
size_t iterator = begin;
|
||||
for (size_t i=0; i<S; i++) {
|
||||
iterator = (iterator + S - 1) % S;
|
||||
if (typeid(T) == typeid(int)) {
|
||||
fprintf(stderr, "buf[%2zu]=%2d\n", iterator, (int)*(buf + iterator));
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "buf[%2zu]=%2f\n", iterator, (float)*(buf + iterator));
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_RUNTIMEENVIRONMENT_HPP
|
||||
#define ZT_RUNTIMEENVIRONMENT_HPP
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "Identity.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class NodeConfig;
|
||||
class Switch;
|
||||
class Topology;
|
||||
class Node;
|
||||
class Multicaster;
|
||||
class NetworkController;
|
||||
class SelfAwareness;
|
||||
class Trace;
|
||||
class Bond;
|
||||
class PacketMultiplexer;
|
||||
|
||||
/**
|
||||
* Holds global state for an instance of ZeroTier::Node
|
||||
*/
|
||||
class RuntimeEnvironment
|
||||
{
|
||||
public:
|
||||
RuntimeEnvironment(Node *n) :
|
||||
node(n)
|
||||
,localNetworkController((NetworkController *)0)
|
||||
,rtmem((void *)0)
|
||||
,sw((Switch *)0)
|
||||
,mc((Multicaster *)0)
|
||||
,topology((Topology *)0)
|
||||
,sa((SelfAwareness *)0)
|
||||
{
|
||||
publicIdentityStr[0] = (char)0;
|
||||
secretIdentityStr[0] = (char)0;
|
||||
}
|
||||
|
||||
~RuntimeEnvironment()
|
||||
{
|
||||
Utils::burn(secretIdentityStr,sizeof(secretIdentityStr));
|
||||
}
|
||||
|
||||
// Node instance that owns this RuntimeEnvironment
|
||||
Node *const node;
|
||||
|
||||
// This is set externally to an instance of this base class
|
||||
NetworkController *localNetworkController;
|
||||
|
||||
// Memory actually occupied by Trace, Switch, etc.
|
||||
void *rtmem;
|
||||
|
||||
/* Order matters a bit here. These are constructed in this order
|
||||
* and then deleted in the opposite order on Node exit. The order ensures
|
||||
* that things that are needed are there before they're needed.
|
||||
*
|
||||
* These are constant and never null after startup unless indicated. */
|
||||
|
||||
Trace *t;
|
||||
Switch *sw;
|
||||
Multicaster *mc;
|
||||
Topology *topology;
|
||||
SelfAwareness *sa;
|
||||
Bond *bc;
|
||||
PacketMultiplexer *pm;
|
||||
|
||||
// This node's identity and string representations thereof
|
||||
Identity identity;
|
||||
char publicIdentityStr[ZT_IDENTITY_STRING_BUFFER_LENGTH];
|
||||
char secretIdentityStr[ZT_IDENTITY_STRING_BUFFER_LENGTH];
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
// This code is public domain, taken from a PD crypto source file on GitHub.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "SHA512.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
#ifndef ZT_HAVE_NATIVE_SHA512
|
||||
|
||||
namespace {
|
||||
|
||||
struct sha512_state {
|
||||
uint64_t length,state[8];
|
||||
unsigned long curlen;
|
||||
uint8_t buf[128];
|
||||
};
|
||||
|
||||
static const uint64_t K[80] = {
|
||||
0x428a2f98d728ae22ULL,0x7137449123ef65cdULL,0xb5c0fbcfec4d3b2fULL,0xe9b5dba58189dbbcULL,
|
||||
0x3956c25bf348b538ULL,0x59f111f1b605d019ULL,0x923f82a4af194f9bULL,0xab1c5ed5da6d8118ULL,
|
||||
0xd807aa98a3030242ULL,0x12835b0145706fbeULL,0x243185be4ee4b28cULL,0x550c7dc3d5ffb4e2ULL,
|
||||
0x72be5d74f27b896fULL,0x80deb1fe3b1696b1ULL,0x9bdc06a725c71235ULL,0xc19bf174cf692694ULL,
|
||||
0xe49b69c19ef14ad2ULL,0xefbe4786384f25e3ULL,0x0fc19dc68b8cd5b5ULL,0x240ca1cc77ac9c65ULL,
|
||||
0x2de92c6f592b0275ULL,0x4a7484aa6ea6e483ULL,0x5cb0a9dcbd41fbd4ULL,0x76f988da831153b5ULL,
|
||||
0x983e5152ee66dfabULL,0xa831c66d2db43210ULL,0xb00327c898fb213fULL,0xbf597fc7beef0ee4ULL,
|
||||
0xc6e00bf33da88fc2ULL,0xd5a79147930aa725ULL,0x06ca6351e003826fULL,0x142929670a0e6e70ULL,
|
||||
0x27b70a8546d22ffcULL,0x2e1b21385c26c926ULL,0x4d2c6dfc5ac42aedULL,0x53380d139d95b3dfULL,
|
||||
0x650a73548baf63deULL,0x766a0abb3c77b2a8ULL,0x81c2c92e47edaee6ULL,0x92722c851482353bULL,
|
||||
0xa2bfe8a14cf10364ULL,0xa81a664bbc423001ULL,0xc24b8b70d0f89791ULL,0xc76c51a30654be30ULL,
|
||||
0xd192e819d6ef5218ULL,0xd69906245565a910ULL,0xf40e35855771202aULL,0x106aa07032bbd1b8ULL,
|
||||
0x19a4c116b8d2d0c8ULL,0x1e376c085141ab53ULL,0x2748774cdf8eeb99ULL,0x34b0bcb5e19b48a8ULL,
|
||||
0x391c0cb3c5c95a63ULL,0x4ed8aa4ae3418acbULL,0x5b9cca4f7763e373ULL,0x682e6ff3d6b2b8a3ULL,
|
||||
0x748f82ee5defb2fcULL,0x78a5636f43172f60ULL,0x84c87814a1f0ab72ULL,0x8cc702081a6439ecULL,
|
||||
0x90befffa23631e28ULL,0xa4506cebde82bde9ULL,0xbef9a3f7b2c67915ULL,0xc67178f2e372532bULL,
|
||||
0xca273eceea26619cULL,0xd186b8c721c0c207ULL,0xeada7dd6cde0eb1eULL,0xf57d4f7fee6ed178ULL,
|
||||
0x06f067aa72176fbaULL,0x0a637dc5a2c898a6ULL,0x113f9804bef90daeULL,0x1b710b35131c471bULL,
|
||||
0x28db77f523047d84ULL,0x32caab7b40c72493ULL,0x3c9ebe0a15c9bebcULL,0x431d67c49c100d4cULL,
|
||||
0x4cc5d4becb3e42b6ULL,0x597f299cfc657e2aULL,0x5fcb6fab3ad6faecULL,0x6c44198c4a475817ULL
|
||||
};
|
||||
|
||||
#define STORE64H(x, y) Utils::storeBigEndian<uint64_t>(y,x)
|
||||
#define LOAD64H(x, y) x = Utils::loadBigEndian<uint64_t>(y)
|
||||
#define ROL64c(x,y) (((x)<<(y)) | ((x)>>(64-(y))))
|
||||
#define ROR64c(x,y) (((x)>>(y)) | ((x)<<(64-(y))))
|
||||
#define Ch(x,y,z) (z ^ (x & (y ^ z)))
|
||||
#define Maj(x,y,z) (((x | y) & z) | (x & y))
|
||||
#define S(x, n) ROR64c(x, n)
|
||||
#define R(x, n) ((x)>>(n))
|
||||
#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39))
|
||||
#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41))
|
||||
#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7))
|
||||
#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6))
|
||||
|
||||
static ZT_INLINE void sha512_compress(sha512_state *const md,uint8_t *const buf)
|
||||
{
|
||||
uint64_t S[8], W[80], t0, t1;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
S[i] = md->state[i];
|
||||
}
|
||||
for (i = 0; i < 16; i++) {
|
||||
LOAD64H(W[i], buf + (8*i));
|
||||
}
|
||||
for (i = 16; i < 80; i++) {
|
||||
W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16];
|
||||
}
|
||||
|
||||
#define RND(a,b,c,d,e,f,g,h,i) \
|
||||
t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \
|
||||
t1 = Sigma0(a) + Maj(a, b, c); \
|
||||
d += t0; \
|
||||
h = t0 + t1;
|
||||
|
||||
for (i = 0; i < 80; i += 8) {
|
||||
RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i+0);
|
||||
RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],i+1);
|
||||
RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],i+2);
|
||||
RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],i+3);
|
||||
RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],i+4);
|
||||
RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],i+5);
|
||||
RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],i+6);
|
||||
RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],i+7);
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
md->state[i] = md->state[i] + S[i];
|
||||
}
|
||||
}
|
||||
|
||||
static ZT_INLINE void sha384_init(sha512_state *const md)
|
||||
{
|
||||
md->curlen = 0;
|
||||
md->length = 0;
|
||||
md->state[0] = 0xcbbb9d5dc1059ed8ULL;
|
||||
md->state[1] = 0x629a292a367cd507ULL;
|
||||
md->state[2] = 0x9159015a3070dd17ULL;
|
||||
md->state[3] = 0x152fecd8f70e5939ULL;
|
||||
md->state[4] = 0x67332667ffc00b31ULL;
|
||||
md->state[5] = 0x8eb44a8768581511ULL;
|
||||
md->state[6] = 0xdb0c2e0d64f98fa7ULL;
|
||||
md->state[7] = 0x47b5481dbefa4fa4ULL;
|
||||
}
|
||||
|
||||
static ZT_INLINE void sha512_init(sha512_state *const md)
|
||||
{
|
||||
md->curlen = 0;
|
||||
md->length = 0;
|
||||
md->state[0] = 0x6a09e667f3bcc908ULL;
|
||||
md->state[1] = 0xbb67ae8584caa73bULL;
|
||||
md->state[2] = 0x3c6ef372fe94f82bULL;
|
||||
md->state[3] = 0xa54ff53a5f1d36f1ULL;
|
||||
md->state[4] = 0x510e527fade682d1ULL;
|
||||
md->state[5] = 0x9b05688c2b3e6c1fULL;
|
||||
md->state[6] = 0x1f83d9abfb41bd6bULL;
|
||||
md->state[7] = 0x5be0cd19137e2179ULL;
|
||||
}
|
||||
|
||||
static void sha512_process(sha512_state *const md,const uint8_t *in,unsigned long inlen)
|
||||
{
|
||||
while (inlen > 0) {
|
||||
if (md->curlen == 0 && inlen >= 128) {
|
||||
sha512_compress(md,(uint8_t *)in);
|
||||
md->length += 128 * 8;
|
||||
in += 128;
|
||||
inlen -= 128;
|
||||
} else {
|
||||
unsigned long n = std::min(inlen,(128 - md->curlen));
|
||||
Utils::copy(md->buf + md->curlen,in,n);
|
||||
md->curlen += n;
|
||||
in += n;
|
||||
inlen -= n;
|
||||
if (md->curlen == 128) {
|
||||
sha512_compress(md,md->buf);
|
||||
md->length += 8*128;
|
||||
md->curlen = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ZT_INLINE void sha512_done(sha512_state *const md,uint8_t *out)
|
||||
{
|
||||
int i;
|
||||
|
||||
md->length += md->curlen * 8ULL;
|
||||
md->buf[md->curlen++] = (uint8_t)0x80;
|
||||
|
||||
if (md->curlen > 112) {
|
||||
while (md->curlen < 128) {
|
||||
md->buf[md->curlen++] = (uint8_t)0;
|
||||
}
|
||||
sha512_compress(md, md->buf);
|
||||
md->curlen = 0;
|
||||
}
|
||||
|
||||
while (md->curlen < 120) {
|
||||
md->buf[md->curlen++] = (uint8_t)0;
|
||||
}
|
||||
|
||||
STORE64H(md->length, md->buf+120);
|
||||
sha512_compress(md, md->buf);
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
STORE64H(md->state[i], out+(8*i));
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void SHA512(void *digest,const void *data,unsigned int len)
|
||||
{
|
||||
sha512_state state;
|
||||
sha512_init(&state);
|
||||
sha512_process(&state,(uint8_t *)data,(unsigned long)len);
|
||||
sha512_done(&state,(uint8_t *)digest);
|
||||
}
|
||||
|
||||
void SHA384(void *digest,const void *data,unsigned int len)
|
||||
{
|
||||
uint8_t tmp[64];
|
||||
sha512_state state;
|
||||
sha384_init(&state);
|
||||
sha512_process(&state,(uint8_t *)data,(unsigned long)len);
|
||||
sha512_done(&state,tmp);
|
||||
Utils::copy<48>(digest,tmp);
|
||||
}
|
||||
|
||||
void SHA384(void *digest,const void *data0,unsigned int len0,const void *data1,unsigned int len1)
|
||||
{
|
||||
uint8_t tmp[64];
|
||||
sha512_state state;
|
||||
sha384_init(&state);
|
||||
sha512_process(&state,(uint8_t *)data0,(unsigned long)len0);
|
||||
sha512_process(&state,(uint8_t *)data1,(unsigned long)len1);
|
||||
sha512_done(&state,tmp);
|
||||
Utils::copy<48>(digest,tmp);
|
||||
}
|
||||
|
||||
#endif // !ZT_HAVE_NATIVE_SHA512
|
||||
|
||||
void HMACSHA384(const uint8_t key[ZT_SYMMETRIC_KEY_SIZE],const void *msg,const unsigned int msglen,uint8_t mac[48])
|
||||
{
|
||||
uint64_t kInPadded[16]; // input padded key
|
||||
uint64_t outer[22]; // output padded key | H(input padded key | msg)
|
||||
|
||||
const uint64_t k0 = Utils::loadMachineEndian< uint64_t >(key);
|
||||
const uint64_t k1 = Utils::loadMachineEndian< uint64_t >(key + 8);
|
||||
const uint64_t k2 = Utils::loadMachineEndian< uint64_t >(key + 16);
|
||||
const uint64_t k3 = Utils::loadMachineEndian< uint64_t >(key + 24);
|
||||
const uint64_t k4 = Utils::loadMachineEndian< uint64_t >(key + 32);
|
||||
const uint64_t k5 = Utils::loadMachineEndian< uint64_t >(key + 40);
|
||||
|
||||
const uint64_t ipad = 0x3636363636363636ULL;
|
||||
kInPadded[0] = k0 ^ ipad;
|
||||
kInPadded[1] = k1 ^ ipad;
|
||||
kInPadded[2] = k2 ^ ipad;
|
||||
kInPadded[3] = k3 ^ ipad;
|
||||
kInPadded[4] = k4 ^ ipad;
|
||||
kInPadded[5] = k5 ^ ipad;
|
||||
kInPadded[6] = ipad;
|
||||
kInPadded[7] = ipad;
|
||||
kInPadded[8] = ipad;
|
||||
kInPadded[9] = ipad;
|
||||
kInPadded[10] = ipad;
|
||||
kInPadded[11] = ipad;
|
||||
kInPadded[12] = ipad;
|
||||
kInPadded[13] = ipad;
|
||||
kInPadded[14] = ipad;
|
||||
kInPadded[15] = ipad;
|
||||
|
||||
const uint64_t opad = 0x5c5c5c5c5c5c5c5cULL;
|
||||
outer[0] = k0 ^ opad;
|
||||
outer[1] = k1 ^ opad;
|
||||
outer[2] = k2 ^ opad;
|
||||
outer[3] = k3 ^ opad;
|
||||
outer[4] = k4 ^ opad;
|
||||
outer[5] = k5 ^ opad;
|
||||
outer[6] = opad;
|
||||
outer[7] = opad;
|
||||
outer[8] = opad;
|
||||
outer[9] = opad;
|
||||
outer[10] = opad;
|
||||
outer[11] = opad;
|
||||
outer[12] = opad;
|
||||
outer[13] = opad;
|
||||
outer[14] = opad;
|
||||
outer[15] = opad;
|
||||
|
||||
// H(output padded key | H(input padded key | msg))
|
||||
SHA384(reinterpret_cast<uint8_t *>(outer) + 128,kInPadded,128,msg,msglen);
|
||||
SHA384(mac,outer,176);
|
||||
}
|
||||
|
||||
void KBKDFHMACSHA384(const uint8_t key[ZT_SYMMETRIC_KEY_SIZE],const char label,const char context,const uint32_t iter,uint8_t out[ZT_SYMMETRIC_KEY_SIZE])
|
||||
{
|
||||
uint8_t kbkdfMsg[13];
|
||||
|
||||
Utils::storeBigEndian<uint32_t>(kbkdfMsg,(uint32_t)iter);
|
||||
|
||||
kbkdfMsg[4] = (uint8_t)'Z';
|
||||
kbkdfMsg[5] = (uint8_t)'T'; // preface our labels with something ZT-specific
|
||||
kbkdfMsg[6] = (uint8_t)label;
|
||||
kbkdfMsg[7] = 0;
|
||||
|
||||
kbkdfMsg[8] = (uint8_t)context;
|
||||
|
||||
// Output key length: 384 bits (as 32-bit big-endian value)
|
||||
kbkdfMsg[9] = 0;
|
||||
kbkdfMsg[10] = 0;
|
||||
kbkdfMsg[11] = 0x01;
|
||||
kbkdfMsg[12] = 0x80;
|
||||
|
||||
static_assert(ZT_SYMMETRIC_KEY_SIZE == ZT_SHA384_DIGEST_SIZE,"sizeof(out) != ZT_SHA384_DIGEST_SIZE");
|
||||
HMACSHA384(key,&kbkdfMsg,sizeof(kbkdfMsg),out);
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
// Internally re-export to included C code, which includes some fast crypto code ported in on some platforms.
|
||||
// This eliminates the need to link against a third party SHA512() from this code
|
||||
extern "C" void ZT_sha512internal(void *digest,const void *data,unsigned int len)
|
||||
{ ZeroTier::SHA512(digest,data,len); }
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (c)2013-2020 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_SHA512_HPP
|
||||
#define ZT_SHA512_HPP
|
||||
|
||||
#include "Constants.hpp"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <CommonCrypto/CommonDigest.h>
|
||||
#endif
|
||||
|
||||
#define ZT_SHA512_DIGEST_SIZE 64
|
||||
#define ZT_SHA384_DIGEST_SIZE 48
|
||||
#define ZT_SHA512_BLOCK_SIZE 128
|
||||
#define ZT_SHA384_BLOCK_SIZE 128
|
||||
|
||||
#define ZT_HMACSHA384_LEN 48
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
// SHA384 and SHA512 are actually in the standard libraries on MacOS and iOS
|
||||
#ifdef __APPLE__
|
||||
#define ZT_HAVE_NATIVE_SHA512 1
|
||||
static ZT_INLINE void SHA512(void *digest,const void *data,unsigned int len)
|
||||
{
|
||||
CC_SHA512_CTX ctx;
|
||||
CC_SHA512_Init(&ctx);
|
||||
CC_SHA512_Update(&ctx,data,len);
|
||||
CC_SHA512_Final(reinterpret_cast<unsigned char *>(digest),&ctx);
|
||||
}
|
||||
static ZT_INLINE void SHA384(void *digest,const void *data,unsigned int len)
|
||||
{
|
||||
CC_SHA512_CTX ctx;
|
||||
CC_SHA384_Init(&ctx);
|
||||
CC_SHA384_Update(&ctx,data,len);
|
||||
CC_SHA384_Final(reinterpret_cast<unsigned char *>(digest),&ctx);
|
||||
}
|
||||
static ZT_INLINE void SHA384(void *digest,const void *data0,unsigned int len0,const void *data1,unsigned int len1)
|
||||
{
|
||||
CC_SHA512_CTX ctx;
|
||||
CC_SHA384_Init(&ctx);
|
||||
CC_SHA384_Update(&ctx,data0,len0);
|
||||
CC_SHA384_Update(&ctx,data1,len1);
|
||||
CC_SHA384_Final(reinterpret_cast<unsigned char *>(digest),&ctx);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef ZT_HAVE_NATIVE_SHA512
|
||||
void SHA512(void *digest,const void *data,unsigned int len);
|
||||
void SHA384(void *digest,const void *data,unsigned int len);
|
||||
void SHA384(void *digest,const void *data0,unsigned int len0,const void *data1,unsigned int len1);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Compute HMAC SHA-384 using a 256-bit key
|
||||
*
|
||||
* @param key Secret key
|
||||
* @param msg Message to HMAC
|
||||
* @param msglen Length of message
|
||||
* @param mac Buffer to fill with result
|
||||
*/
|
||||
void HMACSHA384(const uint8_t key[ZT_SYMMETRIC_KEY_SIZE],const void *msg,unsigned int msglen,uint8_t mac[48]);
|
||||
|
||||
/**
|
||||
* Compute KBKDF (key-based key derivation function) using HMAC-SHA-384 as a PRF
|
||||
*
|
||||
* @param key Source master key
|
||||
* @param label A label indicating the key's purpose in the ZeroTier system
|
||||
* @param context An arbitrary "context" or zero if not applicable
|
||||
* @param iter Key iteration for generation of multiple keys for the same label/context
|
||||
* @param out Output to receive derived key
|
||||
*/
|
||||
void KBKDFHMACSHA384(const uint8_t key[ZT_SYMMETRIC_KEY_SIZE],char label,char context,uint32_t iter,uint8_t out[ZT_SYMMETRIC_KEY_SIZE]);
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
+1347
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Based on public domain code available at: http://cr.yp.to/snuffle.html
|
||||
*
|
||||
* This therefore is public domain.
|
||||
*/
|
||||
|
||||
#ifndef ZT_SALSA20_HPP
|
||||
#define ZT_SALSA20_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
#if (!defined(ZT_SALSA20_SSE)) && (defined(__SSE2__) || (defined(__WINDOWS__) && !defined(__MINGW32__) && !defined(_M_ARM64)))
|
||||
#define ZT_SALSA20_SSE 1
|
||||
#endif
|
||||
|
||||
#ifdef ZT_SALSA20_SSE
|
||||
#include <emmintrin.h>
|
||||
#endif // ZT_SALSA20_SSE
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* Salsa20 stream cipher
|
||||
*/
|
||||
class Salsa20
|
||||
{
|
||||
public:
|
||||
Salsa20() {}
|
||||
~Salsa20() { Utils::burn(&_state,sizeof(_state)); }
|
||||
|
||||
/**
|
||||
* XOR d with s
|
||||
*
|
||||
* This is done efficiently using e.g. SSE if available. It's used when
|
||||
* alternative Salsa20 implementations are used in Packet and is here
|
||||
* since this is where all the SSE stuff is already included.
|
||||
*
|
||||
* @param d Destination to XOR
|
||||
* @param s Source bytes to XOR with destination
|
||||
* @param len Length of s and d
|
||||
*/
|
||||
static inline void memxor(uint8_t *d,const uint8_t *s,unsigned int len)
|
||||
{
|
||||
#ifdef ZT_SALSA20_SSE
|
||||
while (len >= 128) {
|
||||
__m128i s0 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s));
|
||||
__m128i s1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 16));
|
||||
__m128i s2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 32));
|
||||
__m128i s3 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 48));
|
||||
__m128i s4 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 64));
|
||||
__m128i s5 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 80));
|
||||
__m128i s6 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 96));
|
||||
__m128i s7 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 112));
|
||||
__m128i d0 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d));
|
||||
__m128i d1 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 16));
|
||||
__m128i d2 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 32));
|
||||
__m128i d3 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 48));
|
||||
__m128i d4 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 64));
|
||||
__m128i d5 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 80));
|
||||
__m128i d6 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 96));
|
||||
__m128i d7 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 112));
|
||||
d0 = _mm_xor_si128(d0,s0);
|
||||
d1 = _mm_xor_si128(d1,s1);
|
||||
d2 = _mm_xor_si128(d2,s2);
|
||||
d3 = _mm_xor_si128(d3,s3);
|
||||
d4 = _mm_xor_si128(d4,s4);
|
||||
d5 = _mm_xor_si128(d5,s5);
|
||||
d6 = _mm_xor_si128(d6,s6);
|
||||
d7 = _mm_xor_si128(d7,s7);
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(d),d0);
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(d + 16),d1);
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(d + 32),d2);
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(d + 48),d3);
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(d + 64),d4);
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(d + 80),d5);
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(d + 96),d6);
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(d + 112),d7);
|
||||
s += 128;
|
||||
d += 128;
|
||||
len -= 128;
|
||||
}
|
||||
while (len >= 16) {
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(d),_mm_xor_si128(_mm_loadu_si128(reinterpret_cast<__m128i *>(d)),_mm_loadu_si128(reinterpret_cast<const __m128i *>(s))));
|
||||
s += 16;
|
||||
d += 16;
|
||||
len -= 16;
|
||||
}
|
||||
#else
|
||||
#ifndef ZT_NO_TYPE_PUNNING
|
||||
while (len >= 16) {
|
||||
(*reinterpret_cast<uint64_t *>(d)) ^= (*reinterpret_cast<const uint64_t *>(s));
|
||||
s += 8;
|
||||
d += 8;
|
||||
(*reinterpret_cast<uint64_t *>(d)) ^= (*reinterpret_cast<const uint64_t *>(s));
|
||||
s += 8;
|
||||
d += 8;
|
||||
len -= 16;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
while (len) {
|
||||
--len;
|
||||
*(d++) ^= *(s++);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key 256-bit (32 byte) key
|
||||
* @param iv 64-bit initialization vector
|
||||
*/
|
||||
Salsa20(const void *key,const void *iv)
|
||||
{
|
||||
init(key,iv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize cipher
|
||||
*
|
||||
* @param key Key bits
|
||||
* @param iv 64-bit initialization vector
|
||||
*/
|
||||
void init(const void *key,const void *iv);
|
||||
|
||||
/**
|
||||
* Encrypt/decrypt data using Salsa20/12
|
||||
*
|
||||
* @param in Input data
|
||||
* @param out Output buffer
|
||||
* @param bytes Length of data
|
||||
*/
|
||||
void crypt12(const void *in,void *out,unsigned int bytes);
|
||||
|
||||
/**
|
||||
* Encrypt/decrypt data using Salsa20/20
|
||||
*
|
||||
* @param in Input data
|
||||
* @param out Output buffer
|
||||
* @param bytes Length of data
|
||||
*/
|
||||
void crypt20(const void *in,void *out,unsigned int bytes);
|
||||
|
||||
private:
|
||||
union {
|
||||
#ifdef ZT_SALSA20_SSE
|
||||
__m128i v[4];
|
||||
#endif // ZT_SALSA20_SSE
|
||||
uint32_t i[16];
|
||||
} _state;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "SelfAwareness.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Node.hpp"
|
||||
#include "Topology.hpp"
|
||||
#include "Packet.hpp"
|
||||
#include "Peer.hpp"
|
||||
#include "Switch.hpp"
|
||||
#include "Trace.hpp"
|
||||
|
||||
// Entry timeout -- make it fairly long since this is just to prevent stale buildup
|
||||
#define ZT_SELFAWARENESS_ENTRY_TIMEOUT 600000
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class _ResetWithinScope
|
||||
{
|
||||
public:
|
||||
_ResetWithinScope(void *tPtr,int64_t now,int inetAddressFamily,InetAddress::IpScope scope) :
|
||||
_now(now),
|
||||
_tPtr(tPtr),
|
||||
_family(inetAddressFamily),
|
||||
_scope(scope) {}
|
||||
|
||||
inline void operator()(Topology &t,const SharedPtr<Peer> &p) { p->resetWithinScope(_tPtr,_scope,_family,_now); }
|
||||
|
||||
private:
|
||||
uint64_t _now;
|
||||
void *_tPtr;
|
||||
int _family;
|
||||
InetAddress::IpScope _scope;
|
||||
};
|
||||
|
||||
SelfAwareness::SelfAwareness(const RuntimeEnvironment *renv) :
|
||||
RR(renv),
|
||||
_phy(128)
|
||||
{
|
||||
}
|
||||
|
||||
void SelfAwareness::iam(void *tPtr,const Address &reporter,const int64_t receivedOnLocalSocket,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,int64_t now)
|
||||
{
|
||||
const InetAddress::IpScope scope = myPhysicalAddress.ipScope();
|
||||
|
||||
if ((scope != reporterPhysicalAddress.ipScope())||(scope == InetAddress::IP_SCOPE_NONE)||(scope == InetAddress::IP_SCOPE_LOOPBACK)||(scope == InetAddress::IP_SCOPE_MULTICAST)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Mutex::Lock _l(_phy_m);
|
||||
PhySurfaceEntry &entry = _phy[PhySurfaceKey(reporter,receivedOnLocalSocket,reporterPhysicalAddress,scope)];
|
||||
|
||||
if ( (trusted) && ((now - entry.ts) < ZT_SELFAWARENESS_ENTRY_TIMEOUT) && (!entry.mySurface.ipsEqual(myPhysicalAddress)) ) {
|
||||
// Changes to external surface reported by trusted peers causes path reset in this scope
|
||||
RR->t->resettingPathsInScope(tPtr,reporter,reporterPhysicalAddress,myPhysicalAddress,scope);
|
||||
|
||||
entry.mySurface = myPhysicalAddress;
|
||||
entry.ts = now;
|
||||
entry.trusted = trusted;
|
||||
|
||||
// Erase all entries in this scope that were not reported from this remote address to prevent 'thrashing'
|
||||
// due to multiple reports of endpoint change.
|
||||
// Don't use 'entry' after this since hash table gets modified.
|
||||
{
|
||||
Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
|
||||
PhySurfaceKey *k = (PhySurfaceKey *)0;
|
||||
PhySurfaceEntry *e = (PhySurfaceEntry *)0;
|
||||
while (i.next(k,e)) {
|
||||
if ((k->reporterPhysicalAddress != reporterPhysicalAddress)&&(k->scope == scope)) {
|
||||
_phy.erase(*k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset all paths within this scope and address family
|
||||
_ResetWithinScope rset(tPtr,now,myPhysicalAddress.ss_family,(InetAddress::IpScope)scope);
|
||||
RR->topology->eachPeer<_ResetWithinScope &>(rset);
|
||||
} else {
|
||||
// Otherwise just update DB to use to determine external surface info
|
||||
entry.mySurface = myPhysicalAddress;
|
||||
entry.ts = now;
|
||||
entry.trusted = trusted;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<InetAddress> SelfAwareness::whoami()
|
||||
{
|
||||
std::vector<InetAddress> surfaceAddresses;
|
||||
Mutex::Lock _l(_phy_m);
|
||||
Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
|
||||
PhySurfaceKey *k = (PhySurfaceKey *)0;
|
||||
PhySurfaceEntry *e = (PhySurfaceEntry *)0;
|
||||
while (i.next(k,e)) {
|
||||
if (std::find(surfaceAddresses.begin(), surfaceAddresses.end(), e->mySurface) == surfaceAddresses.end()) {
|
||||
surfaceAddresses.push_back(e->mySurface);
|
||||
}
|
||||
}
|
||||
return surfaceAddresses;
|
||||
}
|
||||
|
||||
void SelfAwareness::clean(int64_t now)
|
||||
{
|
||||
Mutex::Lock _l(_phy_m);
|
||||
Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
|
||||
PhySurfaceKey *k = (PhySurfaceKey *)0;
|
||||
PhySurfaceEntry *e = (PhySurfaceEntry *)0;
|
||||
while (i.next(k,e)) {
|
||||
if ((now - e->ts) >= ZT_SELFAWARENESS_ENTRY_TIMEOUT) {
|
||||
_phy.erase(*k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_SELFAWARENESS_HPP
|
||||
#define ZT_SELFAWARENESS_HPP
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "Hashtable.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "Mutex.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class RuntimeEnvironment;
|
||||
|
||||
/**
|
||||
* Tracks changes to this peer's real world addresses
|
||||
*/
|
||||
class SelfAwareness
|
||||
{
|
||||
public:
|
||||
SelfAwareness(const RuntimeEnvironment *renv);
|
||||
|
||||
/**
|
||||
* Called when a trusted remote peer informs us of our external network address
|
||||
*
|
||||
* @param reporter ZeroTier address of reporting peer
|
||||
* @param receivedOnLocalAddress Local address on which report was received
|
||||
* @param reporterPhysicalAddress Physical address that reporting peer seems to have
|
||||
* @param myPhysicalAddress Physical address that peer says we have
|
||||
* @param trusted True if this peer is trusted as an authority to inform us of external address changes
|
||||
* @param now Current time
|
||||
*/
|
||||
void iam(void *tPtr,const Address &reporter,const int64_t receivedOnLocalSocket,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,int64_t now);
|
||||
|
||||
/**
|
||||
* Return all known external surface addresses reported by peers
|
||||
*
|
||||
* @return A vector of InetAddress(es)
|
||||
*/
|
||||
std::vector<InetAddress> whoami();
|
||||
|
||||
/**
|
||||
* Clean up database periodically
|
||||
*
|
||||
* @param now Current time
|
||||
*/
|
||||
void clean(int64_t now);
|
||||
|
||||
private:
|
||||
struct PhySurfaceKey
|
||||
{
|
||||
Address reporter;
|
||||
int64_t receivedOnLocalSocket;
|
||||
InetAddress reporterPhysicalAddress;
|
||||
InetAddress::IpScope scope;
|
||||
|
||||
PhySurfaceKey() : reporter(),scope(InetAddress::IP_SCOPE_NONE) {}
|
||||
PhySurfaceKey(const Address &r,const int64_t rol,const InetAddress &ra,InetAddress::IpScope s) : reporter(r),receivedOnLocalSocket(rol),reporterPhysicalAddress(ra),scope(s) {}
|
||||
|
||||
inline unsigned long hashCode() const { return ((unsigned long)reporter.toInt() + (unsigned long)scope); }
|
||||
inline bool operator==(const PhySurfaceKey &k) const { return ((reporter == k.reporter)&&(receivedOnLocalSocket == k.receivedOnLocalSocket)&&(reporterPhysicalAddress == k.reporterPhysicalAddress)&&(scope == k.scope)); }
|
||||
};
|
||||
struct PhySurfaceEntry
|
||||
{
|
||||
InetAddress mySurface;
|
||||
uint64_t ts;
|
||||
bool trusted;
|
||||
|
||||
PhySurfaceEntry() : mySurface(),ts(0),trusted(false) {}
|
||||
PhySurfaceEntry(const InetAddress &a,const uint64_t t) : mySurface(a),ts(t),trusted(false) {}
|
||||
};
|
||||
|
||||
const RuntimeEnvironment *RR;
|
||||
|
||||
Hashtable< PhySurfaceKey,PhySurfaceEntry > _phy;
|
||||
Mutex _phy_m;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_SHAREDPTR_HPP
|
||||
#define ZT_SHAREDPTR_HPP
|
||||
|
||||
#include "Mutex.hpp"
|
||||
#include "AtomicCounter.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* Simple zero-overhead introspective reference counted pointer
|
||||
*
|
||||
* This is an introspective shared pointer. Classes that need to be reference
|
||||
* counted must list this as a 'friend' and must have a private instance of
|
||||
* AtomicCounter called __refCount.
|
||||
*/
|
||||
template<typename T>
|
||||
class SharedPtr
|
||||
{
|
||||
public:
|
||||
SharedPtr() : _ptr((T *)0) {}
|
||||
SharedPtr(T *obj) : _ptr(obj) { ++obj->__refCount; }
|
||||
SharedPtr(const SharedPtr &sp) : _ptr(sp._getAndInc()) {}
|
||||
|
||||
~SharedPtr()
|
||||
{
|
||||
if (_ptr) {
|
||||
if (--_ptr->__refCount <= 0) {
|
||||
delete _ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline SharedPtr &operator=(const SharedPtr &sp)
|
||||
{
|
||||
if (_ptr != sp._ptr) {
|
||||
T *p = sp._getAndInc();
|
||||
if (_ptr) {
|
||||
if (--_ptr->__refCount <= 0) {
|
||||
delete _ptr;
|
||||
}
|
||||
}
|
||||
_ptr = p;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to a naked pointer and increment its reference count
|
||||
*
|
||||
* This assumes this SharedPtr is NULL and that ptr is not a 'zombie.' No
|
||||
* checks are performed.
|
||||
*
|
||||
* @param ptr Naked pointer to assign
|
||||
*/
|
||||
inline void set(T *ptr)
|
||||
{
|
||||
zero();
|
||||
++ptr->__refCount;
|
||||
_ptr = ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap with another pointer 'for free' without ref count overhead
|
||||
*
|
||||
* @param with Pointer to swap with
|
||||
*/
|
||||
inline void swap(SharedPtr &with)
|
||||
{
|
||||
T *tmp = _ptr;
|
||||
_ptr = with._ptr;
|
||||
with._ptr = tmp;
|
||||
}
|
||||
|
||||
inline operator bool() const { return (_ptr != (T *)0); }
|
||||
inline T &operator*() const { return *_ptr; }
|
||||
inline T *operator->() const { return _ptr; }
|
||||
|
||||
/**
|
||||
* @return Raw pointer to held object
|
||||
*/
|
||||
inline T *ptr() const { return _ptr; }
|
||||
|
||||
/**
|
||||
* Set this pointer to NULL
|
||||
*/
|
||||
inline void zero()
|
||||
{
|
||||
if (_ptr) {
|
||||
if (--_ptr->__refCount <= 0) {
|
||||
delete _ptr;
|
||||
}
|
||||
_ptr = (T *)0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Number of references according to this object's ref count or 0 if NULL
|
||||
*/
|
||||
inline int references()
|
||||
{
|
||||
if (_ptr) {
|
||||
return _ptr->__refCount.load();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline bool operator==(const SharedPtr &sp) const { return (_ptr == sp._ptr); }
|
||||
inline bool operator!=(const SharedPtr &sp) const { return (_ptr != sp._ptr); }
|
||||
inline bool operator>(const SharedPtr &sp) const { return (_ptr > sp._ptr); }
|
||||
inline bool operator<(const SharedPtr &sp) const { return (_ptr < sp._ptr); }
|
||||
inline bool operator>=(const SharedPtr &sp) const { return (_ptr >= sp._ptr); }
|
||||
inline bool operator<=(const SharedPtr &sp) const { return (_ptr <= sp._ptr); }
|
||||
|
||||
private:
|
||||
inline T *_getAndInc() const
|
||||
{
|
||||
if (_ptr) {
|
||||
++_ptr->__refCount;
|
||||
}
|
||||
return _ptr;
|
||||
}
|
||||
T *_ptr;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
+1243
File diff suppressed because it is too large
Load Diff
+332
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
* Copyright (c)2013-2020 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_N_SWITCH_HPP
|
||||
#define ZT_N_SWITCH_HPP
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Mutex.hpp"
|
||||
#include "MAC.hpp"
|
||||
#include "Packet.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "Topology.hpp"
|
||||
#include "Network.hpp"
|
||||
#include "SharedPtr.hpp"
|
||||
#include "IncomingPacket.hpp"
|
||||
#include "Hashtable.hpp"
|
||||
|
||||
/* Ethernet frame types that might be relevant to us */
|
||||
#define ZT_ETHERTYPE_IPV4 0x0800
|
||||
#define ZT_ETHERTYPE_ARP 0x0806
|
||||
#define ZT_ETHERTYPE_RARP 0x8035
|
||||
#define ZT_ETHERTYPE_ATALK 0x809b
|
||||
#define ZT_ETHERTYPE_AARP 0x80f3
|
||||
#define ZT_ETHERTYPE_IPX_A 0x8137
|
||||
#define ZT_ETHERTYPE_IPX_B 0x8138
|
||||
#define ZT_ETHERTYPE_IPV6 0x86dd
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class RuntimeEnvironment;
|
||||
class Peer;
|
||||
|
||||
/**
|
||||
* Core of the distributed Ethernet switch and protocol implementation
|
||||
*
|
||||
* This class is perhaps a bit misnamed, but it's basically where everything
|
||||
* meets. Transport-layer ZT packets come in here, as do virtual network
|
||||
* packets from tap devices, and this sends them where they need to go and
|
||||
* wraps/unwraps accordingly. It also handles queues and timeouts and such.
|
||||
*/
|
||||
class Switch
|
||||
{
|
||||
struct ManagedQueue;
|
||||
struct TXQueueEntry;
|
||||
|
||||
friend class SharedPtr<Peer>;
|
||||
|
||||
typedef struct {
|
||||
TXQueueEntry *p;
|
||||
bool ok_to_drop;
|
||||
} dqr;
|
||||
|
||||
public:
|
||||
Switch(const RuntimeEnvironment *renv);
|
||||
|
||||
/**
|
||||
* Called when a packet is received from the real network
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param localSocket Local I/O socket as supplied by external code
|
||||
* @param fromAddr Internet IP address of origin
|
||||
* @param data Packet data
|
||||
* @param len Packet length
|
||||
*/
|
||||
void onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddress &fromAddr,const void *data,unsigned int len);
|
||||
|
||||
/**
|
||||
* Returns whether our bonding or balancing policy is aware of flows.
|
||||
*/
|
||||
bool isFlowAware();
|
||||
|
||||
/**
|
||||
* Called when a packet comes from a local Ethernet tap
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param network Which network's TAP did this packet come from?
|
||||
* @param from Originating MAC address
|
||||
* @param to Destination MAC address
|
||||
* @param etherType Ethernet packet type
|
||||
* @param vlanId VLAN ID or 0 if none
|
||||
* @param data Ethernet payload
|
||||
* @param len Frame length
|
||||
*/
|
||||
void onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len);
|
||||
|
||||
/**
|
||||
* Determines the next drop schedule for packets in the TX queue
|
||||
*
|
||||
* @param t Current time
|
||||
* @param count Number of packets dropped this round
|
||||
*/
|
||||
uint64_t control_law(uint64_t t, int count);
|
||||
|
||||
/**
|
||||
* Selects a packet eligible for transmission from a TX queue. According to the control law, multiple packets
|
||||
* may be intentionally dropped before a packet is returned to the AQM scheduler.
|
||||
*
|
||||
* @param q The TX queue that is being dequeued from
|
||||
* @param now Current time
|
||||
*/
|
||||
dqr dodequeue(ManagedQueue *q, uint64_t now);
|
||||
|
||||
/**
|
||||
* Presents a packet to the AQM scheduler.
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param network Network that the packet shall be sent over
|
||||
* @param packet Packet to be sent
|
||||
* @param encrypt Encrypt packet payload? (always true except for HELLO)
|
||||
* @param qosBucket Which bucket the rule-system determined this packet should fall into
|
||||
*/
|
||||
void aqm_enqueue(void *tPtr, const SharedPtr<Network> &network, Packet &packet,bool encrypt,int qosBucket,int32_t flowId = ZT_QOS_NO_FLOW);
|
||||
|
||||
/**
|
||||
* Performs a single AQM cycle and dequeues and transmits all eligible packets on all networks
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
*/
|
||||
void aqm_dequeue(void *tPtr);
|
||||
|
||||
/**
|
||||
* Calls the dequeue mechanism and adjust queue state variables
|
||||
*
|
||||
* @param q The TX queue that is being dequeued from
|
||||
* @param isNew Whether or not this queue is in the NEW list
|
||||
* @param now Current time
|
||||
*/
|
||||
Switch::TXQueueEntry * CoDelDequeue(ManagedQueue *q, bool isNew, uint64_t now);
|
||||
|
||||
/**
|
||||
* Removes QoS Queues and flow state variables for a specific network. These queues are created
|
||||
* automatically upon the transmission of the first packet from this peer to another peer on the
|
||||
* given network.
|
||||
*
|
||||
* The reason for existence of queues and flow state variables specific to each network is so that
|
||||
* each network's QoS rules function independently.
|
||||
*
|
||||
* @param nwid Network ID
|
||||
*/
|
||||
void removeNetworkQoSControlBlock(uint64_t nwid);
|
||||
|
||||
/**
|
||||
* Send a packet to a ZeroTier address (destination in packet)
|
||||
*
|
||||
* The packet must be fully composed with source and destination but not
|
||||
* yet encrypted. If the destination peer is known the packet
|
||||
* is sent immediately. Otherwise it is queued and a WHOIS is dispatched.
|
||||
*
|
||||
* The packet may be compressed. Compression isn't done here.
|
||||
*
|
||||
* Needless to say, the packet's source must be this node. Otherwise it
|
||||
* won't be encrypted right. (This is not used for relaying.)
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param packet Packet to send (buffer may be modified)
|
||||
* @param encrypt Encrypt packet payload? (always true except for HELLO)
|
||||
*/
|
||||
void send(void *tPtr,Packet &packet,bool encrypt,int32_t flowId = ZT_QOS_NO_FLOW);
|
||||
|
||||
/**
|
||||
* Request WHOIS on a given address
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param now Current time
|
||||
* @param addr Address to look up
|
||||
*/
|
||||
void requestWhois(void *tPtr,const int64_t now,const Address &addr);
|
||||
|
||||
/**
|
||||
* Run any processes that are waiting for this peer's identity
|
||||
*
|
||||
* Called when we learn of a peer's identity from HELLO, OK(WHOIS), etc.
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param peer New peer
|
||||
*/
|
||||
void doAnythingWaitingForPeer(void *tPtr,const SharedPtr<Peer> &peer);
|
||||
|
||||
/**
|
||||
* Perform retries and other periodic timer tasks
|
||||
*
|
||||
* This can return a very long delay if there are no pending timer
|
||||
* tasks. The caller should cap this comparatively vs. other values.
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param now Current time
|
||||
* @return Number of milliseconds until doTimerTasks() should be run again
|
||||
*/
|
||||
unsigned long doTimerTasks(void *tPtr,int64_t now);
|
||||
|
||||
private:
|
||||
bool _shouldUnite(const int64_t now,const Address &source,const Address &destination);
|
||||
bool _trySend(void *tPtr,Packet &packet,bool encrypt,int32_t flowId = ZT_QOS_NO_FLOW); // packet is modified if return is true
|
||||
void _sendViaSpecificPath(void *tPtr,SharedPtr<Peer> peer,SharedPtr<Path> viaPath,uint16_t userSpecifiedMtu, int64_t now,Packet &packet,bool encrypt,int32_t flowId);
|
||||
void _recordOutgoingPacketMetrics(const Packet &p);
|
||||
|
||||
const RuntimeEnvironment *const RR;
|
||||
int64_t _lastBeaconResponse;
|
||||
volatile int64_t _lastCheckedQueues;
|
||||
|
||||
// Time we last sent a WHOIS request for each address
|
||||
Hashtable< Address,int64_t > _lastSentWhoisRequest;
|
||||
Mutex _lastSentWhoisRequest_m;
|
||||
|
||||
// Packets waiting for WHOIS replies or other decode info or missing fragments
|
||||
struct RXQueueEntry
|
||||
{
|
||||
RXQueueEntry() : timestamp(0) {}
|
||||
volatile int64_t timestamp; // 0 if entry is not in use
|
||||
volatile uint64_t packetId;
|
||||
IncomingPacket frag0; // head of packet
|
||||
Packet::Fragment frags[ZT_MAX_PACKET_FRAGMENTS - 1]; // later fragments (if any)
|
||||
unsigned int totalFragments; // 0 if only frag0 received, waiting for frags
|
||||
uint32_t haveFragments; // bit mask, LSB to MSB
|
||||
volatile bool complete; // if true, packet is complete
|
||||
volatile int32_t flowId;
|
||||
Mutex lock;
|
||||
};
|
||||
RXQueueEntry _rxQueue[ZT_RX_QUEUE_SIZE];
|
||||
AtomicCounter _rxQueuePtr;
|
||||
|
||||
// Returns matching or next available RX queue entry
|
||||
inline RXQueueEntry *_findRXQueueEntry(uint64_t packetId)
|
||||
{
|
||||
const unsigned int current = static_cast<unsigned int>(_rxQueuePtr.load());
|
||||
for(unsigned int k=1;k<=ZT_RX_QUEUE_SIZE;++k) {
|
||||
RXQueueEntry *rq = &(_rxQueue[(current - k) % ZT_RX_QUEUE_SIZE]);
|
||||
if ((rq->packetId == packetId)&&(rq->timestamp)) {
|
||||
return rq;
|
||||
}
|
||||
}
|
||||
++_rxQueuePtr;
|
||||
return &(_rxQueue[static_cast<unsigned int>(current) % ZT_RX_QUEUE_SIZE]);
|
||||
}
|
||||
|
||||
// Returns current entry in rx queue ring buffer and increments ring pointer
|
||||
inline RXQueueEntry *_nextRXQueueEntry()
|
||||
{
|
||||
return &(_rxQueue[static_cast<unsigned int>((++_rxQueuePtr) - 1) % ZT_RX_QUEUE_SIZE]);
|
||||
}
|
||||
|
||||
// ZeroTier-layer TX queue entry
|
||||
struct TXQueueEntry
|
||||
{
|
||||
TXQueueEntry() {}
|
||||
TXQueueEntry(Address d,uint64_t ct,const Packet &p,bool enc,int32_t fid) :
|
||||
dest(d),
|
||||
creationTime(ct),
|
||||
packet(p),
|
||||
encrypt(enc),
|
||||
flowId(fid) {}
|
||||
|
||||
Address dest;
|
||||
uint64_t creationTime;
|
||||
Packet packet; // unencrypted/unMAC'd packet -- this is done at send time
|
||||
bool encrypt;
|
||||
int32_t flowId;
|
||||
};
|
||||
std::list< TXQueueEntry > _txQueue;
|
||||
Mutex _txQueue_m;
|
||||
Mutex _aqm_m;
|
||||
|
||||
// Tracks sending of VERB_RENDEZVOUS to relaying peers
|
||||
struct _LastUniteKey
|
||||
{
|
||||
_LastUniteKey() : x(0),y(0) {}
|
||||
_LastUniteKey(const Address &a1,const Address &a2)
|
||||
{
|
||||
if (a1 > a2) {
|
||||
x = a2.toInt();
|
||||
y = a1.toInt();
|
||||
} else {
|
||||
x = a1.toInt();
|
||||
y = a2.toInt();
|
||||
}
|
||||
}
|
||||
inline unsigned long hashCode() const { return ((unsigned long)x ^ (unsigned long)y); }
|
||||
inline bool operator==(const _LastUniteKey &k) const { return ((x == k.x)&&(y == k.y)); }
|
||||
uint64_t x,y;
|
||||
};
|
||||
Hashtable< _LastUniteKey,uint64_t > _lastUniteAttempt; // key is always sorted in ascending order, for set-like behavior
|
||||
Mutex _lastUniteAttempt_m;
|
||||
|
||||
// Queue with additional flow state variables
|
||||
struct ManagedQueue
|
||||
{
|
||||
ManagedQueue(int id) :
|
||||
id(id),
|
||||
byteCredit(ZT_AQM_QUANTUM),
|
||||
byteLength(0),
|
||||
dropping(false)
|
||||
{}
|
||||
int id;
|
||||
int byteCredit;
|
||||
int byteLength;
|
||||
uint64_t first_above_time;
|
||||
uint32_t count;
|
||||
uint64_t drop_next;
|
||||
bool dropping;
|
||||
uint64_t drop_next_time;
|
||||
std::list< TXQueueEntry *> q;
|
||||
};
|
||||
// To implement fq_codel we need to maintain a queue of queues
|
||||
struct NetworkQoSControlBlock
|
||||
{
|
||||
int _currEnqueuedPackets;
|
||||
std::vector<ManagedQueue *> newQueues;
|
||||
std::vector<ManagedQueue *> oldQueues;
|
||||
std::vector<ManagedQueue *> inactiveQueues;
|
||||
};
|
||||
std::map<uint64_t,NetworkQoSControlBlock*> _netQueueControlBlock;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#include "Tag.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "Topology.hpp"
|
||||
#include "Switch.hpp"
|
||||
#include "Network.hpp"
|
||||
#include "Node.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
int Tag::verify(const RuntimeEnvironment *RR,void *tPtr) const
|
||||
{
|
||||
if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId))) {
|
||||
return -1;
|
||||
}
|
||||
const Identity id(RR->topology->getIdentity(tPtr,_signedBy));
|
||||
if (!id) {
|
||||
RR->sw->requestWhois(tPtr,RR->node->now(),_signedBy);
|
||||
return 1;
|
||||
}
|
||||
try {
|
||||
Buffer<(sizeof(Tag) * 2)> tmp;
|
||||
this->serialize(tmp,true);
|
||||
return (id.verify(tmp.data(),tmp.size(),_signature) ? 0 : -1);
|
||||
} catch ( ... ) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_TAG_HPP
|
||||
#define ZT_TAG_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Credential.hpp"
|
||||
#include "C25519.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "Buffer.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class RuntimeEnvironment;
|
||||
|
||||
/**
|
||||
* A tag that can be associated with members and matched in rules
|
||||
*
|
||||
* Capabilities group rules, while tags group members subject to those
|
||||
* rules. Tag values can be matched in rules, and tags relevant to a
|
||||
* capability are presented along with it.
|
||||
*
|
||||
* E.g. a capability might be "can speak Samba/CIFS within your
|
||||
* department." This cap might have a rule to allow TCP/137 but
|
||||
* only if a given tag ID's value matches between two peers. The
|
||||
* capability is what members can do, while the tag is who they are.
|
||||
* Different departments might have tags with the same ID but different
|
||||
* values.
|
||||
*
|
||||
* Unlike capabilities tags are signed only by the issuer and are never
|
||||
* transferable.
|
||||
*/
|
||||
class Tag : public Credential
|
||||
{
|
||||
public:
|
||||
static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_TAG; }
|
||||
|
||||
Tag() :
|
||||
_id(0),
|
||||
_value(0),
|
||||
_networkId(0),
|
||||
_ts(0)
|
||||
{
|
||||
memset(_signature.data,0,sizeof(_signature.data));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nwid Network ID
|
||||
* @param ts Timestamp
|
||||
* @param issuedTo Address to which this tag was issued
|
||||
* @param id Tag ID
|
||||
* @param value Tag value
|
||||
*/
|
||||
Tag(const uint64_t nwid,const int64_t ts,const Address &issuedTo,const uint32_t id,const uint32_t value) :
|
||||
_id(id),
|
||||
_value(value),
|
||||
_networkId(nwid),
|
||||
_ts(ts),
|
||||
_issuedTo(issuedTo),
|
||||
_signedBy()
|
||||
{
|
||||
memset(_signature.data,0,sizeof(_signature.data));
|
||||
}
|
||||
|
||||
inline uint32_t id() const { return _id; }
|
||||
inline const uint32_t &value() const { return _value; }
|
||||
inline uint64_t networkId() const { return _networkId; }
|
||||
inline int64_t timestamp() const { return _ts; }
|
||||
inline const Address &issuedTo() const { return _issuedTo; }
|
||||
inline const Address &signedBy() const { return _signedBy; }
|
||||
|
||||
/**
|
||||
* Sign this tag
|
||||
*
|
||||
* @param signer Signing identity, must have private key
|
||||
* @return True if signature was successful
|
||||
*/
|
||||
inline bool sign(const Identity &signer)
|
||||
{
|
||||
if (signer.hasPrivate()) {
|
||||
Buffer<sizeof(Tag) + 64> tmp;
|
||||
_signedBy = signer.address();
|
||||
this->serialize(tmp,true);
|
||||
_signature = signer.sign(tmp.data(),tmp.size());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check this tag's signature
|
||||
*
|
||||
* @param RR Runtime environment to allow identity lookup for signedBy
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or tag
|
||||
*/
|
||||
int verify(const RuntimeEnvironment *RR,void *tPtr) const;
|
||||
|
||||
template<unsigned int C>
|
||||
inline void serialize(Buffer<C> &b,const bool forSign = false) const
|
||||
{
|
||||
if (forSign) {
|
||||
b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
|
||||
}
|
||||
|
||||
b.append(_networkId);
|
||||
b.append(_ts);
|
||||
b.append(_id);
|
||||
b.append(_value);
|
||||
|
||||
_issuedTo.appendTo(b);
|
||||
_signedBy.appendTo(b);
|
||||
if (!forSign) {
|
||||
b.append((uint8_t)1); // 1 == Ed25519
|
||||
b.append((uint16_t)ZT_C25519_SIGNATURE_LEN); // length of signature
|
||||
b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
|
||||
}
|
||||
|
||||
b.append((uint16_t)0); // length of additional fields, currently 0
|
||||
|
||||
if (forSign) {
|
||||
b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned int C>
|
||||
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
|
||||
{
|
||||
unsigned int p = startAt;
|
||||
|
||||
*this = Tag();
|
||||
|
||||
_networkId = b.template at<uint64_t>(p);
|
||||
p += 8;
|
||||
_ts = b.template at<uint64_t>(p);
|
||||
p += 8;
|
||||
_id = b.template at<uint32_t>(p);
|
||||
p += 4;
|
||||
|
||||
_value = b.template at<uint32_t>(p);
|
||||
p += 4;
|
||||
|
||||
_issuedTo.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
|
||||
p += ZT_ADDRESS_LENGTH;
|
||||
_signedBy.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
|
||||
p += ZT_ADDRESS_LENGTH;
|
||||
if (b[p++] == 1) {
|
||||
if (b.template at<uint16_t>(p) != ZT_C25519_SIGNATURE_LEN) {
|
||||
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
|
||||
}
|
||||
p += 2;
|
||||
memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN);
|
||||
p += ZT_C25519_SIGNATURE_LEN;
|
||||
} else {
|
||||
p += 2 + b.template at<uint16_t>(p);
|
||||
}
|
||||
|
||||
p += 2 + b.template at<uint16_t>(p);
|
||||
if (p > b.size()) {
|
||||
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
|
||||
}
|
||||
|
||||
return (p - startAt);
|
||||
}
|
||||
|
||||
// Provides natural sort order by ID
|
||||
inline bool operator<(const Tag &t) const { return (_id < t._id); }
|
||||
|
||||
inline bool operator==(const Tag &t) const { return (memcmp(this,&t,sizeof(Tag)) == 0); }
|
||||
inline bool operator!=(const Tag &t) const { return (memcmp(this,&t,sizeof(Tag)) != 0); }
|
||||
|
||||
// For searching sorted arrays or lists of Tags by ID
|
||||
struct IdComparePredicate
|
||||
{
|
||||
inline bool operator()(const Tag &a,const Tag &b) const { return (a.id() < b.id()); }
|
||||
inline bool operator()(const uint32_t a,const Tag &b) const { return (a < b.id()); }
|
||||
inline bool operator()(const Tag &a,const uint32_t b) const { return (a.id() < b); }
|
||||
inline bool operator()(const Tag *a,const Tag *b) const { return (a->id() < b->id()); }
|
||||
inline bool operator()(const Tag *a,const Tag &b) const { return (a->id() < b.id()); }
|
||||
inline bool operator()(const Tag &a,const Tag *b) const { return (a.id() < b->id()); }
|
||||
inline bool operator()(const uint32_t a,const Tag *b) const { return (a < b->id()); }
|
||||
inline bool operator()(const Tag *a,const uint32_t b) const { return (a->id() < b); }
|
||||
inline bool operator()(const uint32_t a,const uint32_t b) const { return (a < b); }
|
||||
};
|
||||
|
||||
private:
|
||||
uint32_t _id;
|
||||
uint32_t _value;
|
||||
uint64_t _networkId;
|
||||
int64_t _ts;
|
||||
Address _issuedTo;
|
||||
Address _signedBy;
|
||||
C25519::Signature _signature;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,435 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Topology.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Node.hpp"
|
||||
#include "Network.hpp"
|
||||
#include "NetworkConfig.hpp"
|
||||
#include "Buffer.hpp"
|
||||
#include "Switch.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
#define ZT_DEFAULT_WORLD_LENGTH 570
|
||||
static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x7e,0xe9,0x57,0x60,0xcd,0xb8,0xb3,0x88,0xa4,0x69,0x22,0x14,0x91,0xaa,0x9a,0xcd,0x66,0xcc,0x76,0x4c,0xde,0xfd,0x56,0x03,0x9f,0x10,0x67,0xae,0x15,0xe6,0x9c,0x6f,0xb4,0x2d,0x7b,0x55,0x33,0x0e,0x3f,0xda,0xac,0x52,0x9c,0x07,0x92,0xfd,0x73,0x40,0xa6,0xaa,0x21,0xab,0xa8,0xa4,0x89,0xfd,0xae,0xa4,0x4a,0x39,0xbf,0x2d,0x00,0x65,0x9a,0xc9,0xc8,0x18,0xeb,0x36,0x00,0x92,0x76,0x37,0xef,0x4d,0x14,0x04,0xa4,0x4d,0x54,0x46,0x84,0x85,0x13,0x79,0x75,0x1f,0xaa,0x79,0xb4,0xc4,0xea,0x85,0x04,0x01,0x75,0xea,0x06,0x58,0x60,0x48,0x24,0x02,0xe1,0xeb,0x34,0x20,0x52,0x00,0x0e,0x62,0x90,0x06,0x1a,0x9b,0xe0,0xcd,0x29,0x3c,0x8b,0x55,0xf1,0xc3,0xd2,0x52,0x48,0x08,0xaf,0xc5,0x49,0x22,0x08,0x0e,0x35,0x39,0xa7,0x5a,0xdd,0xc3,0xce,0xf0,0xf6,0xad,0x26,0x0d,0x58,0x82,0x93,0xbb,0x77,0x86,0xe7,0x1e,0xfa,0x4b,0x90,0x57,0xda,0xd9,0x86,0x7a,0xfe,0x12,0xdd,0x04,0xca,0xfe,0x9e,0xfe,0xb9,0x00,0xcc,0xde,0xf7,0x6b,0xc7,0xb9,0x7d,0xed,0x90,0x4e,0xab,0xc5,0xdf,0x09,0x88,0x6d,0x9c,0x15,0x14,0xa6,0x10,0x03,0x6c,0xb9,0x13,0x9c,0xc2,0x14,0x00,0x1a,0x29,0x58,0x97,0x8e,0xfc,0xec,0x15,0x71,0x2d,0xd3,0x94,0x8c,0x6e,0x6b,0x3a,0x8e,0x89,0x3d,0xf0,0x1f,0xf4,0x93,0xd1,0xf8,0xd9,0x80,0x6a,0x86,0x0c,0x54,0x20,0x57,0x1b,0xf0,0x00,0x02,0x04,0x68,0xc2,0x08,0x86,0x27,0x09,0x06,0x26,0x05,0x98,0x80,0x02,0x00,0x12,0x00,0x00,0x30,0x05,0x71,0x0e,0x34,0x00,0x51,0x27,0x09,0x77,0x8c,0xde,0x71,0x90,0x00,0x3f,0x66,0x81,0xa9,0x9e,0x5a,0xd1,0x89,0x5e,0x9f,0xba,0x33,0xe6,0x21,0x2d,0x44,0x54,0xe1,0x68,0xbc,0xec,0x71,0x12,0x10,0x1b,0xf0,0x00,0x95,0x6e,0xd8,0xe9,0x2e,0x42,0x89,0x2c,0xb6,0xf2,0xec,0x41,0x08,0x81,0xa8,0x4a,0xb1,0x9d,0xa5,0x0e,0x12,0x87,0xba,0x3d,0x92,0x6c,0x3a,0x1f,0x75,0x5c,0xcc,0xf2,0x99,0xa1,0x20,0x70,0x55,0x00,0x02,0x04,0x67,0xc3,0x67,0x42,0x27,0x09,0x06,0x26,0x05,0x98,0x80,0x04,0x00,0x00,0xc3,0x02,0x54,0xf2,0xbc,0xa1,0xf7,0x00,0x19,0x27,0x09,0x62,0xf8,0x65,0xae,0x71,0x00,0xe2,0x07,0x6c,0x57,0xde,0x87,0x0e,0x62,0x88,0xd7,0xd5,0xe7,0x40,0x44,0x08,0xb1,0x54,0x5e,0xfc,0xa3,0x7d,0x67,0xf7,0x7b,0x87,0xe9,0xe5,0x41,0x68,0xc2,0x5d,0x3e,0xf1,0xa9,0xab,0xf2,0x90,0x5e,0xa5,0xe7,0x85,0xc0,0x1d,0xff,0x23,0x88,0x7a,0xd4,0x23,0x2d,0x95,0xc7,0xa8,0xfd,0x2c,0x27,0x11,0x1a,0x72,0xbd,0x15,0x93,0x22,0xdc,0x00,0x02,0x04,0x32,0x07,0xfc,0x8a,0x27,0x09,0x06,0x20,0x01,0x49,0xf0,0xd0,0xdb,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x27,0x09,0xca,0xfe,0x04,0xeb,0xa9,0x00,0x6c,0x6a,0x9d,0x1d,0xea,0x55,0xc1,0x61,0x6b,0xfe,0x2a,0x2b,0x8f,0x0f,0xf9,0xa8,0xca,0xca,0xf7,0x03,0x74,0xfb,0x1f,0x39,0xe3,0xbe,0xf8,0x1c,0xbf,0xeb,0xef,0x17,0xb7,0x22,0x82,0x68,0xa0,0xa2,0xa2,0x9d,0x34,0x88,0xc7,0x52,0x56,0x5c,0x6c,0x96,0x5c,0xbd,0x65,0x06,0xec,0x24,0x39,0x7c,0xc8,0xa5,0xd9,0xd1,0x52,0x85,0xa8,0x7f,0x00,0x02,0x04,0x54,0x11,0x35,0x9b,0x27,0x09,0x06,0x2a,0x02,0x6e,0xa0,0xd4,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x99,0x93,0x27,0x09};
|
||||
|
||||
Topology::Topology(const RuntimeEnvironment *renv,void *tPtr) :
|
||||
RR(renv),
|
||||
_numConfiguredPhysicalPaths(0),
|
||||
_amUpstream(false)
|
||||
{
|
||||
uint8_t tmp[ZT_WORLD_MAX_SERIALIZED_LENGTH];
|
||||
uint64_t idtmp[2];
|
||||
idtmp[0] = 0;
|
||||
idtmp[1] = 0;
|
||||
int n = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_PLANET,idtmp,tmp,sizeof(tmp));
|
||||
if (n > 0) {
|
||||
try {
|
||||
World cachedPlanet;
|
||||
cachedPlanet.deserialize(Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH>(tmp,(unsigned int)n),0);
|
||||
addWorld(tPtr,cachedPlanet,false);
|
||||
} catch ( ... ) {} // ignore invalid cached planets
|
||||
}
|
||||
|
||||
World defaultPlanet;
|
||||
{
|
||||
Buffer<ZT_DEFAULT_WORLD_LENGTH> wtmp(ZT_DEFAULT_WORLD,ZT_DEFAULT_WORLD_LENGTH);
|
||||
defaultPlanet.deserialize(wtmp,0); // throws on error, which would indicate a bad static variable up top
|
||||
}
|
||||
addWorld(tPtr,defaultPlanet,false);
|
||||
}
|
||||
|
||||
Topology::~Topology()
|
||||
{
|
||||
Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
|
||||
Address *a = (Address *)0;
|
||||
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
|
||||
while (i.next(a,p)) {
|
||||
_savePeer((void *)0,*p);
|
||||
}
|
||||
}
|
||||
|
||||
SharedPtr<Peer> Topology::addPeer(void *tPtr,const SharedPtr<Peer> &peer)
|
||||
{
|
||||
SharedPtr<Peer> np;
|
||||
{
|
||||
Mutex::Lock _l(_peers_m);
|
||||
SharedPtr<Peer> &hp = _peers[peer->address()];
|
||||
if (!hp) {
|
||||
hp = peer;
|
||||
}
|
||||
np = hp;
|
||||
}
|
||||
return np;
|
||||
}
|
||||
|
||||
SharedPtr<Peer> Topology::getPeer(void *tPtr,const Address &zta)
|
||||
{
|
||||
if (zta == RR->identity.address()) {
|
||||
return SharedPtr<Peer>();
|
||||
}
|
||||
|
||||
{
|
||||
Mutex::Lock _l(_peers_m);
|
||||
const SharedPtr<Peer> *const ap = _peers.get(zta);
|
||||
if (ap) {
|
||||
return *ap;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Buffer<ZT_PEER_MAX_SERIALIZED_STATE_SIZE> buf;
|
||||
uint64_t idbuf[2];
|
||||
idbuf[0] = zta.toInt();
|
||||
idbuf[1] = 0;
|
||||
int len = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_PEER,idbuf,buf.unsafeData(),ZT_PEER_MAX_SERIALIZED_STATE_SIZE);
|
||||
if (len > 0) {
|
||||
buf.setSize(len);
|
||||
Mutex::Lock _l(_peers_m);
|
||||
SharedPtr<Peer> &ap = _peers[zta];
|
||||
if (ap) {
|
||||
return ap;
|
||||
}
|
||||
ap = Peer::deserializeFromCache(RR->node->now(),tPtr,buf,RR);
|
||||
if (!ap) {
|
||||
_peers.erase(zta);
|
||||
}
|
||||
return SharedPtr<Peer>();
|
||||
}
|
||||
} catch ( ... ) {} // ignore invalid identities or other strange failures
|
||||
|
||||
return SharedPtr<Peer>();
|
||||
}
|
||||
|
||||
Identity Topology::getIdentity(void *tPtr,const Address &zta)
|
||||
{
|
||||
if (zta == RR->identity.address()) {
|
||||
return RR->identity;
|
||||
} else {
|
||||
Mutex::Lock _l(_peers_m);
|
||||
const SharedPtr<Peer> *const ap = _peers.get(zta);
|
||||
if (ap) {
|
||||
return (*ap)->identity();
|
||||
}
|
||||
}
|
||||
return Identity();
|
||||
}
|
||||
|
||||
SharedPtr<Peer> Topology::getUpstreamPeer()
|
||||
{
|
||||
const int64_t now = RR->node->now();
|
||||
unsigned int bestq = ~((unsigned int)0);
|
||||
const SharedPtr<Peer> *best = (const SharedPtr<Peer> *)0;
|
||||
|
||||
Mutex::Lock _l2(_peers_m);
|
||||
Mutex::Lock _l1(_upstreams_m);
|
||||
|
||||
for(std::vector<Address>::const_iterator a(_upstreamAddresses.begin());a!=_upstreamAddresses.end();++a) {
|
||||
const SharedPtr<Peer> *p = _peers.get(*a);
|
||||
if (p) {
|
||||
const unsigned int q = (*p)->relayQuality(now);
|
||||
if (q <= bestq) {
|
||||
bestq = q;
|
||||
best = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!best) {
|
||||
return SharedPtr<Peer>();
|
||||
}
|
||||
return *best;
|
||||
}
|
||||
|
||||
bool Topology::isUpstream(const Identity &id) const
|
||||
{
|
||||
Mutex::Lock _l(_upstreams_m);
|
||||
return (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),id.address()) != _upstreamAddresses.end());
|
||||
}
|
||||
|
||||
bool Topology::shouldAcceptWorldUpdateFrom(const Address &addr) const
|
||||
{
|
||||
Mutex::Lock _l(_upstreams_m);
|
||||
if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),addr) != _upstreamAddresses.end()) {
|
||||
return true;
|
||||
}
|
||||
for(std::vector< std::pair< uint64_t,Address> >::const_iterator s(_moonSeeds.begin());s!=_moonSeeds.end();++s) {
|
||||
if (s->second == addr) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ZT_PeerRole Topology::role(const Address &ztaddr) const
|
||||
{
|
||||
Mutex::Lock _l(_upstreams_m);
|
||||
if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),ztaddr) != _upstreamAddresses.end()) {
|
||||
for(std::vector<World::Root>::const_iterator i(_planet.roots().begin());i!=_planet.roots().end();++i) {
|
||||
if (i->identity.address() == ztaddr) {
|
||||
return ZT_PEER_ROLE_PLANET;
|
||||
}
|
||||
}
|
||||
return ZT_PEER_ROLE_MOON;
|
||||
}
|
||||
return ZT_PEER_ROLE_LEAF;
|
||||
}
|
||||
|
||||
bool Topology::isProhibitedEndpoint(const Address &ztaddr,const InetAddress &ipaddr) const
|
||||
{
|
||||
Mutex::Lock _l(_upstreams_m);
|
||||
|
||||
// For roots the only permitted addresses are those defined. This adds just a little
|
||||
// bit of extra security against spoofing, replaying, etc.
|
||||
if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),ztaddr) != _upstreamAddresses.end()) {
|
||||
for(std::vector<World::Root>::const_iterator r(_planet.roots().begin());r!=_planet.roots().end();++r) {
|
||||
if (r->identity.address() == ztaddr) {
|
||||
if (r->stableEndpoints.empty()) {
|
||||
return false; // no stable endpoints specified, so allow dynamic paths
|
||||
}
|
||||
for(std::vector<InetAddress>::const_iterator e(r->stableEndpoints.begin());e!=r->stableEndpoints.end();++e) {
|
||||
if (ipaddr.ipsEqual(*e)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for(std::vector<World>::const_iterator m(_moons.begin());m!=_moons.end();++m) {
|
||||
for(std::vector<World::Root>::const_iterator r(m->roots().begin());r!=m->roots().end();++r) {
|
||||
if (r->identity.address() == ztaddr) {
|
||||
if (r->stableEndpoints.empty()) {
|
||||
return false; // no stable endpoints specified, so allow dynamic paths
|
||||
}
|
||||
for(std::vector<InetAddress>::const_iterator e(r->stableEndpoints.begin());e!=r->stableEndpoints.end();++e) {
|
||||
if (ipaddr.ipsEqual(*e)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Topology::addWorld(void *tPtr,const World &newWorld,bool alwaysAcceptNew)
|
||||
{
|
||||
if ((newWorld.type() != World::TYPE_PLANET)&&(newWorld.type() != World::TYPE_MOON)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Mutex::Lock _l2(_peers_m);
|
||||
Mutex::Lock _l1(_upstreams_m);
|
||||
|
||||
World *existing = (World *)0;
|
||||
switch(newWorld.type()) {
|
||||
case World::TYPE_PLANET:
|
||||
existing = &_planet;
|
||||
break;
|
||||
case World::TYPE_MOON:
|
||||
for(std::vector< World >::iterator m(_moons.begin());m!=_moons.end();++m) {
|
||||
if (m->id() == newWorld.id()) {
|
||||
existing = &(*m);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (existing) {
|
||||
if (existing->shouldBeReplacedBy(newWorld)) {
|
||||
*existing = newWorld;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (newWorld.type() == World::TYPE_MOON) {
|
||||
if (alwaysAcceptNew) {
|
||||
_moons.push_back(newWorld);
|
||||
existing = &(_moons.back());
|
||||
} else {
|
||||
for(std::vector< std::pair<uint64_t,Address> >::iterator m(_moonSeeds.begin());m!=_moonSeeds.end();++m) {
|
||||
if (m->first == newWorld.id()) {
|
||||
for(std::vector<World::Root>::const_iterator r(newWorld.roots().begin());r!=newWorld.roots().end();++r) {
|
||||
if (r->identity.address() == m->second) {
|
||||
_moonSeeds.erase(m);
|
||||
_moons.push_back(newWorld);
|
||||
existing = &(_moons.back());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (existing) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!existing) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> sbuf;
|
||||
existing->serialize(sbuf,false);
|
||||
uint64_t idtmp[2];
|
||||
idtmp[0] = existing->id();
|
||||
idtmp[1] = 0;
|
||||
RR->node->stateObjectPut(tPtr,(existing->type() == World::TYPE_PLANET) ? ZT_STATE_OBJECT_PLANET : ZT_STATE_OBJECT_MOON,idtmp,sbuf.data(),sbuf.size());
|
||||
} catch ( ... ) {}
|
||||
|
||||
_memoizeUpstreams(tPtr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Topology::addMoon(void *tPtr,const uint64_t id,const Address &seed)
|
||||
{
|
||||
char tmp[ZT_WORLD_MAX_SERIALIZED_LENGTH];
|
||||
uint64_t idtmp[2];
|
||||
idtmp[0] = id;
|
||||
idtmp[1] = 0;
|
||||
int n = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_MOON,idtmp,tmp,sizeof(tmp));
|
||||
if (n > 0) {
|
||||
try {
|
||||
World w;
|
||||
w.deserialize(Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH>(tmp,(unsigned int)n));
|
||||
if ((w.type() == World::TYPE_MOON)&&(w.id() == id)) {
|
||||
addWorld(tPtr,w,true);
|
||||
return;
|
||||
}
|
||||
} catch ( ... ) {}
|
||||
}
|
||||
|
||||
if (seed) {
|
||||
Mutex::Lock _l(_upstreams_m);
|
||||
if (std::find(_moonSeeds.begin(),_moonSeeds.end(),std::pair<uint64_t,Address>(id,seed)) == _moonSeeds.end()) {
|
||||
_moonSeeds.push_back(std::pair<uint64_t,Address>(id,seed));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Topology::removeMoon(void *tPtr,const uint64_t id)
|
||||
{
|
||||
Mutex::Lock _l2(_peers_m);
|
||||
Mutex::Lock _l1(_upstreams_m);
|
||||
|
||||
std::vector<World> nm;
|
||||
for(std::vector<World>::const_iterator m(_moons.begin());m!=_moons.end();++m) {
|
||||
if (m->id() != id) {
|
||||
nm.push_back(*m);
|
||||
} else {
|
||||
uint64_t idtmp[2];
|
||||
idtmp[0] = id;
|
||||
idtmp[1] = 0;
|
||||
RR->node->stateObjectDelete(tPtr,ZT_STATE_OBJECT_MOON,idtmp);
|
||||
}
|
||||
}
|
||||
_moons.swap(nm);
|
||||
|
||||
std::vector< std::pair<uint64_t,Address> > cm;
|
||||
for(std::vector< std::pair<uint64_t,Address> >::const_iterator m(_moonSeeds.begin());m!=_moonSeeds.end();++m) {
|
||||
if (m->first != id) {
|
||||
cm.push_back(*m);
|
||||
}
|
||||
}
|
||||
_moonSeeds.swap(cm);
|
||||
|
||||
_memoizeUpstreams(tPtr);
|
||||
}
|
||||
|
||||
void Topology::doPeriodicTasks(void *tPtr,int64_t now)
|
||||
{
|
||||
{
|
||||
Mutex::Lock _l1(_peers_m);
|
||||
Mutex::Lock _l2(_upstreams_m);
|
||||
Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
|
||||
Address *a = (Address *)0;
|
||||
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
|
||||
while (i.next(a,p)) {
|
||||
if ( (!(*p)->isAlive(now)) && (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),*a) == _upstreamAddresses.end()) ) {
|
||||
_savePeer(tPtr,*p);
|
||||
_peers.erase(*a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Mutex::Lock _l(_paths_m);
|
||||
Hashtable< Path::HashKey,SharedPtr<Path> >::Iterator i(_paths);
|
||||
Path::HashKey *k = (Path::HashKey *)0;
|
||||
SharedPtr<Path> *p = (SharedPtr<Path> *)0;
|
||||
while (i.next(k,p)) {
|
||||
if (p->references() <= 1) {
|
||||
_paths.erase(*k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Topology::_memoizeUpstreams(void *tPtr)
|
||||
{
|
||||
// assumes _upstreams_m and _peers_m are locked
|
||||
_upstreamAddresses.clear();
|
||||
_amUpstream = false;
|
||||
|
||||
for(std::vector<World::Root>::const_iterator i(_planet.roots().begin());i!=_planet.roots().end();++i) {
|
||||
const Identity &id = i->identity;
|
||||
if (id == RR->identity) {
|
||||
_amUpstream = true;
|
||||
} else if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),id.address()) == _upstreamAddresses.end()) {
|
||||
_upstreamAddresses.push_back(id.address());
|
||||
SharedPtr<Peer> &hp = _peers[id.address()];
|
||||
if (!hp) {
|
||||
hp = new Peer(RR,RR->identity,id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(std::vector<World>::const_iterator m(_moons.begin());m!=_moons.end();++m) {
|
||||
for(std::vector<World::Root>::const_iterator i(m->roots().begin());i!=m->roots().end();++i) {
|
||||
if (i->identity == RR->identity) {
|
||||
_amUpstream = true;
|
||||
} else if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),i->identity.address()) == _upstreamAddresses.end()) {
|
||||
_upstreamAddresses.push_back(i->identity.address());
|
||||
SharedPtr<Peer> &hp = _peers[i->identity.address()];
|
||||
if (!hp) {
|
||||
hp = new Peer(RR,RR->identity,i->identity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(_upstreamAddresses.begin(),_upstreamAddresses.end());
|
||||
}
|
||||
|
||||
void Topology::_savePeer(void *tPtr,const SharedPtr<Peer> &peer)
|
||||
{
|
||||
try {
|
||||
Buffer<ZT_PEER_MAX_SERIALIZED_STATE_SIZE> buf;
|
||||
peer->serializeForCache(buf);
|
||||
uint64_t tmpid[2];
|
||||
tmpid[0] = peer->address().toInt();
|
||||
tmpid[1] = 0;
|
||||
RR->node->stateObjectPut(tPtr,ZT_STATE_OBJECT_PEER,tmpid,buf.data(),buf.size());
|
||||
} catch ( ... ) {} // sanity check, discard invalid entries
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
@@ -0,0 +1,468 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_TOPOLOGY_HPP
|
||||
#define ZT_TOPOLOGY_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "../include/ZeroTierOne.h"
|
||||
|
||||
#include "Address.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "Peer.hpp"
|
||||
#include "Path.hpp"
|
||||
#include "Mutex.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "Hashtable.hpp"
|
||||
#include "World.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class RuntimeEnvironment;
|
||||
|
||||
/**
|
||||
* Database of network topology
|
||||
*/
|
||||
class Topology
|
||||
{
|
||||
public:
|
||||
Topology(const RuntimeEnvironment *renv,void *tPtr);
|
||||
~Topology();
|
||||
|
||||
/**
|
||||
* Add a peer to database
|
||||
*
|
||||
* This will not replace existing peers. In that case the existing peer
|
||||
* record is returned.
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param peer Peer to add
|
||||
* @return New or existing peer (should replace 'peer')
|
||||
*/
|
||||
SharedPtr<Peer> addPeer(void *tPtr,const SharedPtr<Peer> &peer);
|
||||
|
||||
/**
|
||||
* Get a peer from its address
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param zta ZeroTier address of peer
|
||||
* @return Peer or NULL if not found
|
||||
*/
|
||||
SharedPtr<Peer> getPeer(void *tPtr,const Address &zta);
|
||||
|
||||
/**
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param zta ZeroTier address of peer
|
||||
* @return Identity or NULL identity if not found
|
||||
*/
|
||||
Identity getIdentity(void *tPtr,const Address &zta);
|
||||
|
||||
/**
|
||||
* Get a peer only if it is presently in memory (no disk cache)
|
||||
*
|
||||
* This also does not update the lastUsed() time for peers, which means
|
||||
* that it won't prevent them from falling out of RAM. This is currently
|
||||
* used in the Cluster code to update peer info without forcing all peers
|
||||
* across the entire cluster to remain in memory cache.
|
||||
*
|
||||
* @param zta ZeroTier address
|
||||
*/
|
||||
inline SharedPtr<Peer> getPeerNoCache(const Address &zta)
|
||||
{
|
||||
Mutex::Lock _l(_peers_m);
|
||||
const SharedPtr<Peer> *const ap = _peers.get(zta);
|
||||
if (ap) {
|
||||
return *ap;
|
||||
}
|
||||
return SharedPtr<Peer>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Path object for a given local and remote physical address, creating if needed
|
||||
*
|
||||
* @param l Local socket
|
||||
* @param r Remote address
|
||||
* @return Pointer to canonicalized Path object
|
||||
*/
|
||||
inline SharedPtr<Path> getPath(const int64_t l,const InetAddress &r)
|
||||
{
|
||||
Mutex::Lock _l(_paths_m);
|
||||
SharedPtr<Path> &p = _paths[Path::HashKey(l,r)];
|
||||
if (!p) {
|
||||
p.set(new Path(l,r));
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current best upstream peer
|
||||
*
|
||||
* @return Upstream or NULL if none available
|
||||
*/
|
||||
SharedPtr<Peer> getUpstreamPeer();
|
||||
|
||||
/**
|
||||
* @param id Identity to check
|
||||
* @return True if this is a root server or a network preferred relay from one of our networks
|
||||
*/
|
||||
bool isUpstream(const Identity &id) const;
|
||||
|
||||
/**
|
||||
* @param addr Address to check
|
||||
* @return True if we should accept a world update from this address
|
||||
*/
|
||||
bool shouldAcceptWorldUpdateFrom(const Address &addr) const;
|
||||
|
||||
/**
|
||||
* @param ztaddr ZeroTier address
|
||||
* @return Peer role for this device
|
||||
*/
|
||||
ZT_PeerRole role(const Address &ztaddr) const;
|
||||
|
||||
/**
|
||||
* Check for prohibited endpoints
|
||||
*
|
||||
* Right now this returns true if the designated ZT address is a root and if
|
||||
* the IP (IP only, not port) does not equal any of the IPs defined in the
|
||||
* current World. This is an extra little security feature in case root keys
|
||||
* get appropriated or something.
|
||||
*
|
||||
* Otherwise it returns false.
|
||||
*
|
||||
* @param ztaddr ZeroTier address
|
||||
* @param ipaddr IP address
|
||||
* @return True if this ZT/IP pair should not be allowed to be used
|
||||
*/
|
||||
bool isProhibitedEndpoint(const Address &ztaddr,const InetAddress &ipaddr) const;
|
||||
|
||||
/**
|
||||
* Gets upstreams to contact and their stable endpoints (if known)
|
||||
*
|
||||
* @param eps Hash table to fill with addresses and their stable endpoints
|
||||
*/
|
||||
inline void getUpstreamsToContact(Hashtable< Address,std::vector<InetAddress> > &eps) const
|
||||
{
|
||||
Mutex::Lock _l(_upstreams_m);
|
||||
for(std::vector<World::Root>::const_iterator i(_planet.roots().begin());i!=_planet.roots().end();++i) {
|
||||
if (i->identity != RR->identity) {
|
||||
std::vector<InetAddress> &ips = eps[i->identity.address()];
|
||||
for(std::vector<InetAddress>::const_iterator j(i->stableEndpoints.begin());j!=i->stableEndpoints.end();++j) {
|
||||
if (std::find(ips.begin(),ips.end(),*j) == ips.end()) {
|
||||
ips.push_back(*j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for(std::vector<World>::const_iterator m(_moons.begin());m!=_moons.end();++m) {
|
||||
for(std::vector<World::Root>::const_iterator i(m->roots().begin());i!=m->roots().end();++i) {
|
||||
if (i->identity != RR->identity) {
|
||||
std::vector<InetAddress> &ips = eps[i->identity.address()];
|
||||
for(std::vector<InetAddress>::const_iterator j(i->stableEndpoints.begin());j!=i->stableEndpoints.end();++j) {
|
||||
if (std::find(ips.begin(),ips.end(),*j) == ips.end()) {
|
||||
ips.push_back(*j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for(std::vector< std::pair<uint64_t,Address> >::const_iterator m(_moonSeeds.begin());m!=_moonSeeds.end();++m) {
|
||||
eps[m->second];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Vector of active upstream addresses (including roots)
|
||||
*/
|
||||
inline std::vector<Address> upstreamAddresses() const
|
||||
{
|
||||
Mutex::Lock _l(_upstreams_m);
|
||||
return _upstreamAddresses;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Current moons
|
||||
*/
|
||||
inline std::vector<World> moons() const
|
||||
{
|
||||
Mutex::Lock _l(_upstreams_m);
|
||||
return _moons;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Moon IDs we are waiting for from seeds
|
||||
*/
|
||||
inline std::vector<uint64_t> moonsWanted() const
|
||||
{
|
||||
Mutex::Lock _l(_upstreams_m);
|
||||
std::vector<uint64_t> mw;
|
||||
for(std::vector< std::pair<uint64_t,Address> >::const_iterator s(_moonSeeds.begin());s!=_moonSeeds.end();++s) {
|
||||
if (std::find(mw.begin(),mw.end(),s->first) == mw.end()) {
|
||||
mw.push_back(s->first);
|
||||
}
|
||||
}
|
||||
return mw;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Current planet
|
||||
*/
|
||||
inline World planet() const
|
||||
{
|
||||
Mutex::Lock _l(_upstreams_m);
|
||||
return _planet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Current planet's world ID
|
||||
*/
|
||||
inline uint64_t planetWorldId() const
|
||||
{
|
||||
return _planet.id(); // safe to read without lock, and used from within eachPeer() so don't lock
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Current planet's world timestamp
|
||||
*/
|
||||
inline uint64_t planetWorldTimestamp() const
|
||||
{
|
||||
return _planet.timestamp(); // safe to read without lock, and used from within eachPeer() so don't lock
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate new world and update if newer and signature is okay
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param newWorld A new or updated planet or moon to learn
|
||||
* @param alwaysAcceptNew If true, always accept new moons even if we're not waiting for one
|
||||
* @return True if it was valid and newer than current (or totally new for moons)
|
||||
*/
|
||||
bool addWorld(void *tPtr,const World &newWorld,bool alwaysAcceptNew);
|
||||
|
||||
/**
|
||||
* Add a moon
|
||||
*
|
||||
* This loads it from moons.d if present, and if not adds it to
|
||||
* a list of moons that we want to contact.
|
||||
*
|
||||
* @param id Moon ID
|
||||
* @param seed If non-NULL, an address of any member of the moon to contact
|
||||
*/
|
||||
void addMoon(void *tPtr,const uint64_t id,const Address &seed);
|
||||
|
||||
/**
|
||||
* Remove a moon
|
||||
*
|
||||
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param id Moon's world ID
|
||||
*/
|
||||
void removeMoon(void *tPtr,const uint64_t id);
|
||||
|
||||
/**
|
||||
* Clean and flush database
|
||||
*/
|
||||
void doPeriodicTasks(void *tPtr,int64_t now);
|
||||
|
||||
/**
|
||||
* @param now Current time
|
||||
* @return Number of peers with active direct paths
|
||||
*/
|
||||
inline unsigned long countActive(int64_t now) const
|
||||
{
|
||||
unsigned long cnt = 0;
|
||||
Mutex::Lock _l(_peers_m);
|
||||
Hashtable< Address,SharedPtr<Peer> >::Iterator i(const_cast<Topology *>(this)->_peers);
|
||||
Address *a = (Address *)0;
|
||||
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
|
||||
while (i.next(a,p)) {
|
||||
const SharedPtr<Path> pp((*p)->getAppropriatePath(now,false));
|
||||
if (pp) {
|
||||
++cnt;
|
||||
}
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a function or function object to all peers
|
||||
*
|
||||
* @param f Function to apply
|
||||
* @tparam F Function or function object type
|
||||
*/
|
||||
template<typename F>
|
||||
inline void eachPeer(F f)
|
||||
{
|
||||
Mutex::Lock _l(_peers_m);
|
||||
Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
|
||||
Address *a = (Address *)0;
|
||||
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
|
||||
while (i.next(a,p)) {
|
||||
f(*this,*((const SharedPtr<Peer> *)p));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return All currently active peers by address (unsorted)
|
||||
*/
|
||||
inline std::vector< std::pair< Address,SharedPtr<Peer> > > allPeers() const
|
||||
{
|
||||
Mutex::Lock _l(_peers_m);
|
||||
return _peers.entries();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if I am a root server in a planet or moon
|
||||
*/
|
||||
inline bool amUpstream() const { return _amUpstream; }
|
||||
|
||||
/**
|
||||
* Get info about a path
|
||||
*
|
||||
* The supplied result variables are not modified if no special config info is found.
|
||||
*
|
||||
* @param physicalAddress Physical endpoint address
|
||||
* @param mtu Variable set to MTU
|
||||
* @param trustedPathId Variable set to trusted path ID
|
||||
*/
|
||||
inline void getOutboundPathInfo(const InetAddress &physicalAddress,unsigned int &mtu,uint64_t &trustedPathId)
|
||||
{
|
||||
for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i<j;++i) {
|
||||
if (_physicalPathConfig[i].first.containsAddress(physicalAddress)) {
|
||||
trustedPathId = _physicalPathConfig[i].second.trustedPathId;
|
||||
mtu = _physicalPathConfig[i].second.mtu;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the payload MTU for an outbound physical path (returns default if not configured)
|
||||
*
|
||||
* @param physicalAddress Physical endpoint address
|
||||
* @return MTU
|
||||
*/
|
||||
inline unsigned int getOutboundPathMtu(const InetAddress &physicalAddress)
|
||||
{
|
||||
for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i<j;++i) {
|
||||
if (_physicalPathConfig[i].first.containsAddress(physicalAddress)) {
|
||||
return _physicalPathConfig[i].second.mtu;
|
||||
}
|
||||
}
|
||||
return ZT_DEFAULT_PHYSMTU;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the outbound trusted path ID for a physical address, or 0 if none
|
||||
*
|
||||
* @param physicalAddress Physical address to which we are sending the packet
|
||||
* @return Trusted path ID or 0 if none (0 is not a valid trusted path ID)
|
||||
*/
|
||||
inline uint64_t getOutboundPathTrust(const InetAddress &physicalAddress)
|
||||
{
|
||||
for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i<j;++i) {
|
||||
if (_physicalPathConfig[i].first.containsAddress(physicalAddress)) {
|
||||
return _physicalPathConfig[i].second.trustedPathId;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether in incoming trusted path marked packet is valid
|
||||
*
|
||||
* @param physicalAddress Originating physical address
|
||||
* @param trustedPathId Trusted path ID from packet (from MAC field)
|
||||
*/
|
||||
inline bool shouldInboundPathBeTrusted(const InetAddress &physicalAddress,const uint64_t trustedPathId)
|
||||
{
|
||||
for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i<j;++i) {
|
||||
if ((_physicalPathConfig[i].second.trustedPathId == trustedPathId)&&(_physicalPathConfig[i].first.containsAddress(physicalAddress))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set or clear physical path configuration (called via Node::setPhysicalPathConfiguration)
|
||||
*/
|
||||
inline void setPhysicalPathConfiguration(const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig)
|
||||
{
|
||||
if (!pathNetwork) {
|
||||
_numConfiguredPhysicalPaths = 0;
|
||||
} else {
|
||||
std::map<InetAddress,ZT_PhysicalPathConfiguration> cpaths;
|
||||
for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i<j;++i) {
|
||||
cpaths[_physicalPathConfig[i].first] = _physicalPathConfig[i].second;
|
||||
}
|
||||
|
||||
if (pathConfig) {
|
||||
ZT_PhysicalPathConfiguration pc(*pathConfig);
|
||||
|
||||
if (pc.mtu <= 0) {
|
||||
pc.mtu = ZT_DEFAULT_PHYSMTU;
|
||||
} else if (pc.mtu < ZT_MIN_PHYSMTU) {
|
||||
pc.mtu = ZT_MIN_PHYSMTU;
|
||||
} else if (pc.mtu > ZT_MAX_PHYSMTU) {
|
||||
pc.mtu = ZT_MAX_PHYSMTU;
|
||||
}
|
||||
|
||||
cpaths[*(reinterpret_cast<const InetAddress *>(pathNetwork))] = pc;
|
||||
} else {
|
||||
cpaths.erase(*(reinterpret_cast<const InetAddress *>(pathNetwork)));
|
||||
}
|
||||
|
||||
unsigned int cnt = 0;
|
||||
for(std::map<InetAddress,ZT_PhysicalPathConfiguration>::const_iterator i(cpaths.begin());((i!=cpaths.end())&&(cnt<ZT_MAX_CONFIGURABLE_PATHS));++i) {
|
||||
_physicalPathConfig[cnt].first = i->first;
|
||||
_physicalPathConfig[cnt].second = i->second;
|
||||
++cnt;
|
||||
}
|
||||
_numConfiguredPhysicalPaths = cnt;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Identity _getIdentity(void *tPtr,const Address &zta);
|
||||
void _memoizeUpstreams(void *tPtr);
|
||||
void _savePeer(void *tPtr,const SharedPtr<Peer> &peer);
|
||||
|
||||
const RuntimeEnvironment *const RR;
|
||||
|
||||
std::pair<InetAddress,ZT_PhysicalPathConfiguration> _physicalPathConfig[ZT_MAX_CONFIGURABLE_PATHS];
|
||||
volatile unsigned int _numConfiguredPhysicalPaths;
|
||||
|
||||
Hashtable< Address,SharedPtr<Peer> > _peers;
|
||||
Mutex _peers_m;
|
||||
|
||||
Hashtable< Path::HashKey,SharedPtr<Path> > _paths;
|
||||
Mutex _paths_m;
|
||||
|
||||
World _planet;
|
||||
std::vector<World> _moons;
|
||||
std::vector< std::pair<uint64_t,Address> > _moonSeeds;
|
||||
std::vector<Address> _upstreamAddresses;
|
||||
bool _amUpstream;
|
||||
Mutex _upstreams_m; // locks worlds, upstream info, moon info, etc.
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
+611
@@ -0,0 +1,611 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
//#define ZT_TRACE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "Trace.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Switch.hpp"
|
||||
#include "Node.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "Dictionary.hpp"
|
||||
#include "CertificateOfMembership.hpp"
|
||||
#include "CertificateOfOwnership.hpp"
|
||||
#include "Tag.hpp"
|
||||
#include "Capability.hpp"
|
||||
#include "Revocation.hpp"
|
||||
#include "../include/ZeroTierDebug.h"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
#ifdef ZT_TRACE
|
||||
static void ZT_LOCAL_TRACE(void *const tPtr,const RuntimeEnvironment *const RR,const char *const fmt,...)
|
||||
{
|
||||
char traceMsgBuf[1024];
|
||||
va_list ap;
|
||||
va_start(ap,fmt);
|
||||
vsnprintf(traceMsgBuf,sizeof(traceMsgBuf),fmt,ap);
|
||||
va_end(ap);
|
||||
traceMsgBuf[sizeof(traceMsgBuf) - 1] = (char)0;
|
||||
RR->node->postEvent(tPtr,ZT_EVENT_TRACE,traceMsgBuf);
|
||||
}
|
||||
#else
|
||||
#define ZT_LOCAL_TRACE(...)
|
||||
#endif
|
||||
|
||||
void Trace::resettingPathsInScope(void *const tPtr,const Address &reporter,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,const InetAddress::IpScope scope)
|
||||
{
|
||||
char tmp[128];
|
||||
|
||||
ZT_LOCAL_TRACE(tPtr,RR,"RESET and revalidate paths in scope %d; new phy address %s reported by trusted peer %.10llx",(int)scope,myPhysicalAddress.toIpString(tmp),reporter.toInt());
|
||||
|
||||
Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__RESETTING_PATHS_IN_SCOPE_S);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,reporter);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,reporterPhysicalAddress.toString(tmp));
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_PHYADDR,myPhysicalAddress.toString(tmp));
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__IP_SCOPE,(uint64_t)scope);
|
||||
|
||||
if (_globalTarget) {
|
||||
_send(tPtr,d,_globalTarget);
|
||||
}
|
||||
_spamToAllNetworks(tPtr,d,Trace::LEVEL_NORMAL);
|
||||
}
|
||||
|
||||
void Trace::peerConfirmingUnknownPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &path,const uint64_t packetId,const Packet::Verb verb)
|
||||
{
|
||||
char tmp[128];
|
||||
if (!path) {
|
||||
return; // sanity check
|
||||
}
|
||||
|
||||
ZT_LOCAL_TRACE(tPtr,RR,"trying unknown path %s to %.10llx (packet %.16llx verb %d local socket %lld network %.16llx)",path->address().toString(tmp),peer.address().toInt(),packetId,verb,path->localSocket(),networkId);
|
||||
|
||||
std::pair<Address,Trace::Level> byn;
|
||||
if (networkId) {
|
||||
Mutex::Lock l(_byNet_m);
|
||||
_byNet.get(networkId,byn);
|
||||
}
|
||||
|
||||
if ((_globalTarget)||(byn.first)) {
|
||||
Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PEER_CONFIRMING_UNKNOWN_PATH_S);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__PACKET_VERB,(uint64_t)verb);
|
||||
if (networkId) {
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,networkId);
|
||||
}
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,peer.address());
|
||||
if (path) {
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp));
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket());
|
||||
}
|
||||
|
||||
if (_globalTarget) {
|
||||
_send(tPtr,d,_globalTarget);
|
||||
}
|
||||
if (byn.first) {
|
||||
_send(tPtr,d,byn.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Trace::bondStateMessage(void *const tPtr,char *msg)
|
||||
{
|
||||
ZT_LOCAL_TRACE(tPtr,RR,"%s",msg);
|
||||
}
|
||||
|
||||
void Trace::peerLearnedNewPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &newPath,const uint64_t packetId)
|
||||
{
|
||||
char tmp[128];
|
||||
if (!newPath) {
|
||||
return; // sanity check
|
||||
}
|
||||
|
||||
ZT_LOCAL_TRACE(tPtr,RR,"learned new path %s to %.10llx (packet %.16llx local socket %lld network %.16llx)",newPath->address().toString(tmp),peer.address().toInt(),packetId,newPath->localSocket(),networkId);
|
||||
|
||||
std::pair<Address,Trace::Level> byn;
|
||||
if (networkId) {
|
||||
Mutex::Lock l(_byNet_m);
|
||||
_byNet.get(networkId,byn);
|
||||
}
|
||||
|
||||
if ((_globalTarget)||(byn.first)) {
|
||||
Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PEER_LEARNED_NEW_PATH_S);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
|
||||
if (networkId) {
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID, networkId);
|
||||
}
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,peer.address());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,newPath->address().toString(tmp));
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,newPath->localSocket());
|
||||
|
||||
if (_globalTarget) {
|
||||
_send(tPtr,d,_globalTarget);
|
||||
}
|
||||
if (byn.first) {
|
||||
_send(tPtr,d,byn.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Trace::peerRedirected(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &newPath)
|
||||
{
|
||||
char tmp[128];
|
||||
if (!newPath) {
|
||||
return; // sanity check
|
||||
}
|
||||
|
||||
ZT_LOCAL_TRACE(tPtr,RR,"explicit redirect from %.10llx to path %s",peer.address().toInt(),newPath->address().toString(tmp));
|
||||
|
||||
std::pair<Address,Trace::Level> byn;
|
||||
if (networkId) {
|
||||
Mutex::Lock l(_byNet_m);
|
||||
_byNet.get(networkId,byn);
|
||||
}
|
||||
|
||||
if ((_globalTarget)||(byn.first)) {
|
||||
Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PEER_REDIRECTED_S);
|
||||
if (networkId) {
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,networkId);
|
||||
}
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,peer.address());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,newPath->address().toString(tmp));
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,newPath->localSocket());
|
||||
|
||||
if (_globalTarget) {
|
||||
_send(tPtr,d,_globalTarget);
|
||||
}
|
||||
if (byn.first) {
|
||||
_send(tPtr,d,byn.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Trace::outgoingNetworkFrameDropped(void *const tPtr,const SharedPtr<Network> &network,const MAC &sourceMac,const MAC &destMac,const unsigned int etherType,const unsigned int vlanId,const unsigned int frameLen,const char *reason)
|
||||
{
|
||||
#ifdef ZT_TRACE
|
||||
char tmp[128],tmp2[128];
|
||||
#endif
|
||||
if (!network) {
|
||||
return; // sanity check
|
||||
}
|
||||
|
||||
ZT_LOCAL_TRACE(tPtr,RR,"%.16llx DROP frame %s -> %s etherType %.4x size %u (%s)",network->id(),sourceMac.toString(tmp),destMac.toString(tmp2),etherType,frameLen,(reason) ? reason : "unknown reason");
|
||||
|
||||
std::pair<Address,Trace::Level> byn;
|
||||
{ Mutex::Lock l(_byNet_m); _byNet.get(network->id(),byn); }
|
||||
|
||||
if ( ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_VERBOSE)) || ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE)) ) {
|
||||
Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__OUTGOING_NETWORK_FRAME_DROPPED_S);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,network->id());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__SOURCE_MAC,sourceMac.toInt());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__DEST_MAC,destMac.toInt());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__ETHERTYPE,(uint64_t)etherType);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__VLAN_ID,(uint64_t)vlanId);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__FRAME_LENGTH,(uint64_t)frameLen);
|
||||
if (reason) {
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
|
||||
}
|
||||
|
||||
if ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_VERBOSE)) {
|
||||
_send(tPtr,d,_globalTarget);
|
||||
}
|
||||
if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE)) {
|
||||
_send(tPtr,d,byn.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Trace::incomingNetworkAccessDenied(void *const tPtr,const SharedPtr<Network> &network,const SharedPtr<Path> &path,const uint64_t packetId,const unsigned int packetLength,const Address &source,const Packet::Verb verb,bool credentialsRequested)
|
||||
{
|
||||
char tmp[128];
|
||||
if (!network) {
|
||||
return; // sanity check
|
||||
}
|
||||
|
||||
ZT_LOCAL_TRACE(tPtr,RR,"%.16llx DENIED packet from %.10llx(%s) verb %d size %u%s",network->id(),source.toInt(),(path) ? (path->address().toString(tmp)) : "???",(int)verb,packetLength,credentialsRequested ? " (credentials requested)" : " (credentials not requested)");
|
||||
|
||||
std::pair<Address,Trace::Level> byn;
|
||||
{ Mutex::Lock l(_byNet_m); _byNet.get(network->id(),byn); }
|
||||
|
||||
if ( ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_VERBOSE)) || ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE)) ) {
|
||||
Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_ACCESS_DENIED_S);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__PACKET_VERB,(uint64_t)verb);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,source);
|
||||
if (path) {
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp));
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket());
|
||||
}
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,network->id());
|
||||
|
||||
if ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_VERBOSE)) {
|
||||
_send(tPtr,d,_globalTarget);
|
||||
}
|
||||
if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE)) {
|
||||
_send(tPtr,d,byn.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Trace::incomingNetworkFrameDropped(void *const tPtr,const SharedPtr<Network> &network,const SharedPtr<Path> &path,const uint64_t packetId,const unsigned int packetLength,const Address &source,const Packet::Verb verb,const MAC &sourceMac,const MAC &destMac,const char *reason)
|
||||
{
|
||||
char tmp[128];
|
||||
if (!network) {
|
||||
return; // sanity check
|
||||
}
|
||||
|
||||
ZT_LOCAL_TRACE(tPtr,RR,"%.16llx DROPPED frame from %.10llx(%s) verb %d size %u",network->id(),source.toInt(),(path) ? (path->address().toString(tmp)) : "???",(int)verb,packetLength);
|
||||
|
||||
std::pair<Address,Trace::Level> byn;
|
||||
{ Mutex::Lock l(_byNet_m); _byNet.get(network->id(),byn); }
|
||||
|
||||
if ( ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_VERBOSE)) || ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE)) ) {
|
||||
Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_FRAME_DROPPED_S);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__PACKET_VERB,(uint64_t)verb);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,source);
|
||||
if (path) {
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp));
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket());
|
||||
}
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,network->id());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__SOURCE_MAC,sourceMac.toInt());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__DEST_MAC,destMac.toInt());
|
||||
if (reason) {
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
|
||||
}
|
||||
|
||||
if ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_VERBOSE)) {
|
||||
_send(tPtr,d,_globalTarget);
|
||||
}
|
||||
if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE)) {
|
||||
_send(tPtr,d,byn.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Trace::incomingPacketMessageAuthenticationFailure(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const unsigned int hops,const char *reason)
|
||||
{
|
||||
char tmp[128];
|
||||
|
||||
ZT_LOCAL_TRACE(tPtr,RR,"MAC failed for packet %.16llx from %.10llx(%s)",packetId,source.toInt(),(path) ? path->address().toString(tmp) : "???");
|
||||
|
||||
if ((_globalTarget)&&((int)_globalLevel >= Trace::LEVEL_DEBUG)) {
|
||||
Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PACKET_MAC_FAILURE_S);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__PACKET_HOPS,(uint64_t)hops);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,source);
|
||||
if (path) {
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp));
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket());
|
||||
}
|
||||
if (reason) {
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
|
||||
}
|
||||
|
||||
_send(tPtr,d,_globalTarget);
|
||||
}
|
||||
}
|
||||
|
||||
void Trace::incomingPacketInvalid(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const unsigned int hops,const Packet::Verb verb,const char *reason)
|
||||
{
|
||||
char tmp[128];
|
||||
|
||||
ZT_LOCAL_TRACE(tPtr,RR,"INVALID packet %.16llx from %.10llx(%s) (%s)",packetId,source.toInt(),(path) ? path->address().toString(tmp) : "???",(reason) ? reason : "unknown reason");
|
||||
|
||||
if ((_globalTarget)&&((int)_globalLevel >= Trace::LEVEL_DEBUG)) {
|
||||
Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PACKET_INVALID_S);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__PACKET_VERB,(uint64_t)verb);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,source);
|
||||
if (path) {
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp));
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket());
|
||||
}
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__PACKET_HOPS,(uint64_t)hops);
|
||||
if (reason) {
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
|
||||
}
|
||||
|
||||
_send(tPtr,d,_globalTarget);
|
||||
}
|
||||
}
|
||||
|
||||
void Trace::incomingPacketDroppedHELLO(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const char *reason)
|
||||
{
|
||||
char tmp[128];
|
||||
|
||||
ZT_LOCAL_TRACE(tPtr,RR,"DROPPED HELLO from %.10llx(%s) (%s)",source.toInt(),(path) ? path->address().toString(tmp) : "???",(reason) ? reason : "???");
|
||||
|
||||
if ((_globalTarget)&&((int)_globalLevel >= Trace::LEVEL_DEBUG)) {
|
||||
Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PACKET_INVALID_S);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,source);
|
||||
if (path) {
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp));
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket());
|
||||
}
|
||||
if (reason) {
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
|
||||
}
|
||||
|
||||
_send(tPtr,d,_globalTarget);
|
||||
}
|
||||
}
|
||||
|
||||
void Trace::networkConfigRequestSent(void *const tPtr,const Network &network,const Address &controller)
|
||||
{
|
||||
ZT_LOCAL_TRACE(tPtr,RR,"requesting configuration for network %.16llx",network.id());
|
||||
if ((_globalTarget)&&((int)_globalLevel >= Trace::LEVEL_DEBUG)) {
|
||||
Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__NETWORK_CONFIG_REQUEST_SENT_S);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,network.id());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_CONTROLLER_ID,controller);
|
||||
_send(tPtr,d,_globalTarget);
|
||||
}
|
||||
}
|
||||
|
||||
void Trace::networkFilter(
|
||||
void *const tPtr,
|
||||
const Network &network,
|
||||
const RuleResultLog &primaryRuleSetLog,
|
||||
const RuleResultLog *const matchingCapabilityRuleSetLog,
|
||||
const Capability *const matchingCapability,
|
||||
const Address &ztSource,
|
||||
const Address &ztDest,
|
||||
const MAC &macSource,
|
||||
const MAC &macDest,
|
||||
const uint8_t *const frameData,
|
||||
const unsigned int frameLen,
|
||||
const unsigned int etherType,
|
||||
const unsigned int vlanId,
|
||||
const bool noTee,
|
||||
const bool inbound,
|
||||
const int accept)
|
||||
{
|
||||
std::pair<Address,Trace::Level> byn;
|
||||
{ Mutex::Lock l(_byNet_m); _byNet.get(network.id(),byn); }
|
||||
|
||||
if ( ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_RULES)) || ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_RULES)) ) {
|
||||
Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__NETWORK_FILTER_TRACE_S);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,network.id());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__SOURCE_ZTADDR,ztSource);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__DEST_ZTADDR,ztDest);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__SOURCE_MAC,macSource.toInt());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__DEST_MAC,macDest.toInt());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__ETHERTYPE,(uint64_t)etherType);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__VLAN_ID,(uint64_t)vlanId);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__FILTER_FLAG_NOTEE,noTee ? "1" : "0");
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__FILTER_FLAG_INBOUND,inbound ? "1" : "0");
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__FILTER_RESULT,(int64_t)accept);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__FILTER_BASE_RULE_LOG,(const char *)primaryRuleSetLog.data(),(int)primaryRuleSetLog.sizeBytes());
|
||||
if (matchingCapabilityRuleSetLog) {
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__FILTER_CAP_RULE_LOG,(const char *)matchingCapabilityRuleSetLog->data(),(int)matchingCapabilityRuleSetLog->sizeBytes());
|
||||
}
|
||||
if (matchingCapability) {
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__FILTER_CAP_ID,(uint64_t)matchingCapability->id());
|
||||
}
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__FRAME_LENGTH,(uint64_t)frameLen);
|
||||
if (frameLen > 0) {
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__FRAME_DATA,(const char *)frameData,(frameLen > 256) ? (int)256 : (int)frameLen);
|
||||
}
|
||||
|
||||
if ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_RULES)) {
|
||||
_send(tPtr,d,_globalTarget);
|
||||
}
|
||||
if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_RULES)) {
|
||||
_send(tPtr,d,byn.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Trace::credentialRejected(void *const tPtr,const CertificateOfMembership &c,const char *reason)
|
||||
{
|
||||
std::pair<Address,Trace::Level> byn;
|
||||
if (c.networkId()) {
|
||||
Mutex::Lock l(_byNet_m);
|
||||
_byNet.get(c.networkId(),byn);
|
||||
}
|
||||
|
||||
if ((_globalTarget)||(byn.first)) {
|
||||
Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,c.networkId());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE,(uint64_t)c.credentialType());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID,(uint64_t)c.id());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TIMESTAMP,c.timestamp());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ISSUED_TO,c.issuedTo());
|
||||
if (reason) {
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
|
||||
}
|
||||
|
||||
if (_globalTarget) {
|
||||
_send(tPtr,d,_globalTarget);
|
||||
}
|
||||
if (byn.first) {
|
||||
_send(tPtr,d,byn.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Trace::credentialRejected(void *const tPtr,const CertificateOfOwnership &c,const char *reason)
|
||||
{
|
||||
std::pair<Address,Trace::Level> byn;
|
||||
if (c.networkId()) {
|
||||
Mutex::Lock l(_byNet_m);
|
||||
_byNet.get(c.networkId(),byn);
|
||||
}
|
||||
|
||||
if ((_globalTarget)||(byn.first)) {
|
||||
Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,c.networkId());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE,(uint64_t)c.credentialType());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID,(uint64_t)c.id());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TIMESTAMP,c.timestamp());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ISSUED_TO,c.issuedTo());
|
||||
if (reason) {
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
|
||||
}
|
||||
|
||||
if (_globalTarget) {
|
||||
_send(tPtr,d,_globalTarget);
|
||||
}
|
||||
if (byn.first) {
|
||||
_send(tPtr,d,byn.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Trace::credentialRejected(void *const tPtr,const Capability &c,const char *reason)
|
||||
{
|
||||
std::pair<Address,Trace::Level> byn;
|
||||
if (c.networkId()) {
|
||||
Mutex::Lock l(_byNet_m);
|
||||
_byNet.get(c.networkId(),byn);
|
||||
}
|
||||
|
||||
if ((_globalTarget)||(byn.first)) {
|
||||
Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,c.networkId());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE,(uint64_t)c.credentialType());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID,(uint64_t)c.id());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TIMESTAMP,c.timestamp());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ISSUED_TO,c.issuedTo());
|
||||
if (reason) {
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
|
||||
}
|
||||
|
||||
if (_globalTarget) {
|
||||
_send(tPtr,d,_globalTarget);
|
||||
}
|
||||
if (byn.first) {
|
||||
_send(tPtr,d,byn.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Trace::credentialRejected(void *const tPtr,const Tag &c,const char *reason)
|
||||
{
|
||||
std::pair<Address,Trace::Level> byn;
|
||||
if (c.networkId()) {
|
||||
Mutex::Lock l(_byNet_m);
|
||||
_byNet.get(c.networkId(),byn);
|
||||
}
|
||||
|
||||
if ((_globalTarget)||(byn.first)) {
|
||||
Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,c.networkId());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE,(uint64_t)c.credentialType());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID,(uint64_t)c.id());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TIMESTAMP,c.timestamp());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ISSUED_TO,c.issuedTo());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_INFO,(uint64_t)c.value());
|
||||
if (reason) {
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
|
||||
}
|
||||
|
||||
if (_globalTarget) {
|
||||
_send(tPtr,d,_globalTarget);
|
||||
}
|
||||
if (byn.first) {
|
||||
_send(tPtr,d,byn.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Trace::credentialRejected(void *const tPtr,const Revocation &c,const char *reason)
|
||||
{
|
||||
std::pair<Address,Trace::Level> byn;
|
||||
if (c.networkId()) {
|
||||
Mutex::Lock l(_byNet_m);
|
||||
_byNet.get(c.networkId(),byn);
|
||||
}
|
||||
|
||||
if ((_globalTarget)||(byn.first)) {
|
||||
Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S);
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,c.networkId());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE,(uint64_t)c.credentialType());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID,(uint64_t)c.id());
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_REVOCATION_TARGET,c.target());
|
||||
if (reason) {
|
||||
d.add(ZT_REMOTE_TRACE_FIELD__REASON, reason);
|
||||
}
|
||||
|
||||
if (_globalTarget) {
|
||||
_send(tPtr,d,_globalTarget);
|
||||
}
|
||||
if (byn.first) {
|
||||
_send(tPtr,d,byn.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Trace::updateMemoizedSettings()
|
||||
{
|
||||
_globalTarget = RR->node->remoteTraceTarget();
|
||||
_globalLevel = RR->node->remoteTraceLevel();
|
||||
const std::vector< SharedPtr<Network> > nws(RR->node->allNetworks());
|
||||
{
|
||||
Mutex::Lock l(_byNet_m);
|
||||
_byNet.clear();
|
||||
for(std::vector< SharedPtr<Network> >::const_iterator n(nws.begin());n!=nws.end();++n) {
|
||||
const Address dest((*n)->config().remoteTraceTarget);
|
||||
if (dest) {
|
||||
std::pair<Address,Trace::Level> &m = _byNet[(*n)->id()];
|
||||
m.first = dest;
|
||||
m.second = (*n)->config().remoteTraceLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Trace::_send(void *const tPtr,const Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> &d,const Address &dest)
|
||||
{
|
||||
Packet outp(dest,RR->identity.address(),Packet::VERB_REMOTE_TRACE);
|
||||
outp.appendCString(d.data());
|
||||
outp.compress();
|
||||
RR->sw->send(tPtr,outp,true);
|
||||
}
|
||||
|
||||
void Trace::_spamToAllNetworks(void *const tPtr,const Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> &d,const Level level)
|
||||
{
|
||||
Mutex::Lock l(_byNet_m);
|
||||
Hashtable< uint64_t,std::pair< Address,Trace::Level > >::Iterator i(_byNet);
|
||||
uint64_t *k = (uint64_t *)0;
|
||||
std::pair<Address,Trace::Level> *v = (std::pair<Address,Trace::Level> *)0;
|
||||
while (i.next(k,v)) {
|
||||
if ((v)&&(v->first)&&((int)v->second >= (int)level)) {
|
||||
_send(tPtr,d,v->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_TRACE_HPP
|
||||
#define ZT_TRACE_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../include/ZeroTierOne.h"
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "SharedPtr.hpp"
|
||||
#include "Packet.hpp"
|
||||
#include "Credential.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "Dictionary.hpp"
|
||||
#include "Mutex.hpp"
|
||||
#include "Hashtable.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class RuntimeEnvironment;
|
||||
class Address;
|
||||
class Identity;
|
||||
class Peer;
|
||||
class Path;
|
||||
class Network;
|
||||
class NetworkConfig;
|
||||
class MAC;
|
||||
class CertificateOfMembership;
|
||||
class CertificateOfOwnership;
|
||||
class Revocation;
|
||||
class Tag;
|
||||
class Capability;
|
||||
|
||||
/**
|
||||
* Remote tracing and trace logging handler
|
||||
*/
|
||||
class Trace
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Trace verbosity level
|
||||
*/
|
||||
enum Level
|
||||
{
|
||||
LEVEL_NORMAL = 0,
|
||||
LEVEL_VERBOSE = 10,
|
||||
LEVEL_RULES = 15,
|
||||
LEVEL_DEBUG = 20,
|
||||
LEVEL_INSANE = 30
|
||||
};
|
||||
|
||||
/**
|
||||
* Filter rule evaluation result log
|
||||
*
|
||||
* Each rule in a rule set gets a four-bit log entry. A log entry
|
||||
* of zero means not evaluated. Otherwise each four-bit log entry
|
||||
* contains two two-bit values of 01 for 'false' and 10 for 'true'.
|
||||
* As with four-bit rules an 00 value here means this was not
|
||||
* evaluated or was not relevant.
|
||||
*/
|
||||
class RuleResultLog
|
||||
{
|
||||
public:
|
||||
RuleResultLog() {}
|
||||
|
||||
inline void log(const unsigned int rn,const uint8_t thisRuleMatches,const uint8_t thisSetMatches)
|
||||
{
|
||||
_l[rn >> 1] |= ( ((thisRuleMatches + 1) << 2) | (thisSetMatches + 1) ) << ((rn & 1) << 2);
|
||||
}
|
||||
inline void logSkipped(const unsigned int rn,const uint8_t thisSetMatches)
|
||||
{
|
||||
_l[rn >> 1] |= (thisSetMatches + 1) << ((rn & 1) << 2);
|
||||
}
|
||||
|
||||
inline void clear()
|
||||
{
|
||||
memset(_l,0,sizeof(_l));
|
||||
}
|
||||
|
||||
inline const uint8_t *data() const { return _l; }
|
||||
inline unsigned int sizeBytes() const { return (ZT_MAX_NETWORK_RULES / 2); }
|
||||
|
||||
private:
|
||||
uint8_t _l[ZT_MAX_NETWORK_RULES / 2];
|
||||
};
|
||||
|
||||
Trace(const RuntimeEnvironment *renv) :
|
||||
RR(renv),
|
||||
_byNet(8)
|
||||
{
|
||||
}
|
||||
|
||||
void resettingPathsInScope(void *const tPtr,const Address &reporter,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,const InetAddress::IpScope scope);
|
||||
|
||||
void peerConfirmingUnknownPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &path,const uint64_t packetId,const Packet::Verb verb);
|
||||
|
||||
void bondStateMessage(void *const tPtr,char *msg);
|
||||
|
||||
void peerLearnedNewPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &newPath,const uint64_t packetId);
|
||||
void peerRedirected(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &newPath);
|
||||
|
||||
void incomingPacketMessageAuthenticationFailure(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const unsigned int hops,const char *reason);
|
||||
void incomingPacketInvalid(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const unsigned int hops,const Packet::Verb verb,const char *reason);
|
||||
void incomingPacketDroppedHELLO(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const char *reason);
|
||||
|
||||
void outgoingNetworkFrameDropped(void *const tPtr,const SharedPtr<Network> &network,const MAC &sourceMac,const MAC &destMac,const unsigned int etherType,const unsigned int vlanId,const unsigned int frameLen,const char *reason);
|
||||
void incomingNetworkAccessDenied(void *const tPtr,const SharedPtr<Network> &network,const SharedPtr<Path> &path,const uint64_t packetId,const unsigned int packetLength,const Address &source,const Packet::Verb verb,bool credentialsRequested);
|
||||
void incomingNetworkFrameDropped(void *const tPtr,const SharedPtr<Network> &network,const SharedPtr<Path> &path,const uint64_t packetId,const unsigned int packetLength,const Address &source,const Packet::Verb verb,const MAC &sourceMac,const MAC &destMac,const char *reason);
|
||||
|
||||
void networkConfigRequestSent(void *const tPtr,const Network &network,const Address &controller);
|
||||
void networkFilter(
|
||||
void *const tPtr,
|
||||
const Network &network,
|
||||
const RuleResultLog &primaryRuleSetLog,
|
||||
const RuleResultLog *const matchingCapabilityRuleSetLog,
|
||||
const Capability *const matchingCapability,
|
||||
const Address &ztSource,
|
||||
const Address &ztDest,
|
||||
const MAC &macSource,
|
||||
const MAC &macDest,
|
||||
const uint8_t *const frameData,
|
||||
const unsigned int frameLen,
|
||||
const unsigned int etherType,
|
||||
const unsigned int vlanId,
|
||||
const bool noTee,
|
||||
const bool inbound,
|
||||
const int accept);
|
||||
|
||||
void credentialRejected(void *const tPtr,const CertificateOfMembership &c,const char *reason);
|
||||
void credentialRejected(void *const tPtr,const CertificateOfOwnership &c,const char *reason);
|
||||
void credentialRejected(void *const tPtr,const Capability &c,const char *reason);
|
||||
void credentialRejected(void *const tPtr,const Tag &c,const char *reason);
|
||||
void credentialRejected(void *const tPtr,const Revocation &c,const char *reason);
|
||||
|
||||
void updateMemoizedSettings();
|
||||
|
||||
private:
|
||||
const RuntimeEnvironment *const RR;
|
||||
|
||||
void _send(void *const tPtr,const Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> &d,const Address &dest);
|
||||
void _spamToAllNetworks(void *const tPtr,const Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> &d,const Level level);
|
||||
|
||||
Address _globalTarget;
|
||||
Trace::Level _globalLevel;
|
||||
Hashtable< uint64_t,std::pair< Address,Trace::Level > > _byNet;
|
||||
Mutex _byNet_m;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
+309
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "Constants.hpp"
|
||||
|
||||
#ifdef __UNIX_LIKE__
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/uio.h>
|
||||
#include <dirent.h>
|
||||
#ifdef ZT_ARCH_ARM_HAS_NEON
|
||||
#ifdef __LINUX__
|
||||
#include <sys/auxv.h>
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
#include <wincrypt.h>
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
#include "Utils.hpp"
|
||||
#include "Mutex.hpp"
|
||||
#include "Salsa20.hpp"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <TargetConditionals.h>
|
||||
#endif
|
||||
|
||||
#if defined(__ANDROID__) && defined(__aarch64__)
|
||||
#include <asm/hwcap.h>
|
||||
#endif
|
||||
|
||||
#ifdef ZT_ARCH_ARM_HAS_NEON
|
||||
|
||||
#ifdef __LINUX__
|
||||
#include <sys/auxv.h>
|
||||
#include <asm/hwcap.h>
|
||||
#endif
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
#include <elf.h>
|
||||
#include <sys/auxv.h>
|
||||
static inline long getauxval(int caps)
|
||||
{
|
||||
long hwcaps = 0;
|
||||
elf_aux_info(caps, &hwcaps, sizeof(hwcaps));
|
||||
return hwcaps;
|
||||
}
|
||||
#endif
|
||||
|
||||
// If these are not even defined, obviously they are not supported.
|
||||
#ifndef HWCAP_AES
|
||||
#define HWCAP_AES 0
|
||||
#endif
|
||||
#ifndef HWCAP_CRC32
|
||||
#define HWCAP_CRC32 0
|
||||
#endif
|
||||
#ifndef HWCAP_PMULL
|
||||
#define HWCAP_PMULL 0
|
||||
#endif
|
||||
#ifndef HWCAP_SHA1
|
||||
#define HWCAP_SHA1 0
|
||||
#endif
|
||||
#ifndef HWCAP_SHA2
|
||||
#define HWCAP_SHA2 0
|
||||
#endif
|
||||
|
||||
#endif // ZT_ARCH_ARM_HAS_NEON
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
const uint64_t Utils::ZERO256[4] = {0ULL,0ULL,0ULL,0ULL};
|
||||
|
||||
const char Utils::HEXCHARS[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };
|
||||
|
||||
#ifdef ZT_ARCH_ARM_HAS_NEON
|
||||
Utils::ARMCapabilities::ARMCapabilities() noexcept
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
|
||||
this->aes = true;
|
||||
this->crc32 = true;
|
||||
this->pmull = true;
|
||||
this->sha1 = true;
|
||||
this->sha2 = true;
|
||||
|
||||
#else
|
||||
|
||||
#ifdef HWCAP2_AES
|
||||
if (sizeof(void *) == 4) {
|
||||
const long hwcaps2 = getauxval(AT_HWCAP2);
|
||||
this->aes = (hwcaps2 & HWCAP2_AES) != 0;
|
||||
this->crc32 = (hwcaps2 & HWCAP2_CRC32) != 0;
|
||||
this->pmull = (hwcaps2 & HWCAP2_PMULL) != 0;
|
||||
this->sha1 = (hwcaps2 & HWCAP2_SHA1) != 0;
|
||||
this->sha2 = (hwcaps2 & HWCAP2_SHA2) != 0;
|
||||
} else {
|
||||
#endif
|
||||
const long hwcaps = getauxval(AT_HWCAP);
|
||||
this->aes = (hwcaps & HWCAP_AES) != 0;
|
||||
this->crc32 = (hwcaps & HWCAP_CRC32) != 0;
|
||||
this->pmull = (hwcaps & HWCAP_PMULL) != 0;
|
||||
this->sha1 = (hwcaps & HWCAP_SHA1) != 0;
|
||||
this->sha2 = (hwcaps & HWCAP_SHA2) != 0;
|
||||
#ifdef HWCAP2_AES
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __APPLE__
|
||||
}
|
||||
|
||||
const Utils::ARMCapabilities Utils::ARMCAP;
|
||||
#endif
|
||||
|
||||
#ifdef ZT_ARCH_X64
|
||||
|
||||
Utils::CPUIDRegisters::CPUIDRegisters() noexcept
|
||||
{
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
int regs[4];
|
||||
__cpuid(regs,1);
|
||||
eax = (uint32_t)regs[0];
|
||||
ebx = (uint32_t)regs[1];
|
||||
ecx = (uint32_t)regs[2];
|
||||
edx = (uint32_t)regs[3];
|
||||
#else
|
||||
__asm__ __volatile__ (
|
||||
"cpuid"
|
||||
: "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
|
||||
: "a"(1), "c"(0)
|
||||
);
|
||||
#endif
|
||||
|
||||
rdrand = ((ecx & (1U << 30U)) != 0);
|
||||
aes = (((ecx & (1U << 25U)) != 0) && ((ecx & (1U << 19U)) != 0) && ((ecx & (1U << 1U)) != 0));
|
||||
avx = ((ecx & (1U << 25U)) != 0);
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
__cpuid(regs,7);
|
||||
eax = (uint32_t)regs[0];
|
||||
ebx = (uint32_t)regs[1];
|
||||
ecx = (uint32_t)regs[2];
|
||||
edx = (uint32_t)regs[3];
|
||||
#else
|
||||
__asm__ __volatile__ (
|
||||
"cpuid"
|
||||
: "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
|
||||
: "a"(7), "c"(0)
|
||||
);
|
||||
#endif
|
||||
|
||||
vaes = aes && avx && ((ecx & (1U << 9U)) != 0);
|
||||
vpclmulqdq = aes && avx && ((ecx & (1U << 10U)) != 0);
|
||||
avx2 = avx && ((ebx & (1U << 5U)) != 0);
|
||||
avx512f = avx && ((ebx & (1U << 16U)) != 0);
|
||||
sha = ((ebx & (1U << 29U)) != 0);
|
||||
fsrm = ((edx & (1U << 4U)) != 0);
|
||||
}
|
||||
|
||||
const Utils::CPUIDRegisters Utils::CPUID;
|
||||
#endif
|
||||
|
||||
// Crazy hack to force memory to be securely zeroed in spite of the best efforts of optimizing compilers.
|
||||
static void _Utils_doBurn(volatile uint8_t *ptr,unsigned int len)
|
||||
{
|
||||
volatile uint8_t *const end = ptr + len;
|
||||
while (ptr != end) {
|
||||
*(ptr++) = (uint8_t)0;
|
||||
}
|
||||
}
|
||||
static void (*volatile _Utils_doBurn_ptr)(volatile uint8_t *,unsigned int) = _Utils_doBurn;
|
||||
void Utils::burn(void *ptr,unsigned int len) { (_Utils_doBurn_ptr)((volatile uint8_t *)ptr,len); }
|
||||
|
||||
static unsigned long _Utils_itoa(unsigned long n,char *s)
|
||||
{
|
||||
if (n == 0) {
|
||||
return 0;
|
||||
}
|
||||
unsigned long pos = _Utils_itoa(n / 10,s);
|
||||
if (pos >= 22) { // sanity check, should be impossible
|
||||
pos = 22;
|
||||
}
|
||||
s[pos] = '0' + (char)(n % 10);
|
||||
return pos + 1;
|
||||
}
|
||||
char *Utils::decimal(unsigned long n,char s[24])
|
||||
{
|
||||
if (n == 0) {
|
||||
s[0] = '0';
|
||||
s[1] = (char)0;
|
||||
return s;
|
||||
}
|
||||
s[_Utils_itoa(n,s)] = (char)0;
|
||||
return s;
|
||||
}
|
||||
|
||||
void Utils::getSecureRandom(void *buf,unsigned int bytes)
|
||||
{
|
||||
static Mutex globalLock;
|
||||
static Salsa20 s20;
|
||||
static bool s20Initialized = false;
|
||||
static uint8_t randomBuf[65536];
|
||||
static unsigned int randomPtr = sizeof(randomBuf);
|
||||
|
||||
Mutex::Lock _l(globalLock);
|
||||
|
||||
/* Just for posterity we Salsa20 encrypt the result of whatever system
|
||||
* CSPRNG we use. There have been several bugs at the OS or OS distribution
|
||||
* level in the past that resulted in systematically weak or predictable
|
||||
* keys due to random seeding problems. This mitigates that by grabbing
|
||||
* a bit of extra entropy and further randomizing the result, and comes
|
||||
* at almost no cost and with no real downside if the random source is
|
||||
* good. */
|
||||
if (!s20Initialized) {
|
||||
s20Initialized = true;
|
||||
uint64_t s20Key[4];
|
||||
s20Key[0] = (uint64_t)time(0); // system clock
|
||||
s20Key[1] = (uint64_t)buf; // address of buf
|
||||
s20Key[2] = (uint64_t)s20Key; // address of s20Key[]
|
||||
s20Key[3] = (uint64_t)&s20; // address of s20
|
||||
s20.init(s20Key,s20Key);
|
||||
}
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
|
||||
static HCRYPTPROV cryptProvider = NULL;
|
||||
|
||||
for(unsigned int i=0;i<bytes;++i) {
|
||||
if (randomPtr >= sizeof(randomBuf)) {
|
||||
if (cryptProvider == NULL) {
|
||||
if (!CryptAcquireContextA(&cryptProvider,NULL,NULL,PROV_RSA_FULL,CRYPT_VERIFYCONTEXT|CRYPT_SILENT)) {
|
||||
fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to obtain WinCrypt context!\r\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (!CryptGenRandom(cryptProvider,(DWORD)sizeof(randomBuf),(BYTE *)randomBuf)) {
|
||||
fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() CryptGenRandom failed!\r\n");
|
||||
exit(1);
|
||||
}
|
||||
randomPtr = 0;
|
||||
s20.crypt12(randomBuf,randomBuf,sizeof(randomBuf));
|
||||
s20.init(randomBuf,randomBuf);
|
||||
}
|
||||
((uint8_t *)buf)[i] = randomBuf[randomPtr++];
|
||||
}
|
||||
|
||||
#else // not __WINDOWS__
|
||||
|
||||
static int devURandomFd = -1;
|
||||
|
||||
if (devURandomFd < 0) {
|
||||
devURandomFd = ::open("/dev/urandom",O_RDONLY);
|
||||
if (devURandomFd < 0) {
|
||||
fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to open /dev/urandom\n");
|
||||
exit(1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for(unsigned int i=0;i<bytes;++i) {
|
||||
if (randomPtr >= sizeof(randomBuf)) {
|
||||
for(;;) {
|
||||
if ((int)::read(devURandomFd,randomBuf,sizeof(randomBuf)) != (int)sizeof(randomBuf)) {
|
||||
::close(devURandomFd);
|
||||
devURandomFd = ::open("/dev/urandom",O_RDONLY);
|
||||
if (devURandomFd < 0) {
|
||||
fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to open /dev/urandom\n");
|
||||
exit(1);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
randomPtr = 0;
|
||||
s20.crypt12(randomBuf,randomBuf,sizeof(randomBuf));
|
||||
s20.init(randomBuf,randomBuf);
|
||||
}
|
||||
((uint8_t *)buf)[i] = randomBuf[randomPtr++];
|
||||
}
|
||||
|
||||
#endif // __WINDOWS__ or not
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
+870
@@ -0,0 +1,870 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_UTILS_HPP
|
||||
#define ZT_UTILS_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
#include <sys/endian.h>
|
||||
#endif
|
||||
|
||||
#include "Constants.hpp"
|
||||
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
#define ZT_CONST_TO_BE_UINT16(x) ((uint16_t)((uint16_t)((uint16_t)(x) << 8U) | (uint16_t)((uint16_t)(x) >> 8U)))
|
||||
#define ZT_CONST_TO_BE_UINT64(x) ( \
|
||||
(((uint64_t)(x) & 0x00000000000000ffULL) << 56U) | \
|
||||
(((uint64_t)(x) & 0x000000000000ff00ULL) << 40U) | \
|
||||
(((uint64_t)(x) & 0x0000000000ff0000ULL) << 24U) | \
|
||||
(((uint64_t)(x) & 0x00000000ff000000ULL) << 8U) | \
|
||||
(((uint64_t)(x) & 0x000000ff00000000ULL) >> 8U) | \
|
||||
(((uint64_t)(x) & 0x0000ff0000000000ULL) >> 24U) | \
|
||||
(((uint64_t)(x) & 0x00ff000000000000ULL) >> 40U) | \
|
||||
(((uint64_t)(x) & 0xff00000000000000ULL) >> 56U))
|
||||
#else
|
||||
#define ZT_CONST_TO_BE_UINT16(x) ((uint16_t)(x))
|
||||
#define ZT_CONST_TO_BE_UINT64(x) ((uint64_t)(x))
|
||||
#endif
|
||||
|
||||
#define ZT_ROR64(x, r) (((x) >> (r)) | ((x) << (64 - (r))))
|
||||
#define ZT_ROL64(x, r) (((x) << (r)) | ((x) >> (64 - (r))))
|
||||
#define ZT_ROR32(x, r) (((x) >> (r)) | ((x) << (32 - (r))))
|
||||
#define ZT_ROL32(x, r) (((x) << (r)) | ((x) >> (32 - (r))))
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* Miscellaneous utility functions and global constants
|
||||
*/
|
||||
class Utils
|
||||
{
|
||||
public:
|
||||
static const uint64_t ZERO256[4];
|
||||
|
||||
#ifdef ZT_ARCH_ARM_HAS_NEON
|
||||
struct ARMCapabilities
|
||||
{
|
||||
ARMCapabilities() noexcept;
|
||||
|
||||
bool aes;
|
||||
bool crc32;
|
||||
bool pmull;
|
||||
bool sha1;
|
||||
bool sha2;
|
||||
};
|
||||
static const ARMCapabilities ARMCAP;
|
||||
#endif
|
||||
|
||||
#ifdef ZT_ARCH_X64
|
||||
struct CPUIDRegisters
|
||||
{
|
||||
CPUIDRegisters() noexcept;
|
||||
|
||||
bool rdrand;
|
||||
bool aes;
|
||||
bool avx;
|
||||
bool vaes; // implies AVX
|
||||
bool vpclmulqdq; // implies AVX
|
||||
bool avx2;
|
||||
bool avx512f;
|
||||
bool sha;
|
||||
bool fsrm;
|
||||
};
|
||||
static const CPUIDRegisters CPUID;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Compute the log2 (most significant bit set) of a 32-bit integer
|
||||
*
|
||||
* @param v Integer to compute
|
||||
* @return log2 or 0 if v is 0
|
||||
*/
|
||||
static inline unsigned int log2(uint32_t v)
|
||||
{
|
||||
uint32_t r = (v > 0xffff) << 4;
|
||||
v >>= r;
|
||||
uint32_t shift = (v > 0xff) << 3;
|
||||
v >>= shift;
|
||||
r |= shift;
|
||||
shift = (v > 0xf) << 2;
|
||||
v >>= shift;
|
||||
r |= shift;
|
||||
shift = (v > 0x3) << 1;
|
||||
v >>= shift;
|
||||
r |= shift;
|
||||
r |= (v >> 1);
|
||||
return (unsigned int)r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a time-invariant binary comparison
|
||||
*
|
||||
* @param a First binary string
|
||||
* @param b Second binary string
|
||||
* @param len Length of strings
|
||||
* @return True if strings are equal
|
||||
*/
|
||||
static inline bool secureEq(const void *a,const void *b,unsigned int len)
|
||||
{
|
||||
uint8_t diff = 0;
|
||||
for(unsigned int i=0;i<len;++i) {
|
||||
diff |= ( (reinterpret_cast<const uint8_t *>(a))[i] ^ (reinterpret_cast<const uint8_t *>(b))[i] );
|
||||
}
|
||||
return (diff == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Securely zero memory, avoiding compiler optimizations and such
|
||||
*/
|
||||
static void burn(void *ptr,unsigned int len);
|
||||
|
||||
/**
|
||||
* @param n Number to convert
|
||||
* @param s Buffer, at least 24 bytes in size
|
||||
* @return String containing 'n' in base 10 form
|
||||
*/
|
||||
static char *decimal(unsigned long n,char s[24]);
|
||||
|
||||
static inline char *hex(uint64_t i,char s[17])
|
||||
{
|
||||
s[0] = HEXCHARS[(i >> 60) & 0xf];
|
||||
s[1] = HEXCHARS[(i >> 56) & 0xf];
|
||||
s[2] = HEXCHARS[(i >> 52) & 0xf];
|
||||
s[3] = HEXCHARS[(i >> 48) & 0xf];
|
||||
s[4] = HEXCHARS[(i >> 44) & 0xf];
|
||||
s[5] = HEXCHARS[(i >> 40) & 0xf];
|
||||
s[6] = HEXCHARS[(i >> 36) & 0xf];
|
||||
s[7] = HEXCHARS[(i >> 32) & 0xf];
|
||||
s[8] = HEXCHARS[(i >> 28) & 0xf];
|
||||
s[9] = HEXCHARS[(i >> 24) & 0xf];
|
||||
s[10] = HEXCHARS[(i >> 20) & 0xf];
|
||||
s[11] = HEXCHARS[(i >> 16) & 0xf];
|
||||
s[12] = HEXCHARS[(i >> 12) & 0xf];
|
||||
s[13] = HEXCHARS[(i >> 8) & 0xf];
|
||||
s[14] = HEXCHARS[(i >> 4) & 0xf];
|
||||
s[15] = HEXCHARS[i & 0xf];
|
||||
s[16] = (char)0;
|
||||
return s;
|
||||
}
|
||||
|
||||
static inline char *hex10(uint64_t i,char s[11])
|
||||
{
|
||||
s[0] = HEXCHARS[(i >> 36) & 0xf];
|
||||
s[1] = HEXCHARS[(i >> 32) & 0xf];
|
||||
s[2] = HEXCHARS[(i >> 28) & 0xf];
|
||||
s[3] = HEXCHARS[(i >> 24) & 0xf];
|
||||
s[4] = HEXCHARS[(i >> 20) & 0xf];
|
||||
s[5] = HEXCHARS[(i >> 16) & 0xf];
|
||||
s[6] = HEXCHARS[(i >> 12) & 0xf];
|
||||
s[7] = HEXCHARS[(i >> 8) & 0xf];
|
||||
s[8] = HEXCHARS[(i >> 4) & 0xf];
|
||||
s[9] = HEXCHARS[i & 0xf];
|
||||
s[10] = (char)0;
|
||||
return s;
|
||||
}
|
||||
|
||||
static inline char *hex(uint32_t i,char s[9])
|
||||
{
|
||||
s[0] = HEXCHARS[(i >> 28) & 0xf];
|
||||
s[1] = HEXCHARS[(i >> 24) & 0xf];
|
||||
s[2] = HEXCHARS[(i >> 20) & 0xf];
|
||||
s[3] = HEXCHARS[(i >> 16) & 0xf];
|
||||
s[4] = HEXCHARS[(i >> 12) & 0xf];
|
||||
s[5] = HEXCHARS[(i >> 8) & 0xf];
|
||||
s[6] = HEXCHARS[(i >> 4) & 0xf];
|
||||
s[7] = HEXCHARS[i & 0xf];
|
||||
s[8] = (char)0;
|
||||
return s;
|
||||
}
|
||||
|
||||
static inline char *hex(uint16_t i,char s[5])
|
||||
{
|
||||
s[0] = HEXCHARS[(i >> 12) & 0xf];
|
||||
s[1] = HEXCHARS[(i >> 8) & 0xf];
|
||||
s[2] = HEXCHARS[(i >> 4) & 0xf];
|
||||
s[3] = HEXCHARS[i & 0xf];
|
||||
s[4] = (char)0;
|
||||
return s;
|
||||
}
|
||||
|
||||
static inline char *hex(uint8_t i,char s[3])
|
||||
{
|
||||
s[0] = HEXCHARS[(i >> 4) & 0xf];
|
||||
s[1] = HEXCHARS[i & 0xf];
|
||||
s[2] = (char)0;
|
||||
return s;
|
||||
}
|
||||
|
||||
static inline char *hex(const void *d,unsigned int l,char *s)
|
||||
{
|
||||
char *const save = s;
|
||||
for(unsigned int i=0;i<l;++i) {
|
||||
const unsigned int b = reinterpret_cast<const uint8_t *>(d)[i];
|
||||
*(s++) = HEXCHARS[b >> 4];
|
||||
*(s++) = HEXCHARS[b & 0xf];
|
||||
}
|
||||
*s = (char)0;
|
||||
return save;
|
||||
}
|
||||
|
||||
static inline unsigned int unhex(const char *h,void *buf,unsigned int buflen)
|
||||
{
|
||||
unsigned int l = 0;
|
||||
while (l < buflen) {
|
||||
uint8_t hc = *(reinterpret_cast<const uint8_t *>(h++));
|
||||
if (!hc) {
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t c = 0;
|
||||
if ((hc >= 48)&&(hc <= 57)) { // 0..9
|
||||
c = hc - 48;
|
||||
} else if ((hc >= 97)&&(hc <= 102)) { // a..f
|
||||
c = hc - 87;
|
||||
} else if ((hc >= 65)&&(hc <= 70)) { // A..F
|
||||
c = hc - 55;
|
||||
}
|
||||
|
||||
hc = *(reinterpret_cast<const uint8_t *>(h++));
|
||||
if (!hc) {
|
||||
break;
|
||||
}
|
||||
|
||||
c <<= 4;
|
||||
if ((hc >= 48)&&(hc <= 57)) {
|
||||
c |= hc - 48;
|
||||
} else if ((hc >= 97)&&(hc <= 102)) {
|
||||
c |= hc - 87;
|
||||
} else if ((hc >= 65)&&(hc <= 70)) {
|
||||
c |= hc - 55;
|
||||
}
|
||||
|
||||
reinterpret_cast<uint8_t *>(buf)[l++] = c;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
static inline unsigned int unhex(const char *h,unsigned int hlen,void *buf,unsigned int buflen)
|
||||
{
|
||||
unsigned int l = 0;
|
||||
const char *hend = h + hlen;
|
||||
while (l < buflen) {
|
||||
if (h == hend) {
|
||||
break;
|
||||
}
|
||||
uint8_t hc = *(reinterpret_cast<const uint8_t *>(h++));
|
||||
if (!hc) {
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t c = 0;
|
||||
if ((hc >= 48)&&(hc <= 57)) {
|
||||
c = hc - 48;
|
||||
} else if ((hc >= 97)&&(hc <= 102)) {
|
||||
c = hc - 87;
|
||||
} else if ((hc >= 65)&&(hc <= 70)) {
|
||||
c = hc - 55;
|
||||
}
|
||||
|
||||
if (h == hend) {
|
||||
break;
|
||||
}
|
||||
hc = *(reinterpret_cast<const uint8_t *>(h++));
|
||||
if (!hc) {
|
||||
break;
|
||||
}
|
||||
|
||||
c <<= 4;
|
||||
if ((hc >= 48)&&(hc <= 57)) {
|
||||
c |= hc - 48;
|
||||
} else if ((hc >= 97)&&(hc <= 102)) {
|
||||
c |= hc - 87;
|
||||
} else if ((hc >= 65)&&(hc <= 70)) {
|
||||
c |= hc - 55;
|
||||
}
|
||||
|
||||
reinterpret_cast<uint8_t *>(buf)[l++] = c;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
static inline float normalize(float value, float bigMin, float bigMax, float targetMin, float targetMax)
|
||||
{
|
||||
float bigSpan = bigMax - bigMin;
|
||||
float smallSpan = targetMax - targetMin;
|
||||
float valueScaled = (value - bigMin) / bigSpan;
|
||||
return targetMin + valueScaled * smallSpan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate secure random bytes
|
||||
*
|
||||
* This will try to use whatever OS sources of entropy are available. It's
|
||||
* guarded by an internal mutex so it's thread-safe.
|
||||
*
|
||||
* @param buf Buffer to fill
|
||||
* @param bytes Number of random bytes to generate
|
||||
*/
|
||||
static void getSecureRandom(void *buf,unsigned int bytes);
|
||||
|
||||
/**
|
||||
* Tokenize a string (alias for strtok_r or strtok_s depending on platform)
|
||||
*
|
||||
* @param str String to split
|
||||
* @param delim Delimiters
|
||||
* @param saveptr Pointer to a char * for temporary reentrant storage
|
||||
*/
|
||||
static inline char *stok(char *str,const char *delim,char **saveptr)
|
||||
{
|
||||
#ifdef __WINDOWS__
|
||||
return strtok_s(str,delim,saveptr);
|
||||
#else
|
||||
return strtok_r(str,delim,saveptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline unsigned int strToUInt(const char *s) { return (unsigned int)strtoul(s,(char **)0,10); }
|
||||
static inline int strToInt(const char *s) { return (int)strtol(s,(char **)0,10); }
|
||||
static inline unsigned long strToULong(const char *s) { return strtoul(s,(char **)0,10); }
|
||||
static inline long strToLong(const char *s) { return strtol(s,(char **)0,10); }
|
||||
static inline double strToDouble(const char *s) { return strtod(s,NULL); }
|
||||
static inline unsigned long long strToU64(const char *s)
|
||||
{
|
||||
#ifdef __WINDOWS__
|
||||
return (unsigned long long)_strtoui64(s,(char **)0,10);
|
||||
#else
|
||||
return strtoull(s,(char **)0,10);
|
||||
#endif
|
||||
}
|
||||
static inline long long strTo64(const char *s)
|
||||
{
|
||||
#ifdef __WINDOWS__
|
||||
return (long long)_strtoi64(s,(char **)0,10);
|
||||
#else
|
||||
return strtoll(s,(char **)0,10);
|
||||
#endif
|
||||
}
|
||||
static inline unsigned int hexStrToUInt(const char *s) { return (unsigned int)strtoul(s,(char **)0,16); }
|
||||
static inline int hexStrToInt(const char *s) { return (int)strtol(s,(char **)0,16); }
|
||||
static inline unsigned long hexStrToULong(const char *s) { return strtoul(s,(char **)0,16); }
|
||||
static inline long hexStrToLong(const char *s) { return strtol(s,(char **)0,16); }
|
||||
static inline unsigned long long hexStrToU64(const char *s)
|
||||
{
|
||||
#ifdef __WINDOWS__
|
||||
return (unsigned long long)_strtoui64(s,(char **)0,16);
|
||||
#else
|
||||
return strtoull(s,(char **)0,16);
|
||||
#endif
|
||||
}
|
||||
static inline long long hexStrTo64(const char *s)
|
||||
{
|
||||
#ifdef __WINDOWS__
|
||||
return (long long)_strtoi64(s,(char **)0,16);
|
||||
#else
|
||||
return strtoll(s,(char **)0,16);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a safe C string copy, ALWAYS null-terminating the result
|
||||
*
|
||||
* This will never ever EVER result in dest[] not being null-terminated
|
||||
* regardless of any input parameter (other than len==0 which is invalid).
|
||||
*
|
||||
* @param dest Destination buffer (must not be NULL)
|
||||
* @param len Length of dest[] (if zero, false is returned and nothing happens)
|
||||
* @param src Source string (if NULL, dest will receive a zero-length string and true is returned)
|
||||
* @return True on success, false on overflow (buffer will still be 0-terminated)
|
||||
*/
|
||||
static inline bool scopy(char *dest,unsigned int len,const char *src)
|
||||
{
|
||||
if (!len) {
|
||||
return false; // sanity check
|
||||
}
|
||||
if (!src) {
|
||||
*dest = (char)0;
|
||||
return true;
|
||||
}
|
||||
char *end = dest + len;
|
||||
while ((*dest++ = *src++)) {
|
||||
if (dest == end) {
|
||||
*(--dest) = (char)0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of bits set in an integer
|
||||
*
|
||||
* @param v 32-bit integer
|
||||
* @return Number of bits set in this integer (0-32)
|
||||
*/
|
||||
static inline uint32_t countBits(uint32_t v)
|
||||
{
|
||||
v = v - ((v >> 1) & (uint32_t)0x55555555);
|
||||
v = (v & (uint32_t)0x33333333) + ((v >> 2) & (uint32_t)0x33333333);
|
||||
return ((((v + (v >> 4)) & (uint32_t)0xF0F0F0F) * (uint32_t)0x1010101) >> 24);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of bits set in an integer
|
||||
*
|
||||
* @param v 64-bit integer
|
||||
* @return Number of bits set in this integer (0-64)
|
||||
*/
|
||||
static inline uint64_t countBits(uint64_t v)
|
||||
{
|
||||
v = v - ((v >> 1) & (uint64_t)~(uint64_t)0/3);
|
||||
v = (v & (uint64_t)~(uint64_t)0/15*3) + ((v >> 2) & (uint64_t)~(uint64_t)0/15*3);
|
||||
v = (v + (v >> 4)) & (uint64_t)~(uint64_t)0/255*15;
|
||||
return (uint64_t)(v * ((uint64_t)~(uint64_t)0/255)) >> 56;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a memory buffer is all-zero
|
||||
*
|
||||
* @param p Memory to scan
|
||||
* @param len Length of memory
|
||||
* @return True if memory is all zero
|
||||
*/
|
||||
static inline bool isZero(const void *p,unsigned int len)
|
||||
{
|
||||
for(unsigned int i=0;i<len;++i) {
|
||||
if (((const unsigned char *)p)[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unconditionally swap bytes regardless of host byte order
|
||||
*
|
||||
* @param n Integer to swap
|
||||
* @return Integer with bytes reversed
|
||||
*/
|
||||
static ZT_INLINE uint64_t swapBytes(const uint64_t n) noexcept
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
return __builtin_bswap64(n);
|
||||
#else
|
||||
#ifdef _MSC_VER
|
||||
return (uint64_t)_byteswap_uint64((unsigned __int64)n);
|
||||
#else
|
||||
return (
|
||||
((n & 0x00000000000000ffULL) << 56) |
|
||||
((n & 0x000000000000ff00ULL) << 40) |
|
||||
((n & 0x0000000000ff0000ULL) << 24) |
|
||||
((n & 0x00000000ff000000ULL) << 8) |
|
||||
((n & 0x000000ff00000000ULL) >> 8) |
|
||||
((n & 0x0000ff0000000000ULL) >> 24) |
|
||||
((n & 0x00ff000000000000ULL) >> 40) |
|
||||
((n & 0xff00000000000000ULL) >> 56)
|
||||
);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Unconditionally swap bytes regardless of host byte order
|
||||
*
|
||||
* @param n Integer to swap
|
||||
* @return Integer with bytes reversed
|
||||
*/
|
||||
static ZT_INLINE uint32_t swapBytes(const uint32_t n) noexcept
|
||||
{
|
||||
#if defined(__GNUC__)
|
||||
return __builtin_bswap32(n);
|
||||
#else
|
||||
#ifdef _MSC_VER
|
||||
return (uint32_t)_byteswap_ulong((unsigned long)n);
|
||||
#else
|
||||
return htonl(n);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Unconditionally swap bytes regardless of host byte order
|
||||
*
|
||||
* @param n Integer to swap
|
||||
* @return Integer with bytes reversed
|
||||
*/
|
||||
static ZT_INLINE uint16_t swapBytes(const uint16_t n) noexcept
|
||||
{
|
||||
#if defined(__GNUC__)
|
||||
return __builtin_bswap16(n);
|
||||
#else
|
||||
#ifdef _MSC_VER
|
||||
return (uint16_t)_byteswap_ushort((unsigned short)n);
|
||||
#else
|
||||
return htons(n);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// These are helper adapters to load and swap integer types special cased by size
|
||||
// to work with all typedef'd variants, signed/unsigned, etc.
|
||||
template< typename I, unsigned int S >
|
||||
class _swap_bytes_bysize;
|
||||
|
||||
template< typename I >
|
||||
class _swap_bytes_bysize< I, 1 >
|
||||
{
|
||||
public:
|
||||
static ZT_INLINE I s(const I n) noexcept
|
||||
{ return n; }
|
||||
};
|
||||
|
||||
template< typename I >
|
||||
class _swap_bytes_bysize< I, 2 >
|
||||
{
|
||||
public:
|
||||
static ZT_INLINE I s(const I n) noexcept
|
||||
{ return (I)swapBytes((uint16_t)n); }
|
||||
};
|
||||
|
||||
template< typename I >
|
||||
class _swap_bytes_bysize< I, 4 >
|
||||
{
|
||||
public:
|
||||
static ZT_INLINE I s(const I n) noexcept
|
||||
{ return (I)swapBytes((uint32_t)n); }
|
||||
};
|
||||
|
||||
template< typename I >
|
||||
class _swap_bytes_bysize< I, 8 >
|
||||
{
|
||||
public:
|
||||
static ZT_INLINE I s(const I n) noexcept
|
||||
{ return (I)swapBytes((uint64_t)n); }
|
||||
};
|
||||
|
||||
template< typename I, unsigned int S >
|
||||
class _load_be_bysize;
|
||||
|
||||
template< typename I >
|
||||
class _load_be_bysize< I, 1 >
|
||||
{
|
||||
public:
|
||||
static ZT_INLINE I l(const uint8_t *const p) noexcept
|
||||
{ return p[0]; }
|
||||
};
|
||||
|
||||
template< typename I >
|
||||
class _load_be_bysize< I, 2 >
|
||||
{
|
||||
public:
|
||||
static ZT_INLINE I l(const uint8_t *const p) noexcept
|
||||
{ return (I)(((unsigned int)p[0] << 8U) | (unsigned int)p[1]); }
|
||||
};
|
||||
|
||||
template< typename I >
|
||||
class _load_be_bysize< I, 4 >
|
||||
{
|
||||
public:
|
||||
static ZT_INLINE I l(const uint8_t *const p) noexcept
|
||||
{ return (I)(((uint32_t)p[0] << 24U) | ((uint32_t)p[1] << 16U) | ((uint32_t)p[2] << 8U) | (uint32_t)p[3]); }
|
||||
};
|
||||
|
||||
template< typename I >
|
||||
class _load_be_bysize< I, 8 >
|
||||
{
|
||||
public:
|
||||
static ZT_INLINE I l(const uint8_t *const p) noexcept
|
||||
{ return (I)(((uint64_t)p[0] << 56U) | ((uint64_t)p[1] << 48U) | ((uint64_t)p[2] << 40U) | ((uint64_t)p[3] << 32U) | ((uint64_t)p[4] << 24U) | ((uint64_t)p[5] << 16U) | ((uint64_t)p[6] << 8U) | (uint64_t)p[7]); }
|
||||
};
|
||||
|
||||
template< typename I, unsigned int S >
|
||||
class _load_le_bysize;
|
||||
|
||||
template< typename I >
|
||||
class _load_le_bysize< I, 1 >
|
||||
{
|
||||
public:
|
||||
static ZT_INLINE I l(const uint8_t *const p) noexcept
|
||||
{ return p[0]; }
|
||||
};
|
||||
|
||||
template< typename I >
|
||||
class _load_le_bysize< I, 2 >
|
||||
{
|
||||
public:
|
||||
static ZT_INLINE I l(const uint8_t *const p) noexcept
|
||||
{ return (I)((unsigned int)p[0] | ((unsigned int)p[1] << 8U)); }
|
||||
};
|
||||
|
||||
template< typename I >
|
||||
class _load_le_bysize< I, 4 >
|
||||
{
|
||||
public:
|
||||
static ZT_INLINE I l(const uint8_t *const p) noexcept
|
||||
{ return (I)((uint32_t)p[0] | ((uint32_t)p[1] << 8U) | ((uint32_t)p[2] << 16U) | ((uint32_t)p[3] << 24U)); }
|
||||
};
|
||||
|
||||
template< typename I >
|
||||
class _load_le_bysize< I, 8 >
|
||||
{
|
||||
public:
|
||||
static ZT_INLINE I l(const uint8_t *const p) noexcept
|
||||
{ return (I)((uint64_t)p[0] | ((uint64_t)p[1] << 8U) | ((uint64_t)p[2] << 16U) | ((uint64_t)p[3] << 24U) | ((uint64_t)p[4] << 32U) | ((uint64_t)p[5] << 40U) | ((uint64_t)p[6] << 48U) | ((uint64_t)p[7]) << 56U); }
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert any signed or unsigned integer type to big-endian ("network") byte order
|
||||
*
|
||||
* @tparam I Integer type (usually inferred)
|
||||
* @param n Value to convert
|
||||
* @return Value in big-endian order
|
||||
*/
|
||||
template< typename I >
|
||||
static ZT_INLINE I hton(const I n) noexcept
|
||||
{
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
return _swap_bytes_bysize< I, sizeof(I) >::s(n);
|
||||
#else
|
||||
return n;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert any signed or unsigned integer type to host byte order from big-endian ("network") byte order
|
||||
*
|
||||
* @tparam I Integer type (usually inferred)
|
||||
* @param n Value to convert
|
||||
* @return Value in host byte order
|
||||
*/
|
||||
template< typename I >
|
||||
static ZT_INLINE I ntoh(const I n) noexcept
|
||||
{
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
return _swap_bytes_bysize< I, sizeof(I) >::s(n);
|
||||
#else
|
||||
return n;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy bits from memory into an integer type without modifying their order
|
||||
*
|
||||
* @tparam I Type to load
|
||||
* @param p Byte stream, must be at least sizeof(I) in size
|
||||
* @return Loaded raw integer
|
||||
*/
|
||||
template< typename I >
|
||||
static ZT_INLINE I loadMachineEndian(const void *const p) noexcept
|
||||
{
|
||||
#ifdef ZT_NO_UNALIGNED_ACCESS
|
||||
I tmp;
|
||||
for(int i=0;i<(int)sizeof(I);++i) {
|
||||
reinterpret_cast<uint8_t *>(&tmp)[i] = reinterpret_cast<const uint8_t *>(p)[i];
|
||||
}
|
||||
return tmp;
|
||||
#else
|
||||
return *reinterpret_cast<const I *>(p);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy bits from memory into an integer type without modifying their order
|
||||
*
|
||||
* @tparam I Type to store
|
||||
* @param p Byte array (must be at least sizeof(I))
|
||||
* @param i Integer to store
|
||||
*/
|
||||
template< typename I >
|
||||
static ZT_INLINE void storeMachineEndian(void *const p, const I i) noexcept
|
||||
{
|
||||
#ifdef ZT_NO_UNALIGNED_ACCESS
|
||||
for(unsigned int k=0;k<sizeof(I);++k) {
|
||||
reinterpret_cast<uint8_t *>(p)[k] = reinterpret_cast<const uint8_t *>(&i)[k];
|
||||
}
|
||||
#else
|
||||
*reinterpret_cast<I *>(p) = i;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a big-endian value from a byte stream
|
||||
*
|
||||
* @tparam I Type to decode (should be unsigned e.g. uint32_t or uint64_t)
|
||||
* @param p Byte stream, must be at least sizeof(I) in size
|
||||
* @return Decoded integer
|
||||
*/
|
||||
template< typename I >
|
||||
static ZT_INLINE I loadBigEndian(const void *const p) noexcept
|
||||
{
|
||||
#ifdef ZT_NO_UNALIGNED_ACCESS
|
||||
return _load_be_bysize<I,sizeof(I)>::l(reinterpret_cast<const uint8_t *>(p));
|
||||
#else
|
||||
return ntoh(*reinterpret_cast<const I *>(p));
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Save an integer in big-endian format
|
||||
*
|
||||
* @tparam I Integer type to store (usually inferred)
|
||||
* @param p Byte stream to write (must be at least sizeof(I))
|
||||
* #param i Integer to write
|
||||
*/
|
||||
template< typename I >
|
||||
static ZT_INLINE void storeBigEndian(void *const p, I i) noexcept
|
||||
{
|
||||
#ifdef ZT_NO_UNALIGNED_ACCESS
|
||||
storeMachineEndian(p,hton(i));
|
||||
#else
|
||||
*reinterpret_cast<I *>(p) = hton(i);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a little-endian value from a byte stream
|
||||
*
|
||||
* @tparam I Type to decode
|
||||
* @param p Byte stream, must be at least sizeof(I) in size
|
||||
* @return Decoded integer
|
||||
*/
|
||||
template< typename I >
|
||||
static ZT_INLINE I loadLittleEndian(const void *const p) noexcept
|
||||
{
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN || defined(ZT_NO_UNALIGNED_ACCESS)
|
||||
return _load_le_bysize<I,sizeof(I)>::l(reinterpret_cast<const uint8_t *>(p));
|
||||
#else
|
||||
return *reinterpret_cast<const I *>(p);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Save an integer in little-endian format
|
||||
*
|
||||
* @tparam I Integer type to store (usually inferred)
|
||||
* @param p Byte stream to write (must be at least sizeof(I))
|
||||
* #param i Integer to write
|
||||
*/
|
||||
template< typename I >
|
||||
static ZT_INLINE void storeLittleEndian(void *const p, const I i) noexcept
|
||||
{
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
storeMachineEndian(p,_swap_bytes_bysize<I,sizeof(I)>::s(i));
|
||||
#else
|
||||
#ifdef ZT_NO_UNALIGNED_ACCESS
|
||||
storeMachineEndian(p,i);
|
||||
#else
|
||||
*reinterpret_cast<I *>(p) = i;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy memory block whose size is known at compile time.
|
||||
*
|
||||
* @tparam L Size of memory
|
||||
* @param dest Destination memory
|
||||
* @param src Source memory
|
||||
*/
|
||||
template< unsigned long L >
|
||||
static ZT_INLINE void copy(void *dest, const void *src) noexcept
|
||||
{
|
||||
#if defined(ZT_ARCH_X64) && defined(__GNUC__)
|
||||
uintptr_t l = L;
|
||||
__asm__ __volatile__ ("cld ; rep movsb" : "+c"(l), "+S"(src), "+D"(dest) :: "memory");
|
||||
#else
|
||||
memcpy(dest, src, L);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy memory block whose size is known at run time
|
||||
*
|
||||
* @param dest Destination memory
|
||||
* @param src Source memory
|
||||
* @param len Bytes to copy
|
||||
*/
|
||||
static ZT_INLINE void copy(void *dest, const void *src, unsigned long len) noexcept
|
||||
{
|
||||
#if defined(ZT_ARCH_X64) && defined(__GNUC__)
|
||||
__asm__ __volatile__ ("cld ; rep movsb" : "+c"(len), "+S"(src), "+D"(dest) :: "memory");
|
||||
#else
|
||||
memcpy(dest, src, len);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Zero memory block whose size is known at compile time
|
||||
*
|
||||
* @tparam L Size in bytes
|
||||
* @param dest Memory to zero
|
||||
*/
|
||||
template< unsigned long L >
|
||||
static ZT_INLINE void zero(void *dest) noexcept
|
||||
{
|
||||
#if defined(ZT_ARCH_X64) && defined(__GNUC__)
|
||||
uintptr_t l = L;
|
||||
__asm__ __volatile__ ("cld ; rep stosb" :"+c" (l), "+D" (dest) : "a" (0) : "memory");
|
||||
#else
|
||||
memset(dest, 0, L);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Zero memory block whose size is known at run time
|
||||
*
|
||||
* @param dest Memory to zero
|
||||
* @param len Size in bytes
|
||||
*/
|
||||
static ZT_INLINE void zero(void *dest, unsigned long len) noexcept
|
||||
{
|
||||
#if defined(ZT_ARCH_X64) && defined(__GNUC__)
|
||||
__asm__ __volatile__ ("cld ; rep stosb" :"+c" (len), "+D" (dest) : "a" (0) : "memory");
|
||||
#else
|
||||
memset(dest, 0, len);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Hexadecimal characters 0-f
|
||||
*/
|
||||
static const char HEXCHARS[16];
|
||||
|
||||
/*
|
||||
* Remove `-` and `:` from a MAC address (in-place).
|
||||
*
|
||||
* @param mac The MAC address
|
||||
*/
|
||||
static inline void cleanMac(std::string& mac)
|
||||
{
|
||||
auto start = mac.begin();
|
||||
auto end = mac.end();
|
||||
auto new_end = std::remove_if(start, end, [](char c) { return c == 45 || c == 58; });
|
||||
mac.erase(new_end, end);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
+292
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* Copyright (c)2019 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
#ifndef ZT_WORLD_HPP
|
||||
#define ZT_WORLD_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "Buffer.hpp"
|
||||
#include "C25519.hpp"
|
||||
|
||||
/**
|
||||
* Maximum number of roots (sanity limit, okay to increase)
|
||||
*
|
||||
* A given root can (through multi-homing) be distributed across any number of
|
||||
* physical endpoints, but having more than one is good to permit total failure
|
||||
* of one root or its withdrawal due to compromise without taking the whole net
|
||||
* down.
|
||||
*/
|
||||
#define ZT_WORLD_MAX_ROOTS 4
|
||||
|
||||
/**
|
||||
* Maximum number of stable endpoints per root (sanity limit, okay to increase)
|
||||
*/
|
||||
#define ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT 32
|
||||
|
||||
/**
|
||||
* The (more than) maximum length of a serialized World
|
||||
*/
|
||||
#define ZT_WORLD_MAX_SERIALIZED_LENGTH (((1024 + (32 * ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT)) * ZT_WORLD_MAX_ROOTS) + ZT_C25519_PUBLIC_KEY_LEN + ZT_C25519_SIGNATURE_LEN + 128)
|
||||
|
||||
/**
|
||||
* World ID for Earth
|
||||
*
|
||||
* This is the ID for the ZeroTier World used on planet Earth. It is unrelated
|
||||
* to the public network 8056c2e21c000001 of the same name. It was chosen
|
||||
* from Earth's approximate distance from the sun in kilometers.
|
||||
*/
|
||||
#define ZT_WORLD_ID_EARTH 149604618
|
||||
|
||||
/**
|
||||
* World ID for Mars -- for future use by SpaceX or others
|
||||
*/
|
||||
#define ZT_WORLD_ID_MARS 227883110
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* A world definition (formerly known as a root topology)
|
||||
*
|
||||
* Think of a World as a single data center. Within this data center a set
|
||||
* of distributed fault tolerant root servers provide stable anchor points
|
||||
* for a peer to peer network that provides VLAN service. Updates to a world
|
||||
* definition can be published by signing them with the previous revision's
|
||||
* signing key, and should be very infrequent.
|
||||
*
|
||||
* The maximum data center size is approximately 2.5 cubic light seconds,
|
||||
* since many protocols have issues with >5s RTT latencies.
|
||||
*
|
||||
* ZeroTier operates a World for Earth capable of encompassing the planet, its
|
||||
* orbits, the Moon (about 1.3 light seconds), and nearby Lagrange points. A
|
||||
* world ID for Mars and nearby space is defined but not yet used, and a test
|
||||
* world ID is provided for testing purposes.
|
||||
*/
|
||||
class World
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* World type -- do not change IDs
|
||||
*/
|
||||
enum Type
|
||||
{
|
||||
TYPE_NULL = 0,
|
||||
TYPE_PLANET = 1, // Planets, of which there is currently one (Earth)
|
||||
TYPE_MOON = 127 // Moons, which are user-created and many
|
||||
};
|
||||
|
||||
/**
|
||||
* Upstream server definition in world/moon
|
||||
*/
|
||||
struct Root
|
||||
{
|
||||
Identity identity;
|
||||
std::vector<InetAddress> stableEndpoints;
|
||||
|
||||
inline bool operator==(const Root &r) const { return ((identity == r.identity)&&(stableEndpoints == r.stableEndpoints)); }
|
||||
inline bool operator!=(const Root &r) const { return (!(*this == r)); }
|
||||
inline bool operator<(const Root &r) const { return (identity < r.identity); } // for sorting
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct an empty / null World
|
||||
*/
|
||||
World() :
|
||||
_id(0),
|
||||
_ts(0),
|
||||
_type(TYPE_NULL) {}
|
||||
|
||||
/**
|
||||
* @return Root servers for this world and their stable endpoints
|
||||
*/
|
||||
inline const std::vector<World::Root> &roots() const { return _roots; }
|
||||
|
||||
/**
|
||||
* @return World type: planet or moon
|
||||
*/
|
||||
inline Type type() const { return _type; }
|
||||
|
||||
/**
|
||||
* @return World unique identifier
|
||||
*/
|
||||
inline uint64_t id() const { return _id; }
|
||||
|
||||
/**
|
||||
* @return World definition timestamp
|
||||
*/
|
||||
inline uint64_t timestamp() const { return _ts; }
|
||||
|
||||
/**
|
||||
* @return C25519 signature
|
||||
*/
|
||||
inline const C25519::Signature &signature() const { return _signature; }
|
||||
|
||||
/**
|
||||
* @return Public key that must sign next update
|
||||
*/
|
||||
inline const C25519::Public &updatesMustBeSignedBy() const { return _updatesMustBeSignedBy; }
|
||||
|
||||
/**
|
||||
* Check whether a world update should replace this one
|
||||
*
|
||||
* @param update Candidate update
|
||||
* @return True if update is newer than current, matches its ID and type, and is properly signed (or if current is NULL)
|
||||
*/
|
||||
inline bool shouldBeReplacedBy(const World &update)
|
||||
{
|
||||
if ((_id == 0)||(_type == TYPE_NULL)) {
|
||||
return true;
|
||||
}
|
||||
if ((_id == update._id)&&(_ts < update._ts)&&(_type == update._type)) {
|
||||
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> tmp;
|
||||
update.serialize(tmp,true);
|
||||
return C25519::verify(_updatesMustBeSignedBy,tmp.data(),tmp.size(),update._signature);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if this World is non-empty
|
||||
*/
|
||||
inline operator bool() const { return (_type != TYPE_NULL); }
|
||||
|
||||
template<unsigned int C>
|
||||
inline void serialize(Buffer<C> &b,bool forSign = false) const
|
||||
{
|
||||
if (forSign) {
|
||||
b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
|
||||
}
|
||||
|
||||
b.append((uint8_t)_type);
|
||||
b.append((uint64_t)_id);
|
||||
b.append((uint64_t)_ts);
|
||||
b.append(_updatesMustBeSignedBy.data,ZT_C25519_PUBLIC_KEY_LEN);
|
||||
if (!forSign) {
|
||||
b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
|
||||
}
|
||||
b.append((uint8_t)_roots.size());
|
||||
for(std::vector<Root>::const_iterator r(_roots.begin());r!=_roots.end();++r) {
|
||||
r->identity.serialize(b);
|
||||
b.append((uint8_t)r->stableEndpoints.size());
|
||||
for(std::vector<InetAddress>::const_iterator ep(r->stableEndpoints.begin());ep!=r->stableEndpoints.end();++ep) {
|
||||
ep->serialize(b);
|
||||
}
|
||||
}
|
||||
if (_type == TYPE_MOON) {
|
||||
b.append((uint16_t)0); // no attached dictionary (for future use)
|
||||
}
|
||||
|
||||
if (forSign) {
|
||||
b.append((uint64_t)0xf7f7f7f7f7f7f7f7ULL);
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned int C>
|
||||
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
|
||||
{
|
||||
unsigned int p = startAt;
|
||||
|
||||
_roots.clear();
|
||||
|
||||
switch((Type)b[p++]) {
|
||||
case TYPE_NULL: // shouldn't ever really happen in serialized data but it's not invalid
|
||||
_type = TYPE_NULL;
|
||||
break;
|
||||
case TYPE_PLANET:
|
||||
_type = TYPE_PLANET;
|
||||
break;
|
||||
case TYPE_MOON:
|
||||
_type = TYPE_MOON;
|
||||
break;
|
||||
default:
|
||||
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
|
||||
}
|
||||
|
||||
_id = b.template at<uint64_t>(p);
|
||||
p += 8;
|
||||
_ts = b.template at<uint64_t>(p);
|
||||
p += 8;
|
||||
memcpy(_updatesMustBeSignedBy.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN);
|
||||
p += ZT_C25519_PUBLIC_KEY_LEN;
|
||||
memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN);
|
||||
p += ZT_C25519_SIGNATURE_LEN;
|
||||
const unsigned int numRoots = (unsigned int)b[p++];
|
||||
if (numRoots > ZT_WORLD_MAX_ROOTS) {
|
||||
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
|
||||
}
|
||||
for(unsigned int k=0;k<numRoots;++k) {
|
||||
_roots.push_back(Root());
|
||||
Root &r = _roots.back();
|
||||
p += r.identity.deserialize(b,p);
|
||||
unsigned int numStableEndpoints = b[p++];
|
||||
if (numStableEndpoints > ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT) {
|
||||
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
|
||||
}
|
||||
for(unsigned int kk=0;kk<numStableEndpoints;++kk) {
|
||||
r.stableEndpoints.push_back(InetAddress());
|
||||
p += r.stableEndpoints.back().deserialize(b,p);
|
||||
}
|
||||
}
|
||||
if (_type == TYPE_MOON) {
|
||||
p += b.template at<uint16_t>(p) + 2;
|
||||
}
|
||||
|
||||
return (p - startAt);
|
||||
}
|
||||
|
||||
inline bool operator==(const World &w) const { return ((_id == w._id)&&(_ts == w._ts)&&(memcmp(_updatesMustBeSignedBy.data,w._updatesMustBeSignedBy.data,ZT_C25519_PUBLIC_KEY_LEN) == 0)&&(memcmp(_signature.data,w._signature.data,ZT_C25519_SIGNATURE_LEN) == 0)&&(_roots == w._roots)&&(_type == w._type)); }
|
||||
inline bool operator!=(const World &w) const { return (!(*this == w)); }
|
||||
|
||||
/**
|
||||
* Create a World object signed with a key pair
|
||||
*
|
||||
* @param t World type
|
||||
* @param id World ID
|
||||
* @param ts World timestamp / revision
|
||||
* @param sk Key that must be used to sign the next future update to this world
|
||||
* @param roots Roots and their stable endpoints
|
||||
* @param signWith Key to sign this World with (can have the same public as the next-update signing key, but doesn't have to)
|
||||
* @return Signed World object
|
||||
*/
|
||||
static inline World make(World::Type t,uint64_t id,uint64_t ts,const C25519::Public &sk,const std::vector<World::Root> &roots,const C25519::Pair &signWith)
|
||||
{
|
||||
World w;
|
||||
w._id = id;
|
||||
w._ts = ts;
|
||||
w._type = t;
|
||||
w._updatesMustBeSignedBy = sk;
|
||||
w._roots = roots;
|
||||
|
||||
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> tmp;
|
||||
w.serialize(tmp,true);
|
||||
w._signature = C25519::sign(signWith,tmp.data(),tmp.size());
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
protected:
|
||||
uint64_t _id;
|
||||
uint64_t _ts;
|
||||
Type _type;
|
||||
C25519::Public _updatesMustBeSignedBy;
|
||||
C25519::Signature _signature;
|
||||
std::vector<Root> _roots;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user