/** * Splice a label and a label fragment together. * */ uint64_t LabelSplicer_splice(uint64_t goHere_be, uint64_t viaHere_be) { uint64_t goHere = Endian_bigEndianToHost64(goHere_be); uint64_t viaHere = Endian_bigEndianToHost64(viaHere_be); uint64_t log2ViaHere = Bits_log2x64(viaHere); if (Bits_log2x64(goHere) + log2ViaHere > 61) { // Too big, can't splice. return UINT64_MAX; } return Endian_hostToBigEndian64(((goHere ^ 1) << log2ViaHere) ^ viaHere); }
uint64_t EncodingScheme_convertLabel(struct EncodingScheme* scheme, uint64_t routeLabel, int convertTo) { int formNum = EncodingScheme_getFormNum(scheme, routeLabel); if (formNum == EncodingScheme_getFormNum_INVALID) { return EncodingScheme_convertLabel_INVALID; } struct EncodingScheme_Form* currentForm = &scheme->forms[formNum]; if (scheme->count == 1 || (routeLabel & Bits_maxBits64(currentForm->prefixLen + currentForm->bitCount)) == 1) { // fixed width encoding or it's a self label, this is easy switch (convertTo) { case 0: case EncodingScheme_convertLabel_convertTo_CANNONICAL: return routeLabel; default: return EncodingScheme_convertLabel_INVALID; } } routeLabel >>= currentForm->prefixLen; uint64_t director = routeLabel & Bits_maxBits64(currentForm->bitCount); routeLabel >>= currentForm->bitCount; // ACKTUNG: Magic afoot! // Conversions are necessary for two reasons. // #1 ensure 0001 always references interface 1, the self interface. // #2 reuse interface the binary encoding for interface 1 in other EncodingForms // because interface 1 cannot be expressed as anything other than 0001 if ((currentForm->prefix & Bits_maxBits64(currentForm->prefixLen)) == 1) { // Swap 0 and 1 if the prefix is 1, this makes 0001 alias to 1 // because 0 can never show up in the wild, we reuse it for 1. Assert_true(director != 0); if (director == 1) { director--; } } else if (director) { // Reuse the number 1 for 2 and 2 for 3 etc. to gain an extra slot in all other encodings. director++; } if (convertTo == EncodingScheme_convertLabel_convertTo_CANNONICAL) { // Take into account the fact that if the destination form does not have a 1 prefix, // an extra number will be available. int minBitsA = Bits_log2x64(director) + 1; int minBitsB = Bits_log2x64(director-1) + 1; for (int i = 0; i < scheme->count; i++) { struct EncodingScheme_Form* form = &scheme->forms[i]; int minBits = ((form->prefix & Bits_maxBits64(form->prefixLen)) == 1) ? minBitsA : minBitsB; if (form->bitCount >= minBits) { convertTo = i; break; } } } if (convertTo < 0 || convertTo >= scheme->count) { // convertTo value is insane return EncodingScheme_convertLabel_INVALID; } struct EncodingScheme_Form* nextForm = &scheme->forms[convertTo]; if ((nextForm->prefix & Bits_maxBits64(nextForm->prefixLen)) == 1) { // Swap 1 and 0 back if necessary. if (director == 0) { director++; } } else if (director) { // Or move the numbers down by one. director--; } if ((Bits_log2x64(director) + 1) > nextForm->bitCount) { // won't fit in requested form return EncodingScheme_convertLabel_INVALID; } if (Bits_log2x64(routeLabel) + EncodingScheme_formSize(nextForm) > 59) { return EncodingScheme_convertLabel_INVALID; } routeLabel <<= nextForm->bitCount; routeLabel |= director; routeLabel <<= nextForm->prefixLen; routeLabel |= nextForm->prefix; if ((routeLabel & Bits_maxBits64(nextForm->prefixLen + nextForm->bitCount)) == 1) { // looks like a self-route return EncodingScheme_convertLabel_INVALID; } return routeLabel; }
static int getBadness(struct Address* badAddr, struct Address* selfAddr) { uint64_t xor = Endian_bigEndianToHost64(badAddr->ip6.longs.one_be ^ selfAddr->ip6.longs.one_be); return Bits_log2x64(xor) + Bits_log2x64(badAddr->path); }
static void dumpTable_addEntries(struct Context* ctx, int i, int j, struct List_Item* last, String* txid) { uint8_t path[20]; uint8_t ip[40]; String* pathStr = &(String) { .len = 19, .bytes = (char*)path }; String* ipStr = &(String) { .len = 39, .bytes = (char*)ip }; Object* link = Int_OBJ(0xFFFFFFFF); Object* version = Int_OBJ(Version_DEFAULT_ASSUMPTION); Dict entry = Dict_CONST( String_CONST("ip"), String_OBJ(ipStr), Dict_CONST( String_CONST("link"), link, Dict_CONST( String_CONST("path"), String_OBJ(pathStr), Dict_CONST( String_CONST("version"), version, NULL )))); struct List_Item next = { .next = last, .elem = Dict_OBJ(&entry) }; if (i >= ctx->store->size || j >= ENTRIES_PER_PAGE) { if (i > j) { dumpTable_send(ctx, last, (j >= ENTRIES_PER_PAGE), txid); return; } Address_printIp(ip, ctx->store->selfAddress); strcpy((char*)path, "0000.0000.0000.0001"); version->as.number = Version_CURRENT_PROTOCOL; dumpTable_send(ctx, &next, (j >= ENTRIES_PER_PAGE), txid); return; } struct Node* n = NodeStore_dumpTable(ctx->store, i); link->as.number = n->reach; version->as.number = n->version; Address_printIp(ip, &n->address); AddrTools_printPath(path, n->address.path); dumpTable_addEntries(ctx, i + 1, j + 1, &next, txid); } static void dumpTable(Dict* args, void* vcontext, String* txid, struct Allocator* requestAlloc) { struct Context* ctx = Identity_cast((struct Context*) vcontext); int64_t* page = Dict_getInt(args, String_CONST("page")); int i = (page) ? *page * ENTRIES_PER_PAGE : 0; dumpTable_addEntries(ctx, i, 0, NULL, txid); } static bool isOneHop(struct Node_Link* link) { struct EncodingScheme* ps = link->parent->encodingScheme; int num = EncodingScheme_getFormNum(ps, link->cannonicalLabel); Assert_always(num > -1 && num < ps->count); return EncodingScheme_formSize(&ps->forms[num]) == Bits_log2x64(link->cannonicalLabel); } static void getLink(Dict* args, void* vcontext, String* txid, struct Allocator* alloc) { struct Context* ctx = Identity_cast((struct Context*) vcontext); Dict* ret = Dict_new(alloc); Dict* result = Dict_new(alloc); Dict_putDict(ret, String_new("result", alloc), result, alloc); Dict_putString(ret, String_new("error", alloc), String_new("none", alloc), alloc); struct Node_Link* link; String* ipStr = Dict_getString(args, String_new("parent", alloc)); int64_t* linkNum = Dict_getInt(args, String_new("linkNum", alloc)); uint8_t ip[16]; if (ipStr->len != 39 || AddrTools_parseIp(ip, ipStr->bytes)) { Dict_remove(ret, String_CONST("result")); Dict_putString(ret, String_new("error", alloc), String_new("Could not parse ip", alloc), alloc); } else if ((link = NodeStore_getLink(ctx->store, ip, *linkNum))) { Dict_putInt(result, String_new("inverseLinkEncodingFormNumber", alloc), link->inverseLinkEncodingFormNumber, alloc); Dict_putInt(result, String_new("linkState", alloc), link->linkState, alloc); Dict_putInt(result, String_new("isOneHop", alloc), isOneHop(link), alloc); String* cannonicalLabel = String_newBinary(NULL, 19, alloc); AddrTools_printPath(cannonicalLabel->bytes, link->cannonicalLabel); Dict_putString(result, String_new("cannonicalLabel", alloc), cannonicalLabel, alloc); String* parent = String_newBinary(NULL, 39, alloc); AddrTools_printIp(parent->bytes, link->parent->address.ip6.bytes); Dict_putString(result, String_new("parent", alloc), parent, alloc); String* child = String_newBinary(NULL, 39, alloc); AddrTools_printIp(child->bytes, link->child->address.ip6.bytes); Dict_putString(result, String_new("child", alloc), child, alloc); } Admin_sendMessage(ret, txid, ctx->admin); } static void getNode(Dict* args, void* vcontext, String* txid, struct Allocator* alloc) { struct Context* ctx = Identity_cast((struct Context*) vcontext); Dict* ret = Dict_new(alloc); Dict* result = Dict_new(alloc); Dict_putDict(ret, String_new("result", alloc), result, alloc); Dict_putString(ret, String_new("error", alloc), String_new("none", alloc), alloc); // no ipStr specified --> return self-node struct Node_Two* node = ctx->store->selfNode; String* ipStr = Dict_getString(args, String_new("ip", alloc)); uint8_t ip[16]; while (ipStr) { if (ipStr->len != 39 || AddrTools_parseIp(ip, ipStr->bytes)) { Dict_remove(ret, String_CONST("result")); Dict_putString(ret, String_new("error", alloc), String_new("Could not parse ip", alloc), alloc); } else if (!(node = NodeStore_getNode2(ctx->store, ip))) { // not found } else { break; } Admin_sendMessage(ret, txid, ctx->admin); return; } Dict_putInt(result, String_new("protocolVersion", alloc), node->version, alloc); String* key = Key_stringify(node->address.key, alloc); Dict_putString(result, String_new("key", alloc), key, alloc); uint32_t linkCount = NodeStore_linkCount(node); Dict_putInt(result, String_new("linkCount", alloc), linkCount, alloc); List* encScheme = EncodingScheme_asList(node->encodingScheme, alloc); Dict_putList(result, String_new("encodingScheme", alloc), encScheme, alloc); Admin_sendMessage(ret, txid, ctx->admin); } static void getRouteLabel(Dict* args, void* vcontext, String* txid, struct Allocator* requestAlloc) { struct Context* ctx = Identity_cast((struct Context*) vcontext); char* err = NULL; String* pathToParentS = Dict_getString(args, String_CONST("pathToParent")); uint64_t pathToParent; if (pathToParentS->len != 19) { err = "pathToParent incorrect length"; } else if (AddrTools_parsePath(&pathToParent, pathToParentS->bytes)) { err = "Failed to parse pathToParent"; } String* childAddressS = Dict_getString(args, String_CONST("childAddress")); uint8_t childAddress[16]; if (childAddressS->len != 39) { err = "childAddress of incorrect length, must be a 39 character full ipv6 address"; } else if (AddrTools_parseIp(childAddress, childAddressS->bytes)) { err = "Failed to parse childAddress"; } uint64_t label = UINT64_MAX; if (!err) { label = NodeStore_getRouteLabel(ctx->store, pathToParent, childAddress); err = NodeStore_getRouteLabel_strerror(label); } Dict* response = Dict_new(requestAlloc); if (!err) { String* printedPath = String_newBinary(NULL, 19, requestAlloc); AddrTools_printPath(printedPath->bytes, label); Dict_putString(response, String_new("result", requestAlloc), printedPath, requestAlloc); Dict_putString(response, String_new("error", requestAlloc), String_new("none", requestAlloc), requestAlloc); Admin_sendMessage(response, txid, ctx->admin); } else { Dict_putString(response, String_new("error", requestAlloc), String_new(err, requestAlloc), requestAlloc); Admin_sendMessage(response, txid, ctx->admin); } } void NodeStore_admin_register(struct NodeStore* nodeStore, struct Admin* admin, struct Allocator* alloc) { struct Context* ctx = Allocator_clone(alloc, (&(struct Context) { .admin = admin, .alloc = alloc, .store = nodeStore })); Identity_set(ctx); Admin_registerFunction("NodeStore_dumpTable", dumpTable, ctx, false, ((struct Admin_FunctionArg[]) { { .name = "page", .required = 1, .type = "Int" }, }), admin); Admin_registerFunction("NodeStore_getLink", getLink, ctx, true, ((struct Admin_FunctionArg[]) { { .name = "parent", .required = 1, .type = "String" }, { .name = "linkNum", .required = 1, .type = "Int" }, }), admin);
/** * Determine if the node at the end of the given label is one hop away. * * @param label_be the label to test. * @return true if the node is 1 hop away, false otherwise. */ bool LabelSplicer_isOneHop(uint64_t label_be) { uint64_t label = Endian_bigEndianToHost64(label_be); return (int)NumberCompress_bitsUsedForLabel(label) == Bits_log2x64(label); }