bool EncodingScheme_isSane(struct EncodingScheme* scheme) { // Check for obviously insane encoding. if (scheme->count == 0) { // No encoding schemes return false; } if (scheme->count > 31) { // impossible, each form must have a different bitCount and bitCount // can only be expressed in 5 bits limiting it to 31 bits max and a form // using zero bits is not allowed so there are only 31 max possibilities. return false; } if (scheme->count == 1) { // Fixed width encoding, prefix is not allowed and bitcount must be non-zero if (scheme->forms[0].prefixLen != 0 || scheme->forms[0].prefix != 0) { // prefixLen must be 0 return false; } if (scheme->forms[0].bitCount == 0 || scheme->forms[0].bitCount > 31) { // bitcount must be non-zero and can't overflow the number return false; } return true; } // Variable width encoding. for (int i = 0; i < scheme->count; i++) { struct EncodingScheme_Form* form = &scheme->forms[i]; if (form->prefixLen == 0 || form->prefixLen > 31) { // Prefix must exist in order to distinguish between forms return false; } if (form->bitCount == 0 || form->bitCount > 31) { // Bitcount must be non-zero return false; } if (EncodingScheme_formSize(form) > 59) { // cannot be represented in the usable space in a label return false; } if (i > 0 && form->bitCount <= scheme->forms[i-1].bitCount) { // Forms must be in ascending order. return false; } for (int j = 0; j < scheme->count; j++) { // Forms must be distinguishable by their prefixes. if (j != i && (scheme->forms[j].prefix & Bits_maxBits64(form->prefixLen)) == form->prefix) { return false; } } } return true; }
static void randomForm(struct EncodingScheme_Form* form, struct Random* rand) { for (;;) { Random_bytes(rand, (uint8_t*)form, sizeof(struct EncodingScheme_Form)); //Bits_memset(form, 0xff, sizeof(struct EncodingScheme_Form)); form->bitCount &= ((1<<5)-1); if (!form->bitCount) { form->bitCount++; } form->prefixLen &= ((1<<5)-1); if (!form->prefixLen) { form->prefixLen++; } if (EncodingScheme_formSize(form) > 59) { continue; } if (form->prefixLen > 3 && (form->prefix & 0xf) == 1) { continue; } break; } form->prefix &= ((((uint64_t)1)<<form->prefixLen)-1); }
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 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);