diff --git a/ccnx/forwarder/metis/config/metisControl_RemoveRoute.c b/ccnx/forwarder/metis/config/metisControl_RemoveRoute.c index 4ff6ba5..430cadb 100644 --- a/ccnx/forwarder/metis/config/metisControl_RemoveRoute.c +++ b/ccnx/forwarder/metis/config/metisControl_RemoveRoute.c @@ -64,6 +64,7 @@ #include #include #include +#include #include @@ -92,18 +93,135 @@ metisControlRemoveRoute_HelpCreate(MetisControlState *state) return metisCommandOps_Create(state, _commandRemoveRouteHelp, NULL, _metisControlRemoveRoute_HelpExecute, metisCommandOps_Destroy); } +/** + * Return true if string is purely an integer + */ +static bool +_isNumber(const char *string) +{ + size_t len = strlen(string); + for (size_t i = 0; i < len; i++) { + if (!isdigit(string[i])) { + return false; + } + } + return true; +} + +/** + * A symbolic name must be at least 1 character and must begin with an alpha. + * The remainder must be an alphanum. + */ +static bool +_validateSymbolicName(const char *symbolic) +{ + bool success = false; + size_t len = strlen(symbolic); + if (len > 0) { + if (isalpha(symbolic[0])) { + success = true; + for (size_t i = 1; i < len; i++) { + if (!isalnum(symbolic[i])) { + success = false; + break; + } + } + } + } + return success; +} + // ==================================================== static MetisCommandReturn _metisControlRemoveRoute_HelpExecute(MetisCommandParser *parser, MetisCommandOps *ops, PARCList *args) { - printf("%s help not implemented (case 149)", ops->command); + printf("commands:\n"); + printf(" remove route \n"); + printf("\n"); + printf(" symbolic: The optional symbolic name for an exgress\n"); + printf(" connid: The optional egress connection id (see 'help list connections')\n"); + printf(" prefix: The CCNx name as a URI (e.g. lci:/foo/bar)\n"); + printf("\n"); + printf("Examples:\n"); + printf(" remove route 25 lci:/foo/bar\n"); + printf(" removes route to prefix '/foo/bar' on egress connection index 25\n"); + printf(" remove route tun3 lci:/foo/bar\n"); + printf(" removes route to prefix '/foo/bar' on egress connection 'tun3'\n"); + printf("\n"); + return MetisCommandReturn_Success; } static MetisCommandReturn _metisControlRemoveRoute_Execute(MetisCommandParser *parser, MetisCommandOps *ops, PARCList *args) { - printf("%s not implemented (case 149)", ops->command); - return MetisCommandReturn_Failure; + MetisControlState *state = ops->closure; + + if (parcList_Size(args) != 4) { + _metisControlRemoveRoute_HelpExecute(parser, ops, args); + return MetisCommandReturn_Failure; + } + + const char *symbolicOrConnid = parcList_GetAtIndex(args, 2); + + if (_validateSymbolicName(symbolicOrConnid) || _isNumber(symbolicOrConnid)) { + + const char *prefixString = parcList_GetAtIndex(args, 3); + CCNxName *prefix = ccnxName_CreateFromCString(prefixString); + if (prefix == NULL) { + printf("ERROR: could not parse prefix '%s'\n", prefixString); + return MetisCommandReturn_Failure; + } + + char *protocolTypeAsString = "static"; + + CPINameRouteProtocolType protocolType = cpiNameRouteProtocolType_FromString(protocolTypeAsString); + CPINameRouteType routeType = cpiNameRouteType_LONGEST_MATCH; + CPIAddress *nexthop = NULL; + + struct timeval *lifetime = NULL; + + CPIRouteEntry *route = NULL; + + if (_isNumber(symbolicOrConnid)) { + unsigned connid = (unsigned) strtold(symbolicOrConnid, NULL); + route = cpiRouteEntry_Create(prefix, connid, nexthop, protocolType, routeType, lifetime, 0); + } else { + route = cpiRouteEntry_CreateSymbolic(prefix, symbolicOrConnid, protocolType, routeType, lifetime, 0); + } + + CCNxControl *addRouteRequest = ccnxControl_CreateRemoveRouteRequest(route); + + cpiRouteEntry_Destroy(&route); + + if (metisControlState_GetDebug(state)) { + char *str = parcJSON_ToString(ccnxControl_GetJson(addRouteRequest)); + printf("request: %s\n", str); + parcMemory_Deallocate((void **) &str); + } + + CCNxMetaMessage *message = ccnxMetaMessage_CreateFromControl(addRouteRequest); + CCNxMetaMessage *rawResponse = metisControlState_WriteRead(state, message); + ccnxMetaMessage_Release(&message); + + ccnxControl_Release(&addRouteRequest); + + CCNxControl *response = ccnxMetaMessage_GetControl(rawResponse); + + if (metisControlState_GetDebug(state)) { + char *str = parcJSON_ToString(ccnxControl_GetJson(response)); + printf("response: %s\n", str); + parcMemory_Deallocate((void **) &str); + } + + ccnxMetaMessage_Release(&rawResponse); + + return MetisCommandReturn_Success; + } + else { + printf("ERROR: Invalid symbolic or connid. Symbolic name must begin with an alpha followed by alphanum. connid must be an integer\n"); + return MetisCommandReturn_Failure; + } + } diff --git a/ccnx/forwarder/metis/config/metis_Configuration.c b/ccnx/forwarder/metis/config/metis_Configuration.c index a103000..b297655 100644 --- a/ccnx/forwarder/metis/config/metis_Configuration.c +++ b/ccnx/forwarder/metis/config/metis_Configuration.c @@ -278,18 +278,52 @@ metisConfiguration_ProcessRegisterPrefix(MetisConfiguration *config, CCNxControl return response; } +static bool +_symbolicUnregisterPrefix(MetisConfiguration *config, CPIRouteEntry *route) +{ + bool success = false; + + const char *symbolic = cpiRouteEntry_GetSymbolicName(route); + unsigned ifidx = metisSymbolicNameTable_Get(config->symbolicNameTable, symbolic); + if (ifidx != UINT32_MAX) { + cpiRouteEntry_SetInterfaceIndex(route, ifidx); + if (metisLogger_IsLoggable(config->logger, MetisLoggerFacility_Config, PARCLogLevel_Debug)) { + metisLogger_Log(config->logger, MetisLoggerFacility_Config, PARCLogLevel_Debug, __func__, + "Remove route resolve name '%s' to connid %u", + symbolic, ifidx); + } + + success = metisForwarder_RemoveRoute(config->metis, route); + } else { + if (metisLogger_IsLoggable(config->logger, MetisLoggerFacility_Config, PARCLogLevel_Warning)) { + metisLogger_Log(config->logger, MetisLoggerFacility_Config, PARCLogLevel_Warning, __func__, + "Remove route symbolic name '%s' could not be resolved", symbolic); + } + // this is a failure + } + return success; +} + static CCNxControl * metisConfiguration_ProcessUnregisterPrefix(MetisConfiguration *config, CCNxControl *control, unsigned ingressId) { CPIRouteEntry *route = cpiForwarding_RouteFromControlMessage(control); - if (cpiRouteEntry_GetInterfaceIndex(route) == CPI_CURRENT_INTERFACE) { - // We want to use the ingress interface - cpiRouteEntry_SetInterfaceIndex(route, ingressId); + bool success = false; + + // if it has a symbolic name set the interface index + if (cpiRouteEntry_GetSymbolicName(route) != NULL) { + success = _symbolicUnregisterPrefix(config, route); + } else { + if (cpiRouteEntry_GetInterfaceIndex(route) == CPI_CURRENT_INTERFACE) { + // We want to use the ingress interface + cpiRouteEntry_SetInterfaceIndex(route, ingressId); + } + success = metisForwarder_RemoveRoute(config->metis, route); } CCNxControl *response = NULL; - if (metisForwarder_RemoveRoute(config->metis, route)) { + if (success) { response = _createAck(config, control, ingressId); } else { response = _createNack(config, control, ingressId); diff --git a/ccnx/forwarder/metis/config/test/test_metisControl_RemoveRoute.c b/ccnx/forwarder/metis/config/test/test_metisControl_RemoveRoute.c index 39d3dc7..255194b 100644 --- a/ccnx/forwarder/metis/config/test/test_metisControl_RemoveRoute.c +++ b/ccnx/forwarder/metis/config/test/test_metisControl_RemoveRoute.c @@ -116,8 +116,11 @@ LONGBOW_TEST_CASE(Global, metisControlRemoveRoute_Create) LONGBOW_TEST_FIXTURE(Local) { + LONGBOW_RUN_TEST_CASE(Local, metisControl_RemoveRoute_Execute_WrongArgCount); + LONGBOW_RUN_TEST_CASE(Local, metisControl_RemoveRoute_Execute_BadPrefix); + LONGBOW_RUN_TEST_CASE(Local, metisControl_RemoveRoute_Execute_Good); + LONGBOW_RUN_TEST_CASE(Local, metisControl_Help_RemoveRoute_Execute); - LONGBOW_RUN_TEST_CASE(Local, metisControl_RemoveRoute_Execute); } LONGBOW_TEST_FIXTURE_SETUP(Local) @@ -137,15 +140,51 @@ LONGBOW_TEST_FIXTURE_TEARDOWN(Local) return LONGBOW_STATUS_SUCCEEDED; } +static MetisCommandReturn +testRemoveRoute(const LongBowTestCase *testCase, int argc, const char *prefix, char *nexthop) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + metisControlState_SetDebug(data->state, true); + + const char *argv[] = { "remove", "route", nexthop, prefix }; + PARCList *args = parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList); + parcList_AddAll(args, argc, (void **) &argv[0]); + + MetisCommandOps *ops = metisControlRemoveRoute_Create(data->state); + + MetisCommandReturn result = ops->execute(data->state->parser, ops, args); + metisCommandOps_Destroy(&ops); + parcList_Release(&args); + return result; +} + LONGBOW_TEST_CASE(Local, metisControl_Help_RemoveRoute_Execute) { testHelpExecute(testCase, &metisControlRemoveRoute_HelpCreate, __func__, MetisCommandReturn_Success); } -LONGBOW_TEST_CASE(Local, metisControl_RemoveRoute_Execute) +LONGBOW_TEST_CASE(Local, metisControl_RemoveRoute_Execute_WrongArgCount) +{ + MetisCommandReturn result = testRemoveRoute(testCase, 3, "lci:/foo", "100"); + + assertTrue(result == MetisCommandReturn_Failure, + "metisControl_AddRoute with wrong argc should return %d, got %d", MetisCommandReturn_Failure, result); +} + +LONGBOW_TEST_CASE(Local, metisControl_RemoveRoute_Execute_BadPrefix) +{ + MetisCommandReturn result = testRemoveRoute(testCase, 4, "blah", "100"); + + assertTrue(result == MetisCommandReturn_Failure, + "metisControl_AddRoute with bad prefix should return %d, got %d", MetisCommandReturn_Failure, result); +} + +LONGBOW_TEST_CASE(Local, metisControl_RemoveRoute_Execute_Good) { - // Change to success once function implemented (case 157) - testHelpExecute(testCase, &metisControlRemoveRoute_Create, __func__, MetisCommandReturn_Failure); + MetisCommandReturn result = testRemoveRoute(testCase, 4, "lci:/foo", "100"); + + assertTrue(result == MetisCommandReturn_Success, + "metisControl_RemoveRoute should return %d, got %d", MetisCommandReturn_Success, result); } int diff --git a/ccnx/forwarder/metis/processor/metis_FIB.c b/ccnx/forwarder/metis/processor/metis_FIB.c index 9a8bf88..e62915a 100644 --- a/ccnx/forwarder/metis/processor/metis_FIB.c +++ b/ccnx/forwarder/metis/processor/metis_FIB.c @@ -70,6 +70,8 @@ #include #include +#include + #include // ===================================================== @@ -110,6 +112,8 @@ struct metis_fib { // KEY = tlvName, VALUE = FibEntry PARCHashCodeTable *tableByName; + PARCRWLock *fibLock; + // KEY = tlvName. We use a tree for the keys because that // has the same average insert and remove time. The tree // is only used by GetEntries, which in turn is used by things @@ -143,6 +147,8 @@ metisFIB_Create(MetisLogger *logger) _hashTableFunction_FibEntryDestroyer, initialSize); + fib->fibLock = parcRWLock_Create(); + fib->tableOfKeys = parcTreeRedBlack_Create(metisHashTableFunction_TlvNameCompare, NULL, NULL, NULL, NULL, NULL); @@ -173,6 +179,7 @@ metisFIB_Destroy(MetisFIB **fibPtr) metisLogger_Release(&fib->logger); parcTreeRedBlack_Destroy(&fib->tableOfKeys); parcHashCodeTable_Destroy(&fib->tableByName); + parcRWLock_Release(&fib->fibLock); parcMemory_Deallocate((void **) &fib); *fibPtr = NULL; } @@ -189,6 +196,7 @@ metisFIB_Match(MetisFIB *fib, const MetisMessage *interestMessage) MetisFibEntry *longestMatchingFibEntry = NULL; // because the FIB table is sparse, we need to scan all the name segments in order. + parcRWLock_ReadLock(fib->fibLock); for (size_t i = 0; i < metisTlvName_SegmentCount(tlvName); i++) { MetisTlvName *prefixName = metisTlvName_Slice(tlvName, i + 1); MetisFibEntry *fibEntry = parcHashCodeTable_Get(fib->tableByName, prefixName); @@ -203,6 +211,7 @@ metisFIB_Match(MetisFIB *fib, const MetisMessage *interestMessage) } metisTlvName_Release(&prefixName); } + parcRWLock_Unlock(fib->fibLock); if (longestMatchingFibEntry != NULL) { // this returns a reference counted copy of the next hops @@ -224,10 +233,12 @@ metisFIB_AddOrUpdate(MetisFIB *fib, CPIRouteEntry *route) unsigned interfaceIndex = cpiRouteEntry_GetInterfaceIndex(route); MetisTlvName *tlvName = metisTlvName_CreateFromCCNxName(ccnxName); + parcRWLock_WriteLock(fib->fibLock); MetisFibEntry *fibEntry = parcHashCodeTable_Get(fib->tableByName, tlvName); if (fibEntry == NULL) { fibEntry = _metisFIB_CreateFibEntry(fib, tlvName); } + parcRWLock_Unlock(fib->fibLock); metisFibEntry_AddNexthop(fibEntry, interfaceIndex); @@ -249,6 +260,7 @@ metisFIB_Remove(MetisFIB *fib, CPIRouteEntry *route) unsigned interfaceIndex = cpiRouteEntry_GetInterfaceIndex(route); MetisTlvName *tlvName = metisTlvName_CreateFromCCNxName(ccnxName); + parcRWLock_WriteLock(fib->fibLock); MetisFibEntry *fibEntry = parcHashCodeTable_Get(fib->tableByName, tlvName); if (fibEntry != NULL) { metisFibEntry_RemoveNexthop(fibEntry, interfaceIndex); @@ -261,6 +273,7 @@ metisFIB_Remove(MetisFIB *fib, CPIRouteEntry *route) routeRemoved = true; } } + parcRWLock_Unlock(fib->fibLock); metisTlvName_Release(&tlvName); return routeRemoved; @@ -270,7 +283,11 @@ size_t metisFIB_Length(const MetisFIB *fib) { assertNotNull(fib, "Parameter fib must be non-null"); - return parcHashCodeTable_Length(fib->tableByName); + size_t length; + parcRWLock_ReadLock(fib->fibLock); + length = parcHashCodeTable_Length(fib->tableByName); + parcRWLock_Unlock(fib->fibLock); + return length; } MetisFibEntryList * @@ -279,11 +296,13 @@ metisFIB_GetEntries(const MetisFIB *fib) assertNotNull(fib, "Parameter fib must be non-null"); MetisFibEntryList *list = metisFibEntryList_Create(); + parcRWLock_ReadLock(fib->fibLock); PARCArrayList *values = parcTreeRedBlack_Values(fib->tableOfKeys); for (size_t i = 0; i < parcArrayList_Size(values); i++) { MetisFibEntry *original = (MetisFibEntry *) parcArrayList_Get(values, i); metisFibEntryList_Append(list, original); } + parcRWLock_Unlock(fib->fibLock); parcArrayList_Destroy(&values); return list; } @@ -296,11 +315,13 @@ metisFIB_RemoveConnectionIdFromRoutes(MetisFIB *fib, unsigned connectionId) // NB: This is very inefficient. We need a reverse-index from connectionId to FibEntry (case 814) // Walk the entire tree and remove the connection id from every entry. + parcRWLock_WriteLock(fib->fibLock); PARCArrayList *values = parcTreeRedBlack_Values(fib->tableOfKeys); for (size_t i = 0; i < parcArrayList_Size(values); i++) { MetisFibEntry *original = (MetisFibEntry *) parcArrayList_Get(values, i); metisFibEntry_RemoveNexthop(original, connectionId); } + parcRWLock_Unlock(fib->fibLock); parcArrayList_Destroy(&values); } diff --git a/documentation/manpage-source/metis.cfg.sgml b/documentation/manpage-source/metis.cfg.sgml index 31649c9..9019717 100644 --- a/documentation/manpage-source/metis.cfg.sgml +++ b/documentation/manpage-source/metis.cfg.sgml @@ -298,10 +298,10 @@ Done - remove route + remove route symbolic prefix - Not implemented. + Removes a static route from the FIB. The prefix and interface must match exactly for the route to be removed. diff --git a/documentation/manpage/metis.cfg.5 b/documentation/manpage/metis.cfg.5 index 0ae6000..463c6d8 100644 --- a/documentation/manpage/metis.cfg.5 +++ b/documentation/manpage/metis.cfg.5 @@ -142,8 +142,9 @@ Done .IP "remove connection" 10 Not implemented. -.IP "remove route" 10 -Not implemented. +.IP "remove route \fIsymbolic\fR \fIprefix\fR" 10 +Removes a static route from the FIB. The prefix and interface must match exactly +for the route to be removed. .SH "MISC COMMANDS" .IP "quit" 10 diff --git a/documentation/manpage/metis.cfg.5.html b/documentation/manpage/metis.cfg.5.html index dcc98a6..7a3b4af 100644 --- a/documentation/manpage/metis.cfg.5.html +++ b/documentation/manpage/metis.cfg.5.html @@ -334,9 +334,11 @@

REMOVE COMMANDS

Not implemented.

-

remove route

+

remove route symbolic prefix

-

Not implemented.

+

Removes a static route from the FIB. + The prefix and interface must match exactly for the route to + be removed.

MISC COMMANDS