123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296 |
- /* vim: set expandtab ts=4 sw=4: */
- /*
- * You may redistribute this program and/or modify it under the terms of
- * the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- #include "subnode/PeeringSeeder.h"
- #include "benc/Dict.h"
- #include "benc/String.h"
- #include "crypto/Ca.h"
- #include "dht/Address.h"
- #include "exception/Err.h"
- #include "memory/Allocator.h"
- #include "net/SwitchPinger.h"
- #include "rust/cjdns_sys/Rffi.h"
- #include "subnode/ReachabilityCollector.h"
- #include "util/Assert.h"
- #include "util/Bits.h"
- #include "util/Identity.h"
- #include "util/events/EventBase.h"
- #include "util/events/Time.h"
- #include "util/events/Timeout.h"
- #include "util/log/Log.h"
- typedef struct PeeringSeeder_pvt {
- PeeringSeeder_t pub;
- struct SwitchPinger* sp;
- struct ReachabilityCollector* rc;
- Allocator_t* alloc;
- Log_t* log;
- struct MsgCore* mc;
- Ca_t* ca;
- Rffi_Seeder* seeder;
- bool active;
- char login[24];
- // Our peering credentials
- uint16_t userNum;
- // 8 random bytes used to derive the password
- uint64_t pass;
- int lastPeerQueried;
- uint64_t lastQueryTimeSec;
- struct SwitchPinger_Ping* requestOutstanding;
- struct Address snode;
- uint64_t lastPostTimeSec;
- struct MsgCore_Promise* snodePost;
- Identity
- } PeeringSeeder_pvt_t;
- void* PeeringSeeder_getRsSeeder(PeeringSeeder_t* self)
- {
- PeeringSeeder_pvt_t* pq = Identity_check((PeeringSeeder_pvt_t*) self);
- return pq->seeder;
- }
- static void ipQueryResp(struct SwitchPinger_Response* resp, void* userData)
- {
- PeeringSeeder_pvt_t* pq = Identity_check((PeeringSeeder_pvt_t*) userData);
- if (resp->ping != pq->requestOutstanding) {
- Log_info(pq->log, "Ping does not match requestOutstanding");
- }
- pq->requestOutstanding = NULL;
- char* err = "";
- switch (resp->res) {
- case SwitchPinger_Result_OK:
- if (Rffi_Seeder_got_lladdr(pq->seeder, &resp->lladdr)) {
- // Since there was a change, post ASAP.
- pq->lastPostTimeSec = 0;
- }
- return;
- case SwitchPinger_Result_LABEL_MISMATCH: err = "LABEL_MISMATCH"; break;
- case SwitchPinger_Result_WRONG_DATA: err = "WRONG_DATA"; break;
- case SwitchPinger_Result_ERROR_RESPONSE: err = "ERROR_RESPONSE"; break;
- case SwitchPinger_Result_LOOP_ROUTE: err = "LOOP_ROUTE"; break;
- case SwitchPinger_Result_TIMEOUT: err = "TIMEOUT"; break;
- default: err = "unknown error"; break;
- }
- Log_debug(pq->log, "Error sending LLADDR query to peer [%llx] [%s]",
- (long long)resp->label, err);
- }
- static void publicIPQuery(PeeringSeeder_pvt_t* pq) {
- if (pq->requestOutstanding) {
- return;
- }
- uint64_t now = Time_currentTimeSeconds();
- if (pq->lastQueryTimeSec + 10 > now) {
- Log_debug(pq->log, "Last query within 10 seconds");
- return;
- }
- if (Rffi_Seeder_has_lladdr(pq->seeder)) {
- if (pq->lastQueryTimeSec + 300 > now) {
- Log_debug(pq->log, "Last query within 5 minutes and we have our lladdr");
- return;
- }
- }
- int peerCount = ReachabilityCollector_peerCount(pq->rc);
- if (!peerCount) {
- Log_debug(pq->log, "No peers");
- return;
- }
- int tryPeerN = (pq->lastPeerQueried + 1) % peerCount;
- pq->lastPeerQueried = tryPeerN;
- struct ReachabilityCollector_PeerInfo* tryPeer =
- ReachabilityCollector_getPeerInfo(pq->rc, tryPeerN);
-
- struct SwitchPinger_Ping* q = SwitchPinger_newPing(
- tryPeer->addr.path,
- NULL,
- 10000,
- ipQueryResp,
- pq->alloc,
- pq->sp);
- q->onResponseContext = pq;
- q->type = SwitchPinger_Type_LLADDR;
- pq->requestOutstanding = q;
- String_t* peerAddr = Address_toString(&tryPeer->addr, q->pingAlloc);
- Log_debug(pq->log, "Sent LlAddr query to [%s]", peerAddr->bytes);
- }
- static void snodeResp(Dict* msg, Gcc_UNUSED struct Address* src, struct MsgCore_Promise* prom)
- {
- PeeringSeeder_pvt_t* pq = Identity_check((PeeringSeeder_pvt_t*) prom->userData);
- String* peers = Dict_getStringC(msg, "pr");
- if (!peers) {
- Log_debug(pq->log, "Got snode reply with no peers");
- return;
- }
- Rffi_Seeder_got_peers(pq->seeder, peers, prom->alloc);
- }
- static void cycle(void* vpq)
- {
- PeeringSeeder_pvt_t* pq = Identity_check((PeeringSeeder_pvt_t*) vpq);
- if (!pq->active) {
- return;
- }
- // Log_debug(pq->log, "cycle()");
- // Try to get our IP if we can
- publicIPQuery(pq);
- uint64_t now = Time_currentTimeSeconds();
- if (pq->lastPostTimeSec + 5*60 > now) {
- // Only post to the snode every 5 minutes (or on new news)
- return;
- }
- if (!Rffi_Seeder_has_lladdr(pq->seeder)) {
- return;
- }
- if (Bits_isZero(&pq->snode, sizeof pq->snode)) {
- return;
- }
- struct MsgCore_Promise* snodeQuery = MsgCore_createQuery(pq->mc, 0, pq->alloc);
- snodeQuery->cb = snodeResp;
- snodeQuery->userData = pq;
- snodeQuery->target = Address_clone(&pq->snode, snodeQuery->alloc);
- String* creds = NULL;
- Err_assert(Rffi_Seeder_mk_creds(pq->seeder, &creds, snodeQuery->alloc));
- snodeQuery->msg = Dict_new(snodeQuery->alloc);
- Dict_putStringCC(snodeQuery->msg, "sq", "pc", snodeQuery->alloc);
- Dict_putStringC(snodeQuery->msg, "pc", creds, snodeQuery->alloc);
- pq->snodePost = snodeQuery;
- Log_debug(pq->log, "Posting our public IP addresses to snode");
- }
- void PeeringSeeder_setSnode(PeeringSeeder_t* self, struct Address* snode)
- {
- PeeringSeeder_pvt_t* pq = Identity_check((PeeringSeeder_pvt_t*) self);
- struct Address esnode = { .path = 0 };
- if (snode != NULL) {
- Bits_memcpy(&esnode, snode, sizeof esnode);
- }
- if (Bits_memcmp(&esnode, &pq->snode, sizeof esnode)) {
- uint8_t fromAddr[60];
- Address_print(fromAddr, &pq->snode);
- if (snode == NULL) {
- Log_debug(pq->log, "Snode [%s] was lost", fromAddr);
- } else {
- uint8_t toAddr[60];
- Address_print(toAddr, &pq->snode);
- if (Bits_isZero(&pq->snode, sizeof pq->snode)) {
- Log_debug(pq->log, "Snode discovered [%s]", toAddr);
- } else {
- Log_debug(pq->log, "Snode was changed [%s] -> [%s]", fromAddr, toAddr);
- }
- }
- pq->snode = esnode;
- }
- }
- Err_DEFUN PeeringSeeder_publicStatus(PeeringSeeder_PublicStatus_t** outP, PeeringSeeder_t* self, Allocator_t* alloc)
- {
- PeeringSeeder_pvt_t* pq = Identity_check((PeeringSeeder_pvt_t*) self);
- PeeringSeeder_PublicStatus_t* out = Allocator_calloc(alloc, sizeof(PeeringSeeder_PublicStatus_t), 1);
- out->active = pq->active;
- if (!Bits_isZero(&pq->snode, sizeof pq->snode)) {
- out->snode = Address_toString(&pq->snode, alloc);
- }
- Err(Rffi_Seeder_public_status(
- pq->seeder, &out->ipv4, &out->ipv6, &out->peerId, alloc));
- *outP = out;
- return NULL;
- }
- Err_DEFUN PeeringSeeder_publicPeer(PeeringSeeder_t* self, String_t* code, Allocator_t* reqAlloc)
- {
- PeeringSeeder_pvt_t* pq = Identity_check((PeeringSeeder_pvt_t*) self);
- if (code == NULL) {
- if (pq->active) {
- String_t* s = String_new(pq->login, reqAlloc);
- Ca_removeUsers(pq->ca, s);
- }
- return NULL;
- }
- if (code->len > PeeringSeeder_publicPeer_ID_MAX_LEN) {
- Err_raise(reqAlloc, "PeerID is too long, max length: [%d]",
- PeeringSeeder_publicPeer_ID_MAX_LEN);
- }
- String_t* user = NULL;
- String_t* pass = NULL;
- Err(Rffi_Seeder_public_peer(
- pq->seeder,
- &user,
- &pass,
- pq->userNum,
- pq->pass,
- code,
- reqAlloc));
- Assert_true(user->len < sizeof pq->login);
- Bits_memset(pq->login, 0, sizeof pq->login);
- Bits_memcpy(pq->login, user->bytes, user->len);
- Assert_true(pq->login[sizeof pq->login - 1] == 0);
- Ca_addUser(pass, user, pq->ca);
- pq->active = true;
- return NULL;
- }
- struct PeeringSeeder* PeeringSeeder_new(
- struct SwitchPinger* sp,
- struct ReachabilityCollector* rc,
- Allocator_t* alloc,
- Log_t* log,
- struct MsgCore* mc,
- EventBase_t* base,
- Ca_t* ca)
- {
- PeeringSeeder_pvt_t* out = Allocator_calloc(alloc, sizeof(PeeringSeeder_pvt_t), 1);
- Identity_set(out);
- out->sp = sp;
- out->rc = rc;
- out->alloc = alloc;
- out->log = log;
- out->mc = mc;
- out->ca = ca;
- uint8_t myPubKey[32];
- Ca_getPubKey(ca, myPubKey);
- Rffi_Seeder_new(&out->seeder, &out->pub.seederIface, myPubKey, alloc);
- // Random (stable across restarts) user num and passwd
- uint8_t secretBuf[64];
- Assert_true(!Ca_getSecret(ca, String_CONST("SEED_PASSWD"), secretBuf));
- Bits_memcpy(&out->pass, secretBuf, sizeof out->pass);
- Bits_memcpy(&out->userNum, &secretBuf[8], sizeof out->userNum);
- Timeout_setInterval(cycle, out, 3000, base, alloc);
- return &out->pub;
- }
|