|
|
|
@ -17,6 +17,7 @@ |
|
|
|
|
|
|
|
|
|
|
|
struct bucket_entry { uint8_t level; uint8_t prefix_bytes; uint64_t prefix; }; |
|
|
|
struct bucket_entry { uint8_t level; uint8_t prefix_bytes; uint64_t prefix; }; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
enum { SESS_SYNCING, SESS_SYNCED }; |
|
|
|
enum { MS_PEND_WAITING = 0, MS_PEND_RESOLVED = 1 }; |
|
|
|
enum { MS_PEND_WAITING = 0, MS_PEND_RESOLVED = 1 }; |
|
|
|
|
|
|
|
|
|
|
|
struct ms_pending { |
|
|
|
struct ms_pending { |
|
|
|
@ -31,8 +32,7 @@ struct ms_session { |
|
|
|
struct ms_session* next; |
|
|
|
struct ms_session* next; |
|
|
|
char ns[64]; |
|
|
|
char ns[64]; |
|
|
|
uint64_t peer; |
|
|
|
uint64_t peer; |
|
|
|
uint8_t active; |
|
|
|
uint8_t sess_state; |
|
|
|
uint8_t synced; |
|
|
|
|
|
|
|
merkle_sync_done_cb done_cb; |
|
|
|
merkle_sync_done_cb done_cb; |
|
|
|
void* cb_arg; |
|
|
|
void* cb_arg; |
|
|
|
struct ms_pending pending[MS_PENDING_MAX]; |
|
|
|
struct ms_pending pending[MS_PENDING_MAX]; |
|
|
|
@ -55,8 +55,6 @@ static sqlite3* _db(struct UTUN_INSTANCE* inst) { |
|
|
|
return inst ? inst->topo_sqlite_db : NULL; |
|
|
|
return inst ? inst->topo_sqlite_db : NULL; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static int _send_msg(struct merkle_sync* ms, uint64_t peer, const uint8_t* payload, size_t len); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ── Prefix arithmetic ── */ |
|
|
|
/* ── Prefix arithmetic ── */ |
|
|
|
|
|
|
|
|
|
|
|
uint64_t merkle_sync_level_prefix(uint64_t key, uint8_t level) { |
|
|
|
uint64_t merkle_sync_level_prefix(uint64_t key, uint8_t level) { |
|
|
|
@ -243,6 +241,24 @@ static int _send_msg(struct merkle_sync* ms, uint64_t peer, |
|
|
|
return r; |
|
|
|
return r; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int _send_broadcast_data(struct merkle_sync* ms, uint64_t peer, const char* ns, |
|
|
|
|
|
|
|
const uint8_t* item_data, size_t item_len) { |
|
|
|
|
|
|
|
uint8_t ch_len = (uint8_t)strlen(ns); |
|
|
|
|
|
|
|
size_t sz = 1 + 1 + ch_len + 1 + 1 + 1 + 2 + item_len; |
|
|
|
|
|
|
|
uint8_t* buf = u_malloc(sz); if (!buf) return -1; |
|
|
|
|
|
|
|
uint8_t* p = buf; |
|
|
|
|
|
|
|
*p++ = ch_len; memcpy(p, ns, ch_len); p += ch_len; |
|
|
|
|
|
|
|
*p++ = 0x01; /* MSG_HASHES */ |
|
|
|
|
|
|
|
*p++ = 0; /* level=0 */ |
|
|
|
|
|
|
|
*p++ = 0; /* prefix_bytes=0 */ |
|
|
|
|
|
|
|
*p++ = 1; /* is_data=1 */ |
|
|
|
|
|
|
|
uint16_t dlen = (uint16_t)item_len; memcpy(p, &dlen, 2); p += 2; |
|
|
|
|
|
|
|
memcpy(p, item_data, item_len); p += item_len; |
|
|
|
|
|
|
|
int r = _send_msg(ms, peer, buf, (size_t)(p - buf)); |
|
|
|
|
|
|
|
u_free(buf); |
|
|
|
|
|
|
|
return r; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static int _send_hashes(struct merkle_sync* ms, uint64_t peer, const char* ns, |
|
|
|
static int _send_hashes(struct merkle_sync* ms, uint64_t peer, const char* ns, |
|
|
|
uint8_t level, uint64_t prefix, uint8_t prefix_bytes, int is_data) { |
|
|
|
uint8_t level, uint64_t prefix, uint8_t prefix_bytes, int is_data) { |
|
|
|
DEBUG_TRACE(DEBUG_CATEGORY_DB_SYNC, "%s: send_hashes peer=%016llx ns=%s L%d P%016llx is_data=%d", MS_ID, (unsigned long long)peer, ns, level, (unsigned long long)prefix, is_data); |
|
|
|
DEBUG_TRACE(DEBUG_CATEGORY_DB_SYNC, "%s: send_hashes peer=%016llx ns=%s L%d P%016llx is_data=%d", MS_ID, (unsigned long long)peer, ns, level, (unsigned long long)prefix, is_data); |
|
|
|
@ -346,12 +362,11 @@ static int _find_pending(struct ms_session* s, uint8_t level, uint64_t prefix) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void _session_done(struct ms_session* s, int result) { |
|
|
|
static void _session_done(struct ms_session* s, int result) { |
|
|
|
if (!s->active && s->synced) { DEBUG_WARN(DEBUG_CATEGORY_DB_SYNC, "%s: session ALREADY DONE peer=%016llx ns=%s — SKIP second call", MS_ID, (unsigned long long)s->peer, s->ns); return; } |
|
|
|
if (s->sess_state == SESS_SYNCED) { DEBUG_WARN(DEBUG_CATEGORY_DB_SYNC, "%s: session ALREADY DONE peer=%016llx ns=%s", MS_ID, (unsigned long long)s->peer, s->ns); return; } |
|
|
|
s->active = 0; |
|
|
|
|
|
|
|
merkle_sync_done_cb cb = s->done_cb; void* arg = s->cb_arg; |
|
|
|
merkle_sync_done_cb cb = s->done_cb; void* arg = s->cb_arg; |
|
|
|
s->done_cb = NULL; s->cb_arg = NULL; |
|
|
|
s->done_cb = NULL; s->cb_arg = NULL; |
|
|
|
if (result == MT_OK) { s->synced = 1; DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: session SYNCED peer=%016llx ns=%s", MS_ID, (unsigned long long)s->peer, s->ns); } |
|
|
|
if (result == MT_OK) { s->sess_state = SESS_SYNCED; DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: session SYNCED peer=%016llx ns=%s", MS_ID, (unsigned long long)s->peer, s->ns); } |
|
|
|
else { s->synced = 0; DEBUG_WARN(DEBUG_CATEGORY_DB_SYNC, "%s: session FAILED peer=%016llx ns=%s result=%d", MS_ID, (unsigned long long)s->peer, s->ns, result); } |
|
|
|
else { s->sess_state = SESS_SYNCING; DEBUG_WARN(DEBUG_CATEGORY_DB_SYNC, "%s: session FAILED peer=%016llx ns=%s result=%d", MS_ID, (unsigned long long)s->peer, s->ns, result); } |
|
|
|
if (cb) cb(s->peer, s->ns, result, arg); |
|
|
|
if (cb) cb(s->peer, s->ns, result, arg); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -374,8 +389,8 @@ static void _handle_hashes(struct merkle_sync* ms, uint64_t peer, const char* ns |
|
|
|
uint16_t dlen; memcpy(&dlen, payload, 2); |
|
|
|
uint16_t dlen; memcpy(&dlen, payload, 2); |
|
|
|
const uint8_t* pd = payload + 2; |
|
|
|
const uint8_t* pd = payload + 2; |
|
|
|
if (paylen - 2 < dlen) return; |
|
|
|
if (paylen - 2 < dlen) return; |
|
|
|
ms->ops->apply_items(ms->data_ctx, ns, pd, dlen); |
|
|
|
ms->ops->apply_items(ms->data_ctx, ns, peer, pd, dlen); |
|
|
|
if (s && s->active) _session_done(s, MT_OK); |
|
|
|
if (s && s->sess_state == SESS_SYNCING) _session_done(s, MT_OK); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -383,8 +398,10 @@ static void _handle_hashes(struct merkle_sync* ms, uint64_t peer, const char* ns |
|
|
|
s = u_calloc(1, sizeof(*s)); if (!s) return; |
|
|
|
s = u_calloc(1, sizeof(*s)); if (!s) return; |
|
|
|
snprintf(s->ns, sizeof(s->ns), "%s", ns); s->peer = peer; |
|
|
|
snprintf(s->ns, sizeof(s->ns), "%s", ns); s->peer = peer; |
|
|
|
s->next = ms->sessions; ms->sessions = s; |
|
|
|
s->next = ms->sessions; ms->sessions = s; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
s->pending_count = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
s->active = 1; |
|
|
|
s->sess_state = SESS_SYNCING; |
|
|
|
|
|
|
|
|
|
|
|
if (paylen < 4) return; |
|
|
|
if (paylen < 4) return; |
|
|
|
uint32_t remote_bm; memcpy(&remote_bm, payload, 4); |
|
|
|
uint32_t remote_bm; memcpy(&remote_bm, payload, 4); |
|
|
|
@ -504,7 +521,7 @@ static void _handle_batch(struct merkle_sync* ms, uint64_t peer, const char* ns, |
|
|
|
if (is_data && rem >= 4) { |
|
|
|
if (is_data && rem >= 4) { |
|
|
|
uint16_t dlen; memcpy(&dlen, bp, 2); bp += 2; rem -= 2; |
|
|
|
uint16_t dlen; memcpy(&dlen, bp, 2); bp += 2; rem -= 2; |
|
|
|
if (rem < dlen) break; |
|
|
|
if (rem < dlen) break; |
|
|
|
ms->ops->apply_items(ms->data_ctx, ns, bp, dlen); |
|
|
|
ms->ops->apply_items(ms->data_ctx, ns, peer, bp, dlen); |
|
|
|
bp += dlen; rem -= dlen; |
|
|
|
bp += dlen; rem -= dlen; |
|
|
|
struct ms_session* sb = _session_find(ms, peer, ns); |
|
|
|
struct ms_session* sb = _session_find(ms, peer, ns); |
|
|
|
if (sb) { int pidx = _find_pending(sb, lvl, pr); if (pidx >= 0) sb->pending[pidx].state = MS_PEND_RESOLVED; } |
|
|
|
if (sb) { int pidx = _find_pending(sb, lvl, pr); if (pidx >= 0) sb->pending[pidx].state = MS_PEND_RESOLVED; } |
|
|
|
@ -536,7 +553,7 @@ static void _handle_batch(struct merkle_sync* ms, uint64_t peer, const char* ns, |
|
|
|
for (int i = 0; i < s->pending_count; i++) { |
|
|
|
for (int i = 0; i < s->pending_count; i++) { |
|
|
|
if (s->pending[i].state != MS_PEND_RESOLVED) { all_done = 0; break; } |
|
|
|
if (s->pending[i].state != MS_PEND_RESOLVED) { all_done = 0; break; } |
|
|
|
} |
|
|
|
} |
|
|
|
if (all_done && s->active) { |
|
|
|
if (all_done && s->sess_state == SESS_SYNCING) { |
|
|
|
_send_hashes(ms, peer, ns, 0, 0, 0, 1); |
|
|
|
_send_hashes(ms, peer, ns, 0, 0, 0, 1); |
|
|
|
_session_done(s, MT_OK); |
|
|
|
_session_done(s, MT_OK); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -604,7 +621,7 @@ void merkle_sync_push_update(struct UTUN_INSTANCE* inst, const char* ns, |
|
|
|
memcpy(p, data, len); |
|
|
|
memcpy(p, data, len); |
|
|
|
|
|
|
|
|
|
|
|
for (struct ms_session* s = ms->sessions; s; s = s->next) { |
|
|
|
for (struct ms_session* s = ms->sessions; s; s = s->next) { |
|
|
|
if (!s->synced || strcmp(s->ns, ns) != 0) continue; |
|
|
|
if (s->sess_state != SESS_SYNCED || strcmp(s->ns, ns) != 0) continue; |
|
|
|
struct ll_entry* entry = queue_entry_new(0); |
|
|
|
struct ll_entry* entry = queue_entry_new(0); |
|
|
|
if (!entry) continue; |
|
|
|
if (!entry) continue; |
|
|
|
uint8_t* dcopy = u_malloc(pkt); |
|
|
|
uint8_t* dcopy = u_malloc(pkt); |
|
|
|
@ -621,6 +638,20 @@ void merkle_sync_push_update(struct UTUN_INSTANCE* inst, const char* ns, |
|
|
|
u_free(buf); |
|
|
|
u_free(buf); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ── Broadcast to synced peers ── */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void merkle_sync_broadcast(struct UTUN_INSTANCE* inst, const char* ns, |
|
|
|
|
|
|
|
uint64_t from_peer, const uint8_t* data, size_t len) { |
|
|
|
|
|
|
|
struct merkle_sync* ms = inst ? inst->msync : NULL; |
|
|
|
|
|
|
|
if (!ms || !ms->initialized || !ns || !data || len < 2) return; |
|
|
|
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DB_SYNC, "%s: broadcast ns=%s from=%016llx len=%zu", |
|
|
|
|
|
|
|
MS_ID, ns, (unsigned long long)from_peer, len); |
|
|
|
|
|
|
|
for (struct ms_session* s = ms->sessions; s; s = s->next) { |
|
|
|
|
|
|
|
if (s->sess_state != SESS_SYNCED || s->peer == from_peer || strcmp(s->ns, ns) != 0) continue; |
|
|
|
|
|
|
|
_send_broadcast_data(ms, s->peer, ns, data, len); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* ── Background consistency check ── */ |
|
|
|
/* ── Background consistency check ── */ |
|
|
|
|
|
|
|
|
|
|
|
int merkle_sync_bg_check(struct UTUN_INSTANCE* inst, const char* ns) { |
|
|
|
int merkle_sync_bg_check(struct UTUN_INSTANCE* inst, const char* ns) { |
|
|
|
@ -733,13 +764,10 @@ int merkle_sync_start(struct UTUN_INSTANCE* inst, uint64_t peer, |
|
|
|
s->next = ms->sessions; |
|
|
|
s->next = ms->sessions; |
|
|
|
ms->sessions = s; |
|
|
|
ms->sessions = s; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
s->synced = 0; |
|
|
|
|
|
|
|
s->pending_count = 0; |
|
|
|
s->pending_count = 0; |
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DB_SYNC, "%s: start OVERWRITE peer=%016llx ns=%s old_cb=%p old_arg=%p → new_cb=%p new_arg=%p", |
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DB_SYNC, "%s: start OVERWRITE peer=%016llx ns=%s", MS_ID, (unsigned long long)peer, ns); |
|
|
|
MS_ID, (unsigned long long)peer, ns, |
|
|
|
|
|
|
|
(void*)s->done_cb, s->cb_arg, (void*)done_cb, arg); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
s->active = 1; |
|
|
|
s->sess_state = SESS_SYNCING; |
|
|
|
s->done_cb = done_cb; s->cb_arg = arg; |
|
|
|
s->done_cb = done_cb; s->cb_arg = arg; |
|
|
|
|
|
|
|
|
|
|
|
_send_hashes(ms, peer, ns, 0, 0, 0, 0); |
|
|
|
_send_hashes(ms, peer, ns, 0, 0, 0, 0); |
|
|
|
|