123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920 |
- /* 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 "memory/Allocator.h"
- #include "wire/PFChan.h"
- #include "net/SessionManager.h"
- #include "crypto/AddressCalc.h"
- #include "util/AddrTools.h"
- #include "wire/Error.h"
- #include "util/events/Time.h"
- #include "util/Defined.h"
- #include "wire/RouteHeader.h"
- #include "util/events/Timeout.h"
- #include "util/Checksum.h"
- #include "wire/Metric.h"
- /** Handle numbers 0-3 are reserved for CryptoAuth nonces. */
- #define MIN_FIRST_HANDLE 4
- #define MAX_FIRST_HANDLE 100000
- struct BufferedMessage
- {
- Message_t* msg;
- struct Allocator* alloc;
- uint64_t timeSentMilliseconds;
- };
- struct Ip6 {
- uint8_t bytes[16];
- };
- #define Map_KEY_TYPE struct Ip6
- #define Map_VALUE_TYPE struct BufferedMessage*
- #define Map_NAME BufferedMessages
- #include "util/Map.h"
- #define Map_KEY_TYPE struct Ip6
- #define Map_VALUE_TYPE struct SessionManager_Session_pvt*
- #define Map_NAME OfSessionsByIp6
- #define Map_ENABLE_HANDLES
- #include "util/Map.h"
- struct SessionManager_pvt
- {
- struct SessionManager pub;
- struct Iface eventIf;
- struct Allocator* alloc;
- struct Map_BufferedMessages bufMap;
- struct Map_OfSessionsByIp6 ifaceMap;
- struct Log* log;
- Ca_t* cryptoAuth;
- EventBase_t* eventBase;
- uint32_t firstHandle;
- uint8_t ourPubKey[32];
- Identity
- };
- struct SessionManager_Session_pvt
- {
- struct SessionManager_Session pub;
- struct Iface plaintext;
- struct Iface ciphertext;
- struct SessionManager_pvt* sessionManager;
- struct Allocator* alloc;
- bool foundKey;
- Identity
- };
- #define debugHandlesAndLabel(logger, session, label, message, ...) \
- do { \
- if (!Defined(Log_DEBUG)) { break; } \
- uint8_t path[20]; \
- AddrTools_printPath(path, label); \
- uint8_t ip[40]; \
- uint8_t ipb[16]; \
- Ca_getHerIp6(session->pub.caSession, ipb); \
- AddrTools_printIp(ip, ipb); \
- Log_debug(logger, "ver[%u] send[%d] recv[%u] ip[%s] path[%s] " message, \
- session->pub.version, \
- session->pub.sendHandle, \
- session->pub.receiveHandle, \
- ip, \
- path, \
- __VA_ARGS__); \
- } while (0)
- //CHECKFILES_IGNORE expecting a ;
- #define debugHandlesAndLabel0(logger, session, label, message) \
- debugHandlesAndLabel(logger, session, label, "%s", message)
- #define debugSession(logger, session, label, message, ...) \
- do { \
- if (!Defined(Log_DEBUG)) { break; } \
- uint8_t sendPath[20]; \
- uint8_t ip[40]; \
- uint8_t ipb[16]; \
- Ca_getHerIp6(session->pub.caSession, ipb); \
- AddrTools_printIp(ip, ipb); \
- AddrTools_printPath(sendPath, (label)); \
- Log_debug((logger), "Session[%p] [%s.%s] " message, \
- (void*)session, \
- sendPath, \
- ip, \
- __VA_ARGS__); \
- } while (0)
- //CHECKFILES_IGNORE ;
- #define debugSession0(logger, session, label, message) \
- debugSession(logger, session, label, "%s", message)
- static void sendSession(struct SessionManager_Session_pvt* sess,
- SessionManager_Path_t* path,
- uint32_t destPf,
- enum PFChan_Core ev)
- {
- struct PFChan_Node session = {
- .path_be = Endian_hostToBigEndian64(path->label),
- .metric_be = Endian_bigEndianToHost32(path->metric),
- .version_be = Endian_hostToBigEndian32(sess->pub.version)
- };
- Ca_getHerPubKey(sess->pub.caSession, session.publicKey);
- Ca_getHerIp6(sess->pub.caSession, session.ip6);
- struct Allocator* alloc = Allocator_child(sess->alloc);
- Message_t* msg = Message_new(0, PFChan_Node_SIZE + 512, alloc);
- Er_assert(Message_epush(msg, &session, PFChan_Node_SIZE));
- Er_assert(Message_epush32be(msg, destPf));
- Er_assert(Message_epush32be(msg, ev));
- Iface_send(&sess->sessionManager->eventIf, msg);
- Allocator_free(alloc);
- }
- static inline void check(struct SessionManager_pvt* sm, int mapIndex)
- {
- struct SessionManager_Session_pvt* ssp = Identity_check(sm->ifaceMap.values[mapIndex]);
- if (ssp->foundKey) { return; }
- uint8_t herPubKey[32];
- Ca_getHerPubKey(ssp->pub.caSession, herPubKey);
- if (!Bits_isZero(herPubKey, 32)) {
- uint8_t ip6[16];
- AddressCalc_addressForPublicKey(ip6, herPubKey);
- Assert_true(!Bits_memcmp(&sm->ifaceMap.keys[mapIndex], ip6, 16));
- ssp->foundKey = true;
- }
- }
- static inline struct SessionManager_Session_pvt* sessionForHandle(uint32_t handle,
- struct SessionManager_pvt* sm)
- {
- int index = Map_OfSessionsByIp6_indexForHandle(handle - sm->firstHandle, &sm->ifaceMap);
- if (index < 0) { return NULL; }
- check(sm, index);
- return Identity_check(sm->ifaceMap.values[index]);
- }
- struct SessionManager_Session* SessionManager_sessionForHandle(uint32_t handle,
- struct SessionManager* manager)
- {
- struct SessionManager_pvt* sm = Identity_check((struct SessionManager_pvt*) manager);
- return (struct SessionManager_Session*) sessionForHandle(handle, sm);
- }
- static inline struct SessionManager_Session_pvt* sessionForIp6(uint8_t ip6[16],
- struct SessionManager_pvt* sm)
- {
- int ifaceIndex = Map_OfSessionsByIp6_indexForKey((struct Ip6*)ip6, &sm->ifaceMap);
- if (ifaceIndex == -1) { return NULL; }
- check(sm, ifaceIndex);
- return Identity_check(sm->ifaceMap.values[ifaceIndex]);
- }
- struct SessionManager_Session* SessionManager_sessionForIp6(uint8_t* ip6,
- struct SessionManager* manager)
- {
- struct SessionManager_pvt* sm = Identity_check((struct SessionManager_pvt*) manager);
- return (struct SessionManager_Session*) sessionForIp6(ip6, sm);
- }
- struct SessionManager_HandleList* SessionManager_getHandleList(struct SessionManager* manager,
- struct Allocator* alloc)
- {
- struct SessionManager_pvt* sm = Identity_check((struct SessionManager_pvt*) manager);
- struct SessionManager_HandleList* out =
- Allocator_calloc(alloc, sizeof(struct SessionManager_HandleList), 1);
- uint32_t* buff = Allocator_calloc(alloc, 4, sm->ifaceMap.count);
- out->length = sm->ifaceMap.count;
- out->handles = buff;
- for (int i = 0; i < out->length; i++) {
- buff[i] = sm->ifaceMap.handles[i] + sm->firstHandle;
- }
- return out;
- }
- int SessionManager_handleCount(struct SessionManager* manager)
- {
- struct SessionManager_pvt* sm = Identity_check((struct SessionManager_pvt*) manager);
- return sm->ifaceMap.count;
- }
- static uint32_t effectiveMetric(struct SessionManager_pvt* sm,
- SessionManager_Path_t* path)
- {
- int64_t x = Time_currentTimeMilliseconds() - path->timeLastValidated;
- x += path->metric;
- return (x > Metric_NO_INFO) ? Metric_NO_INFO : x;
- }
- uint32_t SessionManager_effectiveMetric(struct SessionManager_Session* session)
- {
- struct SessionManager_Session_pvt* sess =
- Identity_check((struct SessionManager_Session_pvt*) session);
- return effectiveMetric(sess->sessionManager, &sess->pub.paths[0]);
- }
- static SessionManager_Path_t* pathForLabel(struct SessionManager_Session_pvt* sess, uint64_t label)
- {
- for (int i = 0; i < SessionManager_PATH_COUNT; i++) {
- if (sess->pub.paths[i].label == label) {
- return &sess->pub.paths[i];
- }
- }
- return NULL;
- }
- static SessionManager_Path_t* worstPath(struct SessionManager_Session_pvt* sess)
- {
- uint32_t worstEm = 0;
- int worstI = 0;
- for (int i = 0; i < SessionManager_PATH_COUNT; i++) {
- uint32_t em = effectiveMetric(sess->sessionManager, &sess->pub.paths[i]);
- if (em > worstEm) {
- worstEm = em;
- worstI = i;
- }
- }
- return &sess->pub.paths[worstI];
- }
- static void rerankPaths(struct SessionManager_Session_pvt* sess)
- {
- uint32_t bestEm = Metric_DEAD_LINK;
- int bestI = 0;
- for (int i = 0; i < SessionManager_PATH_COUNT; i++) {
- uint32_t em = effectiveMetric(sess->sessionManager, &sess->pub.paths[i]);
- if (em < bestEm) {
- bestEm = em;
- bestI = i;
- }
- }
- if (bestI != 0) {
- SessionManager_Path_t path0;
- Bits_memcpy(&path0, &sess->pub.paths[0], sizeof(path0));
- Bits_memcpy(&sess->pub.paths[0], &sess->pub.paths[bestI], sizeof(path0));
- Bits_memcpy(&sess->pub.paths[bestI], &path0, sizeof(path0));
- }
- }
- // Return true if the new path is an improvement
- static bool discoverPath(struct SessionManager_Session_pvt* sess,
- uint64_t label,
- uint32_t metric)
- {
- if (!label) {
- return false;
- }
- SessionManager_Path_t* path = pathForLabel(sess, label);
- uint64_t now = Time_currentTimeMilliseconds();
- if (path) {
- if (metric != Metric_DEAD_LINK && effectiveMetric(sess->sessionManager, path) <= metric) {
- // already have a better source of truth
- return false;
- }
- path->metric = metric;
- path->timeLastValidated = now;
- rerankPaths(sess);
- return (sess->pub.paths[0].label == label);
- } else {
- path = worstPath(sess);
- if (effectiveMetric(sess->sessionManager, path) <= metric) {
- return false;
- }
- path->label = label;
- path->metric = metric;
- path->timeLastValidated = now;
- rerankPaths(sess);
- if (sess->pub.paths[0].label == label) {
- sendSession(sess, &sess->pub.paths[0], 0xffffffff, PFChan_Core_DISCOVERED_PATH);
- return true;
- }
- return false;
- }
- }
- static Iface_DEFUN failedDecrypt(Message_t* msg,
- uint64_t label_be,
- struct SessionManager_pvt* sm)
- {
- Er_assert(Message_epush32be(msg, Error_AUTHENTICATION));
- Er_assert(Message_epush16be(msg, Control_ERROR));
- Er_assert(Message_epush16be(msg, 0));
- uint16_t csum_be = Checksum_engine_be(Message_bytes(msg), Message_getLength(msg));
- Er_assert(Message_epop16h(msg));
- Er_assert(Message_epush16h(msg, csum_be));
- Er_assert(Message_epush32be(msg, 0xffffffff));
- struct SwitchHeader sh;
- Bits_memset(&sh, 0, SwitchHeader_SIZE);
- SwitchHeader_setSuppressErrors(&sh, true);
- SwitchHeader_setVersion(&sh, SwitchHeader_CURRENT_VERSION);
- sh.label_be = label_be;
- Er_assert(Message_epush(msg, &sh, SwitchHeader_SIZE));
- return Iface_next(&sm->pub.switchIf, msg);
- }
- static Iface_DEFUN postDecryption(Message_t* msg, struct Iface* iface)
- {
- struct SessionManager_Session_pvt* session =
- Identity_containerOf(iface, struct SessionManager_Session_pvt, plaintext);
- // The switch header is pushed as associated data
- uint32_t nonceOrHandle = 0xffffffff;
- Er_assert(Message_epopAd(msg, &nonceOrHandle, 4));
- struct RouteHeader header = {
- .flags = RouteHeader_flags_INCOMING,
- .version_be = Endian_hostToBigEndian32(session->pub.version),
- };
- Er_assert(Message_epopAd(msg, &header.sh, SwitchHeader_SIZE));
- Ca_getHerPubKey(session->pub.caSession, header.publicKey);
- Ca_getHerIp6(session->pub.caSession, header.ip6);
- uint64_t label = Endian_bigEndianToHost64(header.sh.label_be);
- enum Ca_DecryptErr ret = Er_assert(Message_epop32h(msg));
- if (ret) {
- // If CryptoAuth fails to decrypt then it gives us:
- // * Ca_DecryptErr - we already popped this
- // * first16
- // * Ca_DecryptErr
- // * state
- debugHandlesAndLabel(session->sessionManager->log, session,
- label,
- "DROP Failed decrypting message NoH[%d] state[%s]",
- nonceOrHandle,
- Ca_stateString(Ca_getState(session->pub.caSession)));
- uint64_t label_be = header.sh.label_be;
- // We want to preserve it "as it was" here
- header.sh.label_be = Bits_bitReverse64(header.sh.label_be);
- Er_assert(Message_epush(msg, &header.sh, SwitchHeader_SIZE));
- return failedDecrypt(msg, label_be, session->sessionManager);
- }
- if (nonceOrHandle <= 3) {
- session->pub.sendHandle = Er_assert(Message_epop32be(msg));
- debugHandlesAndLabel0(
- session->sessionManager->log, session, label, "received start message");
- }
- session->pub.bytesIn += Message_getLength(msg);
- Er_assert(Message_epush(msg, &header, sizeof header));
- discoverPath(session, label, Metric_SM_INCOMING);
- return Iface_next(&session->sessionManager->pub.insideIf, msg);
- }
- static Iface_DEFUN afterEncrypt(Message_t* msg, struct Iface* iface)
- {
- //Assert_true(!Ca_encrypt(sess->pub.caSession, msg));
- struct SessionManager_Session_pvt* sess =
- Identity_containerOf(iface, struct SessionManager_Session_pvt, ciphertext);
- struct RouteHeader header;
- Er_assert(Message_epopAd(msg, &header, RouteHeader_SIZE));
- if (Ca_getState(sess->pub.caSession) >= Ca_State_RECEIVED_KEY) {
- if (0) { // Noisy
- debugHandlesAndLabel0(sess->sessionManager->log,
- sess,
- Endian_bigEndianToHost64(header.sh.label_be),
- "sending run message");
- }
- Er_assert(Message_epush32be(msg, sess->pub.sendHandle));
- } else {
- debugHandlesAndLabel0(sess->sessionManager->log,
- sess,
- Endian_bigEndianToHost64(header.sh.label_be),
- "sending start message");
- }
- if (!header.sh.label_be) {
- Bits_memset(&header.sh, 0, SwitchHeader_SIZE);
- header.sh.label_be = Endian_hostToBigEndian64(sess->pub.paths[0].label);
- SwitchHeader_setVersion(&header.sh, SwitchHeader_CURRENT_VERSION);
- }
- Er_assert(Message_epush(msg, &header.sh, SwitchHeader_SIZE));
- return Iface_next(&sess->sessionManager->pub.switchIf, msg);
- }
- static struct SessionManager_Session_pvt* getSession(struct SessionManager_pvt* sm,
- uint8_t ip6[16],
- uint8_t pubKey[32],
- uint32_t version,
- uint64_t label,
- uint32_t metric)
- {
- Assert_true(AddressCalc_validAddress(ip6));
- if (!label) { metric = Metric_DEAD_LINK; }
- struct SessionManager_Session_pvt* sess = sessionForIp6(ip6, sm);
- if (sess) {
- sess->pub.version = (sess->pub.version) ? sess->pub.version : version;
- if (discoverPath(sess, label, metric)) {
- sess->pub.version = (version) ? version : sess->pub.version;
- }
- return sess;
- }
- struct Allocator* alloc = Allocator_child(sm->alloc);
- sess = Allocator_calloc(alloc, sizeof(struct SessionManager_Session_pvt), 1);
- Identity_set(sess);
- sess->plaintext.send = postDecryption;
- sess->ciphertext.send = afterEncrypt;
- sess->pub.caSession = Ca_newSession(sm->cryptoAuth, alloc, pubKey, false, "inner", false);
- Iface_plumb(sess->pub.caSession->plaintext, &sess->plaintext);
- Iface_plumb(sess->pub.caSession->ciphertext, &sess->ciphertext);
- sess->foundKey = !Bits_isZero(pubKey, 32);
- if (sess->foundKey) {
- uint8_t realIp6[16];
- AddressCalc_addressForPublicKey(realIp6, pubKey);
- Assert_true(!Bits_memcmp(realIp6, ip6, 16));
- }
- int ifaceIndex = Map_OfSessionsByIp6_put((struct Ip6*)ip6, &sess, &sm->ifaceMap);
- sess->pub.receiveHandle = sm->ifaceMap.handles[ifaceIndex] + sm->firstHandle;
- if (Defined(Log_DEBUG)) {
- uint8_t printedIp6[40];
- AddrTools_printIp(printedIp6, ip6);
- Log_debug(sm->log, "Created session for [%s] handle [%u]",
- printedIp6, sess->pub.receiveHandle);
- }
- int64_t now = Time_currentTimeMilliseconds();
- sess->alloc = alloc;
- sess->sessionManager = sm;
- sess->pub.version = version;
- sess->pub.paths[0].timeLastValidated = now;
- sess->pub.paths[0].label = label;
- sess->pub.paths[0].metric = metric;
- //Allocator_onFree(alloc, sessionCleanup, sess);
- sendSession(sess, &sess->pub.paths[0], 0xffffffff, PFChan_Core_SESSION);
- check(sm, ifaceIndex);
- return sess;
- }
- static Iface_DEFUN ctrlFrame(Message_t* msg, struct SessionManager_pvt* sm)
- {
- struct RouteHeader rh;
- Bits_memset(&rh, 0, RouteHeader_SIZE);
- Er_assert(Message_epop(msg, &rh.sh, SwitchHeader_SIZE));
- Er_assert(Message_epop(msg, NULL, 4));
- rh.flags = RouteHeader_flags_INCOMING | RouteHeader_flags_CTRLMSG;
- Er_assert(Message_epush(msg, &rh, RouteHeader_SIZE));
- return Iface_next(&sm->pub.insideIf, msg);
- }
- static const uint8_t ADDR_PFX[8] = { 0xff, 0xfb, 0, 0, 0, 0, 0, 0 };
- static Iface_DEFUN incomingFromSwitchIf(Message_t* msg, struct Iface* iface)
- {
- struct SessionManager_pvt* sm =
- Identity_containerOf(iface, struct SessionManager_pvt, pub.switchIf);
- // SwitchHeader, handle, 0 or more bytes of control frame
- if (Message_getLength(msg) < SwitchHeader_SIZE + 4) {
- Log_debug(sm->log, "DROP runt");
- return Error(msg, "RUNT");
- }
- struct SwitchHeader switchHeader;
- Er_assert(Message_epop(msg, &switchHeader, SwitchHeader_SIZE));
- // The label comes in reversed from the switch because the switch doesn't know that we aren't
- // another switch ready to parse more bits, bit reversing the label yields the source address.
- // (the field is still big endian!)
- switchHeader.label_be = Bits_bitReverse64(switchHeader.label_be);
- struct SessionManager_Session_pvt* session = NULL;
- uint32_t nonceOrHandle = Endian_bigEndianToHost32(((uint32_t*)Message_bytes(msg))[0]);
- if (nonceOrHandle == 0xffffffff) {
- Er_assert(Message_epush(msg, &switchHeader, SwitchHeader_SIZE));
- return ctrlFrame(msg, sm);
- }
- // handle, small cryptoAuth header
- if (Message_getLength(msg) < 4 + 20) {
- Log_debug(sm->log, "DROP runt");
- return Error(msg, "RUNT");
- }
- if (nonceOrHandle > 3) {
- // > 3 it's a handle.
- session = sessionForHandle(nonceOrHandle, sm);
- if (!session) {
- Log_debug(sm->log, "DROP message with unrecognized handle [%u]", nonceOrHandle);
- return Error(msg, "INVALID");
- }
- Er_assert(Message_eshift(msg, -4));
- uint32_t nonce = Endian_bigEndianToHost32(((uint32_t*)Message_bytes(msg))[0]);
- if (nonce < 4) {
- Log_debug(sm->log, "DROP setup message [%u] with specified handle [%u]",
- nonce, nonceOrHandle);
- return Error(msg, "INVALID");
- }
- } else {
- // handle + big cryptoauth header
- if (Message_getLength(msg) < CryptoHeader_SIZE + 4) {
- Log_debug(sm->log, "DROP runt");
- return Error(msg, "RUNT");
- }
- struct CryptoHeader* caHeader = (struct CryptoHeader*) Message_bytes(msg);
- uint8_t ip6[16];
- // a packet which claims to be "from us" causes problems
- if (!AddressCalc_addressForPublicKey(ip6, caHeader->publicKey)) {
- Log_debug(sm->log, "DROP Handshake with non-fc key");
- return Error(msg, "INVALID");
- }
- if (!Bits_memcmp(caHeader->publicKey, sm->ourPubKey, 32)) {
- Log_debug(sm->log, "DROP Handshake from 'ourselves'");
- return Error(msg, "INVALID");
- }
- uint64_t label = Endian_bigEndianToHost64(switchHeader.label_be);
- session = getSession(sm, ip6, caHeader->publicKey, 0, label, Metric_SM_INCOMING);
- Ca_resetIfTimeout(session->pub.caSession);
- debugHandlesAndLabel(sm->log, session, label, "new session nonce[%d]", nonceOrHandle);
- }
- Er_assert(Message_epushAd(msg, &switchHeader, SwitchHeader_SIZE));
- Er_assert(Message_epushAd(msg, &nonceOrHandle, 4));
- Er_assert(Message_epush(msg, &switchHeader.label_be, 8));
- Er_assert(Message_epush(msg, ADDR_PFX, 8));
- return Iface_next(&session->ciphertext, msg);
- }
- static void checkTimedOutBuffers(struct SessionManager_pvt* sm)
- {
- for (int i = 0; i < (int)sm->bufMap.count; i++) {
- struct BufferedMessage* buffered = sm->bufMap.values[i];
- int64_t lag = Time_currentTimeMilliseconds() - buffered->timeSentMilliseconds;
- if (lag < 10000) { continue; }
- Map_BufferedMessages_remove(i, &sm->bufMap);
- Allocator_free(buffered->alloc);
- i--;
- }
- }
- static void unsetupSession(struct SessionManager_pvt* sm, struct SessionManager_Session_pvt* sess)
- {
- if (!sess->pub.version || sess->pub.paths[0].metric == Metric_DEAD_LINK) {
- // Nothing we can do here because it's not ok to send traffic without a version and path.
- return;
- }
- struct Allocator* eventAlloc = Allocator_child(sm->alloc);
- Message_t* eventMsg = Message_new(0, 512, eventAlloc);
- struct PFChan_Node n = { .metric_be = Endian_hostToBigEndian32(Metric_DEAD_LINK) };
- n.path_be = Endian_hostToBigEndian64(sess->pub.paths[0].label);
- Assert_true(n.path_be);
- n.version_be = Endian_hostToBigEndian32(sess->pub.version);
- n.metric_be = Endian_bigEndianToHost32(sess->pub.paths[0].metric);
- Ca_getHerPubKey(sess->pub.caSession, n.publicKey);
- Ca_getHerIp6(sess->pub.caSession, n.ip6);
- Er_assert(Message_epush(eventMsg, &n, PFChan_Node_SIZE));
- Er_assert(Message_epush32be(eventMsg, 0xffffffff));
- Er_assert(Message_epush32be(eventMsg, PFChan_Core_UNSETUP_SESSION));
- Iface_send(&sm->eventIf, eventMsg);
- Allocator_free(eventAlloc);
- }
- static void triggerSearch(struct SessionManager_pvt* sm, uint8_t target[16], uint32_t version)
- {
- struct Allocator* eventAlloc = Allocator_child(sm->alloc);
- Message_t* eventMsg = Message_new(0, 512, eventAlloc);
- Er_assert(Message_epush32be(eventMsg, version));
- Er_assert(Message_epush32be(eventMsg, 0));
- Er_assert(Message_epush(eventMsg, target, 16));
- Er_assert(Message_epush32be(eventMsg, 0xffffffff));
- Er_assert(Message_epush32be(eventMsg, PFChan_Core_SEARCH_REQ));
- Iface_send(&sm->eventIf, eventMsg);
- Allocator_free(eventAlloc);
- }
- static SessionManager_Path_t* mostRecentValidatedPath(struct SessionManager_Session_pvt* sess)
- {
- int64_t bestTime = 0;
- int bestI = 0;
- for (int i = 0; i < SessionManager_PATH_COUNT; i++) {
- if (sess->pub.paths[i].timeLastValidated > bestTime) {
- bestTime = sess->pub.paths[i].timeLastValidated;
- bestI = i;
- }
- }
- return &sess->pub.paths[bestI];
- }
- static void checkTimedOutSessions(struct SessionManager_pvt* sm)
- {
- for (int i = (int)sm->ifaceMap.count - 1; i >= 0; i--) {
- struct SessionManager_Session_pvt* sess = sm->ifaceMap.values[i];
- int64_t now = Time_currentTimeMilliseconds();
- // Check if the session is timed out...
- SessionManager_Path_t* path = mostRecentValidatedPath(sess);
- if (now - path->timeLastValidated > sm->pub.sessionTimeoutMilliseconds) {
- debugSession0(sm->log, sess, path->label, "ended");
- // Only need to send this once because PFChan_Core_SESSION_ENDED
- // means the whole session is done
- sendSession(sess, path, 0xffffffff, PFChan_Core_SESSION_ENDED);
- Map_OfSessionsByIp6_remove(i, &sm->ifaceMap);
- Allocator_free(sess->alloc);
- continue;
- }
- if (now - sess->pub.timeOfLastUsage > sm->pub.sessionTimeoutMilliseconds) {
- // This session is either only used by the pathfinder or it is nolonger used
- // let the pathfinder maintain it if it wants to, otherwise let it drop.
- //
- // This is a convenience for user tools to know that it's unmanaged.
- sess->pub.timeOfLastUsage = 0;
- } else if (now - sess->pub.lastSearchTime >= sm->pub.sessionSearchAfterMilliseconds) {
- // Session is not in idle state and requires a search
- debugSession0(sm->log, sess, sess->pub.paths[0].label,
- "it's been a while, triggering search");
- uint8_t herIp6[16];
- Ca_getHerIp6(sess->pub.caSession, herIp6);
- triggerSearch(sm, herIp6, sess->pub.version);
- sess->pub.lastSearchTime = now;
- } else if (Ca_getState(sess->pub.caSession) < Ca_State_ESTABLISHED) {
- debugSession0(sm->log, sess, sess->pub.paths[0].label, "triggering unsetupSession");
- unsetupSession(sm, sess);
- }
- }
- }
- static void periodically(void* vSessionManager)
- {
- struct SessionManager_pvt* sm = Identity_check((struct SessionManager_pvt*) vSessionManager);
- checkTimedOutSessions(sm);
- checkTimedOutBuffers(sm);
- }
- static void bufferPacket(struct SessionManager_pvt* sm, Message_t* msg)
- {
- Assert_true(Message_getLength(msg) >= (RouteHeader_SIZE + DataHeader_SIZE));
- struct RouteHeader* header = (struct RouteHeader*) Message_bytes(msg);
- // We should never be sending CJDHT messages without full version, key, path known.
- struct DataHeader* dataHeader = (struct DataHeader*) &header[1];
- Assert_true(DataHeader_getContentType(dataHeader) != ContentType_CJDHT);
- uint8_t ipStr[40];
- AddrTools_printIp(ipStr, header->ip6);
- int index = Map_BufferedMessages_indexForKey((struct Ip6*)header->ip6, &sm->bufMap);
- if (index > -1) {
- struct BufferedMessage* buffered = sm->bufMap.values[index];
- Map_BufferedMessages_remove(index, &sm->bufMap);
- Allocator_free(buffered->alloc);
- Log_debug(sm->log, "Buffering a packet to [%s] DROP replacing one in the buffer", ipStr);
- } else {
- Log_debug(sm->log, "Buffering a packet to [%s]", ipStr);
- }
- if ((int)sm->bufMap.count >= sm->pub.maxBufferedMessages) {
- checkTimedOutBuffers(sm);
- if ((int)sm->bufMap.count >= sm->pub.maxBufferedMessages) {
- Log_debug(sm->log, "DROP message needing lookup maxBufferedMessages ([%d]) is reached",
- sm->pub.maxBufferedMessages);
- return;
- }
- }
- struct Allocator* lookupAlloc = Allocator_child(sm->alloc);
- struct BufferedMessage* buffered =
- Allocator_calloc(lookupAlloc, sizeof(struct BufferedMessage), 1);
- buffered->msg = msg;
- buffered->alloc = lookupAlloc;
- buffered->timeSentMilliseconds = Time_currentTimeMilliseconds();
- Allocator_adopt(lookupAlloc, Message_getAlloc(msg));
- Assert_true(Map_BufferedMessages_put((struct Ip6*)header->ip6, &buffered, &sm->bufMap) > -1);
- }
- static void needsLookup(struct SessionManager_pvt* sm, Message_t* msg)
- {
- struct RouteHeader* header = (struct RouteHeader*) Message_bytes(msg);
- bufferPacket(sm, msg);
- triggerSearch(sm, header->ip6, Endian_hostToBigEndian32(header->version_be));
- }
- static Iface_DEFUN readyToSend(Message_t* msg,
- struct SessionManager_pvt* sm,
- struct SessionManager_Session_pvt* sess)
- {
- struct RouteHeader header;
- Er_assert(Message_epop(msg, &header, RouteHeader_SIZE));
- Assert_true(header.sh.label_be != 0xffffffffffffffffull);
- Ca_resetIfTimeout(sess->pub.caSession);
- if (Ca_getState(sess->pub.caSession) < Ca_State_RECEIVED_KEY) {
- // Put the handle into the message so that the other end knows how to address the session
- Er_assert(Message_epush32be(msg, sess->pub.receiveHandle));
- }
- sess->pub.bytesOut += Message_getLength(msg);
- // Move the route header to additional data
- Er_assert(Message_epushAd(msg, &header, RouteHeader_SIZE));
- return Iface_next(&sess->plaintext, msg); // --> afterEncrypt
- }
- static Iface_DEFUN outgoingCtrlFrame(Message_t* msg, struct SessionManager_pvt* sm)
- {
- Assert_true(Message_getLength(msg) >= RouteHeader_SIZE);
- struct RouteHeader* header = (struct RouteHeader*) Message_bytes(msg);
- if (!Bits_isZero(header->publicKey, 32) || !Bits_isZero(header->ip6, 16)) {
- Log_debug(sm->log, "DROP Ctrl frame with non-zero destination key or IP");
- return Error(msg, "INVALID");
- }
- if (!(header->flags & RouteHeader_flags_CTRLMSG)) {
- Log_debug(sm->log, "DROP Ctrl frame w/o RouteHeader_flags_CTRLMSG flag");
- return Error(msg, "INVALID");
- }
- struct SwitchHeader sh;
- Bits_memcpy(&sh, &header->sh, SwitchHeader_SIZE);
- Er_assert(Message_epop(msg, NULL, RouteHeader_SIZE));
- Er_assert(Message_epush32be(msg, 0xffffffff));
- Er_assert(Message_epush(msg, &sh, SwitchHeader_SIZE));
- return Iface_next(&sm->pub.switchIf, msg);
- }
- static Iface_DEFUN incomingFromInsideIf(Message_t* msg, struct Iface* iface)
- {
- struct SessionManager_pvt* sm =
- Identity_containerOf(iface, struct SessionManager_pvt, pub.insideIf);
- Assert_true(Message_getLength(msg) >= RouteHeader_SIZE);
- struct RouteHeader* header = (struct RouteHeader*) Message_bytes(msg);
- if (header->flags & RouteHeader_flags_CTRLMSG) {
- return outgoingCtrlFrame(msg, sm);
- }
- Assert_true(Message_getLength(msg) >= RouteHeader_SIZE + DataHeader_SIZE);
- struct DataHeader* dataHeader = (struct DataHeader*) &header[1];
- struct SessionManager_Session_pvt* sess = sessionForIp6(header->ip6, sm);
- if (!sess) {
- if (!Bits_isZero(header->publicKey, 32) && header->version_be) {
- sess = getSession(sm,
- header->ip6,
- header->publicKey,
- Endian_bigEndianToHost32(header->version_be),
- Endian_bigEndianToHost64(header->sh.label_be),
- ((header->sh.label_be) ? Metric_SM_SEND : Metric_DEAD_LINK));
- } else {
- needsLookup(sm, msg);
- return NULL;
- }
- }
- if (!(header->flags & RouteHeader_flags_PATHFINDER)) {
- // It's real life user traffic, lets tag the time of last use
- sess->pub.timeOfLastUsage = Time_currentTimeMilliseconds();
- }
- if (header->version_be) { sess->pub.version = Endian_bigEndianToHost32(header->version_be); }
- if (!sess->pub.version) {
- needsLookup(sm, msg);
- return NULL;
- }
- if (header->sh.label_be) {
- // fallthrough
- } else if (sess->pub.paths[0].metric < Metric_DEAD_LINK) {
- Bits_memset(&header->sh, 0, SwitchHeader_SIZE);
- header->sh.label_be = Endian_hostToBigEndian64(sess->pub.paths[0].label);
- SwitchHeader_setVersion(&header->sh, SwitchHeader_CURRENT_VERSION);
- } else {
- needsLookup(sm, msg);
- return NULL;
- }
- // Forward secrecy, only send dht messages until the session is setup.
- Ca_resetIfTimeout(sess->pub.caSession);
- if (Ca_getState(sess->pub.caSession) < Ca_State_RECEIVED_KEY) {
- if (DataHeader_getContentType(dataHeader) == ContentType_CJDHT) {
- if (sess->pub.timeOfLastUsage) {
- // Any time any message of any kind is sent down a link that is
- // currently in use, keep firing off unsetupSession
- unsetupSession(sm, sess);
- }
- } else {
- debugSession0(sm->log, sess,
- Endian_bigEndianToHost64(header->sh.label_be), "user traffic, unsetupSession");
- bufferPacket(sm, msg);
- unsetupSession(sm, sess);
- return NULL;
- }
- }
- return readyToSend(msg, sm, sess);
- }
- static Iface_DEFUN sessions(struct SessionManager_pvt* sm,
- uint32_t sourcePf,
- struct Allocator* tempAlloc)
- {
- for (int i = 0; i < (int)sm->ifaceMap.count; i++) {
- struct SessionManager_Session_pvt* sess = sm->ifaceMap.values[i];
- sendSession(sess, &sess->pub.paths[0], sourcePf, PFChan_Core_SESSION);
- }
- return NULL;
- }
- static Iface_DEFUN incomingFromEventIf(Message_t* msg, struct Iface* iface)
- {
- struct SessionManager_pvt* sm = Identity_containerOf(iface, struct SessionManager_pvt, eventIf);
- enum PFChan_Pathfinder ev = Er_assert(Message_epop32be(msg));
- uint32_t sourcePf = Er_assert(Message_epop32be(msg));
- if (ev == PFChan_Pathfinder_SESSIONS) {
- Assert_true(!Message_getLength(msg));
- return sessions(sm, sourcePf, Message_getAlloc(msg));
- }
- Assert_true(ev == PFChan_Pathfinder_NODE);
- struct PFChan_Node node;
- Er_assert(Message_epop(msg, &node, PFChan_Node_SIZE));
- Assert_true(!Message_getLength(msg));
- int index = Map_BufferedMessages_indexForKey((struct Ip6*)node.ip6, &sm->bufMap);
- struct SessionManager_Session_pvt* sess = sessionForIp6(node.ip6, sm);
- if (!sess) {
- // Node we don't care about.
- if (index == -1) { return NULL; }
- // Broken path to a node we don't have a session for...
- if (node.metric_be == Metric_DEAD_LINK) { return NULL; }
- }
- sess = getSession(sm,
- node.ip6,
- node.publicKey,
- Endian_bigEndianToHost32(node.version_be),
- Endian_bigEndianToHost64(node.path_be),
- Endian_bigEndianToHost32(node.metric_be));
- // Send what's on the buffer...
- if (index > -1 && Ca_getState(sess->pub.caSession) >= Ca_State_RECEIVED_KEY) {
- struct BufferedMessage* bm = sm->bufMap.values[index];
- Iface_CALL(readyToSend, bm->msg, sm, sess);
- Map_BufferedMessages_remove(index, &sm->bufMap);
- Allocator_free(bm->alloc);
- }
- return NULL;
- }
- struct SessionManager* SessionManager_new(struct Allocator* allocator,
- EventBase_t* eventBase,
- Ca_t* cryptoAuth,
- struct Random* rand,
- struct Log* log,
- struct EventEmitter* ee)
- {
- struct Allocator* alloc = Allocator_child(allocator);
- struct SessionManager_pvt* sm = Allocator_calloc(alloc, sizeof(struct SessionManager_pvt), 1);
- sm->alloc = alloc;
- sm->pub.switchIf.send = incomingFromSwitchIf;
- sm->pub.insideIf.send = incomingFromInsideIf;
- sm->bufMap.allocator = alloc;
- sm->ifaceMap.allocator = alloc;
- sm->log = log;
- sm->cryptoAuth = cryptoAuth;
- sm->eventBase = eventBase;
- sm->pub.sessionTimeoutMilliseconds = SessionManager_SESSION_TIMEOUT_MILLISECONDS_DEFAULT;
- sm->pub.maxBufferedMessages = SessionManager_MAX_BUFFERED_MESSAGES_DEFAULT;
- sm->pub.sessionSearchAfterMilliseconds =
- SessionManager_SESSION_SEARCH_AFTER_MILLISECONDS_DEFAULT;
- Ca_getPubKey(cryptoAuth, sm->ourPubKey);
- sm->eventIf.send = incomingFromEventIf;
- EventEmitter_regCore(ee, &sm->eventIf, PFChan_Pathfinder_NODE);
- EventEmitter_regCore(ee, &sm->eventIf, PFChan_Pathfinder_SESSIONS);
- sm->firstHandle =
- (Random_uint32(rand) % (MAX_FIRST_HANDLE - MIN_FIRST_HANDLE)) + MIN_FIRST_HANDLE;
- Timeout_setInterval(periodically, sm, 1000, eventBase, alloc);
- Identity_set(sm);
- return &sm->pub;
- }
|