diff options
Diffstat (limited to 'hicn-light/src')
225 files changed, 34115 insertions, 0 deletions
diff --git a/hicn-light/src/CMakeLists.txt b/hicn-light/src/CMakeLists.txt new file mode 100755 index 000000000..939f38a34 --- /dev/null +++ b/hicn-light/src/CMakeLists.txt @@ -0,0 +1,45 @@ +# Define a few configuration variables that we want accessible in the software + +include(BuildMacros) +configure_file(config.h.in config.h @ONLY) + +if(NOT ANDROID_API AND NOT COMPILE_FOR_IOS) + add_subdirectory(command_line) +endif () + +add_subdirectory(config) +add_subdirectory(content_store) +add_subdirectory(core) +add_subdirectory(io) +add_subdirectory(messenger) +add_subdirectory(platforms) +add_subdirectory(processor) +add_subdirectory(socket) +add_subdirectory(strategies) +add_subdirectory(utils) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_BINARY_DIR}/config.h +) + +set(COMPILER_DEFINITIONS "-DWITH_MAPME -DWITH_MAPME_FIXES") + +list(APPEND HICN_LIGHT_INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/.. +) + +if (INSTALL_HEADER) + set(TO_INSTALL_HEADERS ${HEADER_FILES}) +endif() + +build_library(${LIBHICN_LIGHT} + STATIC + SOURCES ${SOURCE_FILES} + INSTALL_HEADERS ${TO_INSTALL_HEADERS} + LINK_LIBRARIES ${LIBRARIES} + DEPENDS ${DEPENDENCIES} + COMPONENT hicn-light + INCLUDE_DIRS ${HICN_LIGHT_INCLUDE_DIRS} + INSTALL_ROOT_DIR hicn/hicn-light + DEFINITIONS ${COMPILER_DEFINITIONS} +) diff --git a/hicn-light/src/command_line/CMakeLists.txt b/hicn-light/src/command_line/CMakeLists.txt new file mode 100755 index 000000000..16c23dc5c --- /dev/null +++ b/hicn-light/src/command_line/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(controller) +add_subdirectory(daemon) diff --git a/hicn-light/src/command_line/controller/CMakeLists.txt b/hicn-light/src/command_line/controller/CMakeLists.txt new file mode 100755 index 000000000..b53e610a1 --- /dev/null +++ b/hicn-light/src/command_line/controller/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +list(APPEND CONTROLLER_SRC + hicnLightControl_main.c +) + +build_executable(${HICN_LIGHT_CONTROL} + SOURCES ${CONTROLLER_SRC} + LINK_LIBRARIES ${HICN_LIGHT_LINK_LIBRARIES} + DEPENDS hicn-light + COMPONENT hicn-light +) diff --git a/hicn-light/src/command_line/controller/hicnLightControl_main.c b/hicn-light/src/command_line/controller/hicnLightControl_main.c new file mode 100755 index 000000000..4641bddf5 --- /dev/null +++ b/hicn-light/src/command_line/controller/hicnLightControl_main.c @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <getopt.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <unistd.h> + +#include <parc/assert/parc_Assert.h> +#include <string.h> + +#include <parc/security/parc_IdentityFile.h> +#include <parc/security/parc_Security.h> + +#include <parc/algol/parc_ArrayList.h> +#include <parc/algol/parc_List.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_SafeMemory.h> + +#include <src/core/dispatcher.h> +#include <src/core/forwarder.h> + +#include <errno.h> +#include <src/config/controlRoot.h> +#include <src/config/controlState.h> + +#include <src/utils/commands.h> + +size_t commandOutputLen = 0; // preserve the number of structs composing + // payload in case on not interactive call. + +// REMINDER: when a new_command is added, the following array has to be updated +// with the sizeof(new_command). It allows to allocate the buffer for receiving +// the payload of the DAEMON RESPONSE after the header has beed read. Each +// command identifier (typedef enum command_id) corresponds to a position in the +// following array. +static int payloadLengthController[LAST_COMMAND_VALUE] = { + sizeof(add_listener_command), + sizeof(add_connection_command), + sizeof(list_connections_command), // needed when get response from FWD + sizeof(add_route_command), + sizeof(list_routes_command), // needed when get response from FWD + sizeof(remove_connection_command), + sizeof(remove_route_command), + sizeof(cache_store_command), + sizeof(cache_serve_command), + 0, // cache clear + sizeof(set_strategy_command), + sizeof(set_wldr_command), + sizeof(add_punting_command), + sizeof(list_listeners_command), // needed when get response from FWD + sizeof(mapme_activator_command), + sizeof(mapme_activator_command), + sizeof(mapme_timing_command), + sizeof(mapme_timing_command)}; + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/uio.h> + +typedef struct controller_main_state { + ControlState *controlState; +} ControlMainState; + +static void _displayForwarderLogo(void){ + const char cli_banner [] = + "\033[0;31m ____ ___ _ \033[0m __ _ __ _ __ __\n" + "\033[0;31m / __// _ \\ (_)___ \033[0m / / (_)____ ___ ____/ /(_)___ _ / / / /_\n" + "\033[0;31m / _/ / // /_ / // _ \\ \033[0m / _ \\ / // __// _ \\___/ // // _ `// _ \\/ __/\n" + "\033[0;31m/_/ /____/(_)/_/ \\___/ \033[0m/_//_//_/ \\__//_//_/ /_//_/ \\_, //_//_/\\__/\n" + " /___/ \n"; + printf("%s", cli_banner); + printf("\n"); +} + +static void _displayUsage(char *programName) { + printf("Usage: %s -h\n", programName); + printf( + "hicn-light is the 1.0 source, which runs on each end system and as a " + "software source\n"); + printf( + "on intermediate systems. controller is the program to configure the " + "source, daemon.\n"); + printf("\n"); + printf("Options:\n"); + printf("-h = This help screen\n"); + printf( + "commands = configuration line to send to hicn-light (use 'help' " + "for list)\n"); + printf("\n"); +} + +static int _parseArgs(int argc, char *argv[], char **keystorePath, + char **keystorePassword, PARCList *commandList) { + static struct option longFormOptions[] = { + {"help", no_argument, 0, 'h'}, + {"keystore", required_argument, 0, 'k'}, + {"password", required_argument, 0, 'p'}, + {0, 0, 0, 0}}; + + int c; + + while (1) { + // getopt_long stores the option index here. + int optionIndex = 0; + + c = getopt_long(argc, argv, "hk:p:", longFormOptions, &optionIndex); + + // Detect the end of the options. + if (c == -1) { + break; + } + + switch (c) { + case 'k': + *keystorePath = optarg; + break; + + case 'p': + *keystorePassword = optarg; + break; + + case 'h': + default: + _displayUsage(argv[0]); + return 0; + } + } + + // Any remaining parameters get put in the command list. + if (optind < argc) { + while (optind < argc) { + parcList_Add(commandList, argv[optind]); + optind++; + } + } + + return 1; +} + +struct iovec *_writeAndReadMessage(ControlState *state, struct iovec *msg) { + parcAssertNotNull(msg, "Parameter msg must be non-null"); + int sockfd = controlState_GetSockfd(state); + + // check if request has a payload + if (((header_control_message *)msg[0].iov_base)->length > + 0) { // command with payload + // write header + payload (compatibility issue: two write needed instead of + // the writev) + if (write(sockfd, msg[0].iov_base, msg[0].iov_len) < 0 || + write(sockfd, msg[1].iov_base, msg[1].iov_len) < 0) { + printf("\nError while sending the Message: cannot write on socket \n"); + exit(EXIT_FAILURE); + } + parcMemory_Deallocate(&msg[1].iov_base); + } else { // command without payload, e.g. 'list' + // write header only + if (write(sockfd, msg[0].iov_base, msg[0].iov_len) < 0) { + printf("\nError while sending the Message: cannot write on socket \n"); + exit(EXIT_FAILURE); + } + } + parcMemory_Deallocate(&msg[0].iov_base); + + // ======= RECEIVE ======= + + header_control_message *headerResponse = + (header_control_message *)parcMemory_AllocateAndClear( + sizeof(header_control_message)); + if (recv(sockfd, headerResponse, sizeof(header_control_message), 0) < 0) { + printf("\nError in Receiving the Message \n"); + exit(EXIT_FAILURE); + } + + if (headerResponse->messageType < RESPONSE_LIGHT || + headerResponse->messageType >= LAST_MSG_TYPE_VALUE) { + char *checkFinMsg = parcMemory_Reallocate(headerResponse, 32); + if (recv(sockfd, checkFinMsg, sizeof(checkFinMsg), + MSG_PEEK | MSG_DONTWAIT) == 0) { + // if recv returns zero, that means the connection has been closed: + close(sockfd); + printf("\nConnection terminated by the Daemon. Exiting... \n"); + exit(EXIT_SUCCESS); + } else { + printf("\nError: Unrecognized message type received \n"); + exit(EXIT_FAILURE); + } + } + + void *payloadResponse = NULL; + + if ((commandOutputLen = headerResponse->length) > 0) { + payloadResponse = parcMemory_AllocateAndClear( + payloadLengthController[headerResponse->commandID] * + headerResponse->length); + + if (recv(sockfd, payloadResponse, + payloadLengthController[headerResponse->commandID] * + headerResponse->length, + 0) < 0) { + printf("\nError in Receiving the Message \n"); + exit(EXIT_FAILURE); + } + } + + struct iovec *response = + parcMemory_AllocateAndClear(sizeof(struct iovec) * 2); + + response[0].iov_base = headerResponse; + response[0].iov_len = sizeof(header_control_message); + response[1].iov_base = payloadResponse; + response[1].iov_len = payloadLengthController[headerResponse->commandID] * + headerResponse->length; + + return response; +} + +int main(int argc, char *argv[]) { + _displayForwarderLogo(); + + if (argc == 2 && strcmp("-h", argv[1]) == 0) { + _displayUsage(argv[0]); + exit(EXIT_SUCCESS); + } + + PARCList *commands = + parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList); + + if (!_parseArgs(argc, argv, NULL, NULL, commands)) { + parcList_Release(&commands); + exit(EXIT_FAILURE); + } + + ControlMainState mainState; + mainState.controlState = + controlState_Create(&mainState, _writeAndReadMessage, true); + + controlState_RegisterCommand(mainState.controlState, + controlRoot_HelpCreate(mainState.controlState)); + controlState_RegisterCommand(mainState.controlState, + controlRoot_Create(mainState.controlState)); + + if (parcList_Size(commands) > 0) { + controlState_SetInteractiveFlag(mainState.controlState, false); + controlState_DispatchCommand(mainState.controlState, commands); + char **commandOutputMain = + controlState_GetCommandOutput(mainState.controlState); + if (commandOutputMain != NULL && commandOutputLen > 0) { + for (size_t j = 0; j < commandOutputLen; j++) { + printf("Output %zu: %s \n", j, commandOutputMain[j]); + } + controlState_ReleaseCommandOutput(mainState.controlState, + commandOutputMain, commandOutputLen); + } + // release + + } else { + controlState_Interactive(mainState.controlState); + } + + parcList_Release(&commands); + + controlState_Destroy(&mainState.controlState); + + return EXIT_SUCCESS; +} diff --git a/hicn-light/src/command_line/daemon/CMakeLists.txt b/hicn-light/src/command_line/daemon/CMakeLists.txt new file mode 100755 index 000000000..fd6cc9310 --- /dev/null +++ b/hicn-light/src/command_line/daemon/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +list(APPEND DAEMON_SRC + hicnLightDaemon_main.c +) + +build_executable(${HICN_LIGHT_DAEMON} + SOURCES ${DAEMON_SRC} + LINK_LIBRARIES ${HICN_LIGHT_LINK_LIBRARIES} + DEPENDS hicn-light + COMPONENT hicn-light +)
\ No newline at end of file diff --git a/hicn-light/src/command_line/daemon/hicnLightDaemon_main.c b/hicn-light/src/command_line/daemon/hicnLightDaemon_main.c new file mode 100755 index 000000000..f6d521711 --- /dev/null +++ b/hicn-light/src/command_line/daemon/hicnLightDaemon_main.c @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <fcntl.h> +#include <src/config.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <parc/algol/parc_FileOutputStream.h> +#include <parc/logging/parc_LogLevel.h> +#include <parc/logging/parc_LogReporterFile.h> +#include <parc/logging/parc_LogReporterTextStdout.h> + +#include <parc/assert/parc_Assert.h> + +#include <src/core/dispatcher.h> +#include <src/core/forwarder.h> + +static void _displayForwarderLogo(void){ + const char cli_banner [] = + "\033[0;31m ____ ___ _ \033[0m __ _ __ _ __ __\n" + "\033[0;31m / __// _ \\ (_)___ \033[0m / / (_)____ ___ ____/ /(_)___ _ / / / /_\n" + "\033[0;31m / _/ / // /_ / // _ \\ \033[0m / _ \\ / // __// _ \\___/ // // _ `// _ \\/ __/\n" + "\033[0;31m/_/ /____/(_)/_/ \\___/ \033[0m/_//_//_/ \\__//_//_/ /_//_/ \\_, //_//_/\\__/\n" + " /___/ \n"; + printf("%s", cli_banner); + printf("\n"); +} + +static void _usage(int exitCode) { + printf( + "Usage: daemon [--port port] [--daemon] [--capacity objectStoreSize] " + "[--log facility=level] [--log-file filename] [--config file]\n"); + printf("\n"); + printf( + "hicn-light run as a daemon is the program to launch the forwarder, " + "either as a console program\n"); + printf( + "or a background daemon (detatched from console). Once running, use the " + "program controller to\n"); + printf("configure hicn-light.\n"); + printf("\n"); + printf( + "The configuration file contains configuration lines as per " + "controller\n"); + printf( + "If logging level or content store capacity is set in the configuraiton " + "file, it overrides the command_line\n"); + printf( + "When a configuration file is specified, no default listeners on 'port' " + "are setup. Only 'add listener' lines\n"); + printf("in the configuration file matter.\n"); + printf("\n"); + printf( + "If no configuration file is specified, daemon will listen on TCP and " + "UDP ports specified by\n"); + printf( + "the --port flag (or default port). It will listen on both IPv4 and " + "IPv6 if available.\n"); + printf("\n"); + printf("Options:\n"); + printf("--port = tcp port for in-bound connections\n"); + printf("--daemon = start as daemon process\n"); + printf("--objectStoreSize = maximum number of content objects to cache\n"); + printf( + "--log = sets a facility to a given log level. You can have " + "multiple of these.\n"); + printf( + " facilities: all, config, core, io, message, " + "processor\n"); + printf( + " levels: debug, info, notice, warning, error, " + "critical, alert, off\n"); + printf(" example: daemon --log io=debug --log core=off\n"); + printf( + "--log-file = file to write log messages to (required in daemon " + "mode)\n"); + printf("--config = configuration filename\n"); + printf("\n"); + exit(exitCode); +} + +static void _setLogLevelToLevel(int logLevelArray[LoggerFacility_END], + LoggerFacility facility, + const char *levelString) { + PARCLogLevel level = parcLogLevel_FromString(levelString); + + if (level < PARCLogLevel_All) { + // we have a good facility and level + logLevelArray[facility] = level; + } else { + printf("Invalid log level string %s\n", levelString); + _usage(EXIT_FAILURE); + } +} + +/** + * string: "facility=level" + * Set the right thing in the logger + */ +static void _setLogLevel(int logLevelArray[LoggerFacility_END], + const char *string) { + char *tofree = parcMemory_StringDuplicate(string, strlen(string)); + char *p = tofree; + + char *facilityString = strsep(&p, "="); + if (facilityString) { + char *levelString = p; + + if (strcasecmp(facilityString, "all") == 0) { + for (LoggerFacility facility = 0; facility < LoggerFacility_END; + facility++) { + _setLogLevelToLevel(logLevelArray, facility, levelString); + } + } else { + LoggerFacility facility; + for (facility = 0; facility < LoggerFacility_END; facility++) { + if (strcasecmp(facilityString, logger_FacilityString(facility)) == 0) { + break; + } + } + + if (facility < LoggerFacility_END) { + _setLogLevelToLevel(logLevelArray, facility, levelString); + } else { + printf("Invalid facility string %s\n", facilityString); + _usage(EXIT_FAILURE); + } + } + } + + parcMemory_Deallocate((void **)&tofree); +} + +static void _daemonize(void) { + if (getppid() == 1) { + // already a daemon + return; + } + + int forkReturn = fork(); + parcTrapUnexpectedStateIf(forkReturn < 0, "Fork error"); + + if (forkReturn > 0) { + // parent exits + exit(EXIT_SUCCESS); + } + + // Child daemon detaches + printf("child continuing, pid = %u\n", getpid()); + + // get a new process group independent from old parent + setsid(); + + /* close all descriptors */ + for (int i = getdtablesize(); i >= 0; --i) { + close(i); + } + + // reset errno because it might be seg to EBADF from the close calls above + errno = 0; + + // Redirect stdin and stdout and stderr to /dev/null + const char *devnull = "/dev/null"; + int nullfile = open(devnull, O_RDWR); + parcAssertTrue(nullfile >= 0, "Error opening file '%s': (%d) %s", devnull, + errno, strerror(errno)); + + int ret; + ret = dup(nullfile); + parcAssertTrue(ret == 1, "Error duping fd 1 got %d file: (%d) %s", ret, errno, + strerror(errno)); + ret = dup(nullfile); + parcAssertTrue(ret == 2, "Error duping fd 2, got %d file: (%d) %s", ret, + errno, strerror(errno)); + + // forwarder will capture signals +} + +static Logger *_createLogfile(const char *logfile) { + int logfd = open(logfile, O_WRONLY | O_APPEND | O_CREAT, S_IWUSR | S_IRUSR); + if (logfd < 0) { + fprintf(stderr, "Error opening %s for writing: (%d) %s\n", logfile, errno, + strerror(errno)); + exit(EXIT_FAILURE); + } + + chmod(logfile, S_IRWXU); + + PARCFileOutputStream *fos = parcFileOutputStream_Create(logfd); + PARCOutputStream *pos = parcFileOutputStream_AsOutputStream(fos); + PARCLogReporter *reporter = parcLogReporterFile_Create(pos); + + Logger *logger = logger_Create(reporter, parcClock_Wallclock()); + + parcOutputStream_Release(&pos); + parcLogReporter_Release(&reporter); + + return logger; +} + +int main(int argc, const char *argv[]) { + _displayForwarderLogo(); + + uint16_t port = PORT_NUMBER; + uint16_t configurationPort = 2001; + bool daemon = false; + int capacity = -1; + const char *configFileName = NULL; + + char *logfile = NULL; + + if (argc == 2 && strcasecmp(argv[1], "-h") == 0) { + _usage(EXIT_SUCCESS); + } + + int logLevelArray[LoggerFacility_END]; + for (int i = 0; i < LoggerFacility_END; i++) { + logLevelArray[i] = -1; + } + + for (int i = 0; i < argc; i++) { + if (argv[i][0] == '-') { + if (strcmp(argv[i], "--config") == 0) { + configFileName = argv[i + 1]; + i++; + } else if (strcmp(argv[i], "--port") == 0) { + port = atoi(argv[i + 1]); + i++; + } else if (strcmp(argv[i], "--daemon") == 0) { + daemon = true; + } else if (strcmp(argv[i], "--capacity") == 0 || + strcmp(argv[i], "-c") == 0) { + capacity = atoi(argv[i + 1]); + i++; + } else if (strcmp(argv[i], "--log") == 0) { + _setLogLevel(logLevelArray, argv[i + 1]); + i++; + } else if (strcmp(argv[i], "--log-file") == 0) { + if (logfile) { + // error cannot repeat + fprintf(stderr, "Cannot specify --log-file more than once\n"); + _usage(EXIT_FAILURE); + } + + logfile = parcMemory_StringDuplicate(argv[i + 1], strlen(argv[i + 1])); + i++; + } else { + _usage(EXIT_FAILURE); + } + } + } + + // set restrictive umask, in case we create any files + umask(027); + + if (daemon && (logfile == NULL)) { + fprintf(stderr, "Must specify a logfile when running in daemon mode\n"); + _usage(EXIT_FAILURE); + } + + if (daemon) { + // inside this call, parent will EXIT_SUCCESS and child will continue + _daemonize(); + } + + Logger *logger = NULL; + if (logfile) { + logger = _createLogfile(logfile); + parcMemory_Deallocate((void **)&logfile); + } else { + PARCLogReporter *stdoutReporter = parcLogReporterTextStdout_Create(); + logger = logger_Create(stdoutReporter, parcClock_Wallclock()); + parcLogReporter_Release(&stdoutReporter); + } + + for (int i = 0; i < LoggerFacility_END; i++) { + if (logLevelArray[i] > -1) { + logger_SetLogLevel(logger, i, logLevelArray[i]); + } + } + + // this will update the clock to the tick clock + Forwarder *forwarder = forwarder_Create(logger); + + Configuration *configuration = forwarder_GetConfiguration(forwarder); + + if (capacity > -1) { + configuration_SetObjectStoreSize(configuration, capacity); + } + + if (configFileName) { + forwarder_SetupAllListeners(forwarder, port, NULL); + forwarder_SetupFromConfigFile(forwarder, configFileName); + } else { + // NULL to not setup AF_UNIX + forwarder_SetupAllListeners(forwarder, port, NULL); + } + + Dispatcher *dispatcher = forwarder_GetDispatcher(forwarder); + + logger_Log(logger, LoggerFacility_Core, PARCLogLevel_Alert, "daemon", + "hicn-light running port %d configuration-port %d", port, + configurationPort); + + dispatcher_Run(dispatcher); + + logger_Log(logger, LoggerFacility_Core, PARCLogLevel_Alert, "daemon", + "hicn-light exiting port %d", port); + + forwarder_Destroy(&forwarder); + + sleep(2); + + logger_Release(&logger); + return 0; +} diff --git a/hicn-light/src/config.h.in b/hicn-light/src/config.h.in new file mode 100755 index 000000000..16ec1ab3a --- /dev/null +++ b/hicn-light/src/config.h.in @@ -0,0 +1,5 @@ +/* CPU Cache line size */ +#define LEVEL1_DCACHE_LINESIZE @LEVEL1_DCACHE_LINESIZE@ + +#define _GNU_SOURCE + diff --git a/hicn-light/src/config/CMakeLists.txt b/hicn-light/src/config/CMakeLists.txt new file mode 100755 index 000000000..5ce680bfc --- /dev/null +++ b/hicn-light/src/config/CMakeLists.txt @@ -0,0 +1,97 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/commandOps.h + ${CMAKE_CURRENT_SOURCE_DIR}/commandParser.h + ${CMAKE_CURRENT_SOURCE_DIR}/configuration.h + ${CMAKE_CURRENT_SOURCE_DIR}/commandReturn.h + ${CMAKE_CURRENT_SOURCE_DIR}/symbolicNameTable.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlState.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlRoot.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlAddConnection.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlAdd.h + ${CMAKE_CURRENT_SOURCE_DIR}/configurationFile.h + ${CMAKE_CURRENT_SOURCE_DIR}/configurationListeners.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlAddRoute.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlAddListener.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlListConnections.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlList.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlListListeners.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlListRoutes.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlQuit.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlRemove.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlRemoveConnection.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlRemoveRoute.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlSet.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlUnset.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlSetDebug.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlUnsetDebug.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlMapMe.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlMapMeEnable.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlMapMeDiscovery.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlMapMeTimescale.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlCacheServe.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlCacheStore.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlCacheClear.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlCache.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlSetStrategy.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlSetWldr.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlAddPunting.h + ${CMAKE_CURRENT_SOURCE_DIR}/controlRemovePunting.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/commandOps.c + ${CMAKE_CURRENT_SOURCE_DIR}/commandParser.c + ${CMAKE_CURRENT_SOURCE_DIR}/configuration.c + ${CMAKE_CURRENT_SOURCE_DIR}/configurationFile.c + ${CMAKE_CURRENT_SOURCE_DIR}/configurationListeners.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlState.c + ${CMAKE_CURRENT_SOURCE_DIR}/symbolicNameTable.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlAdd.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlAddConnection.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlAddRoute.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlAddListener.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlList.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlListConnections.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlListListeners.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlListRoutes.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlQuit.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlRemove.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlRemoveConnection.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlRemoveRoute.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlRoot.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlSet.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlSetDebug.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlUnset.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlUnsetDebug.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlMapMe.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlMapMeEnable.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlMapMeDiscovery.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlMapMeTimescale.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlMapMeRetx.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlCacheServe.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlCacheStore.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlCacheClear.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlCache.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlSetStrategy.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlSetWldr.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlAddPunting.c + ${CMAKE_CURRENT_SOURCE_DIR}/controlRemovePunting.c +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE)
\ No newline at end of file diff --git a/hicn-light/src/config/commandOps.c b/hicn-light/src/config/commandOps.c new file mode 100755 index 000000000..027c86e0a --- /dev/null +++ b/hicn-light/src/config/commandOps.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/assert/parc_Assert.h> +#include <string.h> + +#ifndef __ANDROID__ +#ifdef HAVE_ERRNO_H +#include <errno.h> +#else +extern int errno; +#endif +#endif + +#include <parc/algol/parc_Memory.h> + +#include <src/config/commandOps.h> +#include <src/config/commandParser.h> + +CommandOps *commandOps_Create(void *closure, const char *command, + void (*init)(CommandParser *parser, + CommandOps *ops), + CommandReturn (*execute)(CommandParser *parser, + CommandOps *ops, + PARCList *args), + void (*destroyer)(CommandOps **opsPtr)) { + parcAssertNotNull(command, "Parameter command must be non-null"); + parcAssertNotNull(execute, "Parameter execute must be non-null"); + CommandOps *ops = parcMemory_AllocateAndClear(sizeof(CommandOps)); + parcAssertNotNull(ops, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(CommandOps)); + + ops->closure = closure; + ops->command = parcMemory_StringDuplicate(command, strlen(command) + 1); + ops->init = init; + ops->execute = execute; + ops->destroyer = destroyer; + return ops; +} + +void commandOps_Destroy(CommandOps **opsPtr) { + parcAssertNotNull(opsPtr, "Parameter opsPtr must be non-null"); + parcAssertNotNull(*opsPtr, + "Parameter opsPtr must dereference to non-null pointer"); + + CommandOps *ops = *opsPtr; + parcMemory_Deallocate((void **)&(ops->command)); + // DO NOT call ops->destroyer, we are one! + parcMemory_Deallocate((void **)&ops); + + *opsPtr = NULL; +} diff --git a/hicn-light/src/config/commandOps.h b/hicn-light/src/config/commandOps.h new file mode 100755 index 000000000..6428a3ebf --- /dev/null +++ b/hicn-light/src/config/commandOps.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file command_Ops.h + * @brief The function structure defining a CLI command + * + * The function structure that defines a CLI command. Each command will return + * one of these which defines how to run the command. + * + */ + +#ifndef command_Ops_h +#define command_Ops_h + +#include <parc/algol/parc_List.h> + +#include <src/config/commandReturn.h> + +// forward reference +struct command_parser; + +struct command_ops; +typedef struct command_ops CommandOps; + +/** + * @typedef CommandOps + * @abstract Each command implements a CommandOps + * @constant closure is a user-specified pointer for any state the user needs + * @constant command The text string of the command, must be the spelled out + * string, e.g. "help list routes" + * @constant init A function to call to initialize the command at program + * startup + * @constant execute A function to call to execute the command + * @constant destroyer A function to call to release the command + * @discussion + * Typically, the root of the thee has an Init function that then initilizes + * the rest of the tree. For example: + * + * @code + * const CommandOps control_Root = { + * .closure = NULL, + * .command = "", // empty string for root + * .init = control_Root_Init, + * .execute = control_Root_Execute + * .destroyer = NULL + * }; + * @endcode + * + * The control_Root_Init function will then begin adding the subtree under root. + * For example: + * + * @code + * const CommandOps control_Add = { + * .closure = NULL, + * .command = "add", + * .init = control_Add_Init, + * .execute = control_Add_Execute, + * .destroyer = NULL + * }; + * + * static void + * control_Root_Init(ControlState *state, CommandOps *ops) + * { + * controlState_RegisterCommand(state, &control_Add); + * } + * @endcode + */ +struct command_ops { + void *closure; + char *command; + void (*init)(struct command_parser *parser, CommandOps *ops); + CommandReturn (*execute)(struct command_parser *parser, CommandOps *ops, + PARCList *args); + void (*destroyer)(CommandOps **opsPtr); +}; + +/** + * A helper function to create the pubically defined CommandOps. + * + * Retruns allocated memory of the command + * + * @param [in] command The string is copied + * + * @retval <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +CommandOps *commandOps_Create( + void *closure, const char *command, + void (*init)(struct command_parser *parser, CommandOps *ops), + CommandReturn (*execute)(struct command_parser *parser, CommandOps *ops, + PARCList *args), + void (*destroyer)(CommandOps **opsPtr)); + +/** + * De-allocates the memory of the CommandOps and the copied command string + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @retval <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +void commandOps_Destroy(CommandOps **opsPtr); +#endif // command_Ops_h diff --git a/hicn-light/src/config/commandParser.c b/hicn-light/src/config/commandParser.c new file mode 100755 index 000000000..84d273c9d --- /dev/null +++ b/hicn-light/src/config/commandParser.c @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/assert/parc_Assert.h> +#include <string.h> + +#include <parc/security/parc_Security.h> + +#include <parc/algol/parc_List.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Time.h> +#include <parc/algol/parc_TreeRedBlack.h> + +#include <src/config/commandParser.h> + +#ifndef __ANDROID__ +#ifdef HAVE_ERRNO_H +#include <errno.h> +#else +extern int errno; +#endif +#endif + +struct command_parser { + // key = command, value = CommandOps + PARCTreeRedBlack *commandTree; + bool debugFlag; +}; + +static int _stringCompare(const void *key1, const void *key2) { + return strcasecmp((const char *)key1, (const char *)key2); +} + +CommandParser *commandParser_Create(void) { + CommandParser *state = parcMemory_AllocateAndClear(sizeof(CommandParser)); + parcAssertNotNull(state, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(CommandParser)); + + state->commandTree = parcTreeRedBlack_Create(_stringCompare, // key compare + NULL, // key free + NULL, // key copy + NULL, // value equals + NULL, // value free + NULL // value copy + ); + state->debugFlag = false; + return state; +} + +void commandParser_Destroy(CommandParser **parserPtr) { + CommandParser *parser = *parserPtr; + + // destroy every element if it has a destroyer + PARCArrayList *values = parcTreeRedBlack_Values(parser->commandTree); + if (values) { + for (int i = 0; i < parcArrayList_Size(values); i++) { + CommandOps *ops = parcArrayList_Get(values, i); + parcTreeRedBlack_Remove(parser->commandTree, ops->command); + if (ops->destroyer) { + ops->destroyer(&ops); + } + } + parcArrayList_Destroy(&values); + } + + parcTreeRedBlack_Destroy(&parser->commandTree); + + parcMemory_Deallocate((void **)&parser); + *parserPtr = NULL; +} + +void commandParser_SetDebug(CommandParser *state, bool debugFlag) { + state->debugFlag = debugFlag; +} + +bool commandParser_GetDebug(CommandParser *state) { return state->debugFlag; } + +void commandParser_RegisterCommand(CommandParser *state, CommandOps *ops) { + parcAssertNotNull(state, "Parameter state must be non-null"); + parcAssertNotNull(ops, "Parameter ops must be non-null"); + parcAssertNotNull(ops->command, "Operation command string must be non-null"); + + void *exists = parcTreeRedBlack_Get(state->commandTree, ops->command); + parcAssertNull(exists, "Command '%s' already exists in the tree %p\n", + ops->command, (void *)exists); + + parcTreeRedBlack_Insert(state->commandTree, (void *)ops->command, + (void *)ops); + + // if the command being registered asked for an init function to be called, + // call it + if (ops->init != NULL) { + ops->init(state, ops); + } +} + +static PARCList *parseStringIntoTokens(const char *originalString) { + PARCList *list = + parcList(parcArrayList_Create(parcArrayList_StdlibFreeFunction), + PARCArrayListAsPARCList); + + char *token; + + char *tofree = + parcMemory_StringDuplicate(originalString, strlen(originalString) + 1); + char *string = tofree; + + while ((token = strsep(&string, " \t\n")) != NULL) { + if (strlen(token) > 0) { + parcList_Add(list, strdup(token)); + } + } + + parcMemory_Deallocate((void **)&tofree); + + return list; +} + +/** + * Matches the user arguments to available commands, returning the command or + * NULL if not found + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +static CommandOps *commandParser_MatchCommand(CommandParser *state, + PARCList *args) { + // Find the longest matching prefix command. + // Pretty wildly inefficient + + size_t longest_token_count = 0; + char *longest_command = NULL; + + PARCArrayList *commands = parcTreeRedBlack_Keys(state->commandTree); + for (int i = 0; i < parcArrayList_Size(commands); i++) { + char *command = parcArrayList_Get(commands, i); + PARCList *command_tokens = parseStringIntoTokens(command); + + // is it a prefix match? + if (parcList_Size(args) >= parcList_Size(command_tokens)) { + bool possible_match = true; + for (int i = 0; i < parcList_Size(command_tokens) && possible_match; + i++) { + const char *a = parcList_GetAtIndex(command_tokens, i); + const char *b = parcList_GetAtIndex(args, i); + if (strncasecmp(a, b, strlen(a) + 1) != 0) { + possible_match = false; + } + } + + if (possible_match && + parcList_Size(command_tokens) > longest_token_count) { + longest_token_count = parcList_Size(command_tokens); + longest_command = command; + } + } + + parcList_Release(&command_tokens); + } + + parcArrayList_Destroy(&commands); + + if (longest_token_count == 0) { + return NULL; + } else { + CommandOps *ops = parcTreeRedBlack_Get(state->commandTree, longest_command); + parcAssertNotNull(ops, "Got null operations for command '%s'\n", + longest_command); + return ops; + } +} + +CommandReturn commandParser_DispatchCommand(CommandParser *state, + PARCList *args) { + CommandOps *ops = commandParser_MatchCommand(state, args); + + if (ops == NULL) { + printf("Command not found.\n"); + return CommandReturn_Failure; + } else { + return ops->execute(state, ops, args); + } +} + +bool commandParser_ContainsCommand(CommandParser *parser, const char *command) { + CommandOps *ops = parcTreeRedBlack_Get(parser->commandTree, command); + return (ops != NULL); +} diff --git a/hicn-light/src/config/commandParser.h b/hicn-light/src/config/commandParser.h new file mode 100755 index 000000000..78e19e6e3 --- /dev/null +++ b/hicn-light/src/config/commandParser.h @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file command_Parser.h + * @brief Creates a dictionary of commands and parses a command_line to match + * against them + * + * A user creates individual CommandParserEntry that map a command_line to a + * function to execute. The CommandParser then does a longest-matching prefix + * match of a command_line to the dictionary of commands and executes the + * appropriate command. + * + */ + +#ifndef command_parser_h +#define command_parser_h + +#include <src/config/commandOps.h> +#include <src/config/commandReturn.h> + +struct command_parser; +typedef struct command_parser CommandParser; + +/** + * controlState_Create + * + * Creates the global state for the Control program + * + * @return non-null A command parser + * + * Example: + * @code + * <#example#> + * @endcode + */ +CommandParser *commandParser_Create(void); + +/** + * Destroys the control state, closing all network connections + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +void commandParser_Destroy(CommandParser **statePtr); + +/** + * Registers a CommandOps with the system. + * + * Each command has its complete command prefix in the "command" field. + * RegisterCommand will put these command prefixes in to a tree and then match + * what a user types against the longest-matching prefix in the tree. If + * there's a match, it will call the "execute" function. + * + * When the parser is destroyed, each command's destroyer function will be + * called. + * + * @param [in] state An allocated ControlState + * @param [in] command The command to register with the system + * + * Example: + * @code + * static ControlReturn + * control_Root_Execute(CommandParser *parser, CommandOps *ops, PARCList + * *args) + * { + * printf("Root Command\n"); + * return CommandReturn_Success; + * } + * + * static ControlReturn + * control_FooBar_Execute(CommandParser *parser, CommandOps *ops, PARCList + * *args) + * { + * printf("Foo Bar Command\n"); + * return CommandReturn_Success; + * } + * + * const CommandOps control_Root = { + * .closure = NULL, + * .command = "", // empty string for root + * .init = NULL, + * .execute = control_Root_Execute + * }; + * + * const CommandOps control_FooBar = { + * .closure = NULL, + * .command = "foo bar", // empty string for root + * .init = NULL, + * .execute = control_FooBar_Execute + * }; + * + * void startup(void) + * { + * ControlState *state = controlState_Create("happy", "day"); + * controlState_RegisterCommand(state, control_FooBar); + * controlState_RegisterCommand(state, control_Root); + * + * // this executes "root" + * controlState_DispatchCommand(state, "foo"); + * controlState_Destroy(&state); + * } + * @endcode + */ +void commandParser_RegisterCommand(CommandParser *state, CommandOps *command); + +/** + * Performs a longest-matching prefix of the args to the command tree + * + * The command tree is created with controlState_RegisterCommand. + * + * @param [in] state The allocated ControlState + * @param [in] args Each command_line word parsed to the ordered list + * + * @return CommandReturn_Success the command was successful + * @return CommandReturn_Failure the command failed or was not found + * @return CommandReturn_Exit the command indicates that the interactive mode + * should exit + * + * Example: + * @code + * <#example#> + * @endcode + */ +CommandReturn commandParser_DispatchCommand(CommandParser *state, + PARCList *args); + +/** + * Sets the Debug mode, which will print out much more information. + * + * Prints out much more diagnostic information about what hicn-light controller + * is doing. yes, you would make a CommandOps to set and unset this :) + * + * @param [in] debugFlag true means to print debug info, false means to turn it + * off + * + * Example: + * @code + * <#example#> + * @endcode + */ +void commandParser_SetDebug(CommandParser *state, bool debugFlag); + +/** + * Returns the debug state of ControlState + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool commandParser_GetDebug(CommandParser *state); + +/** + * Checks if the command is registered + * + * Checks if the exact command given is registered. This is not a prefix match. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return true The command is registered + * @return false The command is not registered + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool commandParser_ContainsCommand(CommandParser *parser, const char *command); +#endif // command_parser_h diff --git a/hicn-light/src/config/commandReturn.h b/hicn-light/src/config/commandReturn.h new file mode 100755 index 000000000..16ee93db1 --- /dev/null +++ b/hicn-light/src/config/commandReturn.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file command_Return.h + * @brief The return code used by CLI commands + * + * This return code is used throughout the command parser and command + * implementations to indicate success, failure, or if the program should exit. + * + */ + +#ifndef command_return_h +#define command_return_h + +/** + * @typedef ControlReturn + * @abstract A command returns one of (SUCCESS, FAILURE, EXIT) + * @constant SUCCESS means the command succeeded + * @constant FAILURE indicates failure + * @constant EXIT means the command indicated that hicn-light controller should + * exit. + * @discussion <#Discussion#> + */ +typedef enum command_return { + CommandReturn_Success, // command returned success + CommandReturn_Failure, // command failure + CommandReturn_Exit // command indicates program should exit +} CommandReturn; + +#endif // command_return_h diff --git a/hicn-light/src/config/configuration.c b/hicn-light/src/config/configuration.c new file mode 100755 index 000000000..cccd60620 --- /dev/null +++ b/hicn-light/src/config/configuration.c @@ -0,0 +1,1076 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * + * Example: + * @code + * <#example#> + * @endcode + */ +#include <arpa/inet.h> +#include <ctype.h> +#include <parc/assert/parc_Assert.h> +#include <src/config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <parc/algol/parc_HashMap.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_String.h> + +#include <src/config/configurationListeners.h> +#include <src/config/symbolicNameTable.h> + +#include <src/core/connection.h> +#include <src/core/connectionTable.h> +#include <src/core/forwarder.h> +#include <src/core/system.h> +#ifdef WITH_MAPME +#include <src/core/mapMe.h> +#endif /* WITH_MAPME */ + +#include <src/io/streamConnection.h> + +#include <src/io/hicnTunnel.h> +#include <src/io/tcpTunnel.h> +#include <src/io/udpTunnel.h> + +#include <parc/algol/parc_Unsigned.h> +#include <src/io/listener.h> //the listener list +#include <src/io/listenerSet.h> // needed to print +#include <src/utils/commands.h> +#include <src/utils/utils.h> + +#include <src/utils/address.h> + +#define ETHERTYPE 0x0801 + +struct configuration { + Forwarder *forwarder; + Logger *logger; + + size_t maximumContentObjectStoreSize; + + // map from prefix (parcString) to strategy (parcString) + PARCHashMap *strategy_map; + + // translates between a symblic name and a connection id + SymbolicNameTable *symbolicNameTable; +}; + +// ======================================================================================== + +Configuration *configuration_Create(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter hicn-fwd must be non-null"); + Configuration *config = parcMemory_AllocateAndClear(sizeof(Configuration)); + parcAssertNotNull(config, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Configuration)); + config->forwarder = forwarder; + config->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + config->maximumContentObjectStoreSize = 100000; + config->strategy_map = parcHashMap_Create(); + config->symbolicNameTable = symbolicNameTable_Create(); + + return config; +} + +void configuration_Destroy(Configuration **configPtr) { + parcAssertNotNull(configPtr, "Parameter must be non-null double poitner"); + parcAssertNotNull(*configPtr, + "Parameter must dereference to non-null pointer"); + + Configuration *config = *configPtr; + logger_Release(&config->logger); + parcHashMap_Release(&(config->strategy_map)); + symbolicNameTable_Destroy(&config->symbolicNameTable); + parcMemory_Deallocate((void **)&config); + *configPtr = NULL; +} + +struct iovec *configuration_ProcessRegisterHIcnPrefix(Configuration *config, + struct iovec *request, + unsigned ingressId) { + header_control_message *header = request[0].iov_base; + add_route_command *control = request[1].iov_base; + + bool success = false; + + const char *symbolicOrConnid = control->symbolicOrConnid; + + if (strcmp(symbolicOrConnid, "SELF_ROUTE") == 0) { + success = forwarder_AddOrUpdateRoute(config->forwarder, control, ingressId); + } else if (utils_IsNumber(symbolicOrConnid)) { + // case for connid as input + unsigned connid = (unsigned)strtold(symbolicOrConnid, NULL); + ConnectionTable *table = forwarder_GetConnectionTable(config->forwarder); + + // check if iconnID present in the fwd table + if (connectionTable_FindById(table, connid)) { + success = forwarder_AddOrUpdateRoute(config->forwarder, control, connid); + } else { + logger_Log(forwarder_GetLogger(config->forwarder), LoggerFacility_IO, + PARCLogLevel_Error, __func__, + "ConnID not found, check list connections"); + // failure + } + + } else { + // case for symbolic as input: check if symbolic name can be resolved + unsigned connid = + symbolicNameTable_Get(config->symbolicNameTable, symbolicOrConnid); + // connid = UINT_MAX when symbolicName is not found + if (connid != UINT32_MAX) { + if (logger_IsLoggable(config->logger, LoggerFacility_Config, + PARCLogLevel_Debug)) { + logger_Log(config->logger, LoggerFacility_Config, PARCLogLevel_Debug, + __func__, "Add route resolve name '%s' to connid %u", + symbolicOrConnid, connid); + } + + success = forwarder_AddOrUpdateRoute(config->forwarder, control, connid); + + } else { + if (logger_IsLoggable(config->logger, LoggerFacility_Config, + PARCLogLevel_Warning)) { + logger_Log(config->logger, LoggerFacility_Config, PARCLogLevel_Warning, + __func__, + "Add route symbolic name '%s' could not be resolved", + symbolicOrConnid); + } + // failure + } + } + + // generate ACK/NACK + struct iovec *response; + + if (success) { // ACK + response = utils_CreateAck(header, control, sizeof(add_route_command)); + } else { // NACK + response = utils_CreateNack(header, control, sizeof(add_route_command)); + } + + return response; +} + +struct iovec *configuration_ProcessUnregisterHIcnPrefix(Configuration *config, + struct iovec *request) { + header_control_message *header = request[0].iov_base; + remove_route_command *control = request[1].iov_base; + + bool success = false; + + const char *symbolicOrConnid = control->symbolicOrConnid; + + if (utils_IsNumber(symbolicOrConnid)) { + // case for connid as input + unsigned connid = (unsigned)strtold(symbolicOrConnid, NULL); + ConnectionTable *table = forwarder_GetConnectionTable(config->forwarder); + + // check if interface index present in the fwd table + if (connectionTable_FindById(table, connid)) { + success = forwarder_RemoveRoute(config->forwarder, control, connid); + } else { + logger_Log(forwarder_GetLogger(config->forwarder), LoggerFacility_IO, + PARCLogLevel_Error, __func__, + "ConnID not found, check list connections"); + // failure + } + + } else { + // case for symbolic as input: chech if symbolic name can be resolved + unsigned connid = + symbolicNameTable_Get(config->symbolicNameTable, symbolicOrConnid); + // connid = UINT_MAX when symbolicName is not found + if (connid != UINT32_MAX) { + if (logger_IsLoggable(config->logger, LoggerFacility_Config, + PARCLogLevel_Debug)) { + logger_Log(config->logger, LoggerFacility_Config, PARCLogLevel_Debug, + __func__, "Remove route resolve name '%s' to connid %u", + symbolicOrConnid, connid); + } + success = forwarder_RemoveRoute(config->forwarder, control, connid); + } else { + if (logger_IsLoggable(config->logger, LoggerFacility_Config, + PARCLogLevel_Warning)) { + logger_Log(config->logger, LoggerFacility_Config, PARCLogLevel_Warning, + __func__, + "Remove route symbolic name '%s' could not be resolved", + symbolicOrConnid); + } + // failure + } + } + + // generate ACK/NACK + struct iovec *response; + + if (success) { // ACK + response = utils_CreateAck(header, control, sizeof(remove_route_command)); + } else { // NACK + response = utils_CreateNack(header, control, sizeof(remove_route_command)); + } + + return response; +} + +struct iovec *configuration_ProcessRegistrationList(Configuration *config, + struct iovec *request) { + FibEntryList *fibList = forwarder_GetFibEntries(config->forwarder); + + size_t payloadSize = fibEntryList_Length(fibList); + size_t pointerLocation = 0; + struct sockaddr_in tmpAddr; + struct sockaddr_in6 tmpAddr6; + + // allocate payload, cast from void* to uint8_t* = bytes granularity + uint8_t *payloadResponse = + parcMemory_AllocateAndClear(sizeof(list_routes_command) * payloadSize); + + for (size_t i = 0; i < fibEntryList_Length(fibList); i++) { + FibEntry *entry = (FibEntry *)fibEntryList_Get(fibList, i); + NameBitvector *prefix = name_GetContentName(fibEntry_GetPrefix(entry)); + const NumberSet *nexthops = fibEntry_GetNexthops(entry); + + if (numberSet_Length(nexthops) > 1) { + // payload extended, need reallocate, further entries via nexthops + payloadSize = payloadSize + numberSet_Length(nexthops) - 1; + payloadResponse = (uint8_t *)parcMemory_Reallocate( + payloadResponse, sizeof(list_routes_command) * payloadSize); + } + + for (size_t j = 0; j < numberSet_Length(nexthops); j++) { + list_routes_command *listRouteCommand = + (list_routes_command *)(payloadResponse + + (pointerLocation * + sizeof(list_routes_command))); + + Address *addressEntry = nameBitvector_ToAddress(prefix); + if (addressGetType(addressEntry) == ADDR_INET) { + addressGetInet(addressEntry, &tmpAddr); + listRouteCommand->addressType = ADDR_INET; + listRouteCommand->address.ipv4 = tmpAddr.sin_addr.s_addr; + } else if (addressGetType(addressEntry) == ADDR_INET6) { + addressGetInet6(addressEntry, &tmpAddr6); + listRouteCommand->addressType = ADDR_INET6; + listRouteCommand->address.ipv6 = tmpAddr6.sin6_addr; + } + listRouteCommand->connid = numberSet_GetItem(nexthops, j); + listRouteCommand->len = nameBitvector_GetLength(prefix); + listRouteCommand->cost = 1; // cost + + pointerLocation++; + addressDestroy(&addressEntry); + } + } + + // send response + header_control_message *header = request[0].iov_base; + header->messageType = RESPONSE_LIGHT; + header->length = payloadSize; + + struct iovec *response = + parcMemory_AllocateAndClear(sizeof(struct iovec) * 2); + + response[0].iov_base = header; + response[0].iov_len = sizeof(header_control_message); + response[1].iov_base = payloadResponse; + response[1].iov_len = sizeof(list_routes_command) * payloadSize; + + return response; +} + +static void configuration_SendResponse(Configuration *config, struct iovec *msg, + unsigned egressId) { + ConnectionTable *connectionTable = + forwarder_GetConnectionTable(config->forwarder); + const Connection *conn = connectionTable_FindById(connectionTable, egressId); + parcAssertNotNull(conn, + "Got null connection for control message we just received"); + + IoOperations *ops = connection_GetIoOperations(conn); + streamState_SendCommandResponse(ops, msg); +} + +struct iovec *configuration_ProcessCreateTunnel(Configuration *config, + struct iovec *request) { + header_control_message *header = request[0].iov_base; + add_connection_command *control = request[1].iov_base; + + bool success = false; + bool exists = true; + + const char *symbolicName = control->symbolic; + + Address *source = NULL; + Address *destination = NULL; + + if (!symbolicNameTable_Exists(config->symbolicNameTable, symbolicName)) { + if (control->ipType == ADDR_INET) { + source = + utils_AddressFromInet(&control->localIp.ipv4, &control->localPort); + destination = + utils_AddressFromInet(&control->remoteIp.ipv4, &control->remotePort); + } else if (control->ipType == ADDR_INET6) { + source = + utils_AddressFromInet6(&control->localIp.ipv6, &control->localPort); + destination = + utils_AddressFromInet6(&control->remoteIp.ipv6, &control->remotePort); + } else { + printf("Invalid IP type.\n"); // will generate a Nack + } + + AddressPair *pair = addressPair_Create(source, destination); + const Connection *conn = connectionTable_FindByAddressPair( + forwarder_GetConnectionTable(config->forwarder), pair); + + addressPair_Release(&pair); + + if (conn == NULL) { + // the connection does not exists (even without a name) + exists = false; + } + } + + if (!exists) { + IoOperations *ops = NULL; + switch (control->connectionType) { + case TCP_CONN: + // logger_Log(config->logger, LoggerFacility_Config, PARCLogLevel_Error, + // __func__, + // "Unsupported tunnel protocol: TCP"); + ops = tcpTunnel_Create(config->forwarder, source, destination); + break; + case UDP_CONN: + ops = udpTunnel_Create(config->forwarder, source, destination); + break; + case GRE_CONN: + logger_Log(config->logger, LoggerFacility_Config, PARCLogLevel_Error, + __func__, "Unsupported tunnel protocol: GRE"); + break; +#ifndef __APPLE__ + case HICN_CONN: + ops = hicnTunnel_Create(config->forwarder, source, destination); + break; +#endif /* __APPLE__ */ + default: + logger_Log(config->logger, LoggerFacility_Config, PARCLogLevel_Error, + __func__, "Unsupported tunnel protocol: %d", + control->connectionType); + break; + } + + if (ops != NULL) { + Connection *conn = connection_Create(ops); + + connectionTable_Add(forwarder_GetConnectionTable(config->forwarder), + conn); + symbolicNameTable_Add(config->symbolicNameTable, symbolicName, + connection_GetConnectionId(conn)); + + success = true; + + } else { + printf("failed, could not create IoOperations"); + } + + } else { + printf("failed, symbolic name or connextion already exist\n"); + } + + addressDestroy(&source); + addressDestroy(&destination); + + // generate ACK/NACK + struct iovec *response; + + if (success) { // ACK + response = utils_CreateAck(header, control, sizeof(add_connection_command)); + } else { // NACK + response = + utils_CreateNack(header, control, sizeof(add_connection_command)); + } + + return response; +} + +/** + * Add an IP-based tunnel. + * + * The call cal fail if the symbolic name is a duplicate. It could also fail if + * there's an problem creating the local side of the tunnel (i.e. the local + * socket address is not usable). + * + * @return true Tunnel added + * @return false Tunnel not added (an error) + */ + +struct iovec *configuration_ProcessRemoveTunnel(Configuration *config, + struct iovec *request) { + header_control_message *header = request[0].iov_base; + remove_connection_command *control = request[1].iov_base; + + bool success = false; + + const char *symbolicOrConnid = control->symbolicOrConnid; + ConnectionTable *table = forwarder_GetConnectionTable(config->forwarder); + + if (utils_IsNumber(symbolicOrConnid)) { + // case for connid as input + unsigned connid = (unsigned)strtold(symbolicOrConnid, NULL); + + // check if interface index present in the fwd table + //(it was missing and therefore caused a program crash) + if (connectionTable_FindById(table, connid)) { + // remove connection from the FIB + forwarder_RemoveConnectionIdFromRoutes(config->forwarder, connid); + // remove connection + connectionTable_RemoveById(table, connid); + + success = true; + } else { + logger_Log(forwarder_GetLogger(config->forwarder), LoggerFacility_IO, + PARCLogLevel_Error, __func__, + "ConnID not found, check list connections"); + // failure + } + + } else { + // case for symbolic as input + // chech if symbolic name can be resolved + unsigned connid = + symbolicNameTable_Get(config->symbolicNameTable, symbolicOrConnid); + // connid = UINT_MAX when symbolicName is not found + if (connid != UINT32_MAX) { + if (logger_IsLoggable(config->logger, LoggerFacility_Config, + PARCLogLevel_Debug)) { + logger_Log(config->logger, LoggerFacility_Config, PARCLogLevel_Debug, + __func__, "Remove connection resolve name '%s' to connid %u", + symbolicOrConnid, connid); + } + + // remove connection from the FIB + forwarder_RemoveConnectionIdFromRoutes(config->forwarder, connid); + // remove connection + connectionTable_RemoveById(table, connid); + // remove connection from symbolicNameTable since we have symbolic input + symbolicNameTable_Remove(config->symbolicNameTable, symbolicOrConnid); + success = true; // to write + } else { + if (logger_IsLoggable(config->logger, LoggerFacility_Config, + PARCLogLevel_Warning)) { + logger_Log(config->logger, LoggerFacility_Config, PARCLogLevel_Error, + __func__, + "Remove connection symbolic name '%s' could not be resolved", + symbolicOrConnid); + } + // failure + } + } + + // generate ACK/NACK + struct iovec *response; + + if (success) { // ACK + response = + utils_CreateAck(header, control, sizeof(remove_connection_command)); + } else { // NACK + response = + utils_CreateNack(header, control, sizeof(remove_connection_command)); + } + + return response; +} + +struct iovec *configuration_ProcessConnectionList(Configuration *config, + struct iovec *request) { + ConnectionTable *table = forwarder_GetConnectionTable(config->forwarder); + ConnectionList *connList = connectionTable_GetEntries(table); + struct sockaddr_in tmpAddr; + struct sockaddr_in6 tmpAddr6; + + // allocate payload, cast from void* to uint8_t* fot bytes granularity + uint8_t *payloadResponse = parcMemory_AllocateAndClear( + sizeof(list_connections_command) * connectionList_Length(connList)); + + for (size_t i = 0; i < connectionList_Length(connList); i++) { + // Don't release original, it is not stored + Connection *original = connectionList_Get(connList, i); + + const AddressPair *addressPair = connection_GetAddressPair(original); + Address *localAddress = addressCopy(addressPair_GetLocal(addressPair)); + Address *remoteAddress = addressCopy(addressPair_GetRemote(addressPair)); + + // Fill payload by shifting and casting at each 'i' step. + list_connections_command *listConnectionsCommand = + (list_connections_command *)(payloadResponse + + (i * sizeof(list_connections_command))); + + // set structure fields + listConnectionsCommand->connid = connection_GetConnectionId(original); + listConnectionsCommand->state = + connection_IsUp(original) ? IFACE_UP : IFACE_DOWN; + listConnectionsCommand->connectionData.connectionType = + ioOperations_GetConnectionType(connection_GetIoOperations(original)); + + if (addressGetType(localAddress) == ADDR_INET && + addressGetType(remoteAddress) == ADDR_INET) { + listConnectionsCommand->connectionData.ipType = ADDR_INET; + + // get local port/address + addressGetInet(localAddress, &tmpAddr); + listConnectionsCommand->connectionData.localPort = tmpAddr.sin_port; + listConnectionsCommand->connectionData.localIp.ipv4 = + tmpAddr.sin_addr.s_addr; + memset(&tmpAddr, 0, sizeof(tmpAddr)); + // get remote port/address + addressGetInet(remoteAddress, &tmpAddr); + listConnectionsCommand->connectionData.remotePort = tmpAddr.sin_port; + listConnectionsCommand->connectionData.remoteIp.ipv4 = + tmpAddr.sin_addr.s_addr; + + } else if (addressGetType(localAddress) == ADDR_INET6 && + addressGetType(remoteAddress) == ADDR_INET6) { + listConnectionsCommand->connectionData.ipType = ADDR_INET6; + + // get local port/address + addressGetInet6(localAddress, &tmpAddr6); + listConnectionsCommand->connectionData.localPort = tmpAddr6.sin6_port; + listConnectionsCommand->connectionData.localIp.ipv6 = tmpAddr6.sin6_addr; + memset(&tmpAddr6, 0, sizeof(tmpAddr6)); + // get remote port/address + addressGetInet6(remoteAddress, &tmpAddr6); + listConnectionsCommand->connectionData.remotePort = tmpAddr6.sin6_port; + listConnectionsCommand->connectionData.remoteIp.ipv6 = tmpAddr6.sin6_addr; + + } // no need further else, control on the addressed already done at the + // time of insertion in the connection table + addressDestroy(&localAddress); + addressDestroy(&remoteAddress); + } + + // send response + header_control_message *header = request[0].iov_base; + header->messageType = RESPONSE_LIGHT; + header->length = (uint16_t)connectionList_Length(connList); + + struct iovec *response = + parcMemory_AllocateAndClear(sizeof(struct iovec) * 2); + + response[0].iov_base = header; + response[0].iov_len = sizeof(header_control_message); + response[1].iov_base = payloadResponse; + response[1].iov_len = + sizeof(list_connections_command) * connectionList_Length(connList); + + return response; +} + +struct iovec *configuration_ProcessListenersList(Configuration *config, + struct iovec *request) { + ListenerSet *listenerList = forwarder_GetListenerSet(config->forwarder); + struct sockaddr_in tmpAddr; + struct sockaddr_in6 tmpAddr6; + + // allocate payload, cast from void* to uint8_t* fot bytes granularity + uint8_t *payloadResponse = parcMemory_AllocateAndClear( + sizeof(list_listeners_command) * listenerSet_Length(listenerList)); + + for (size_t i = 0; i < listenerSet_Length(listenerList); i++) { + ListenerOps *listenerEntry = listenerSet_Get(listenerList, i); + + // Fill payload by shifting and casting at each 'i' step. + list_listeners_command *listListenersCommand = + (list_listeners_command *)(payloadResponse + + (i * sizeof(list_listeners_command))); + + listListenersCommand->connid = + (uint32_t)listenerEntry->getInterfaceIndex(listenerEntry); + listListenersCommand->encapType = + (uint8_t)listenerEntry->getEncapType(listenerEntry); + if (addressGetType((const Address *)listenerEntry->getListenAddress( + listenerEntry)) == ADDR_INET) { + addressGetInet( + (const Address *)listenerEntry->getListenAddress(listenerEntry), + &tmpAddr); + listListenersCommand->addressType = ADDR_INET; + listListenersCommand->address.ipv4 = tmpAddr.sin_addr.s_addr; + listListenersCommand->port = tmpAddr.sin_port; + } else if (addressGetType((const Address *)listenerEntry->getListenAddress( + listenerEntry)) == ADDR_INET6) { + addressGetInet6( + (const Address *)listenerEntry->getListenAddress(listenerEntry), + &tmpAddr6); + listListenersCommand->addressType = ADDR_INET6; + listListenersCommand->address.ipv6 = tmpAddr6.sin6_addr; + listListenersCommand->port = tmpAddr6.sin6_port; + } + } + + // send response + header_control_message *header = request[0].iov_base; + header->messageType = RESPONSE_LIGHT; + header->length = (uint16_t)listenerSet_Length(listenerList); + + struct iovec *response = + parcMemory_AllocateAndClear(sizeof(struct iovec) * 2); + + response[0].iov_base = header; + response[0].iov_len = sizeof(header_control_message); + response[1].iov_base = payloadResponse; + response[1].iov_len = + sizeof(list_listeners_command) * listenerSet_Length(listenerList); + + return response; +} + +struct iovec *configuration_ProcessCacheStore(Configuration *config, + struct iovec *request) { + header_control_message *header = request[0].iov_base; + ; + cache_store_command *control = request[1].iov_base; + ; + + bool success = false; + + switch (control->activate) { + case ACTIVATE_ON: + forwarder_SetChacheStoreFlag(config->forwarder, true); + if (forwarder_GetChacheStoreFlag(config->forwarder)) { + success = true; + } + break; + + case ACTIVATE_OFF: + forwarder_SetChacheStoreFlag(config->forwarder, false); + if (!forwarder_GetChacheStoreFlag(config->forwarder)) { + success = true; + } + break; + + default: + break; + } + + struct iovec *response; + if (success) { // ACK + response = utils_CreateAck(header, control, sizeof(cache_store_command)); + } else { // NACK + response = utils_CreateNack(header, control, sizeof(cache_store_command)); + } + + return response; +} + +struct iovec *configuration_ProcessCacheServe(Configuration *config, + struct iovec *request) { + header_control_message *header = request[0].iov_base; + cache_serve_command *control = request[1].iov_base; + + bool success = false; + + switch (control->activate) { + case ACTIVATE_ON: + forwarder_SetChacheServeFlag(config->forwarder, true); + if (forwarder_GetChacheServeFlag(config->forwarder)) { + success = true; + } + break; + + case ACTIVATE_OFF: + forwarder_SetChacheServeFlag(config->forwarder, false); + if (!forwarder_GetChacheServeFlag(config->forwarder)) { + success = true; + } + break; + + default: + break; + } + + struct iovec *response; + if (success) { // ACK + response = utils_CreateAck(header, control, sizeof(cache_store_command)); + } else { // NACK + response = utils_CreateNack(header, control, sizeof(cache_store_command)); + } + + return response; +} + +struct iovec *configuration_ProcessCacheClear(Configuration *config, + struct iovec *request) { + header_control_message *header = request[0].iov_base; + + forwarder_ClearCache(config->forwarder); + + struct iovec *response = utils_CreateAck(header, NULL, 0); + return response; +} + +size_t configuration_GetObjectStoreSize(Configuration *config) { + return config->maximumContentObjectStoreSize; +} + +void _configuration_StoreFwdStrategy(Configuration *config, const char *prefix, + strategy_type strategy) { + PARCString *prefixStr = parcString_Create(prefix); + PARCUnsigned *strategyValue = parcUnsigned_Create((unsigned)strategy); + parcHashMap_Put(config->strategy_map, prefixStr, strategyValue); + parcUnsigned_Release(&strategyValue); + parcString_Release(&prefixStr); +} + +struct iovec *configuration_SetWldr(Configuration *config, + struct iovec *request) { + header_control_message *header = request[0].iov_base; + set_wldr_command *control = request[1].iov_base; + ConnectionTable *table = forwarder_GetConnectionTable(config->forwarder); + Connection *conn = NULL; + bool success = false; + + const char *symbolicOrConnid = control->symbolicOrConnid; + + if (utils_IsNumber(symbolicOrConnid)) { + // case for connid as input: check if connID present in the fwd table + conn = (Connection *)connectionTable_FindById( + table, (unsigned)strtold(symbolicOrConnid, NULL)); + if (conn) { + success = true; + } else { + logger_Log(forwarder_GetLogger(config->forwarder), LoggerFacility_IO, + PARCLogLevel_Error, __func__, + "ConnID not found, check list connections"); // failure + } + } else { + // case for symbolic as input: check if symbolic name can be resolved + unsigned connid = + symbolicNameTable_Get(config->symbolicNameTable, symbolicOrConnid); + if (connid != UINT32_MAX) { + conn = (Connection *)connectionTable_FindById(table, connid); + if (conn) { + if (logger_IsLoggable(config->logger, LoggerFacility_Config, + PARCLogLevel_Debug)) { + logger_Log(config->logger, LoggerFacility_Config, PARCLogLevel_Debug, + __func__, "Set wldr resolve name '%s' to connid %u", + symbolicOrConnid, connid); + } + success = true; + } + } else { + if (logger_IsLoggable(config->logger, LoggerFacility_Config, + PARCLogLevel_Warning)) { + logger_Log(config->logger, LoggerFacility_Config, PARCLogLevel_Error, + __func__, "Symbolic name '%s' could not be resolved", + symbolicOrConnid); + } // failure + } + } + + // generate ACK/NACK + struct iovec *response; + + if (success) { + switch (control->activate) { + case ACTIVATE_ON: + connection_EnableWldr(conn); + response = utils_CreateAck(header, control, sizeof(set_wldr_command)); + break; + + case ACTIVATE_OFF: + connection_DisableWldr(conn); + response = utils_CreateAck(header, control, sizeof(set_wldr_command)); + break; + + default: // received wrong value + response = utils_CreateNack(header, control, sizeof(set_wldr_command)); + break; + } + } else { + response = utils_CreateNack(header, control, sizeof(set_wldr_command)); + } + + return response; +} + +strategy_type configuration_GetForwardingStrategy(Configuration *config, + const char *prefix) { + PARCString *prefixStr = parcString_Create(prefix); + const unsigned *val = parcHashMap_Get(config->strategy_map, prefixStr); + parcString_Release(&prefixStr); + + if (val == NULL) { + return LAST_STRATEGY_VALUE; + } else { + return (strategy_type)*val; + } +} + +struct iovec *configuration_SetForwardingStrategy(Configuration *config, + struct iovec *request) { + header_control_message *header = request[0].iov_base; + set_strategy_command *control = request[1].iov_base; + + const char *prefix = utils_PrefixLenToString( + control->addressType, &control->address, &control->len); + strategy_type strategy = control->strategyType; + strategy_type existingFwdStrategy = + configuration_GetForwardingStrategy(config, prefix); + + if (existingFwdStrategy == LAST_STRATEGY_VALUE || + strategy != existingFwdStrategy) { + // means such a new strategy is not present in the hash table or has to be + // updated + _configuration_StoreFwdStrategy(config, prefix, strategy); + Name *hicnPrefix = name_CreateFromAddress(control->addressType, + control->address, control->len); + forwarder_SetStrategy(config->forwarder, hicnPrefix, strategy); + name_Release(&hicnPrefix); + } + + struct iovec *response = + utils_CreateAck(header, control, sizeof(set_strategy_command)); + + return response; +} + +void configuration_SetObjectStoreSize(Configuration *config, + size_t maximumObjectCount) { + config->maximumContentObjectStoreSize = maximumObjectCount; + + forwarder_SetContentObjectStoreSize(config->forwarder, + config->maximumContentObjectStoreSize); +} + +Forwarder *configuration_GetForwarder(const Configuration *config) { + return config->forwarder; +} + +Logger *configuration_GetLogger(const Configuration *config) { + return config->logger; +} + +struct iovec *configuration_MapMeEnable(Configuration *config, + struct iovec *request) { + header_control_message *header = request[0].iov_base; + mapme_activator_command *control = request[1].iov_base; + const char *stateString[2] = {"on", "off"}; + + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcBufferComposer_Format(composer, + "The mapme enable setting received is: %s", + stateString[control->activate]); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + puts(result); + parcMemory_Deallocate((void **)&result); + parcBufferComposer_Release(&composer); + + struct iovec *response = + utils_CreateAck(header, control, sizeof(mapme_activator_command)); + + return response; +} + +struct iovec *configuration_MapMeDiscovery(Configuration *config, + struct iovec *request) { + header_control_message *header = request[0].iov_base; + mapme_activator_command *control = request[1].iov_base; + const char *stateString[2] = {"on", "off"}; + + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcBufferComposer_Format(composer, + "The mapme discovery setting received is: %s", + stateString[control->activate]); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + puts(result); + parcMemory_Deallocate((void **)&result); + parcBufferComposer_Release(&composer); + + struct iovec *response = + utils_CreateAck(header, control, sizeof(mapme_activator_command)); + + return response; +} + +struct iovec *configuration_MapMeTimescale(Configuration *config, + struct iovec *request) { + header_control_message *header = request[0].iov_base; + mapme_timing_command *control = request[1].iov_base; + + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcBufferComposer_Format(composer, + "The mapme timescale value received is: %u", + control->timePeriod); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + puts(result); + parcMemory_Deallocate((void **)&result); + parcBufferComposer_Release(&composer); + + struct iovec *response = + utils_CreateAck(header, control, sizeof(mapme_timing_command)); + + return response; +} + +struct iovec *configuration_MapMeRetx(Configuration *config, + struct iovec *request) { + header_control_message *header = request[0].iov_base; + mapme_timing_command *control = request[1].iov_base; + + PARCBufferComposer *composer = parcBufferComposer_Create(); + parcBufferComposer_Format( + composer, "The mapme retransmission time value received is: %u", + control->timePeriod); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + puts(result); + parcMemory_Deallocate((void **)&result); + parcBufferComposer_Release(&composer); + + struct iovec *response = + utils_CreateAck(header, control, sizeof(mapme_timing_command)); + + return response; +} + +// =========================== +// Main functions that deal with receiving commands, executing them, and sending +// ACK/NACK + +struct iovec *configuration_DispatchCommand(Configuration *config, + command_id command, + struct iovec *control, + unsigned ingressId) { + struct iovec *response = NULL; + + switch (command) { + case ADD_LISTENER: + response = configurationListeners_Add(config, control, ingressId); + break; + + case ADD_CONNECTION: + response = configuration_ProcessCreateTunnel(config, control); + break; + + case LIST_CONNECTIONS: + response = configuration_ProcessConnectionList(config, control); + break; + + case ADD_ROUTE: + response = + configuration_ProcessRegisterHIcnPrefix(config, control, ingressId); + break; + + case LIST_ROUTES: + response = configuration_ProcessRegistrationList(config, control); + break; + + case REMOVE_CONNECTION: + response = configuration_ProcessRemoveTunnel(config, control); + break; + + case REMOVE_ROUTE: + response = configuration_ProcessUnregisterHIcnPrefix(config, control); + break; + + case CACHE_STORE: + response = configuration_ProcessCacheStore(config, control); + break; + + case CACHE_SERVE: + response = configuration_ProcessCacheServe(config, control); + break; + + case CACHE_CLEAR: + response = configuration_ProcessCacheClear(config, control); + break; + + case SET_STRATEGY: + response = configuration_SetForwardingStrategy(config, control); + break; + + case SET_WLDR: + response = configuration_SetWldr(config, control); + break; + + case ADD_PUNTING: + response = configurationListeners_AddPunting(config, control, ingressId); + break; + + case LIST_LISTENERS: + response = configuration_ProcessListenersList(config, control); + break; + + case MAPME_ENABLE: + response = configuration_MapMeEnable(config, control); + break; + + case MAPME_DISCOVERY: + response = configuration_MapMeDiscovery(config, control); + break; + + case MAPME_TIMESCALE: + response = configuration_MapMeTimescale(config, control); + break; + + case MAPME_RETX: + response = configuration_MapMeRetx(config, control); + break; + + default: + break; + } + + return response; +} + +void configuration_ReceiveCommand(Configuration *config, command_id command, + struct iovec *request, unsigned ingressId) { + parcAssertNotNull(config, "Parameter config must be non-null"); + parcAssertNotNull(request, "Parameter request must be non-null"); + struct iovec *response = + configuration_DispatchCommand(config, command, request, ingressId); + configuration_SendResponse(config, response, ingressId); + + switch (command) { + case LIST_CONNECTIONS: + case LIST_ROUTES: // case LIST_INTERFACES: case ETC...: + parcMemory_Deallocate( + &response[1] + .iov_base); // deallocate payload only if generated at fwd side + break; + default: + break; + } + + // deallocate received request. It coincides with response[0].iov_base memory + // parcMemory_Deallocate(&request); //deallocate header and payload (if + // same sent by controller) + parcMemory_Deallocate(&response); // deallocate iovec pointer +} diff --git a/hicn-light/src/config/configuration.h b/hicn-light/src/config/configuration.h new file mode 100755 index 000000000..2bf66c0b1 --- /dev/null +++ b/hicn-light/src/config/configuration.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file configuration.h + * @brief hicn-light configuration, such as in-band commands or CLI + * + * Manages all user configuration of the system, such as from the CLI or web + * interface It remembers the user commands and will be able to write out a + * config file. + * + */ + +#ifndef configuration_h +#define configuration_h + +#include <src/utils/commands.h> +#include <src/core/logger.h> + +struct configuration; +typedef struct configuration Configuration; + +struct forwarder; +typedef struct forwarder Forwarder; + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @retval <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +Configuration *configuration_Create(Forwarder *forwarder); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @retval <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +void configuration_Destroy(Configuration **configPtr); + +void configuration_SetupAllListeners(Configuration *config, uint16_t port, + const char *localPath); + +void configuration_ReceiveCommand(Configuration *config, command_id command, + struct iovec *request, unsigned ingressId); + +/** + * Returns the configured size of the content store + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @retval <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t configuration_GetObjectStoreSize(Configuration *config); + +/** + * Sets the size of the content store (in objects, not bytes) + * + * Must be set before starting the forwarder + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +void configuration_SetObjectStoreSize(Configuration *config, + size_t maximumContentObjectCount); + +strategy_type configuration_GetForwardingStrategy(Configuration *config, + const char *prefix); + +/** + * Returns the Forwarder that owns the Configuration + * + * Returns the hicn-light Forwarder. Used primarily by associated classes in + * the configuration group. + * + * @param [in] config An allocated Configuration + * + * @return non-null The owning Forwarder + * @return null An error + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +Forwarder *configuration_GetForwarder(const Configuration *config); + +/** + * Returns the logger used by the Configuration subsystem + * + * Returns the logger specified when the Configuration was created. + * + * @param [in] config An allocated Configuration + * + * @retval non-null The logger + * @retval null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +Logger *configuration_GetLogger(const Configuration *config); + +struct iovec *configuration_DispatchCommand(Configuration *config, + command_id command, + struct iovec *control, + unsigned ingressId); + +#endif // configuration_h diff --git a/hicn-light/src/config/configurationFile.c b/hicn-light/src/config/configurationFile.c new file mode 100755 index 000000000..eab8f9362 --- /dev/null +++ b/hicn-light/src/config/configurationFile.c @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ctype.h> +#include <errno.h> +#include <src/config.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <parc/algol/parc_ArrayList.h> +#include <parc/algol/parc_List.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> +#include <parc/assert/parc_Assert.h> +#include <src/config/configuration.h> +#include <src/config/configurationFile.h> +#include <src/config/controlRoot.h> +#include <src/config/controlState.h> + +struct configuration_file { + Forwarder *forwarder; + const char *filename; + FILE *fh; + + size_t linesRead; + + // our custom state machine. + ControlState *controlState; +}; + +/* + * Called by a command to dispatch the correct command + */ +struct iovec *_writeRead(ControlState *state, struct iovec *msg) { + ConfigurationFile *configFile = + (ConfigurationFile *)controlState_GetUserdata(state); + + parcAssertNotNull(msg, "Parameter msg must be non-null"); + struct iovec *response = configuration_DispatchCommand( + forwarder_GetConfiguration(configFile->forwarder), + ((header_control_message *)msg[0].iov_base)->commandID, msg, 0); + + return response; +} + +/** + * Removes leading whitespace (space + tab). + * + * If the string is all whitespace, the return value will point to the + * terminating '\0'. + * + * @param [in] str A null-terminated c-string + * + * @retval non-null A pointer in to string of the first non-whitespace + * + * + * Example: + * @code + * <#example#> + * @endcode + */ +static char *_stripLeadingWhitespace(char *str) { + while (isspace(*str)) { + str++; + } + return str; +} + +/** + * Removes trailing whitespace + * + * Inserts a NULL after the last non-whitespace character, modiyfing the input + * string. + * + * @param [in] str A null-terminated c-string + * + * @return non-null A pointer to the input string + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +static char *_stripTrailingWhitespace(char *str) { + char *p = str + strlen(str) - 1; + while (p > str && isspace(*p)) { + p--; + } + + // cap it. If no whitespace, p+1 == str + strlen(str), so will overwrite the + // current null. If all whitespace p+1 == str+1. For an empty string, p+1 = + // str. + *(p + 1) = 0; + + // this does not catch the case where the entire string is whitespace + if (p == str && isspace(*p)) { + *p = 0; + } + + return str; +} + +/** + * Removed leading and trailing whitespace + * + * Modifies the input string (may add a NULL at the end). Will return + * a pointer to the first non-whitespace character or the terminating NULL. + * + * @param [in] str A null-terminated c-string + * + * @return non-null A pointer in to the input string + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +static char *_trim(char *str) { + return _stripTrailingWhitespace(_stripLeadingWhitespace(str)); +} + +/** + * Parse a string in to a PARCList with one word per element + * + * The string passed will be modified by inserting NULLs after each token. + * + * @param [in] str A c-string (will be modified) + * + * @retval non-null A PARCList where each item is a single word + * + * Example: + * @code + * <#example#> + * @endcode + */ +static PARCList *_parseArgs(char *str) { + PARCList *list = + parcList(parcArrayList_Create(NULL), PARCArrayListAsPARCList); + + const char delimiters[] = " \t"; + + char *token; + while ((token = strsep(&str, delimiters)) != NULL) { + parcList_Add(list, token); + } + + return list; +} + +// ============================================================= + +static void _destroy(ConfigurationFile **configFilePtr) { + ConfigurationFile *configFile = *configFilePtr; + parcMemory_Deallocate((void **)&configFile->filename); + + if (configFile->fh != NULL) { + fclose(configFile->fh); + } + + controlState_Destroy(&configFile->controlState); +} + +parcObject_ExtendPARCObject(ConfigurationFile, _destroy, NULL, NULL, NULL, NULL, + NULL, NULL); + +parcObject_ImplementRelease(configurationFile, ConfigurationFile); + +ConfigurationFile *configurationFile_Create(Forwarder *forwarder, + const char *filename) { + parcAssertNotNull(forwarder, "Parameter hicn-fwd must be non-null"); + parcAssertNotNull(filename, "Parameter filename must be non-null"); + + ConfigurationFile *configFile = parcObject_CreateInstance(ConfigurationFile); + + if (configFile) { + configFile->linesRead = 0; + configFile->forwarder = forwarder; + configFile->filename = + parcMemory_StringDuplicate(filename, strlen(filename)); + parcAssertNotNull(configFile->filename, "Could not copy string '%s'", + filename); + + // setup the control state for the command parser: last parameter NULL + // because + // writeRead still not implemented from configuration file. + configFile->controlState = + controlState_Create(configFile, _writeRead, false); + + // we do not register Help commands + controlState_RegisterCommand(configFile->controlState, + controlRoot_Create(configFile->controlState)); + + // open the file and make sure we can read it + configFile->fh = fopen(configFile->filename, "r"); + + if (configFile->fh) { + if (logger_IsLoggable(forwarder_GetLogger(forwarder), + LoggerFacility_Config, PARCLogLevel_Debug)) { + logger_Log(forwarder_GetLogger(forwarder), LoggerFacility_Config, + PARCLogLevel_Debug, __func__, "Open config file %s", + configFile->filename); + } + } else { + if (logger_IsLoggable(forwarder_GetLogger(forwarder), + LoggerFacility_Config, PARCLogLevel_Error)) { + logger_Log(forwarder_GetLogger(forwarder), LoggerFacility_Config, + PARCLogLevel_Error, __func__, + "Could not open config file %s: (%d) %s", + configFile->filename, errno, strerror(errno)); + } + + // failure cleanup the object -- this nulls it so final return null be + // NULL + configurationFile_Release(&configFile); + } + } + return configFile; +} + +bool configurationFile_Process(ConfigurationFile *configFile) { + parcAssertNotNull(configFile, "Parameter configFile must be non-null"); + + // default to a "true" return value and only set to false if we encounter an + // error. + bool success = true; + +#define BUFFERLEN 2048 + char buffer[BUFFERLEN]; + + configFile->linesRead = 0; + + // always clear errors and fseek to start of file in case we get called + // multiple times. + clearerr(configFile->fh); + rewind(configFile->fh); + + while (success && fgets(buffer, BUFFERLEN, configFile->fh) != NULL) { + configFile->linesRead++; + + char *stripedBuffer = _trim(buffer); + if (strlen(stripedBuffer) > 0) { + if (stripedBuffer[0] != '#') { + // not empty and not a comment + + // _parseArgs will modify the string + char *copy = + parcMemory_StringDuplicate(stripedBuffer, strlen(stripedBuffer)); + PARCList *args = _parseArgs(copy); + CommandReturn result = + controlState_DispatchCommand(configFile->controlState, args); + + // we ignore EXIT from the configuration file + if (result == CommandReturn_Failure) { + if (logger_IsLoggable(forwarder_GetLogger(configFile->forwarder), + LoggerFacility_Config, PARCLogLevel_Error)) { + logger_Log(forwarder_GetLogger(configFile->forwarder), + LoggerFacility_Config, PARCLogLevel_Error, __func__, + "Error on input file %s line %d: %s", + configFile->filename, configFile->linesRead, + stripedBuffer); + } + success = false; + } + parcList_Release(&args); + parcMemory_Deallocate((void **)©); + } + } + } + + if (ferror(configFile->fh)) { + if (logger_IsLoggable(forwarder_GetLogger(configFile->forwarder), + LoggerFacility_Config, PARCLogLevel_Error)) { + logger_Log(forwarder_GetLogger(configFile->forwarder), + LoggerFacility_Config, PARCLogLevel_Error, __func__, + "Error on input file %s line %d: (%d) %s", + configFile->filename, configFile->linesRead, errno, + strerror(errno)); + } + success = false; + } + + return success; +} diff --git a/hicn-light/src/config/configurationFile.h b/hicn-light/src/config/configurationFile.h new file mode 100755 index 000000000..54548191d --- /dev/null +++ b/hicn-light/src/config/configurationFile.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file configurationFile.h + * @brief Accepts a filename and provides a means to read it into Configuration + * + * Reads a configuration file and converts the lines in to configuration + * commands for use in Configuration. + * + * Accepts '#' lines as comments. Skips blank and whitespace-only lines. + * + */ + +#ifndef configurationFile_h +#define configurationFile_h + +#include <src/core/forwarder.h> + +struct configuration_file; +typedef struct configuration_file ConfigurationFile; + +/** + * Creates a ConfigurationFile to prepare to process the file + * + * Prepares the object and opens the file. Makes sure we can read the file. + * Does not read the file or process any commands from the file. + * + * @param [in] hicn-light An allocated Forwarder to configure with the file + * @param [in] filename The file to use + * + * @retval non-null An allocated ConfigurationFile that is readable + * @retval null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +ConfigurationFile *configurationFile_Create(Forwarder *forwarder, + const char *filename); + +/** + * Reads the configuration file line-by-line and issues commands to + * Configuration + * + * Reads the file line by line. Skips '#' and blank lines. + * + * Will stop on the first error. Lines already processed will not be un-done. + * + * @param [in] configFile An allocated ConfigurationFile + * + * @retval true The entire files was processed without error. + * @retval false There was an error in the file. + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool configurationFile_Process(ConfigurationFile *configFile); + +// void configurationFile_ProcessForwardingStrategies(Configuration * config, +// ConfigurationFile * configFile); + +/** + * Closes the underlying file and releases memory + * + * <#Paragraphs Of Explanation#> + * + * @param [in,out] configFilePtr An allocated ConfigurationFile that will be + * NULL'd as output + * + * Example: + * @code + * <#example#> + * @endcode + */ +void configurationFile_Release(ConfigurationFile **configFilePtr); + +#endif /* defined(configurationFile_h) */ diff --git a/hicn-light/src/config/configurationListeners.c b/hicn-light/src/config/configurationListeners.c new file mode 100755 index 000000000..be30e2a49 --- /dev/null +++ b/hicn-light/src/config/configurationListeners.c @@ -0,0 +1,535 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <arpa/inet.h> +#include <parc/assert/parc_Assert.h> +#include <src/config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Network.h> + +#include <src/core/system.h> +#include <src/utils/interfaceSet.h> +#include <src/utils/punting.h> + +#include <src/config/configurationListeners.h> +#include <src/io/hicnListener.h> +#include <src/io/tcpListener.h> +#include <src/io/udpListener.h> + +#include <src/utils/addressList.h> +#include <src/utils/commands.h> +#include <src/utils/utils.h> + +static bool _setupHIcnListenerOnInet4(Forwarder *forwarder, + const char *symbolic, Address *address) { + bool success = false; +#ifndef __APPLE__ + ListenerOps *ops = + hicnListener_CreateInet(forwarder, (char *)symbolic, address); + if (ops != NULL) { + success = listenerSet_Add(forwarder_GetListenerSet(forwarder), ops); + parcAssertTrue(success, "Failed to add HIcn listener %s to ListenerSet", + symbolic); + } +#endif /* __APPLE__ */ + return success; +} + +static bool _setupHIcnListenerOnInet6(Forwarder *forwarder, + const char *symbolic, Address *address) { + bool success = false; +#ifndef __APPLE__ + ListenerOps *ops = + hicnListener_CreateInet6(forwarder, (char *)symbolic, address); + if (ops != NULL) { + success = listenerSet_Add(forwarder_GetListenerSet(forwarder), ops); + parcAssertTrue(success, "Failed to add HIcn listener %s to ListenerSet", + symbolic); + } +#endif /* __APPLE__ */ + return success; +} + +bool configurationListeners_Remove(const Configuration *config) { + Logger *logger = configuration_GetLogger(config); + if (logger_IsLoggable(logger, LoggerFacility_Config, PARCLogLevel_Warning)) { + logger_Log(logger, LoggerFacility_Config, PARCLogLevel_Warning, __func__, + "Removing a listener not supported: ingress %u control %s"); + } + + return false; +} + +bool _AddPuntingInet(const Configuration *config, Punting *punting, + unsigned ingressId) { +#ifndef __APPLE__ + struct sockaddr *addr = parcNetwork_SockAddress("0.0.0.0", 1234); + if (addr == NULL) { + printf("Error creating address\n"); + return false; + } + + Address *fakeAddr = addressCreateFromInet((struct sockaddr_in *)addr); + + ListenerOps *listenerOps = listenerSet_Find( + forwarder_GetListenerSet(configuration_GetForwarder(config)), ENCAP_HICN, + fakeAddr); + addressDestroy(&fakeAddr); + + if (listenerOps == NULL) { + printf("the main listener (IPV4) does not exists\n"); + return false; + } + + struct sockaddr_in puntingAddr; + + Address *address = puntingGetAddress(punting); + if (address == NULL) return false; + + bool res = addressGetInet(address, &puntingAddr); + if (!res) { + printf("unable to read the punting address\n"); + return false; + } + + char prefix[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &(puntingAddr.sin_addr), prefix, INET_ADDRSTRLEN); + + char len[5]; + sprintf(len, "%d", puntingPrefixLen(punting)); + + char *prefixStr = + malloc(strlen(prefix) + strlen(len) + 2); //+1 for the zero-terminator + if (prefixStr == NULL) { + printf("error while create the prefix string\n"); + return false; + } + strcpy(prefixStr, prefix); + strcat(prefixStr, "/"); + strcat(prefixStr, len); + + res = hicnListener_Punting(listenerOps, prefixStr); + if (!res) { + printf("error while adding the punting rule\n"); + return false; + } + + return true; +#else + return false; +#endif +} + +bool _AddPuntingInet6(const Configuration *config, Punting *punting, + unsigned ingressId) { +#ifndef __APPLE__ + struct sockaddr *addr = parcNetwork_SockAddress("0::0", 1234); + if (addr == NULL) { + printf("Error creating address\n"); + return false; + } + + Address *fakeAddr = addressCreateFromInet6((struct sockaddr_in6 *)addr); + + // comments: + // EncapType: I use the HIcn encap since the puting is available only for HIcn + // listeners LocalAddress: The only listern for which we need punting rules is + // the main one, which has no address + // so I create a fake empty address. This need to be consistent + // with the address set at creation time + + ListenerOps *listenerOps = listenerSet_Find( + forwarder_GetListenerSet(configuration_GetForwarder(config)), ENCAP_HICN, + fakeAddr); + addressDestroy(&fakeAddr); + + if (listenerOps == NULL) { + printf("the main listener does not exists\n"); + return false; + } + + struct sockaddr_in6 puntingAddr; + bool res = addressGetInet6(puntingGetAddress(punting), &puntingAddr); + if (!res) { + printf("unable to read the punting address\n"); + return false; + } + + char prefix[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &(puntingAddr.sin6_addr), prefix, INET6_ADDRSTRLEN); + + char len[5]; + sprintf(len, "%d", puntingPrefixLen(punting)); + + char *prefixStr = + malloc(strlen(prefix) + strlen(len) + 2); //+1 for the zero-terminator + if (prefixStr == NULL) { + printf("error while create the prefix string\n"); + return false; + } + strcpy(prefixStr, prefix); + strcat(prefixStr, "/"); + strcat(prefixStr, len); + + res = hicnListener_Punting(listenerOps, prefixStr); + if (!res) { + printf("error while adding the punting rule\n"); + return false; + } + + return true; +#else + return false; +#endif +} + +//============= LIGHT COMMAN =============== + +static bool _addEther(Configuration *config, add_listener_command *control, + unsigned ingressId) { + // Not implemented + return false; +} + +static bool _setupTcpListenerOnInet(Forwarder *forwarder, ipv4_addr_t *addr4, + uint16_t *port) { + bool success = false; + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = *port; + addr.sin_addr.s_addr = *addr4; + + ListenerOps *ops = tcpListener_CreateInet(forwarder, addr); + if (ops) { + success = listenerSet_Add(forwarder_GetListenerSet(forwarder), ops); + parcAssertTrue(success, "Failed to add TCP listener on %s to ListenerSet", + addressToString(ops->getListenAddress(ops))); + } + return success; +} + +static bool _setupUdpListenerOnInet(Forwarder *forwarder, ipv4_addr_t *addr4, + uint16_t *port) { + bool success = false; + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = *port; + addr.sin_addr.s_addr = *addr4; + + ListenerOps *ops = udpListener_CreateInet(forwarder, addr); + if (ops) { + success = listenerSet_Add(forwarder_GetListenerSet(forwarder), ops); + parcAssertTrue(success, "Failed to add UDP listener on %s to ListenerSet", + addressToString(ops->getListenAddress(ops))); + } + return success; +} + +static bool _setupTcpListenerOnInet6Light(Forwarder *forwarder, + ipv6_addr_t *addr6, uint16_t *port, + uint32_t scopeId) { + bool success = false; + + struct sockaddr_in6 addr; + memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_port = *port; + addr.sin6_addr = *addr6; + addr.sin6_scope_id = scopeId; + + ListenerOps *ops = tcpListener_CreateInet6(forwarder, addr); + if (ops) { + success = listenerSet_Add(forwarder_GetListenerSet(forwarder), ops); + parcAssertTrue(success, "Failed to add TCP6 listener on %s to ListenerSet", + addressToString(ops->getListenAddress(ops))); + } + return success; +} + +static bool _setupUdpListenerOnInet6Light(Forwarder *forwarder, + ipv6_addr_t *addr6, uint16_t *port) { + bool success = false; + + struct sockaddr_in6 addr; + memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_port = *port; + addr.sin6_addr = *addr6; + addr.sin6_scope_id = 0; + + ListenerOps *ops = udpListener_CreateInet6(forwarder, addr); + if (ops) { + success = listenerSet_Add(forwarder_GetListenerSet(forwarder), ops); + parcAssertTrue(success, "Failed to add UDP6 listener on %s to ListenerSet", + addressToString(ops->getListenAddress(ops))); + } + return success; +} + +bool _addHicn(Configuration *config, add_listener_command *control, + unsigned ingressId) { + bool success = false; + const char *symbolic = control->symbolic; + Address *localAddress = NULL; + + switch (control->addressType) { + case ADDR_INET: { + localAddress = + utils_AddressFromInet(&control->address.ipv4, &control->port); + success = _setupHIcnListenerOnInet4(configuration_GetForwarder(config), + symbolic, localAddress); + break; + } + + case ADDR_INET6: { + localAddress = + utils_AddressFromInet6(&control->address.ipv6, &control->port); + success = _setupHIcnListenerOnInet6(configuration_GetForwarder(config), + symbolic, localAddress); + break; + } + + default: + if (logger_IsLoggable(configuration_GetLogger(config), + LoggerFacility_Config, PARCLogLevel_Warning)) { + logger_Log(configuration_GetLogger(config), LoggerFacility_Config, + PARCLogLevel_Warning, __func__, + "Unsupported address type for HICN (ingress id %u): " + "must be either IPV4 or IPV6", + ingressId); + } + break; + } + + if (success == true && localAddress != NULL) { + if (logger_IsLoggable(configuration_GetLogger(config), + LoggerFacility_Config, PARCLogLevel_Info)) { + logger_Log(configuration_GetLogger(config), LoggerFacility_Config, + PARCLogLevel_Info, __func__, + "Setup hicn listener on address %s", + addressToString(localAddress)); + } + } + + addressDestroy(&localAddress); + + return success; +} + +bool _addIP(Configuration *config, add_listener_command *control, + unsigned ingressId) { + bool success = false; + + switch (control->addressType) { + case ADDR_INET: { + if (control->connectionType == UDP_CONN) { + success = + _setupUdpListenerOnInet(configuration_GetForwarder(config), + &control->address.ipv4, &control->port); + } else if (control->connectionType == TCP_CONN) { + success = + _setupTcpListenerOnInet(configuration_GetForwarder(config), + &control->address.ipv4, &control->port); + } + break; + } + + case ADDR_INET6: { + if (control->connectionType == UDP_CONN) { + success = _setupUdpListenerOnInet6Light( + configuration_GetForwarder(config), &control->address.ipv6, + &control->port); + } else if (control->connectionType == TCP_CONN) { + success = _setupTcpListenerOnInet6Light( + configuration_GetForwarder(config), &control->address.ipv6, + &control->port, 0); + } + break; + } + + default: + if (logger_IsLoggable(configuration_GetLogger(config), + LoggerFacility_Config, PARCLogLevel_Warning)) { + char *addrStr = utils_CommandAddressToString( + control->addressType, &control->address, &control->port); + logger_Log( + configuration_GetLogger(config), LoggerFacility_Config, + PARCLogLevel_Warning, __func__, + "Unsupported address type for IP encapsulation ingress id %u: %s", + ingressId, addrStr); + parcMemory_Deallocate((void **)&addrStr); + } + break; + } + + if (success) { + if (logger_IsLoggable(configuration_GetLogger(config), + LoggerFacility_Config, PARCLogLevel_Info)) { + char *addrStr = utils_CommandAddressToString( + control->addressType, &control->address, &control->port); + logger_Log(configuration_GetLogger(config), LoggerFacility_Config, + PARCLogLevel_Info, __func__, "Setup listener on address %s", + addrStr); + parcMemory_Deallocate((void **)&addrStr); + } + } + + return success; +} + +struct iovec *configurationListeners_Add(Configuration *config, + struct iovec *request, + unsigned ingressId) { + header_control_message *header = request[0].iov_base; + add_listener_command *control = request[1].iov_base; + + bool success = false; + + if (control->listenerMode == ETHER_MODE) { + parcTrapNotImplemented("Add Ethernet Listener is not supported"); + success = _addEther(config, control, ingressId); + // it is a failure + } else if (control->listenerMode == IP_MODE) { + success = _addIP(config, control, ingressId); + } else if (control->listenerMode == HICN_MODE) { + success = _addHicn(config, control, ingressId); + } else { + Logger *logger = configuration_GetLogger(config); + if (logger_IsLoggable(logger, LoggerFacility_Config, + PARCLogLevel_Warning)) { + logger_Log(logger, LoggerFacility_Config, PARCLogLevel_Warning, __func__, + "Unsupported encapsulation mode (ingress id %u)", ingressId); + } + } + + // generate ACK/NACK + struct iovec *response; + + if (success) { // ACK + response = utils_CreateAck(header, control, sizeof(add_listener_command)); + } else { // NACK + response = utils_CreateNack(header, control, sizeof(add_listener_command)); + } + + return response; +} + +struct iovec *configurationListeners_AddPunting(Configuration *config, + struct iovec *request, + unsigned ingressId) { + header_control_message *header = request[0].iov_base; + add_punting_command *control = request[1].iov_base; + + const char *symbolicOrConnid = control->symbolicOrConnid; + uint32_t len = control->len; + in_port_t port = htons(1234); + bool success = false; + + if (control->addressType == ADDR_INET) { + Address *address = utils_AddressFromInet(&control->address.ipv4, &port); + Punting *punting = puntingCreate(symbolicOrConnid, address, len); + success = _AddPuntingInet(config, punting, ingressId); + addressDestroy(&address); + } else if (control->addressType == ADDR_INET6) { + Address *address = utils_AddressFromInet6(&control->address.ipv6, &port); + Punting *punting = puntingCreate(symbolicOrConnid, address, len); + success = _AddPuntingInet6(config, punting, ingressId); + addressDestroy(&address); + } else { + printf("Invalid IP type.\n"); // will generate a Nack + return utils_CreateNack(header, control, sizeof(add_punting_command)); + } + + // generate ACK/NACK + struct iovec *response; + if (success) { // ACK + response = utils_CreateAck(header, control, sizeof(add_punting_command)); + } else { // NACK + response = utils_CreateNack(header, control, sizeof(add_punting_command)); + } + + return response; +} + +//=========================== INITIAL LISTENERS ==================== + +static void _setupListenersOnAddress(Forwarder *forwarder, + const Address *address, uint16_t port, + const char *interfaceName) { + address_type type = addressGetType(address); + switch (type) { + case ADDR_INET: { + struct sockaddr_in tmp; + addressGetInet(address, &tmp); + _setupTcpListenerOnInet(forwarder, &tmp.sin_addr.s_addr, &port); + break; + } + + case ADDR_INET6: { + struct sockaddr_in6 tmp; + addressGetInet6(address, &tmp); + _setupTcpListenerOnInet6Light(forwarder, &tmp.sin6_addr, &port, + tmp.sin6_scope_id); + break; + } + + case ADDR_LINK: + // not used + break; + + default: + // dont' know how to handle this, so no listeners + break; + } +} + +void configurationListeners_SetupAll(const Configuration *config, uint16_t port, + const char *localPath) { + Forwarder *forwarder = configuration_GetForwarder(config); + InterfaceSet *set = system_Interfaces(forwarder); + + size_t interfaceSetLen = interfaceSetLength(set); + for (size_t i = 0; i < interfaceSetLen; i++) { + Interface *iface = interfaceSetGetByOrdinalIndex(set, i); + + const AddressList *addresses = interfaceGetAddresses(iface); + size_t addressListLen = addressListLength(addresses); + + for (size_t j = 0; j < addressListLen; j++) { + const Address *address = addressListGetItem(addresses, j); + + // Do not start on link address + if (addressGetType(address) != ADDR_LINK) { + _setupListenersOnAddress(forwarder, address, htons(port), + interfaceGetName(iface)); + } + } + } + + // if (localPath != NULL) { + // _setupLocalListener(forwarder, localPath); + //} + + interfaceSetDestroy(&set); +} diff --git a/hicn-light/src/config/configurationListeners.h b/hicn-light/src/config/configurationListeners.h new file mode 100755 index 000000000..7332b0c64 --- /dev/null +++ b/hicn-light/src/config/configurationListeners.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file configurationListeners.h + * @brief Configuration routines related to Listeners + * + * Adding and removing listeners. + * + */ + +#ifndef configurationListeners_h +#define configurationListeners_h + +#include <src/config/configuration.h> +#include <src/core/forwarder.h> + +#include <src/utils/address.h> + +/** + * Setup udp, tcp, and local listeners + * + * Will bind to all available IP protocols on the given port. + * Does not add Ethernet listeners. + * + * @param port is the UPD and TCP port to use + * @param localPath is the AF_UNIX path to use, if NULL no AF_UNIX listener is + * setup + * + * Example: + * @code + * <#example#> + * @endcode + */ +void configurationListeners_SetupAll(const Configuration *config, uint16_t port, + const char *localPath); + +bool configurationListeners_Remove(const Configuration *config); + +// light functions + +struct iovec *configurationListeners_Add(Configuration *config, + struct iovec *request, + unsigned ingressId); + +struct iovec *configurationListeners_AddPunting(Configuration *config, + struct iovec *request, + unsigned ingressId); + +#endif /* defined(configurationListeners_h) */ diff --git a/hicn-light/src/config/controlAdd.c b/hicn-light/src/config/controlAdd.c new file mode 100755 index 000000000..72f8e9759 --- /dev/null +++ b/hicn-light/src/config/controlAdd.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <parc/assert/parc_Assert.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/algol/parc_Memory.h> + +#include <src/config/controlAdd.h> +#include <src/config/controlAddConnection.h> +#include <src/config/controlAddListener.h> +#include <src/config/controlAddPunting.h> +#include <src/config/controlAddRoute.h> + +// =================================================== + +static void _controlAdd_Init(CommandParser *parser, CommandOps *ops); +static CommandReturn _controlAdd_Execute(CommandParser *parser, CommandOps *ops, + PARCList *args); +static CommandReturn _controlAdd_HelpExecute(CommandParser *parser, + CommandOps *ops, PARCList *args); + +// =================================================== + +static const char *command_add = "add"; +static const char *help_command_add = "help add"; + +CommandOps *webControlAdd_Create(ControlState *state) { + return commandOps_Create(state, command_add, _controlAdd_Init, + _controlAdd_Execute, commandOps_Destroy); +} + +CommandOps *controlAdd_CreateHelp(ControlState *state) { + return commandOps_Create(state, help_command_add, NULL, + _controlAdd_HelpExecute, commandOps_Destroy); +} + +// =================================================== + +static CommandReturn _controlAdd_HelpExecute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + CommandOps *ops_add_connection = controlAddConnection_Create(NULL); + CommandOps *ops_add_route = controlAddRoute_Create(NULL); + CommandOps *ops_add_punting = controlAddPunting_Create(NULL); + CommandOps *ops_add_listener = controlAddListener_Create(NULL); + + printf("Available commands:\n"); + printf(" %s\n", ops_add_connection->command); + printf(" %s\n", ops_add_route->command); + printf(" %s\n", ops_add_punting->command); + printf(" %s\n", ops_add_listener->command); + printf("\n"); + + commandOps_Destroy(&ops_add_connection); + commandOps_Destroy(&ops_add_route); + commandOps_Destroy(&ops_add_punting); + commandOps_Destroy(&ops_add_listener); + return CommandReturn_Success; +} + +static void _controlAdd_Init(CommandParser *parser, CommandOps *ops) { + ControlState *state = ops->closure; + controlState_RegisterCommand(state, controlAddListener_HelpCreate(state)); + controlState_RegisterCommand(state, controlAddListener_Create(state)); + controlState_RegisterCommand(state, controlAddConnection_HelpCreate(state)); + controlState_RegisterCommand(state, controlAddRoute_HelpCreate(state)); + controlState_RegisterCommand(state, controlAddConnection_Create(state)); + controlState_RegisterCommand(state, controlAddRoute_Create(state)); + controlState_RegisterCommand(state, controlAddPunting_Create(state)); + controlState_RegisterCommand(state, controlAddPunting_HelpCreate(state)); +} + +static CommandReturn _controlAdd_Execute(CommandParser *parser, CommandOps *ops, + PARCList *args) { + return _controlAdd_HelpExecute(parser, ops, args); +} diff --git a/hicn-light/src/config/controlAdd.h b/hicn-light/src/config/controlAdd.h new file mode 100755 index 000000000..e1955f200 --- /dev/null +++ b/hicn-light/src/config/controlAdd.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_Add.h + * @brief Command-line "add" node + * + * Implements the "add" node of the CLI tree + * + * + */ + +#ifndef control_Add_h +#define control_Add_h + +#include <src/config/controlState.h> + +CommandOps *webControlAdd_Create(ControlState *state); +CommandOps *controlAdd_CreateHelp(ControlState *state); +#endif // control_Add_h diff --git a/hicn-light/src/config/controlAddConnection.c b/hicn-light/src/config/controlAddConnection.c new file mode 100755 index 000000000..a0a966ddf --- /dev/null +++ b/hicn-light/src/config/controlAddConnection.c @@ -0,0 +1,390 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <ctype.h> +#include <parc/assert/parc_Assert.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Network.h> + +#include <src/config/controlAddConnection.h> + +#include <src/utils/commands.h> +#include <src/utils/utils.h> + +// =================================================== + +static void _controlAddConnection_Init(CommandParser *parser, CommandOps *ops); +static CommandReturn _controlAddConnection_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlAddConnection_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +// =================================================== + +static CommandReturn _controlAddConnection_HIcnHelpExecute( + CommandParser *parser, CommandOps *ops, PARCList *args); +static CommandReturn _controlAddConnection_HIcnExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static CommandReturn _controlAddConnection_UdpHelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlAddConnection_UdpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static CommandReturn _controlAddConnection_TcpHelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlAddConnection_TcpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +// =================================================== + +static const char *_commandAddConnection = "add connection"; +static const char *_commandAddConnectionHIcn = "add connection hicn"; +static const char *_commandAddConnectionUdp = "add connection udp"; +static const char *_commandAddConnectionTcp = "add connection tcp"; +static const char *_commandAddConnectionHelp = "help add connection"; +static const char *_commandAddConnectionHIcnHelp = "help add connection hicn"; +static const char *_commandAddConnectionUdpHelp = "help add connection udp"; +static const char *_commandAddConnectionTcpHelp = "help add connection tcp"; + +// =================================================== + +CommandOps *controlAddConnection_Create(ControlState *state) { + return commandOps_Create(state, _commandAddConnection, + _controlAddConnection_Init, + _controlAddConnection_Execute, commandOps_Destroy); +} + +CommandOps *controlAddConnection_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandAddConnectionHelp, NULL, + _controlAddConnection_HelpExecute, + commandOps_Destroy); +} + +// =================================================== + +static CommandOps *_controlAddConnection_HIcnCreate(ControlState *state) { + return commandOps_Create(state, _commandAddConnectionHIcn, NULL, + _controlAddConnection_HIcnExecute, + commandOps_Destroy); +} + +static CommandOps *_controlAddConnection_UdpCreate(ControlState *state) { + return commandOps_Create(state, _commandAddConnectionUdp, NULL, + _controlAddConnection_UdpExecute, + commandOps_Destroy); +} + +static CommandOps *_controlAddConnection_TcpCreate(ControlState *state) { + return commandOps_Create(state, _commandAddConnectionTcp, NULL, + _controlAddConnection_TcpExecute, + commandOps_Destroy); +} + +// =================================================== + +static CommandOps *_controlAddConnection_HIcnHelpCreate(ControlState *state) { + return commandOps_Create(state, _commandAddConnectionHIcnHelp, NULL, + _controlAddConnection_HIcnHelpExecute, + commandOps_Destroy); +} + +static CommandOps *_controlAddConnection_UdpHelpCreate(ControlState *state) { + return commandOps_Create(state, _commandAddConnectionUdpHelp, NULL, + _controlAddConnection_UdpHelpExecute, + commandOps_Destroy); +} + +static CommandOps *_controlAddConnection_TcpHelpCreate(ControlState *state) { + return commandOps_Create(state, _commandAddConnectionTcpHelp, NULL, + _controlAddConnection_TcpHelpExecute, + commandOps_Destroy); +} + +// =================================================== + +static CommandReturn _controlAddConnection_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("Available commands:\n"); + printf(" %s\n", _commandAddConnectionHIcn); + printf(" %s\n", _commandAddConnectionUdp); + printf(" %s\n", _commandAddConnectionTcp); + printf("\n"); + return CommandReturn_Success; +} + +static void _controlAddConnection_Init(CommandParser *parser, CommandOps *ops) { + ControlState *state = ops->closure; + controlState_RegisterCommand(state, + _controlAddConnection_HIcnHelpCreate(state)); + controlState_RegisterCommand(state, + _controlAddConnection_UdpHelpCreate(state)); + controlState_RegisterCommand(state, + _controlAddConnection_TcpHelpCreate(state)); + + controlState_RegisterCommand(state, _controlAddConnection_HIcnCreate(state)); + controlState_RegisterCommand(state, _controlAddConnection_UdpCreate(state)); + controlState_RegisterCommand(state, _controlAddConnection_TcpCreate(state)); +} + +static CommandReturn _controlAddConnection_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + return _controlAddConnection_HelpExecute(parser, ops, args); +} + +// =================================================== +// functions general to all connection types + +/** + * Create a tunnel in the forwarder based on the addresses + * + * Caller retains ownership of memory. + * The symbolic name will be used to refer to this connection. It must be unqiue + * otherwise the forwarder will reject this commend. + * + * @param [in] parser An allocated CommandParser + * @param [in] ops Allocated CommandOps (needed to extract ControlState) + * @param [in] localAddress the local IP and port. The port may be the wildcard + * value. + * @param [in] remoteAddress The remote IP and port (both must be specified) + * @param [in] tunnelType The tunneling protocol + * @param [in] symbolic The symbolic name for the connection (must be unique) + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * struct sockaddr_in *anyAddress = parcNetwork_SockInet4AddressAny(); + * struct sockaddr_in *remote = + * parcNetwork_SockInet4Address("192.168.1.2", 9695); + * + * Address *localAddress = addressCreateFromInet(anyAddress); + * Address *remoteAddress = addressCreateFromInet(remote); + * + * control_CreateTunnel(state, localAddress, remoteAddress, IPTUN_TCP, + * "conn7"); + * + * addressDestroy(&localAddress); + * addressDestroy(&remoteAddress); + * parcMemory_Deallocate((void **)&remote); + * parcMemory_Deallocate((void **)&anyAddress); + * } + * @endcode + */ + +static CommandReturn _controlAddConnection_CreateTunnel( + CommandParser *parser, CommandOps *ops, const char *local_ip, + const char *local_port, const char *remote_ip, const char *remote_port, + connection_type tunnelType, const char *symbolic) { + ControlState *state = ops->closure; + // a request like this always has an interface index of 0 [FIELD REMOVED] + // unsigned int interfaceIndex = 0; + + // allocate command payload + add_connection_command *addConnectionCommand = + parcMemory_AllocateAndClear(sizeof(add_connection_command)); + + // check and set IP addresses + if (inet_pton(AF_INET, remote_ip, &addConnectionCommand->remoteIp.ipv4) == + 1 && + inet_pton(AF_INET, local_ip, &addConnectionCommand->localIp.ipv4) == 1) { + addConnectionCommand->ipType = ADDR_INET; + + } else if (inet_pton(AF_INET6, remote_ip, + &addConnectionCommand->remoteIp.ipv6) == 1 && + inet_pton(AF_INET6, local_ip, + &addConnectionCommand->localIp.ipv6) == 1) { + addConnectionCommand->ipType = ADDR_INET6; + + } else { + printf("Error: local address %s not same type as remote address %s\n", + local_ip, remote_ip); + parcMemory_Deallocate(&addConnectionCommand); + return CommandReturn_Failure; + } + + // Fill remaining payload fields + addConnectionCommand->connectionType = tunnelType; + strcpy(addConnectionCommand->symbolic, symbolic); + addConnectionCommand->remotePort = htons((uint16_t)atoi(remote_port)); + addConnectionCommand->localPort = htons((uint16_t)atoi(local_port)); + + // send message and receive response + struct iovec *response = + utils_SendRequest(state, ADD_CONNECTION, addConnectionCommand, + sizeof(add_connection_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} + +static CommandReturn _controlAddConnection_IpHelp(CommandParser *parser, + CommandOps *ops, + PARCList *args, + const char *protocol) { + printf("add connection hicn <symbolic> <remote_ip> <local_ip>\n"); + printf( + "add connection udp <symbolic> <remote_ip> <port> <local_ip> <port>\n"); + printf( + " <symbolic> : symbolic name, e.g. 'conn1' (must be " + "unique, start with alpha)\n"); + printf( + " <remote_ip> : the IPv4 or IPv6 or hostname of the remote system\n"); + printf(" <local_ip> : optional local IP address to bind to\n"); + printf("\n"); + return CommandReturn_Success; +} + +static CommandReturn _controlAddConnection_HIcnHelpExecute( + CommandParser *parser, CommandOps *ops, PARCList *args) { + _controlAddConnection_IpHelp(parser, ops, args, "hicn"); + + return CommandReturn_Success; +} + +static CommandReturn _controlAddConnection_HIcnExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + static const int _indexSymbolic = 3; + static const int _indexRemAddr = 4; + static const int _indexLocAddr = 5; + + if (parcList_Size(args) != 6) { + _controlAddConnection_HIcnHelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + char *symbolic = parcList_GetAtIndex(args, _indexSymbolic); + + if (!utils_ValidateSymbolicName(symbolic)) { + printf( + "Invalid symbolic name. Must begin with alpha and contain only " + "alphanum.\n"); + return CommandReturn_Failure; + } + + char *remote_ip = parcList_GetAtIndex(args, _indexRemAddr); + char *local_ip = parcList_GetAtIndex(args, _indexLocAddr); + char *port = "1234"; // this is a random port number that will be ignored + + return _controlAddConnection_CreateTunnel( + parser, ops, local_ip, port, remote_ip, port, HICN_CONN, symbolic); +} + +static CommandReturn _controlAddConnection_UdpHelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + _controlAddConnection_IpHelp(parser, ops, args, "udp"); + + return CommandReturn_Success; +} + +static CommandReturn _controlAddConnection_UdpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + static const int _indexSymbolic = 3; + static const int _indexRemAddr = 4; + static const int _indexRemPort = 5; + static const int _indexLocAddr = 6; + static const int _indexLocPort = 7; + + if (parcList_Size(args) != 8) { + _controlAddConnection_UdpHelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + char *symbolic = parcList_GetAtIndex(args, _indexSymbolic); + + if (!utils_ValidateSymbolicName(symbolic)) { + printf( + "Invalid symbolic name. Must begin with alpha and contain only " + "alphanum.\n"); + return CommandReturn_Failure; + } + + char *remote_ip = parcList_GetAtIndex(args, _indexRemAddr); + char *local_ip = parcList_GetAtIndex(args, _indexLocAddr); + + char *remote_port = parcList_GetAtIndex(args, _indexRemPort); + char *local_port = parcList_GetAtIndex(args, _indexLocPort); + + return _controlAddConnection_CreateTunnel(parser, ops, local_ip, local_port, + remote_ip, remote_port, UDP_CONN, + symbolic); +} + +static CommandReturn _controlAddConnection_TcpHelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + _controlAddConnection_IpHelp(parser, ops, args, "tcp"); + + return CommandReturn_Success; +} + +static CommandReturn _controlAddConnection_TcpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + static const int _indexSymbolic = 3; + static const int _indexRemAddr = 4; + static const int _indexRemPort = 5; + static const int _indexLocAddr = 6; + static const int _indexLocPort = 7; + + if (parcList_Size(args) != 8) { + _controlAddConnection_UdpHelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + char *symbolic = parcList_GetAtIndex(args, _indexSymbolic); + + if (!utils_ValidateSymbolicName(symbolic)) { + printf( + "Invalid symbolic name. Must begin with alpha and contain only " + "alphanum.\n"); + return CommandReturn_Failure; + } + + char *remote_ip = parcList_GetAtIndex(args, _indexRemAddr); + char *local_ip = parcList_GetAtIndex(args, _indexLocAddr); + + char *remote_port = parcList_GetAtIndex(args, _indexRemPort); + char *local_port = parcList_GetAtIndex(args, _indexLocPort); + + return _controlAddConnection_CreateTunnel(parser, ops, local_ip, local_port, + remote_ip, remote_port, TCP_CONN, + symbolic); +} diff --git a/hicn-light/src/config/controlAddConnection.h b/hicn-light/src/config/controlAddConnection.h new file mode 100755 index 000000000..788306989 --- /dev/null +++ b/hicn-light/src/config/controlAddConnection.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_AddConnection.h + * @brief Command-line "add connection" node + * + * Implements the "add connection" node of the CLI tree + * + * + */ + +#ifndef controlAddConnection_h +#define controlAddConnection_h + +#include <src/config/controlState.h> +CommandOps *controlAddConnection_Create(ControlState *state); +CommandOps *controlAddConnection_HelpCreate(ControlState *state); +#endif // controlAddConnection_h diff --git a/hicn-light/src/config/controlAddListener.c b/hicn-light/src/config/controlAddListener.c new file mode 100755 index 000000000..8f687c3a6 --- /dev/null +++ b/hicn-light/src/config/controlAddListener.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <ctype.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Network.h> + +#include <src/config/controlAddListener.h> + +#include <src/utils/commands.h> +#include <src/utils/utils.h> + +static CommandReturn _controlAddListener_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlAddListener_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *command_add_listener = "add listener"; +static const char *command_help_add_listener = "help add listener"; + +CommandOps *controlAddListener_Create(ControlState *state) { + return commandOps_Create(state, command_add_listener, NULL, + _controlAddListener_Execute, commandOps_Destroy); +} + +CommandOps *controlAddListener_HelpCreate(ControlState *state) { + return commandOps_Create(state, command_help_add_listener, NULL, + _controlAddListener_HelpExecute, commandOps_Destroy); +} + +// ==================================================== + +static const int _indexProtocol = 2; +static const int _indexSymbolic = 3; +static const int _indexAddress = 4; +static const int _indexPort = 5; + +static CommandReturn _controlAddListener_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("commands:\n"); + printf(" add listener hicn <symbolic> <localAddress> \n"); + printf(" add listener udp <symbolic> <localAddress> <port> \n"); + printf(" add listener tcp <symbolic> <localAddress> <port> \n"); + printf("\n"); + printf( + " symbolic: User defined name for listener, must start with " + "alpha and be alphanum\n"); + printf(" protocol: hicn | udp\n"); + printf( + " localAddress: IPv4 or IPv6 address (or prefix protocol = hicn) " + "assigend to the local interface\n"); + printf(" port: Udp port\n"); + printf("\n"); + printf("Notes:\n"); + printf(" The symblic name must be unique or the source will reject it.\n"); + printf( + " If protocol = hinc: the address 0::0 indicates the main listern, " + "for which we can set punting rules.\n"); + return CommandReturn_Success; +} + +static CommandReturn _CreateListener(CommandParser *parser, CommandOps *ops, + const char *symbolic, const char *addr, + const char *port, listener_mode mode, + connection_type type) { + ControlState *state = ops->closure; + + // allocate command payload + add_listener_command *addListenerCommand = + parcMemory_AllocateAndClear(sizeof(add_listener_command)); + + // check and set IP address + if (inet_pton(AF_INET, addr, &addListenerCommand->address.ipv4) == 1) { + addListenerCommand->addressType = ADDR_INET; + + } else if (inet_pton(AF_INET6, addr, &addListenerCommand->address.ipv6) == + 1) { + addListenerCommand->addressType = ADDR_INET6; + + } else { + printf("Error: %s is not a valid network address \n", addr); + parcMemory_Deallocate(&addListenerCommand); + return CommandReturn_Failure; + } + + // Fill remaining payload fields + addListenerCommand->listenerMode = mode; + addListenerCommand->connectionType = type; + addListenerCommand->port = htons((uint16_t)atoi(port)); + strcpy(addListenerCommand->symbolic, symbolic); + + // send message and receive response + struct iovec *response = utils_SendRequest( + state, ADD_LISTENER, addListenerCommand, sizeof(add_listener_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} + +static CommandReturn _controlAddListener_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + if (parcList_Size(args) != 5 && parcList_Size(args) != 6) { + _controlAddListener_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + CommandReturn result = CommandReturn_Failure; + + const char *symbolic = parcList_GetAtIndex(args, _indexSymbolic); + + if (!utils_ValidateSymbolicName(symbolic)) { + printf( + "Error: symbolic name must begin with an alpha and be alphanum " + "after\n"); + return result; + } + + const char *host = parcList_GetAtIndex(args, _indexAddress); + const char *protocol = parcList_GetAtIndex(args, _indexProtocol); + + if ((strcasecmp("hicn", protocol) == 0)) { + const char *port = + "1234"; // this is a random port number that will be ignored + + // here we discard the prefix len if it exists, since we don't use it in + // code but we let libhicn to find the right ip address. + return _CreateListener(parser, ops, symbolic, host, port, HICN_MODE, + HICN_CONN); + } + + const char *port = parcList_GetAtIndex(args, _indexPort); + + if ((strcasecmp("udp", protocol) == 0)) { + return _CreateListener(parser, ops, symbolic, host, port, IP_MODE, + UDP_CONN); + } else if ((strcasecmp("tcp", protocol) == 0)) { + return _CreateListener(parser, ops, symbolic, host, port, IP_MODE, + TCP_CONN); + } else { + _controlAddListener_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + if (result == CommandReturn_Failure) printf("creation failed\n"); + + return result; +} diff --git a/hicn-light/src/config/controlAddListener.h b/hicn-light/src/config/controlAddListener.h new file mode 100755 index 000000000..d3fc55aaf --- /dev/null +++ b/hicn-light/src/config/controlAddListener.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_AddListener.h + * @brief Add a listener to an interface + * + * <#Detailed Description#> + * + */ + +#ifndef Control_AddListener_h +#define Control_AddListener_h + +#include <src/config/controlState.h> +CommandOps *controlAddListener_Create(ControlState *state); +CommandOps *controlAddListener_HelpCreate(ControlState *state); +#endif // Control_AddListener_h diff --git a/hicn-light/src/config/controlAddPunting.c b/hicn-light/src/config/controlAddPunting.c new file mode 100755 index 000000000..bd87e517c --- /dev/null +++ b/hicn-light/src/config/controlAddPunting.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <ctype.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Network.h> +#include <src/utils/punting.h> + +#include <src/config/controlAddPunting.h> + +#include <src/utils/commands.h> +#include <src/utils/utils.h> + +static CommandReturn _controlAddPunting_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlAddPunting_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandAddPunting = "add punting"; +static const char *_commandAddPuntingHelp = "help add punting"; + +static const int _indexSymbolic = 2; +static const int _indexPrefix = 3; + +CommandOps *controlAddPunting_Create(ControlState *state) { + return commandOps_Create(state, _commandAddPunting, NULL, + _controlAddPunting_Execute, commandOps_Destroy); +} + +CommandOps *controlAddPunting_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandAddPuntingHelp, NULL, + _controlAddPunting_HelpExecute, commandOps_Destroy); +} + +// ===================================================== + +static CommandReturn _controlAddPunting_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("add punting <symbolic> <prefix>\n"); + printf(" <symbolic> : listener symbolic name\n"); + printf( + " <address> : prefix to add as a punting rule. (example " + "1234::0/64)\n"); + printf("\n"); + + return CommandReturn_Success; +} + +static CommandReturn _controlAddPunting_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + ControlState *state = ops->closure; + + if (parcList_Size(args) != 4) { + _controlAddPunting_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + const char *symbolicOrConnid = parcList_GetAtIndex(args, _indexSymbolic); + + if (!utils_ValidateSymbolicName(symbolicOrConnid) && + !utils_IsNumber(symbolicOrConnid)) { + printf( + "ERROR: Invalid symbolic or connid:\n" + "symbolic name must begin with an alpha followed by alphanum;\nconnid " + "must be an integer\n"); + return CommandReturn_Failure; + } + + const char *prefixStr = parcList_GetAtIndex(args, _indexPrefix); + char addr[strlen(prefixStr) + 1]; + + // separate address and len + char *slash; + uint32_t len = 0; + strcpy(addr, prefixStr); + slash = strrchr(addr, '/'); + if (slash != NULL) { + len = atoi(slash + 1); + *slash = '\0'; + } + + if (len == 0) { + printf("ERROR: a prefix can not be of length 0\n"); + return CommandReturn_Failure; + } + + // allocate command payload + add_punting_command *addPuntingCommand = + parcMemory_AllocateAndClear(sizeof(add_punting_command)); + + // check and set IP address + if (inet_pton(AF_INET, addr, &addPuntingCommand->address.ipv4) == 1) { + if (len > 32) { + printf("ERROR: exceeded INET mask length, max=32\n"); + parcMemory_Deallocate(&addPuntingCommand); + return CommandReturn_Failure; + } + addPuntingCommand->addressType = ADDR_INET; + } else if (inet_pton(AF_INET6, addr, &addPuntingCommand->address.ipv6) == 1) { + if (len > 128) { + printf("ERROR: exceeded INET6 mask length, max=128\n"); + parcMemory_Deallocate(&addPuntingCommand); + return CommandReturn_Failure; + } + addPuntingCommand->addressType = ADDR_INET6; + } else { + printf("Error: %s is not a valid network address \n", addr); + parcMemory_Deallocate(&addPuntingCommand); + return CommandReturn_Failure; + } + + // Fill remaining payload fields + addPuntingCommand->len = len; + strcpy(addPuntingCommand->symbolicOrConnid, symbolicOrConnid); + + // send message and receive response + struct iovec *response = utils_SendRequest( + state, ADD_PUNTING, addPuntingCommand, sizeof(add_punting_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} + +// ====================================================================== diff --git a/hicn-light/src/config/controlAddPunting.h b/hicn-light/src/config/controlAddPunting.h new file mode 100755 index 000000000..e4d4aac7e --- /dev/null +++ b/hicn-light/src/config/controlAddPunting.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef controlAddPunting_h +#define controlAddPunting_h + +#include <src/config/controlState.h> +CommandOps *controlAddPunting_Create(ControlState *state); +CommandOps *controlAddPunting_HelpCreate(ControlState *state); +#endif diff --git a/hicn-light/src/config/controlAddRoute.c b/hicn-light/src/config/controlAddRoute.c new file mode 100755 index 000000000..c5ddab523 --- /dev/null +++ b/hicn-light/src/config/controlAddRoute.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <ctype.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Network.h> + +#include <src/config/controlAddRoute.h> + +#include <src/utils/commands.h> +#include <src/utils/utils.h> + +static CommandReturn _controlAddRoute_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args); +static CommandReturn _controlAddRoute_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandAddRoute = "add route"; +static const char *_commandAddRouteHelp = "help add route"; + +CommandOps *controlAddRoute_Create(ControlState *state) { + return commandOps_Create(state, _commandAddRoute, NULL, + _controlAddRoute_Execute, commandOps_Destroy); +} + +CommandOps *controlAddRoute_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandAddRouteHelp, NULL, + _controlAddRoute_HelpExecute, commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlAddRoute_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("commands:\n"); + printf(" add route <symbolic | connid> <prefix> <cost>\n"); + printf("\n"); + printf(" symbolic: The symbolic name for an exgress\n"); + printf( + " connid: The egress connection id (see 'help list connections')\n"); + printf( + " prefix: The hicn name as IPv4 or IPv6 address (e.g 1234::0/64)\n"); + printf(" cost: positive integer representing cost\n"); + printf("\n"); + return CommandReturn_Success; +} + +static CommandReturn _controlAddRoute_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + ControlState *state = ops->closure; + + if (parcList_Size(args) != 5) { + _controlAddRoute_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + const char *symbolicOrConnid = parcList_GetAtIndex(args, 2); + + if (!utils_ValidateSymbolicName(symbolicOrConnid) && + !utils_IsNumber(symbolicOrConnid)) { + printf( + "ERROR: Invalid symbolic or connid:\nsymbolic name must begin with an " + "alpha followed by alphanum;\nconnid must be an integer\n"); + return CommandReturn_Failure; + } + + unsigned cost = atoi(parcList_GetAtIndex(args, 4)); + + if (cost == 0) { + printf("ERROR: cost must be positive integer, got %u from '%s'\n", cost, + (char *)parcList_GetAtIndex(args, 4)); + return CommandReturn_Failure; + } + + const char *prefixStr = parcList_GetAtIndex(args, 3); + char addr[strlen(prefixStr) + 1]; + + // separate address and len + char *slash; + uint32_t len = 0; + strcpy(addr, prefixStr); + slash = strrchr(addr, '/'); + if (slash != NULL) { + len = atoi(slash + 1); + *slash = '\0'; + } + + if (len == 0) { + printf("ERROR: a prefix can not be of length 0\n"); + return CommandReturn_Failure; + } + + // allocate command payload + add_route_command *addRouteCommand = + parcMemory_AllocateAndClear(sizeof(add_route_command)); + + // check and set IP address + if (inet_pton(AF_INET, addr, &addRouteCommand->address.ipv4) == 1) { + if (len > 32) { + printf("ERROR: exceeded INET mask length, max=32\n"); + parcMemory_Deallocate(&addRouteCommand); + return CommandReturn_Failure; + } + addRouteCommand->addressType = ADDR_INET; + } else if (inet_pton(AF_INET6, addr, &addRouteCommand->address.ipv6) == 1) { + if (len > 128) { + printf("ERROR: exceeded INET6 mask length, max=128\n"); + parcMemory_Deallocate(&addRouteCommand); + return CommandReturn_Failure; + } + addRouteCommand->addressType = ADDR_INET6; + } else { + printf("Error: %s is not a valid network address \n", addr); + parcMemory_Deallocate(&addRouteCommand); + return CommandReturn_Failure; + } + + // Fill remaining payload fields + addRouteCommand->len = len; + addRouteCommand->cost = (uint16_t)cost; + strcpy(addRouteCommand->symbolicOrConnid, symbolicOrConnid); + + // send message and receive response + struct iovec *response = utils_SendRequest(state, ADD_ROUTE, addRouteCommand, + sizeof(add_route_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlAddRoute.h b/hicn-light/src/config/controlAddRoute.h new file mode 100755 index 000000000..be0ad1368 --- /dev/null +++ b/hicn-light/src/config/controlAddRoute.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_AddRoute.h + * @brief Add a static route + * + * Implements the "add route" node of the CLI tree + * + */ + +#ifndef Control_AddRoute_h +#define Control_AddRoute_h + +#include <src/config/controlState.h> +CommandOps *controlAddRoute_Create(ControlState *state); +CommandOps *controlAddRoute_HelpCreate(ControlState *state); +#endif // Control_AddRoute_h diff --git a/hicn-light/src/config/controlCache.c b/hicn-light/src/config/controlCache.c new file mode 100755 index 000000000..d7afbfe7d --- /dev/null +++ b/hicn-light/src/config/controlCache.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/security/parc_Security.h> + +#include <parc/algol/parc_Memory.h> + +#include <src/config/controlCache.h> +#include <src/config/controlCacheClear.h> +#include <src/config/controlCacheServe.h> +#include <src/config/controlCacheStore.h> + +static void _controlCache_Init(CommandParser *parser, CommandOps *ops); +static CommandReturn _controlCache_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args); +static CommandReturn _controlCache_HelpExecute(CommandParser *parser, + CommandOps *ops, PARCList *args); + +static const char *_commandCache = "cache"; +static const char *_commandCacheHelp = "help cache"; + +CommandOps *controlCache_Create(ControlState *state) { + return commandOps_Create(state, _commandCache, _controlCache_Init, + _controlCache_Execute, commandOps_Destroy); +} + +CommandOps *controlCache_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandCacheHelp, NULL, + _controlCache_HelpExecute, commandOps_Destroy); +} + +// ===================================================== + +static CommandReturn _controlCache_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + CommandOps *ops_cache_serve = controlCacheServe_HelpCreate(NULL); + CommandOps *ops_cache_store = controlCacheStore_HelpCreate(NULL); + CommandOps *ops_cache_clear = controlCacheClear_HelpCreate(NULL); + + printf("Available commands:\n"); + printf(" %s\n", ops_cache_serve->command); + printf(" %s\n", ops_cache_store->command); + printf(" %s\n", ops_cache_clear->command); + printf("\n"); + + commandOps_Destroy(&ops_cache_serve); + commandOps_Destroy(&ops_cache_store); + commandOps_Destroy(&ops_cache_clear); + + return CommandReturn_Success; +} + +static void _controlCache_Init(CommandParser *parser, CommandOps *ops) { + ControlState *state = ops->closure; + controlState_RegisterCommand(state, controlCacheServe_HelpCreate(state)); + controlState_RegisterCommand(state, controlCacheStore_HelpCreate(state)); + controlState_RegisterCommand(state, controlCacheClear_HelpCreate(state)); + controlState_RegisterCommand(state, controlCacheServe_Create(state)); + controlState_RegisterCommand(state, controlCacheStore_Create(state)); + controlState_RegisterCommand(state, controlCacheClear_Create(state)); +} + +static CommandReturn _controlCache_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + return _controlCache_HelpExecute(parser, ops, args); +} + +// ====================================================================== diff --git a/hicn-light/src/config/controlCache.h b/hicn-light/src/config/controlCache.h new file mode 100755 index 000000000..a3614fce1 --- /dev/null +++ b/hicn-light/src/config/controlCache.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef controlCache_h +#define controlCache_h + +#include <src/config/controlState.h> +CommandOps *controlCache_Create(ControlState *state); +CommandOps *controlCache_HelpCreate(ControlState *state); +#endif // controlCache_h diff --git a/hicn-light/src/config/controlCacheClear.c b/hicn-light/src/config/controlCacheClear.c new file mode 100755 index 000000000..c5a4e9fde --- /dev/null +++ b/hicn-light/src/config/controlCacheClear.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_Memory.h> + +#include <src/config/controlCacheClear.h> + +#include <src/utils/commands.h> +#include <src/utils/utils.h> + +static CommandReturn _controlCacheClear_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlCacheClear_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandCacheClear = "cache clear"; +static const char *_commandCacheClearHelp = "help cache clear"; + +// ==================================================== + +CommandOps *controlCacheClear_Create(ControlState *state) { + return commandOps_Create(state, _commandCacheClear, NULL, + _controlCacheClear_Execute, commandOps_Destroy); +} + +CommandOps *controlCacheClear_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandCacheClearHelp, NULL, + _controlCacheClear_HelpExecute, commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlCacheClear_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("cache clear\n"); + printf("\n"); + + return CommandReturn_Success; +} + +static CommandReturn _controlCacheClear_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + if (parcList_Size(args) != 2) { + _controlCacheClear_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + ControlState *state = ops->closure; + // send message and receive response + struct iovec *response = utils_SendRequest(state, CACHE_CLEAR, NULL, 0); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlCacheClear.h b/hicn-light/src/config/controlCacheClear.h new file mode 100755 index 000000000..348ddba12 --- /dev/null +++ b/hicn-light/src/config/controlCacheClear.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file controlCacheClear.h + * @brief Clear the cache + * + * Removes all the cached data form the local content store (if available) + * + */ + +#ifndef Control_CacheClear_h +#define Control_CacheClear_h + +#include <src/config/controlState.h> +CommandOps *controlCacheClear_Create(ControlState *state); +CommandOps *controlCacheClear_HelpCreate(ControlState *state); +#endif // Control_CacheClear_h diff --git a/hicn-light/src/config/controlCacheServe.c b/hicn-light/src/config/controlCacheServe.c new file mode 100755 index 000000000..85d598025 --- /dev/null +++ b/hicn-light/src/config/controlCacheServe.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_Memory.h> + +#include <src/config/controlCacheServe.h> + +#include <src/utils/commands.h> +#include <src/utils/utils.h> + +static CommandReturn _controlCacheServe_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlCacheServe_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandCacheServe = "cache serve"; +static const char *_commandCacheServeHelp = "help cache serve"; + +// ==================================================== + +CommandOps *controlCacheServe_Create(ControlState *state) { + return commandOps_Create(state, _commandCacheServe, NULL, + _controlCacheServe_Execute, commandOps_Destroy); +} + +CommandOps *controlCacheServe_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandCacheServeHelp, NULL, + _controlCacheServe_HelpExecute, commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlCacheServe_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("cache serve [on|off]\n"); + printf("\n"); + + return CommandReturn_Success; +} + +static CommandReturn _controlCacheServe_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + if (parcList_Size(args) != 3) { + _controlCacheServe_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + bool active; + if (strcmp(parcList_GetAtIndex(args, 2), "on") == 0) { + active = true; + } else if (strcmp(parcList_GetAtIndex(args, 2), "off") == 0) { + active = false; + } else { + _controlCacheServe_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + cache_serve_command *cacheServeCommand = + parcMemory_AllocateAndClear(sizeof(cache_serve_command)); + if (active) { + cacheServeCommand->activate = ACTIVATE_ON; + } else { + cacheServeCommand->activate = ACTIVATE_OFF; + } + + ControlState *state = ops->closure; + // send message and receive response + struct iovec *response = utils_SendRequest( + state, CACHE_SERVE, cacheServeCommand, sizeof(cache_serve_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlCacheServe.h b/hicn-light/src/config/controlCacheServe.h new file mode 100755 index 000000000..4bcec51f0 --- /dev/null +++ b/hicn-light/src/config/controlCacheServe.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef Control_CacheServe_h +#define Control_CacheServe_h + +#include <src/config/controlState.h> +CommandOps *controlCacheServe_Create(ControlState *state); +CommandOps *controlCacheServe_HelpCreate(ControlState *state); +#endif // Control_CacheServe_h diff --git a/hicn-light/src/config/controlCacheStore.c b/hicn-light/src/config/controlCacheStore.c new file mode 100755 index 000000000..3bbb34386 --- /dev/null +++ b/hicn-light/src/config/controlCacheStore.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_Memory.h> + +#include <src/config/controlCacheStore.h> + +#include <src/utils/commands.h> +#include <src/utils/utils.h> + +static CommandReturn _controlCacheStore_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlCacheStore_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandCacheStore = "cache store"; +static const char *_commandCacheStoreHelp = "help cache store"; + +// ==================================================== + +CommandOps *controlCacheStore_Create(ControlState *state) { + return commandOps_Create(state, _commandCacheStore, NULL, + _controlCacheStore_Execute, commandOps_Destroy); +} + +CommandOps *controlCacheStore_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandCacheStoreHelp, NULL, + _controlCacheStore_HelpExecute, commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlCacheStore_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("cache store [on|off]\n"); + printf("\n"); + + return CommandReturn_Success; +} + +static CommandReturn _controlCacheStore_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + if (parcList_Size(args) != 3) { + _controlCacheStore_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + bool active; + if (strcmp(parcList_GetAtIndex(args, 2), "on") == 0) { + active = true; + } else if (strcmp(parcList_GetAtIndex(args, 2), "off") == 0) { + active = false; + } else { + _controlCacheStore_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + cache_store_command *cacheStoreCommand = + parcMemory_AllocateAndClear(sizeof(cache_store_command)); + if (active) { + cacheStoreCommand->activate = ACTIVATE_ON; + } else { + cacheStoreCommand->activate = ACTIVATE_OFF; + } + + ControlState *state = ops->closure; + // send message and receive response + struct iovec *response = utils_SendRequest( + state, CACHE_STORE, cacheStoreCommand, sizeof(cache_store_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlCacheStore.h b/hicn-light/src/config/controlCacheStore.h new file mode 100755 index 000000000..ef5aca504 --- /dev/null +++ b/hicn-light/src/config/controlCacheStore.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef Control_CacheStore_h +#define Control_CacheStore_h + +#include <src/config/controlState.h> +CommandOps *controlCacheStore_Create(ControlState *state); +CommandOps *controlCacheStore_HelpCreate(ControlState *state); +#endif // Control_CacheStore_h diff --git a/hicn-light/src/config/controlList.c b/hicn-light/src/config/controlList.c new file mode 100755 index 000000000..8afaa60dc --- /dev/null +++ b/hicn-light/src/config/controlList.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/security/parc_Security.h> + +#include <parc/algol/parc_Memory.h> + +#include <src/config/controlList.h> +#include <src/config/controlListConnections.h> +//#include <src/config/controlListInterfaces.h> +#include <src/config/controlListListeners.h> +#include <src/config/controlListRoutes.h> + +static void _controlList_Init(CommandParser *parser, CommandOps *ops); +static CommandReturn _controlList_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args); +static CommandReturn _controlList_HelpExecute(CommandParser *parser, + CommandOps *ops, PARCList *args); + +static const char *_commandList = "list"; +static const char *_commandListHelp = "help list"; + +CommandOps *controlList_Create(ControlState *state) { + return commandOps_Create(state, _commandList, _controlList_Init, + _controlList_Execute, commandOps_Destroy); +} + +CommandOps *controlList_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandListHelp, NULL, + _controlList_HelpExecute, commandOps_Destroy); +} + +// ===================================================== + +static CommandReturn _controlList_HelpExecute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + CommandOps *ops_list_connections = controlListConnections_HelpCreate(NULL); + // CommandOps *ops_list_interfaces = controlListInterfaces_HelpCreate(NULL); + CommandOps *ops_list_routes = controlListRoutes_HelpCreate(NULL); + CommandOps *ops_list_listeners = controlListListeners_HelpCreate(NULL); + + printf("Available commands:\n"); + printf(" %s\n", ops_list_connections->command); + // printf(" %s\n", ops_list_interfaces->command); + printf(" %s\n", ops_list_routes->command); + printf(" %s\n", ops_list_listeners->command); + printf("\n"); + + commandOps_Destroy(&ops_list_connections); + // commandOps_Destroy(&ops_list_interfaces); + commandOps_Destroy(&ops_list_routes); + commandOps_Destroy(&ops_list_listeners); + + return CommandReturn_Success; +} + +static void _controlList_Init(CommandParser *parser, CommandOps *ops) { + ControlState *state = ops->closure; + controlState_RegisterCommand(state, controlListConnections_HelpCreate(state)); + // controlState_RegisterCommand(state, + // controlListInterfaces_HelpCreate(state)); + controlState_RegisterCommand(state, controlListListeners_HelpCreate(state)); + controlState_RegisterCommand(state, controlListRoutes_HelpCreate(state)); + controlState_RegisterCommand(state, controlListConnections_Create(state)); + // controlState_RegisterCommand(state, controlListInterfaces_Create(state)); + controlState_RegisterCommand(state, controlListRoutes_Create(state)); + controlState_RegisterCommand(state, controlListListeners_Create(state)); +} + +static CommandReturn _controlList_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + return _controlList_HelpExecute(parser, ops, args); +} + +// ====================================================================== diff --git a/hicn-light/src/config/controlList.h b/hicn-light/src/config/controlList.h new file mode 100755 index 000000000..53197331f --- /dev/null +++ b/hicn-light/src/config/controlList.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_List.h + * @brief Root node for the "list" commands + * + * Implements the "list" node of the CLI tree. + * + */ + +#ifndef controlList_h +#define controlList_h + +#include <src/config/controlState.h> +CommandOps *controlList_Create(ControlState *state); +CommandOps *controlList_HelpCreate(ControlState *state); +#endif // controlList_h diff --git a/hicn-light/src/config/controlListConnections.c b/hicn-light/src/config/controlListConnections.c new file mode 100755 index 000000000..474ddc45f --- /dev/null +++ b/hicn-light/src/config/controlListConnections.c @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_Memory.h> + +#include <src/config/controlListConnections.h> + +#include <src/utils/commands.h> +#include <src/utils/utils.h> + +static CommandReturn _controlListConnections_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlListConnections_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandListConnections = "list connections"; +static const char *_commandListConnectionsHelp = "help list connections"; +const char *connTypeString[6] = {"GRE", "TCP", "UDP", "MCAST", "L2", "HICN"}; +const char *stateString[3] = {"UP", "DOWN", "UNKNOWN"}; + +CommandOps *controlListConnections_Create(ControlState *state) { + return commandOps_Create(state, _commandListConnections, NULL, + _controlListConnections_Execute, commandOps_Destroy); +} + +CommandOps *controlListConnections_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandListConnectionsHelp, NULL, + _controlListConnections_HelpExecute, + commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlListConnections_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("list connections: displays a 1-line summary of each connection\n"); + printf("\n"); + printf("The columns are:\n"); + printf(" connection id : an integer index for the connection\n"); + printf(" state : UP or DOWN\n"); + printf( + " local address : the local network address associated with the " + "connection\n"); + printf( + " remote address: the remote network address associated with the " + "connection\n"); + printf( + " protocol : the network protocol (tcp, udp, gre, mcast, " + "ether)\n"); + printf("\n"); + return CommandReturn_Success; +} + +static CommandReturn _controlListConnections_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + if (parcList_Size(args) != 2) { + _controlListConnections_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + ControlState *state = ops->closure; + + // send message and receive response + struct iovec *response = utils_SendRequest(state, LIST_CONNECTIONS, NULL, 0); + if (!response) { // get NULL pointer = FAILURE + return CommandReturn_Failure; + } + + // Process/Print message + header_control_message *receivedHeader = + (header_control_message *)response[0].iov_base; + uint8_t *receivedPayload = (uint8_t *)response[1].iov_base; + + char *sourceString = NULL; + char *destinationString = NULL; + + // Allocate output to pass to the main function if the call is not interactive + char **commandOutputMain = NULL; + if (!controlState_IsInteractive(state) && receivedHeader->length > 0) { + commandOutputMain = + parcMemory_Allocate(sizeof(char *) * receivedHeader->length); + for (size_t j = 0; j < receivedHeader->length; j++) { + commandOutputMain[j] = parcMemory_Allocate(sizeof(char) * 128); + } + } + + // Process/Print payload + for (int i = 0; i < receivedHeader->length; i++) { + list_connections_command *listConnectionsCommand = + (list_connections_command *)(receivedPayload + + (i * sizeof(list_connections_command))); + + sourceString = utils_CommandAddressToString( + listConnectionsCommand->connectionData.ipType, + &listConnectionsCommand->connectionData.localIp, + &listConnectionsCommand->connectionData.localPort); + + destinationString = utils_CommandAddressToString( + listConnectionsCommand->connectionData.ipType, + &listConnectionsCommand->connectionData.remoteIp, + &listConnectionsCommand->connectionData.remotePort); + + PARCBufferComposer *composer = parcBufferComposer_Create(); + + parcBufferComposer_Format( + composer, "%5d %4s %s %s %s", listConnectionsCommand->connid, + stateString[listConnectionsCommand->state], sourceString, + destinationString, + connTypeString[listConnectionsCommand->connectionData.connectionType]); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + + if (!controlState_IsInteractive(state)) { + strcpy(commandOutputMain[i], result); + } + + puts(result); + parcMemory_Deallocate((void **)&result); + parcBufferComposer_Release(&composer); + } + + controlState_SetCommandOutput(state, commandOutputMain); + + // DEALLOCATE + parcMemory_Deallocate((void **)&sourceString); + parcMemory_Deallocate((void **)&destinationString); + parcMemory_Deallocate(&receivedHeader); // free response[0].iov_base + parcMemory_Deallocate(&receivedPayload); // free response[1].iov_base + parcMemory_Deallocate(&response); // free iovec pointer + + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlListConnections.h b/hicn-light/src/config/controlListConnections.h new file mode 100755 index 000000000..17422c963 --- /dev/null +++ b/hicn-light/src/config/controlListConnections.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_ListConnections.h + * @brief List the current connections of hicn-light + * + * Implements the "list connections" node of the CLI tree + * + */ + +#ifndef Control_ListConnections_h +#define Control_ListConnections_h + +#include <src/config/controlState.h> +CommandOps *controlListConnections_Create(ControlState *state); +CommandOps *controlListConnections_HelpCreate(ControlState *state); +#endif // Control_ListConnections_h diff --git a/hicn-light/src/config/controlListInterfaces.c b/hicn-light/src/config/controlListInterfaces.c new file mode 100755 index 000000000..20338b553 --- /dev/null +++ b/hicn-light/src/config/controlListInterfaces.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_Memory.h> + +#include <src/config/controlListInterfaces.h> + +static CommandReturn _controlListInterfaces_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlListInterfaces_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandListInterfaces = "list interfaces"; +static const char *_commandListInterfacesHelp = "help list interfaces"; + +// ==================================================== + +CommandOps *controlListInterfaces_Create(ControlState *state) { + return commandOps_Create(state, _commandListInterfaces, NULL, + _controlListInterfaces_Execute, commandOps_Destroy); +} + +CommandOps *controlListInterfaces_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandListInterfacesHelp, NULL, + _controlListInterfaces_HelpExecute, + commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlListInterfaces_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("list interfaces\n"); + printf("\n"); + + return CommandReturn_Success; +} + +static CommandReturn _controlListInterfaces_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + if (parcList_Size(args) != 2) { + _controlListInterfaces_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + //========================== NOT IMPLEMENTED + //=========================== + + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlListInterfaces.h b/hicn-light/src/config/controlListInterfaces.h new file mode 100755 index 000000000..0c0ca95cf --- /dev/null +++ b/hicn-light/src/config/controlListInterfaces.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_ListInterfaces.h + * @brief List the icn-light interfaces + * + * Implements the "list interfaces" and "help list interfaces" nodes of the + * command tree + * + */ + +#ifndef Control_ListInterfaces_h +#define Control_ListInterfaces_h + +#include <src/config/controlState.h> +CommandOps *controlListInterfaces_Create(ControlState *state); +CommandOps *controlListInterfaces_HelpCreate(ControlState *state); +#endif // Control_ListInterfaces_h diff --git a/hicn-light/src/config/controlListListeners.c b/hicn-light/src/config/controlListListeners.c new file mode 100755 index 000000000..a149051e2 --- /dev/null +++ b/hicn-light/src/config/controlListListeners.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/assert/parc_Assert.h> + +#include <src/config/controlListListeners.h> +#include <src/utils/commands.h> +#include <src/utils/utils.h> + +static CommandReturn _controlListListeners_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlListListeners_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandListListeners = "list listeners"; +static const char *_commandListListenersHelp = "help list listeners"; +static const char *listenerType[5] = {"TCP", "UDP", "ETHER", "LOCAL", "HICN"}; + +// ==================================================== + +CommandOps *controlListListeners_Create(ControlState *state) { + return commandOps_Create(state, _commandListListeners, NULL, + _controlListListeners_Execute, commandOps_Destroy); +} + +CommandOps *controlListListeners_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandListListenersHelp, NULL, + _controlListListeners_HelpExecute, + commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlListListeners_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("list listeners\n"); + printf("\n"); + + return CommandReturn_Success; +} + +static CommandReturn _controlListListeners_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + if (parcList_Size(args) != 2) { + _controlListListeners_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + ControlState *state = ops->closure; + + // send message and receive response + struct iovec *response = utils_SendRequest(state, LIST_LISTENERS, NULL, 0); + if (!response) { // get NULL pointer = FAILURE + return CommandReturn_Failure; + } + + // Process/Print message + header_control_message *receivedHeader = + (header_control_message *)response[0].iov_base; + uint8_t *receivedPayload = (uint8_t *)response[1].iov_base; + + // Allocate output to pass to the main function if the call is not interactive + char **commandOutputMain = NULL; + if (!controlState_IsInteractive(state) && receivedHeader->length > 0) { + commandOutputMain = + parcMemory_Allocate(sizeof(char *) * receivedHeader->length); + for (size_t j = 0; j < receivedHeader->length; j++) { + commandOutputMain[j] = parcMemory_Allocate(sizeof(char) * 128); + } + } + + char *addrString = NULL; + if (receivedHeader->length > 0) { + printf("%6.6s %50.70s %s\n", "iface", "address", "type"); + } else { + printf(" --- No entry in the list \n"); + } + + for (int i = 0; i < receivedHeader->length; i++) { + list_listeners_command *listListenersCommand = + (list_listeners_command *)(receivedPayload + + (i * sizeof(list_listeners_command))); + + addrString = utils_CommandAddressToString(listListenersCommand->addressType, + &listListenersCommand->address, + &listListenersCommand->port); + + PARCBufferComposer *composer = parcBufferComposer_Create(); + + parcBufferComposer_Format(composer, "%6u %50.70s %3s", + listListenersCommand->connid, addrString, + listenerType[listListenersCommand->encapType]); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + + if (!controlState_IsInteractive(state)) { + strcpy(commandOutputMain[i], result); + } + + puts(result); + parcMemory_Deallocate((void **)&result); + parcBufferComposer_Release(&composer); + } + + controlState_SetCommandOutput(state, commandOutputMain); + + // DEALLOCATE + parcMemory_Deallocate((void **)&addrString); + parcMemory_Deallocate(&receivedHeader); // free response[0].iov_base + parcMemory_Deallocate(&receivedPayload); // free response[1].iov_base + parcMemory_Deallocate(&response); // free iovec pointer + + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlListListeners.h b/hicn-light/src/config/controlListListeners.h new file mode 100755 index 000000000..1f34eea56 --- /dev/null +++ b/hicn-light/src/config/controlListListeners.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_ListListeners.h + * @brief List the icn-light listeners + * + * Implements the "list listeners" and "help list listeners" nodes of the + * command tree + * + */ + +#ifndef Control_ListListeners_h +#define Control_ListListeners_h + +#include <src/config/controlState.h> +CommandOps *controlListListeners_Create(ControlState *state); +CommandOps *controlListListeners_HelpCreate(ControlState *state); +#endif // Control_ListListeners_h diff --git a/hicn-light/src/config/controlListRoutes.c b/hicn-light/src/config/controlListRoutes.c new file mode 100755 index 000000000..4a21b5ef4 --- /dev/null +++ b/hicn-light/src/config/controlListRoutes.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Time.h> + +#include <src/config/controlListRoutes.h> + +#include <src/utils/commands.h> +#include <src/utils/utils.h> + +static CommandReturn _controlListRoutes_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlListRoutes_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandListRoutes = "list routes"; +static const char *_commandListRoutesHelp = "help list routes"; + +// ==================================================== + +CommandOps *controlListRoutes_Create(ControlState *state) { + return commandOps_Create(state, _commandListRoutes, NULL, + _controlListRoutes_Execute, commandOps_Destroy); +} + +CommandOps *controlListRoutes_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandListRoutesHelp, NULL, + _controlListRoutes_HelpExecute, commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlListRoutes_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("command: list routes\n"); + printf("\n"); + printf( + "This command will fetch the prefix routing table. For each route, it " + "will list:\n"); + printf(" iface: interface\n"); + printf( + " protocol: the routing protocol, such as STATIC, CONNECTED, etc.\n"); + printf( + " type: LMP or EXACT (longest matching prefix or exact match)\n"); + printf(" cost: The route cost, lower being preferred\n"); + printf(" next: List of next hops by interface id\n"); + printf(" prefix: name prefix\n"); + printf("\n"); + return CommandReturn_Success; +} + +static CommandReturn _controlListRoutes_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + if (parcList_Size(args) != 2) { + _controlListRoutes_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + ControlState *state = ops->closure; + + // send message and receive response + struct iovec *response = utils_SendRequest(state, LIST_ROUTES, NULL, 0); + if (!response) { // get NULL pointer = FAILURE + return CommandReturn_Failure; + } + + // Process/Print message + header_control_message *receivedHeader = + (header_control_message *)response[0].iov_base; + uint8_t *receivedPayload = (uint8_t *)response[1].iov_base; + + // Allocate output to pass to the main function if the call is not interactive + char **commandOutputMain = NULL; + if (!controlState_IsInteractive(state) && receivedHeader->length > 0) { + commandOutputMain = + parcMemory_Allocate(sizeof(char *) * receivedHeader->length); + for (size_t j = 0; j < receivedHeader->length; j++) { + commandOutputMain[j] = parcMemory_Allocate(sizeof(char) * 128); + } + } + + char *addrString = NULL; + in_port_t port = htons(1234); // this is a random port number that is ignored + + if (receivedHeader->length > 0) { + printf("%6.6s %8.8s %70.70s %s\n", "iface", "cost", "prefix", "len"); + } else { + printf(" --- No entry in the list \n"); + } + + for (int i = 0; i < receivedHeader->length; i++) { + list_routes_command *listRoutesCommand = + (list_routes_command *)(receivedPayload + + (i * sizeof(list_routes_command))); + + addrString = utils_CommandAddressToString( + listRoutesCommand->addressType, &listRoutesCommand->address, &port); + + PARCBufferComposer *composer = parcBufferComposer_Create(); + + parcBufferComposer_Format( + composer, "%6u %8u %70.70s %3d", listRoutesCommand->connid, + listRoutesCommand->cost, addrString, listRoutesCommand->len); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + + if (!controlState_IsInteractive(state)) { + strcpy(commandOutputMain[i], result); + } + + puts(result); + parcMemory_Deallocate((void **)&result); + parcBufferComposer_Release(&composer); + } + + controlState_SetCommandOutput(state, commandOutputMain); + + // DEALLOCATE + parcMemory_Deallocate((void **)&addrString); + parcMemory_Deallocate(&receivedHeader); // free response[0].iov_base + parcMemory_Deallocate(&receivedPayload); // free response[1].iov_base + parcMemory_Deallocate(&response); // free iovec pointer + + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlListRoutes.h b/hicn-light/src/config/controlListRoutes.h new file mode 100755 index 000000000..018c88ab0 --- /dev/null +++ b/hicn-light/src/config/controlListRoutes.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_ListRoutes.h + * @brief List the icn-light routes + * + * Implements the "list routes" and "help list routes" nodes of the command tree + * + */ +#ifndef Control_ListRoutes_h +#define Control_ListRoutes_h + +#include <src/config/controlState.h> +CommandOps *controlListRoutes_Create(ControlState *state); +CommandOps *controlListRoutes_HelpCreate(ControlState *state); +#endif // Control_ListRoutes_h diff --git a/hicn-light/src/config/controlMapMe.c b/hicn-light/src/config/controlMapMe.c new file mode 100755 index 000000000..2253f52b6 --- /dev/null +++ b/hicn-light/src/config/controlMapMe.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/assert/parc_Assert.h> + +#include <src/config/controlMapMe.h> +#include <src/config/controlMapMeDiscovery.h> +#include <src/config/controlMapMeEnable.h> +#include <src/config/controlMapMeRetx.h> +#include <src/config/controlMapMeTimescale.h> + +static void _controlMapMe_Init(CommandParser *parser, CommandOps *ops); +static CommandReturn _controlMapMe_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args); +static CommandReturn _controlMapMe_HelpExecute(CommandParser *parser, + CommandOps *ops, PARCList *args); + +static const char *_commandMapMe = "mapme"; +static const char *_commandMapMeHelp = "help mapme"; + +CommandOps *controlMapMe_Create(ControlState *state) { + return commandOps_Create(state, _commandMapMe, _controlMapMe_Init, + _controlMapMe_Execute, commandOps_Destroy); +} + +CommandOps *controlMapMe_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandMapMeHelp, NULL, + _controlMapMe_HelpExecute, commandOps_Destroy); +} + +// ===================================================== + +static CommandReturn _controlMapMe_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + CommandOps *ops_mapme_enable = controlMapMeEnable_HelpCreate(NULL); + CommandOps *ops_mapme_discovery = controlMapMeDiscovery_HelpCreate(NULL); + CommandOps *ops_mapme_timescale = controlMapMeTimescale_HelpCreate(NULL); + CommandOps *ops_mapme_retx = controlMapMeRetx_HelpCreate(NULL); + + printf("Available commands:\n"); + printf(" %s\n", ops_mapme_enable->command); + printf(" %s\n", ops_mapme_discovery->command); + printf(" %s\n", ops_mapme_timescale->command); + printf(" %s\n", ops_mapme_retx->command); + printf("\n"); + + commandOps_Destroy(&ops_mapme_enable); + commandOps_Destroy(&ops_mapme_discovery); + commandOps_Destroy(&ops_mapme_timescale); + commandOps_Destroy(&ops_mapme_retx); + + return CommandReturn_Success; +} + +static void _controlMapMe_Init(CommandParser *parser, CommandOps *ops) { + ControlState *state = ops->closure; + controlState_RegisterCommand(state, controlMapMeEnable_HelpCreate(state)); + controlState_RegisterCommand(state, controlMapMeDiscovery_HelpCreate(state)); + controlState_RegisterCommand(state, controlMapMeTimescale_HelpCreate(state)); + controlState_RegisterCommand(state, controlMapMeRetx_HelpCreate(state)); + controlState_RegisterCommand(state, controlMapMeEnable_Create(state)); + controlState_RegisterCommand(state, controlMapMeDiscovery_Create(state)); + controlState_RegisterCommand(state, controlMapMeTimescale_Create(state)); + controlState_RegisterCommand(state, controlMapMeRetx_Create(state)); +} + +static CommandReturn _controlMapMe_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + return _controlMapMe_HelpExecute(parser, ops, args); +} + +// ====================================================================== diff --git a/hicn-light/src/config/controlMapMe.h b/hicn-light/src/config/controlMapMe.h new file mode 100755 index 000000000..d9cfdb82c --- /dev/null +++ b/hicn-light/src/config/controlMapMe.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef controlMapMe_h +#define controlMapMe_h + +#include <src/config/controlState.h> +CommandOps *controlMapMe_Create(ControlState *state); +CommandOps *controlMapMe_HelpCreate(ControlState *state); +#endif // controlMapMe_h diff --git a/hicn-light/src/config/controlMapMeDiscovery.c b/hicn-light/src/config/controlMapMeDiscovery.c new file mode 100755 index 000000000..f8f4bf082 --- /dev/null +++ b/hicn-light/src/config/controlMapMeDiscovery.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/assert/parc_Assert.h> +#include <src/config/controlMapMeDiscovery.h> + +#include <src/utils/commands.h> +#include <src/utils/utils.h> + +static CommandReturn _controlMapMeDiscovery_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlMapMeDiscovery_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandMapMeDiscovery = "mapme discovery"; +static const char *_commandMapMeDiscoveryHelp = "help mapme discovery"; + +// ==================================================== + +CommandOps *controlMapMeDiscovery_Create(ControlState *state) { + return commandOps_Create(state, _commandMapMeDiscovery, NULL, + _controlMapMeDiscovery_Execute, commandOps_Destroy); +} + +CommandOps *controlMapMeDiscovery_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandMapMeDiscoveryHelp, NULL, + _controlMapMeDiscovery_HelpExecute, + commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlMapMeDiscovery_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("mapme discovery [on|off]\n"); + printf("\n"); + + return CommandReturn_Success; +} + +static CommandReturn _controlMapMeDiscovery_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + if (parcList_Size(args) != 3) { + _controlMapMeDiscovery_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + bool active; + if (strcmp(parcList_GetAtIndex(args, 2), "on") == 0) { + active = true; + } else if (strcmp(parcList_GetAtIndex(args, 2), "off") == 0) { + active = false; + } else { + _controlMapMeDiscovery_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + mapme_activator_command *mapmeDiscoveryCommand = + parcMemory_AllocateAndClear(sizeof(mapme_activator_command)); + if (active) { + mapmeDiscoveryCommand->activate = ACTIVATE_ON; + } else { + mapmeDiscoveryCommand->activate = ACTIVATE_OFF; + } + + ControlState *state = ops->closure; + // send message and receive response + struct iovec *response = + utils_SendRequest(state, MAPME_DISCOVERY, mapmeDiscoveryCommand, + sizeof(mapme_activator_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlMapMeDiscovery.h b/hicn-light/src/config/controlMapMeDiscovery.h new file mode 100755 index 000000000..c492fa0ab --- /dev/null +++ b/hicn-light/src/config/controlMapMeDiscovery.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef Control_MapMeDiscovery_h +#define Control_MapMeDiscovery_h + +#include <src/config/controlState.h> +CommandOps *controlMapMeDiscovery_Create(ControlState *state); +CommandOps *controlMapMeDiscovery_HelpCreate(ControlState *state); +#endif // Control_MapMeDiscovery_h diff --git a/hicn-light/src/config/controlMapMeEnable.c b/hicn-light/src/config/controlMapMeEnable.c new file mode 100755 index 000000000..db77450e5 --- /dev/null +++ b/hicn-light/src/config/controlMapMeEnable.c @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/assert/parc_Assert.h> +#include <src/config/controlMapMeEnable.h> + +#include <src/utils/commands.h> +#include <src/utils/utils.h> + +static CommandReturn _controlMapMeEnable_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlMapMeEnable_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandMapMeEnable = "mapme enable"; +static const char *_commandMapMeEnableHelp = "help mapme enable"; + +// ==================================================== + +CommandOps *controlMapMeEnable_Create(ControlState *state) { + return commandOps_Create(state, _commandMapMeEnable, NULL, + _controlMapMeEnable_Execute, commandOps_Destroy); +} + +CommandOps *controlMapMeEnable_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandMapMeEnableHelp, NULL, + _controlMapMeEnable_HelpExecute, commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlMapMeEnable_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("mapme enable [on|off]\n"); + printf("\n"); + + return CommandReturn_Success; +} + +static CommandReturn _controlMapMeEnable_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + if (parcList_Size(args) != 3) { + _controlMapMeEnable_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + bool active; + if (strcmp(parcList_GetAtIndex(args, 2), "on") == 0) { + active = true; + } else if (strcmp(parcList_GetAtIndex(args, 2), "off") == 0) { + active = false; + } else { + _controlMapMeEnable_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + mapme_activator_command *mapmeEnableCommand = + parcMemory_AllocateAndClear(sizeof(mapme_activator_command)); + if (active) { + mapmeEnableCommand->activate = ACTIVATE_ON; + } else { + mapmeEnableCommand->activate = ACTIVATE_OFF; + } + + ControlState *state = ops->closure; + // send message and receive response + struct iovec *response = utils_SendRequest( + state, MAPME_ENABLE, mapmeEnableCommand, sizeof(mapme_activator_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlMapMeEnable.h b/hicn-light/src/config/controlMapMeEnable.h new file mode 100755 index 000000000..f7ca6204d --- /dev/null +++ b/hicn-light/src/config/controlMapMeEnable.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef Control_MapMeEnable_h +#define Control_MapMeEnable_h + +#include <src/config/controlState.h> +CommandOps *controlMapMeEnable_Create(ControlState *state); +CommandOps *controlMapMeEnable_HelpCreate(ControlState *state); +#endif // Control_MapMeEnable_h diff --git a/hicn-light/src/config/controlMapMeRetx.c b/hicn-light/src/config/controlMapMeRetx.c new file mode 100755 index 000000000..bb16b8833 --- /dev/null +++ b/hicn-light/src/config/controlMapMeRetx.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/assert/parc_Assert.h> +#include <src/config/controlMapMeRetx.h> + +#include <src/utils/commands.h> +#include <src/utils/utils.h> + +static CommandReturn _controlMapMeRetx_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args); +static CommandReturn _controlMapMeRetx_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandMapMeRetx = "mapme retx"; +static const char *_commandMapMeRetxHelp = "help mapme retx"; + +// ==================================================== + +CommandOps *controlMapMeRetx_Create(ControlState *state) { + return commandOps_Create(state, _commandMapMeRetx, NULL, + _controlMapMeRetx_Execute, commandOps_Destroy); +} + +CommandOps *controlMapMeRetx_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandMapMeRetxHelp, NULL, + _controlMapMeRetx_HelpExecute, commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlMapMeRetx_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("mapme retx <milliseconds>n"); + printf("\n"); + + return CommandReturn_Success; +} + +static CommandReturn _controlMapMeRetx_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + if (parcList_Size(args) != 3) { + _controlMapMeRetx_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + const char *rtx = parcList_GetAtIndex(args, 2); + if (!utils_IsNumber(rtx)) { + printf( + "ERROR: retransmission value (expressed in ms) must be a positive " + "integer \n"); + return CommandReturn_Failure; + } + + mapme_timing_command *mapmeRetxCommand = + parcMemory_AllocateAndClear(sizeof(mapme_timing_command)); + mapmeRetxCommand->timePeriod = (unsigned)strtold(rtx, NULL); + + ControlState *state = ops->closure; + // send message and receive response + struct iovec *response = utils_SendRequest( + state, MAPME_RETX, mapmeRetxCommand, sizeof(mapme_timing_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlMapMeRetx.h b/hicn-light/src/config/controlMapMeRetx.h new file mode 100755 index 000000000..611bd3663 --- /dev/null +++ b/hicn-light/src/config/controlMapMeRetx.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef Control_MapMeRetx_h +#define Control_MapMeRetx_h + +#include <src/config/controlState.h> +CommandOps *controlMapMeRetx_Create(ControlState *state); +CommandOps *controlMapMeRetx_HelpCreate(ControlState *state); +#endif // Control_MapMeRetx_h diff --git a/hicn-light/src/config/controlMapMeTimescale.c b/hicn-light/src/config/controlMapMeTimescale.c new file mode 100755 index 000000000..9303b4b0f --- /dev/null +++ b/hicn-light/src/config/controlMapMeTimescale.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/assert/parc_Assert.h> +#include <src/config/controlMapMeTimescale.h> + +#include <src/utils/commands.h> +#include <src/utils/utils.h> + +static CommandReturn _controlMapMeTimescale_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlMapMeTimescale_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandMapMeTimescale = "mapme timescale"; +static const char *_commandMapMeTimescaleHelp = "help mapme timescale"; + +// ==================================================== + +CommandOps *controlMapMeTimescale_Create(ControlState *state) { + return commandOps_Create(state, _commandMapMeTimescale, NULL, + _controlMapMeTimescale_Execute, commandOps_Destroy); +} + +CommandOps *controlMapMeTimescale_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandMapMeTimescaleHelp, NULL, + _controlMapMeTimescale_HelpExecute, + commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlMapMeTimescale_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("mapme timescale <milliseconds>n"); + printf("\n"); + + return CommandReturn_Success; +} + +static CommandReturn _controlMapMeTimescale_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + if (parcList_Size(args) != 3) { + _controlMapMeTimescale_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + const char *ts = parcList_GetAtIndex(args, 2); + if (!utils_IsNumber(ts)) { + printf( + "ERROR: timescale value (expressed in ms) must be a positive integer " + "\n"); + return CommandReturn_Failure; + } + + mapme_timing_command *mapmeTimescaleCommand = + parcMemory_AllocateAndClear(sizeof(mapme_timing_command)); + mapmeTimescaleCommand->timePeriod = (unsigned)strtold(ts, NULL); + + ControlState *state = ops->closure; + // send message and receive response + struct iovec *response = + utils_SendRequest(state, MAPME_TIMESCALE, mapmeTimescaleCommand, + sizeof(mapme_timing_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlMapMeTimescale.h b/hicn-light/src/config/controlMapMeTimescale.h new file mode 100755 index 000000000..d4b383696 --- /dev/null +++ b/hicn-light/src/config/controlMapMeTimescale.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef Control_MapMeTimescale_h +#define Control_MapMeTimescale_h + +#include <src/config/controlState.h> +CommandOps *controlMapMeTimescale_Create(ControlState *state); +CommandOps *controlMapMeTimescale_HelpCreate(ControlState *state); +#endif // Control_MapMeTimescale_h diff --git a/hicn-light/src/config/controlQuit.c b/hicn-light/src/config/controlQuit.c new file mode 100755 index 000000000..635fe278f --- /dev/null +++ b/hicn-light/src/config/controlQuit.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/security/parc_Security.h> + +#include <src/config/controlQuit.h> + +static CommandReturn _controlQuit_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args); +static CommandReturn _controlQuit_HelpExecute(CommandParser *parser, + CommandOps *ops, PARCList *args); + +static const char *_commandQuit = "quit"; +static const char *_commandQuitHelp = "help quit"; + +// ==================================================== + +CommandOps *controlQuit_Create(ControlState *state) { + return commandOps_Create(state, _commandQuit, NULL, _controlQuit_Execute, + commandOps_Destroy); +} + +CommandOps *controlQuit_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandQuitHelp, NULL, + _controlQuit_HelpExecute, commandOps_Destroy); +} + +// ============================================== + +static CommandReturn _controlQuit_HelpExecute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + printf("Exits the interactive control program\n\n"); + return CommandReturn_Success; +} + +static CommandReturn _controlQuit_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + printf("exiting interactive shell\n"); + return CommandReturn_Exit; +} diff --git a/hicn-light/src/config/controlQuit.h b/hicn-light/src/config/controlQuit.h new file mode 100755 index 000000000..e2ba3540e --- /dev/null +++ b/hicn-light/src/config/controlQuit.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_Quit.h + * @brief The quit command + * + * Implements the "quit" and "help quit" nodes of the command tree + * + */ +#ifndef Control_Quit_h +#define Control_Quit_h + +#include <src/config/controlState.h> +CommandOps *controlQuit_Create(ControlState *state); +CommandOps *controlQuit_HelpCreate(ControlState *state); +#endif // Control_Quit_h diff --git a/hicn-light/src/config/controlRemove.c b/hicn-light/src/config/controlRemove.c new file mode 100755 index 000000000..ede075a1b --- /dev/null +++ b/hicn-light/src/config/controlRemove.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/security/parc_Security.h> + +#include <parc/algol/parc_Memory.h> + +#include <src/config/controlRemove.h> +#include <src/config/controlRemoveConnection.h> +#include <src/config/controlRemovePunting.h> +#include <src/config/controlRemoveRoute.h> + +static void _controlRemove_Init(CommandParser *parser, CommandOps *ops); +static CommandReturn _controlRemove_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args); +static CommandReturn _controlRemove_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandRemove = "remove"; +static const char *_commandRemoveHelp = "help remove"; + +// ==================================================== + +CommandOps *controlRemove_Create(ControlState *state) { + return commandOps_Create(state, _commandRemove, _controlRemove_Init, + _controlRemove_Execute, commandOps_Destroy); +} + +CommandOps *controlRemove_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandRemoveHelp, NULL, + _controlRemove_HelpExecute, commandOps_Destroy); +} + +// ============================================== + +static CommandReturn _controlRemove_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + CommandOps *ops_remove_connection = controlRemoveConnection_Create(NULL); + CommandOps *ops_remove_route = controlRemoveRoute_Create(NULL); + CommandOps *ops_remove_punting = controlRemovePunting_Create(NULL); + + printf("Available commands:\n"); + printf(" %s\n", ops_remove_connection->command); + printf(" %s\n", ops_remove_route->command); + printf(" %s\n", ops_remove_punting->command); + printf("\n"); + + commandOps_Destroy(&ops_remove_connection); + commandOps_Destroy(&ops_remove_route); + commandOps_Destroy(&ops_remove_punting); + return CommandReturn_Success; +} + +static void _controlRemove_Init(CommandParser *parser, CommandOps *ops) { + ControlState *state = ops->closure; + controlState_RegisterCommand(state, + controlRemoveConnection_HelpCreate(state)); + controlState_RegisterCommand(state, controlRemoveRoute_HelpCreate(state)); + controlState_RegisterCommand(state, controlRemoveConnection_Create(state)); + controlState_RegisterCommand(state, controlRemoveRoute_Create(state)); + controlState_RegisterCommand(state, controlRemovePunting_Create(state)); + controlState_RegisterCommand(state, controlRemovePunting_HelpCreate(state)); +} + +static CommandReturn _controlRemove_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + return _controlRemove_HelpExecute(parser, ops, args); +} diff --git a/hicn-light/src/config/controlRemove.h b/hicn-light/src/config/controlRemove.h new file mode 100755 index 000000000..d75ecfe70 --- /dev/null +++ b/hicn-light/src/config/controlRemove.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_Remove.h + * @brief Implements the remove node of the CLI tree + * + * Implements the "remove" and "help remove" nodes of the command tree + * + */ +#ifndef controlRemove_h +#define controlRemove_h + +#include <src/config/controlState.h> +CommandOps *controlRemove_Create(ControlState *state); +CommandOps *controlRemove_HelpCreate(ControlState *state); +#endif // controlRemove_h diff --git a/hicn-light/src/config/controlRemoveConnection.c b/hicn-light/src/config/controlRemoveConnection.c new file mode 100755 index 000000000..93365ad17 --- /dev/null +++ b/hicn-light/src/config/controlRemoveConnection.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <ctype.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Network.h> +#include <src/utils/address.h> + +#include <src/config/controlRemoveConnection.h> + +#include <src/utils/commands.h> +#include <src/utils/utils.h> + +static CommandReturn _controlRemoveConnection_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlRemoveConnection_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +// =================================================== + +static const char *_commandRemoveConnection = "remove connection"; +static const char *_commandRemoveConnectionHelp = "help remove connection"; + +// ==================================================== + +CommandOps *controlRemoveConnection_Create(ControlState *state) { + return commandOps_Create(state, _commandRemoveConnection, NULL, + _controlRemoveConnection_Execute, + commandOps_Destroy); +} + +CommandOps *controlRemoveConnection_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandRemoveConnectionHelp, NULL, + _controlRemoveConnection_HelpExecute, + commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlRemoveConnection_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("command:\n"); + printf(" remove connection <symbolic|id>\n"); + return CommandReturn_Success; +} + +static CommandReturn _controlRemoveConnection_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + ControlState *state = ops->closure; + + if (parcList_Size(args) != 3) { + _controlRemoveConnection_HelpExecute(parser, ops, args); + return false; + } + + if ((strcmp(parcList_GetAtIndex(args, 0), "remove") != 0) || + (strcmp(parcList_GetAtIndex(args, 1), "connection") != 0)) { + _controlRemoveConnection_HelpExecute(parser, ops, args); + return false; + } + + const char *symbolicOrConnid = parcList_GetAtIndex(args, 2); + + if (!utils_ValidateSymbolicName(symbolicOrConnid) && + !utils_IsNumber(symbolicOrConnid)) { + printf( + "ERROR: Invalid symbolic or connid:\nsymbolic name must begin with an " + "alpha followed by alphanum;\nconnid must be an integer\n"); + return CommandReturn_Failure; + } + + // allocate command payload + remove_connection_command *removeConnectionCommand = + parcMemory_AllocateAndClear(sizeof(remove_connection_command)); + // fill payload + strcpy(removeConnectionCommand->symbolicOrConnid, symbolicOrConnid); + + // send message and receive response + struct iovec *response = + utils_SendRequest(state, REMOVE_CONNECTION, removeConnectionCommand, + sizeof(remove_connection_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlRemoveConnection.h b/hicn-light/src/config/controlRemoveConnection.h new file mode 100755 index 000000000..1dd1af23b --- /dev/null +++ b/hicn-light/src/config/controlRemoveConnection.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_RemoveConnection.h + * @brief Remove a connection from the connection table + * + * Implements the "remove connection" and "help remove connection" nodes of the + * CLI tree + * + */ + +#ifndef Control_RemoveConnection_h +#define Control_RemoveConnection_h + +#include <src/config/controlState.h> +CommandOps *controlRemoveConnection_Create(ControlState *state); +CommandOps *controlRemoveConnection_HelpCreate(ControlState *state); +#endif // Control_RemoveConnection_h diff --git a/hicn-light/src/config/controlRemovePunting.c b/hicn-light/src/config/controlRemovePunting.c new file mode 100755 index 000000000..cf4c4fbd4 --- /dev/null +++ b/hicn-light/src/config/controlRemovePunting.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <ctype.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Network.h> +#include <src/utils/address.h> + +#include <src/config/controlRemovePunting.h> + +static CommandReturn _controlRemovePunting_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlRemovePunting_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +// =================================================== + +static const char *_commandRemovePunting = "remove punting"; +static const char *_commandRemovePuntingHelp = "help punting connection"; + +// ==================================================== + +CommandOps *controlRemovePunting_Create(ControlState *state) { + return commandOps_Create(state, _commandRemovePunting, NULL, + _controlRemovePunting_Execute, commandOps_Destroy); +} + +CommandOps *controlRemovePunting_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandRemovePuntingHelp, NULL, + _controlRemovePunting_HelpExecute, + commandOps_Destroy); +} + +// ==================================================== + +// ==================================================== + +static CommandReturn _controlRemovePunting_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("remove punting <symbolic> <prefix>\n"); + return CommandReturn_Success; +} + +static CommandReturn _controlRemovePunting_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("command not implemented\n"); + return _controlRemovePunting_HelpExecute(parser, ops, args); +} + +// ================================================== diff --git a/hicn-light/src/config/controlRemovePunting.h b/hicn-light/src/config/controlRemovePunting.h new file mode 100755 index 000000000..89b1343e7 --- /dev/null +++ b/hicn-light/src/config/controlRemovePunting.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_RemovePunting.h + * + */ + +#ifndef Control_RemovePunting_h +#define Control_RemovePunting_h + +#include <src/config/controlState.h> +CommandOps *controlRemovePunting_Create(ControlState *state); +CommandOps *controlRemovePunting_HelpCreate(ControlState *state); +#endif // Control_RemovePunting_h diff --git a/hicn-light/src/config/controlRemoveRoute.c b/hicn-light/src/config/controlRemoveRoute.c new file mode 100755 index 000000000..b9b4ed1e4 --- /dev/null +++ b/hicn-light/src/config/controlRemoveRoute.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <ctype.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_List.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Network.h> + +#include <src/utils/address.h> + +#include <src/config/controlRemoveRoute.h> + +#include <src/utils/commands.h> +#include <src/utils/utils.h> + +static CommandReturn _controlRemoveRoute_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlRemoveRoute_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +// =================================================== + +static const char *_commandRemoveRoute = "remove route"; +static const char *_commandRemoveRouteHelp = "help remove route"; + +// ==================================================== + +CommandOps *controlRemoveRoute_Create(ControlState *state) { + return commandOps_Create(state, _commandRemoveRoute, NULL, + _controlRemoveRoute_Execute, commandOps_Destroy); +} + +CommandOps *controlRemoveRoute_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandRemoveRouteHelp, NULL, + _controlRemoveRoute_HelpExecute, commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlRemoveRoute_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("commands:\n"); + printf(" remove route <symbolic | connid> <prefix>\n"); + return CommandReturn_Success; +} + +static CommandReturn _controlRemoveRoute_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + ControlState *state = ops->closure; + + if (parcList_Size(args) != 4) { + _controlRemoveRoute_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + const char *symbolicOrConnid = parcList_GetAtIndex(args, 2); + + if (!utils_ValidateSymbolicName(symbolicOrConnid) && + !utils_IsNumber(symbolicOrConnid)) { + printf( + "ERROR: Invalid symbolic or connid:\nsymbolic name must begin with an " + "alpha followed by alphanum;\nconnid must be an integer\n"); + return CommandReturn_Failure; + } + + const char *prefixStr = parcList_GetAtIndex(args, 3); + char addr[strlen(prefixStr) + 1]; + + // separate address and len + char *slash; + uint32_t len = 0; + strcpy(addr, prefixStr); + slash = strrchr(addr, '/'); + if (slash != NULL) { + len = atoi(slash + 1); + *slash = '\0'; + } + + if (len == 0) { + printf("ERROR: a prefix can not be of length 0\n"); + return CommandReturn_Failure; + } + + // allocate command payload + remove_route_command *removeRouteCommand = + parcMemory_AllocateAndClear(sizeof(remove_route_command)); + + // check and set IP address + if (inet_pton(AF_INET, addr, &removeRouteCommand->address.ipv4) == 1) { + if (len > 32) { + printf("ERROR: exceeded INET mask length, max=32\n"); + parcMemory_Deallocate(&removeRouteCommand); + return CommandReturn_Failure; + } + removeRouteCommand->addressType = ADDR_INET; + } else if (inet_pton(AF_INET6, addr, &removeRouteCommand->address.ipv6) == + 1) { + if (len > 128) { + printf("ERROR: exceeded INET6 mask length, max=128\n"); + parcMemory_Deallocate(&removeRouteCommand); + return CommandReturn_Failure; + } + removeRouteCommand->addressType = ADDR_INET6; + } else { + printf("Error: %s is not a valid network address \n", addr); + parcMemory_Deallocate(&removeRouteCommand); + return CommandReturn_Failure; + } + + // Fill remaining payload fields + removeRouteCommand->len = len; + strcpy(removeRouteCommand->symbolicOrConnid, symbolicOrConnid); + + // send message and receive response + struct iovec *response = utils_SendRequest( + state, REMOVE_ROUTE, removeRouteCommand, sizeof(remove_route_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlRemoveRoute.h b/hicn-light/src/config/controlRemoveRoute.h new file mode 100755 index 000000000..a3c0ee46a --- /dev/null +++ b/hicn-light/src/config/controlRemoveRoute.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_RemoveRoute.h + * @brief Remove a route from the FIB + * + * Implements the "remove route" and "help remove route" nodes of the command + * tree + * + */ + +#ifndef Control_RemoveRoute_h +#define Control_RemoveRoute_h + +#include <src/config/controlState.h> +CommandOps *controlRemoveRoute_Create(ControlState *state); +CommandOps *controlRemoveRoute_HelpCreate(ControlState *state); +#endif // Control_RemoveRoute_h diff --git a/hicn-light/src/config/controlRoot.c b/hicn-light/src/config/controlRoot.c new file mode 100755 index 000000000..5d6c5f98e --- /dev/null +++ b/hicn-light/src/config/controlRoot.c @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/assert/parc_Assert.h> + +#include <src/config/controlAdd.h> +#include <src/config/controlCache.h> +#include <src/config/controlList.h> +#include <src/config/controlMapMe.h> +#include <src/config/controlQuit.h> +#include <src/config/controlRemove.h> +#include <src/config/controlRoot.h> +#include <src/config/controlSet.h> +#include <src/config/controlUnset.h> + +static void _controlRoot_Init(CommandParser *parser, CommandOps *ops); +static CommandReturn _controlRoot_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args); +static CommandReturn _controlRoot_HelpExecute(CommandParser *parser, + CommandOps *ops, PARCList *args); + +static const char *_commandRoot = ""; +static const char *_commandRootHelp = "help"; + +// ==================================================== + +CommandOps *controlRoot_Create(ControlState *state) { + return commandOps_Create(state, _commandRoot, _controlRoot_Init, + _controlRoot_Execute, commandOps_Destroy); +} + +CommandOps *controlRoot_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandRootHelp, NULL, + _controlRoot_HelpExecute, commandOps_Destroy); +} + +// =================================================== + +static CommandReturn _controlRoot_HelpExecute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + printf("Command-line execution:\n"); + printf( + " controller [--keystore <keystorepath>] [--password <password>] " + "command\n"); + printf("\n"); + printf("Interactive execution:\n"); + printf(" controller [--keystore <keystorepath>] [--password <password>]\n"); + printf("\n"); + printf( + "If the keystore is not specified, the default path is used. Keystore " + "must exist prior to running program.\n"); + printf("If the password is not specified, the user will be prompted.\n"); + printf("\n"); + + CommandOps *ops_help_add = controlAdd_CreateHelp(NULL); + CommandOps *ops_help_list = controlList_HelpCreate(NULL); + CommandOps *ops_help_quit = controlQuit_HelpCreate(NULL); + CommandOps *ops_help_remove = controlRemove_HelpCreate(NULL); + CommandOps *ops_help_set = controlSet_HelpCreate(NULL); + CommandOps *ops_help_unset = controlUnset_HelpCreate(NULL); + CommandOps *ops_help_cache = controlCache_HelpCreate(NULL); + CommandOps *ops_help_mapme = controlMapMe_HelpCreate(NULL); + + printf("Available commands:\n"); + printf(" %s\n", ops_help_add->command); + printf(" %s\n", ops_help_list->command); + printf(" %s\n", ops_help_quit->command); + printf(" %s\n", ops_help_remove->command); + printf(" %s\n", ops_help_set->command); + printf(" %s\n", ops_help_unset->command); + printf(" %s\n", ops_help_cache->command); + printf(" %s\n", ops_help_mapme->command); + printf("\n"); + + commandOps_Destroy(&ops_help_add); + commandOps_Destroy(&ops_help_list); + commandOps_Destroy(&ops_help_quit); + commandOps_Destroy(&ops_help_remove); + commandOps_Destroy(&ops_help_set); + commandOps_Destroy(&ops_help_unset); + commandOps_Destroy(&ops_help_cache); + commandOps_Destroy(&ops_help_mapme); + + return CommandReturn_Success; +} + +static void _controlRoot_Init(CommandParser *parser, CommandOps *ops) { + ControlState *state = ops->closure; + + controlState_RegisterCommand(state, controlAdd_CreateHelp(state)); + controlState_RegisterCommand(state, controlList_HelpCreate(state)); + controlState_RegisterCommand(state, controlQuit_HelpCreate(state)); + controlState_RegisterCommand(state, controlRemove_HelpCreate(state)); + controlState_RegisterCommand(state, controlSet_HelpCreate(state)); + controlState_RegisterCommand(state, controlUnset_HelpCreate(state)); + controlState_RegisterCommand(state, controlCache_HelpCreate(state)); + controlState_RegisterCommand(state, controlMapMe_HelpCreate(state)); + + controlState_RegisterCommand(state, webControlAdd_Create(state)); + controlState_RegisterCommand(state, controlList_Create(state)); + controlState_RegisterCommand(state, controlQuit_Create(state)); + controlState_RegisterCommand(state, controlRemove_Create(state)); + controlState_RegisterCommand(state, controlSet_Create(state)); + controlState_RegisterCommand(state, controlUnset_Create(state)); + controlState_RegisterCommand(state, controlCache_Create(state)); + controlState_RegisterCommand(state, controlMapMe_Create(state)); +} + +static CommandReturn _controlRoot_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + return CommandReturn_Success; +} + +// ====================================================================== diff --git a/hicn-light/src/config/controlRoot.h b/hicn-light/src/config/controlRoot.h new file mode 100755 index 000000000..a62126eba --- /dev/null +++ b/hicn-light/src/config/controlRoot.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_Root.h + * @brief Root of the command tree + * + * Implements the root of the command tree. This is the one module that + * needs to be seeded to the control state to build the whole tree. + * + */ + +#ifndef Control_Root_h +#define Control_Root_h + +#include <src/config/controlState.h> +CommandOps *controlRoot_Create(ControlState *state); +CommandOps *controlRoot_HelpCreate(ControlState *state); +#endif // Control_Root_h diff --git a/hicn-light/src/config/controlSet.c b/hicn-light/src/config/controlSet.c new file mode 100755 index 000000000..c6fd9aa3e --- /dev/null +++ b/hicn-light/src/config/controlSet.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/security/parc_Security.h> + +#include <src/config/controlSet.h> +#include <src/config/controlSetDebug.h> +#include <src/config/controlSetStrategy.h> +#include <src/config/controlSetWldr.h> + +static void _controlSet_Init(CommandParser *parser, CommandOps *ops); +static CommandReturn _controlSet_Execute(CommandParser *parser, CommandOps *ops, + PARCList *args); +static CommandReturn _controlSet_HelpExecute(CommandParser *parser, + CommandOps *ops, PARCList *args); + +static const char *_commandSet = "set"; +static const char *_commandSetHelp = "help set"; + +// =========================================================== + +CommandOps *controlSet_Create(ControlState *state) { + return commandOps_Create(state, _commandSet, _controlSet_Init, + _controlSet_Execute, commandOps_Destroy); +} + +CommandOps *controlSet_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandSetHelp, NULL, + _controlSet_HelpExecute, commandOps_Destroy); +} + +// =========================================================== + +static void _controlSet_Init(CommandParser *parser, CommandOps *ops) { + ControlState *state = ops->closure; + controlState_RegisterCommand(state, controlSetDebug_Create(state)); + controlState_RegisterCommand(state, controlSetDebug_HelpCreate(state)); + controlState_RegisterCommand(state, controlSetStrategy_Create(state)); + controlState_RegisterCommand(state, controlSetStrategy_HelpCreate(state)); + controlState_RegisterCommand(state, controlSetWldr_Create(state)); + controlState_RegisterCommand(state, controlSetWldr_HelpCreate(state)); +} + +static CommandReturn _controlSet_HelpExecute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + CommandOps *ops_help_set_debug = controlSetDebug_HelpCreate(NULL); + CommandOps *ops_help_set_strategy = controlSetStrategy_HelpCreate(NULL); + CommandOps *ops_help_set_wldr = controlSetWldr_HelpCreate(NULL); + + printf("Available commands:\n"); + printf(" %s\n", ops_help_set_debug->command); + printf(" %s\n", ops_help_set_strategy->command); + printf(" %s\n", ops_help_set_wldr->command); + printf("\n"); + + commandOps_Destroy(&ops_help_set_debug); + commandOps_Destroy(&ops_help_set_strategy); + commandOps_Destroy(&ops_help_set_wldr); + return CommandReturn_Success; +} + +static CommandReturn _controlSet_Execute(CommandParser *parser, CommandOps *ops, + PARCList *args) { + return _controlSet_HelpExecute(parser, ops, args); +} diff --git a/hicn-light/src/config/controlSet.h b/hicn-light/src/config/controlSet.h new file mode 100755 index 000000000..4289aad8c --- /dev/null +++ b/hicn-light/src/config/controlSet.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_Set.h + * @brief Implements the set node of the CLI tree + * + * Implements the "set" and "help set" nodes of the command tree + * + */ +#ifndef Control_Set_h +#define Control_Set_h + +#include <src/config/controlState.h> +CommandOps *controlSet_Create(ControlState *state); +CommandOps *controlSet_HelpCreate(ControlState *state); +#endif // Control_Set_h diff --git a/hicn-light/src/config/controlSetDebug.c b/hicn-light/src/config/controlSetDebug.c new file mode 100755 index 000000000..ca432420e --- /dev/null +++ b/hicn-light/src/config/controlSetDebug.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_Memory.h> + +#include <src/config/controlSetDebug.h> +#include <src/core/dispatcher.h> +#include <src/core/forwarder.h> + +static CommandReturn _controlSetDebug_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args); +static CommandReturn _controlSetDebug_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandSetDebug = "set debug"; +static const char *_commandSetDebugHelp = "help set debug"; + +// ==================================================== + +CommandOps *controlSetDebug_Create(ControlState *state) { + return commandOps_Create(state, _commandSetDebug, NULL, + _controlSetDebug_Execute, commandOps_Destroy); +} + +CommandOps *controlSetDebug_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandSetDebugHelp, NULL, + _controlSetDebug_HelpExecute, commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlSetDebug_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("set debug: will enable the debug flag for more verbose output\n"); + printf("\n"); + return CommandReturn_Success; +} + +static CommandReturn _controlSetDebug_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + if (parcList_Size(args) != 2) { + _controlSetDebug_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + ControlState *state = ops->closure; + controlState_SetDebug(state, true); + printf("Debug flag set\n\n"); + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlSetDebug.h b/hicn-light/src/config/controlSetDebug.h new file mode 100755 index 000000000..5335ebcab --- /dev/null +++ b/hicn-light/src/config/controlSetDebug.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_SetDebug.h + * @brief Sets the debug flag for more verbose output + * + * Implements the "set debug" and "help set debug" nodes of the command tree + * + */ + +#ifndef Control_SetDebug_h +#define Control_SetDebug_h + +#include <src/config/controlState.h> +CommandOps *controlSetDebug_Create(ControlState *state); +CommandOps *controlSetDebug_HelpCreate(ControlState *state); +#endif // Control_SetDebug_h diff --git a/hicn-light/src/config/controlSetStrategy.c b/hicn-light/src/config/controlSetStrategy.c new file mode 100755 index 000000000..7b7c11762 --- /dev/null +++ b/hicn-light/src/config/controlSetStrategy.c @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Network.h> + +#include <src/config/controlSetDebug.h> +#include <src/core/dispatcher.h> +#include <src/core/forwarder.h> + +#include <src/utils/commands.h> +#include <src/utils/utils.h> + +static CommandReturn _controlSetStrategy_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlSetStrategy_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandSetStrategy = "set strategy"; +static const char *_commandSetStrategyHelp = "help set strategy"; + +static const char *_commandSetStrategyOptions[LAST_STRATEGY_VALUE] = { + "loadbalancer", + "random", + "random_per_dash_segment", + "loadbalancer_with_delay", + "loadbalancer_by_rate", + "loadbalancer_best_route"}; + +// ==================================================== + +CommandOps *controlSetStrategy_Create(ControlState *state) { + return commandOps_Create(state, _commandSetStrategy, NULL, + _controlSetStrategy_Execute, commandOps_Destroy); +} + +CommandOps *controlSetStrategy_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandSetStrategyHelp, NULL, + _controlSetStrategy_HelpExecute, commandOps_Destroy); +} + +// ==================================================== + +strategy_type _validStrategy(const char *strategy) { + strategy_type validStrategy = LAST_STRATEGY_VALUE; + + for (int i = 0; i < LAST_STRATEGY_VALUE; i++) { + if (strcmp(_commandSetStrategyOptions[i], strategy) == 0) { + validStrategy = i; + break; + } + } + return validStrategy; +} + +static CommandReturn _controlSetStrategy_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("set strategy <prefix> <strategy>\n"); + printf("prefix: ipv4/ipv6 address (ex: 1234::/64)\n"); + printf("strategy: strategy identifier\n"); + printf("available strategies:\n"); + printf(" random\n"); + printf(" loadbalancer\n"); + printf(" random_per_dash_segment\n"); + printf(" loadbalancer_with_delay\n"); + printf("\n"); + return CommandReturn_Success; +} + +static CommandReturn _controlSetStrategy_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + ControlState *state = ops->closure; + + if (parcList_Size(args) != 4) { + _controlSetStrategy_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + if (((strcmp(parcList_GetAtIndex(args, 0), "set") != 0) || + (strcmp(parcList_GetAtIndex(args, 1), "strategy") != 0))) { + _controlSetStrategy_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + const char *prefixStr = parcList_GetAtIndex(args, 2); + char addr[strlen(prefixStr) + 1]; + // separate address and len + char *slash; + uint32_t len = UINT32_MAX; + strcpy(addr, prefixStr); + slash = strrchr(addr, '/'); + if (slash != NULL) { + len = atoi(slash + 1); + *slash = '\0'; + } + if (len == 0) { + printf("ERROR: a prefix can not be of length 0\n"); + return CommandReturn_Failure; + } + + // allocate command payload + set_strategy_command *setStrategyCommand = + parcMemory_AllocateAndClear(sizeof(set_strategy_command)); + + // check and set IP address + if (inet_pton(AF_INET, addr, &setStrategyCommand->address.ipv4) == 1) { + if (len == UINT32_MAX) { + printf("Netmask not specified: set to 32 by default\n"); + len = 32; + } else if (len > 32) { + printf("ERROR: exceeded INET mask length, max=32\n"); + parcMemory_Deallocate(&setStrategyCommand); + return CommandReturn_Failure; + } + setStrategyCommand->addressType = ADDR_INET; + } else if (inet_pton(AF_INET6, addr, &setStrategyCommand->address.ipv6) == + 1) { + if (len == UINT32_MAX) { + printf("Netmask not specified: set to 128 by default\n"); + len = 128; + } else if (len > 128) { + printf("ERROR: exceeded INET6 mask length, max=128\n"); + parcMemory_Deallocate(&setStrategyCommand); + return CommandReturn_Failure; + } + setStrategyCommand->addressType = ADDR_INET6; + } else { + printf("Error: %s is not a valid network address \n", addr); + parcMemory_Deallocate(&setStrategyCommand); + return CommandReturn_Failure; + } + + const char *strategyStr = parcList_GetAtIndex(args, 3); + // check valid strategy + strategy_type strategy; + if ((strategy = _validStrategy(strategyStr)) == LAST_STRATEGY_VALUE) { + printf("Error: invalid strategy \n"); + parcMemory_Deallocate(&setStrategyCommand); + _controlSetStrategy_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + // Fill remaining payload fields + setStrategyCommand->len = len; + setStrategyCommand->strategyType = strategy; + + // send message and receive response + struct iovec *response = utils_SendRequest( + state, SET_STRATEGY, setStrategyCommand, sizeof(set_strategy_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlSetStrategy.h b/hicn-light/src/config/controlSetStrategy.h new file mode 100755 index 000000000..53ce8912c --- /dev/null +++ b/hicn-light/src/config/controlSetStrategy.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef Control_SetStrategy_h +#define Control_SetStrategy_h + +#include <src/config/controlState.h> +CommandOps *controlSetStrategy_Create(ControlState *state); +CommandOps *controlSetStrategy_HelpCreate(ControlState *state); +#endif // Control_SetStrategy_h diff --git a/hicn-light/src/config/controlSetWldr.c b/hicn-light/src/config/controlSetWldr.c new file mode 100755 index 000000000..9da404036 --- /dev/null +++ b/hicn-light/src/config/controlSetWldr.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_Memory.h> + +#include <src/config/controlSetDebug.h> +#include <src/core/dispatcher.h> +#include <src/core/forwarder.h> + +#include <src/utils/commands.h> +#include <src/utils/utils.h> + +static CommandReturn _controlSetWldr_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args); +static CommandReturn _controlSetWldr_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandSetWldr = "set wldr"; +static const char *_commandSetWldrHelp = "help set wldr"; + +// ==================================================== + +CommandOps *controlSetWldr_Create(ControlState *state) { + return commandOps_Create(state, _commandSetWldr, NULL, + _controlSetWldr_Execute, commandOps_Destroy); +} + +CommandOps *controlSetWldr_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandSetWldrHelp, NULL, + _controlSetWldr_HelpExecute, commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlSetWldr_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("set wldr <on|off> <connection_id>\n"); + printf("\n"); + return CommandReturn_Success; +} + +static CommandReturn _controlSetWldr_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + ControlState *state = ops->closure; + + if (parcList_Size(args) != 4) { + _controlSetWldr_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + if (((strcmp(parcList_GetAtIndex(args, 0), "set") != 0) || + (strcmp(parcList_GetAtIndex(args, 1), "wldr") != 0))) { + _controlSetWldr_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + bool active; + if (strcmp(parcList_GetAtIndex(args, 2), "on") == 0) { + active = true; + } else if (strcmp(parcList_GetAtIndex(args, 2), "off") == 0) { + active = false; + } else { + _controlSetWldr_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + // check if valid connid + const char *symbolicOrConnid = parcList_GetAtIndex(args, 3); + + if (!utils_ValidateSymbolicName(symbolicOrConnid) && + !utils_IsNumber(symbolicOrConnid)) { + printf( + "ERROR: Invalid symbolic or connid:\nsymbolic name must begin with an " + "alpha followed by alphanum;\nconnid must be an integer\n"); + return CommandReturn_Failure; + } + + // allocate command payload + set_wldr_command *setWldrCommand = + parcMemory_AllocateAndClear(sizeof(set_wldr_command)); + strcpy(setWldrCommand->symbolicOrConnid, symbolicOrConnid); + if (active) { + setWldrCommand->activate = ACTIVATE_ON; + } else { + setWldrCommand->activate = ACTIVATE_OFF; + } + + // send message and receive response + struct iovec *response = utils_SendRequest(state, SET_WLDR, setWldrCommand, + sizeof(set_wldr_command)); + + if (!response) { // get NULL pointer + return CommandReturn_Failure; + } + + parcMemory_Deallocate(&response); // free iovec pointer + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlSetWldr.h b/hicn-light/src/config/controlSetWldr.h new file mode 100755 index 000000000..59c0b0fe6 --- /dev/null +++ b/hicn-light/src/config/controlSetWldr.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef Control_SetWldr_h +#define Control_SetWldr_h + +#include <src/config/controlState.h> +CommandOps *controlSetWldr_Create(ControlState *state); +CommandOps *controlSetWldr_HelpCreate(ControlState *state); +#endif // Control_SetWldr_h diff --git a/hicn-light/src/config/controlState.c b/hicn-light/src/config/controlState.c new file mode 100755 index 000000000..d8260e8e8 --- /dev/null +++ b/hicn-light/src/config/controlState.c @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/assert/parc_Assert.h> +#include <string.h> + +#include <parc/security/parc_Security.h> + +#include <parc/algol/parc_List.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Network.h> +#include <parc/algol/parc_Time.h> +#include <parc/algol/parc_TreeRedBlack.h> + +#include <src/config/commandParser.h> +#include <src/config/controlRoot.h> +#include <src/config/controlState.h> + +#include <src/utils/commands.h> + +#define SRV_IP "127.0.0.1" +#define PORT 9695 + +struct controller_state { + CommandParser *parser; + bool debugFlag; + + void *userdata; + struct iovec *(*writeRead)(ControlState *state, struct iovec *msg); + int sockfd; + char **commandOutput; + bool isInteractive; +}; + +int controlState_connectToFwdDeamon() { + int sockfd; + struct sockaddr_in servaddr; + + if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + printf("\nSocket Creation Failed \n"); + exit(EXIT_FAILURE); + } + + memset(&servaddr, 0, sizeof(servaddr)); + + // Filling server information + servaddr.sin_family = AF_INET; + servaddr.sin_port = htons(PORT); + servaddr.sin_addr.s_addr = INADDR_ANY; + + // Establish connection + if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { + printf("\nConnection Failed: hicn-light Daemon is not running \n"); + exit(EXIT_FAILURE); + } + + return sockfd; +} + +ControlState *controlState_Create( + void *userdata, + struct iovec *(*writeRead)(ControlState *state, struct iovec *msg), + bool openControllerConnetion) { + ControlState *state = parcMemory_AllocateAndClear(sizeof(ControlState)); + parcAssertNotNull(state, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ControlState)); + state->parser = commandParser_Create(); + + state->userdata = userdata; + state->writeRead = writeRead; + state->debugFlag = false; + state->commandOutput = NULL; + state->isInteractive = true; + + if (openControllerConnetion) { + state->sockfd = controlState_connectToFwdDeamon(); + } else { + state->sockfd = 2; // stderr + } + + return state; +} + +void controlState_Destroy(ControlState **statePtr) { + parcAssertNotNull(statePtr, "Parameter statePtr must be non-null"); + parcAssertNotNull(*statePtr, + "Parameter statePtr must dereference t non-null"); + ControlState *state = *statePtr; + // printf("sockid destroyed: %d\n", state->sockfd); + // close the connection with the fwd deamon + shutdown(state->sockfd, 2); + + commandParser_Destroy(&state->parser); + parcMemory_Deallocate((void **)&state); + *statePtr = NULL; +} + +void controlState_SetDebug(ControlState *state, bool debugFlag) { + parcAssertNotNull(state, "Parameter state must be non-null"); + state->debugFlag = debugFlag; + commandParser_SetDebug(state->parser, debugFlag); +} + +bool controlState_GetDebug(ControlState *state) { + parcAssertNotNull(state, "Parameter state must be non-null"); + return state->debugFlag; +} + +void controlState_RegisterCommand(ControlState *state, CommandOps *ops) { + parcAssertNotNull(state, "Parameter state must be non-null"); + commandParser_RegisterCommand(state->parser, ops); +} + +struct iovec *controlState_WriteRead(ControlState *state, struct iovec *msg) { + parcAssertNotNull(state, "Parameter state must be non-null"); + parcAssertNotNull(msg, "Parameter msg must be non-null"); + + return state->writeRead(state, msg); +} + +static PARCList *_controlState_ParseStringIntoTokens( + const char *originalString) { + PARCList *list = + parcList(parcArrayList_Create(parcArrayList_StdlibFreeFunction), + PARCArrayListAsPARCList); + + char *token; + + char *tofree = + parcMemory_StringDuplicate(originalString, strlen(originalString) + 1); + char *string = tofree; + + while ((token = strsep(&string, " \t\n")) != NULL) { + if (strlen(token) > 0) { + parcList_Add(list, strdup(token)); + } + } + + parcMemory_Deallocate((void **)&tofree); + + return list; +} + +CommandReturn controlState_DispatchCommand(ControlState *state, + PARCList *args) { + parcAssertNotNull(state, "Parameter state must be non-null"); + return commandParser_DispatchCommand(state->parser, args); +} + +int controlState_Interactive(ControlState *state) { + parcAssertNotNull(state, "Parameter state must be non-null"); + char *line = NULL; + size_t linecap = 0; + CommandReturn controlReturn = CommandReturn_Success; + + while (controlReturn != CommandReturn_Exit && !feof(stdin)) { + fputs("> ", stdout); + fflush(stdout); + ssize_t failure = getline(&line, &linecap, stdin); + parcAssertTrue(failure > -1, "Error getline"); + + PARCList *args = _controlState_ParseStringIntoTokens(line); + controlReturn = controlState_DispatchCommand(state, args); + // release and get command + parcList_Release(&args); + } + return 0; +} + +void controlState_SetCommandOutput(ControlState *state, char **commandData) { + state->commandOutput = commandData; +} + +void controlState_ReleaseCommandOutput(ControlState *state, char **commandData, + size_t commandLenght) { + for (size_t i = 0; i < commandLenght; i++) { + parcMemory_Deallocate(&commandData[i]); + } + parcMemory_Deallocate(&commandData); + state->commandOutput = NULL; +} + +char **controlState_GetCommandOutput(ControlState *state) { + return state->commandOutput; +} + +// size_t +// controlState_GetCommandLen(ControlState *state){ + +// } + +void controlState_SetInteractiveFlag(ControlState *state, bool interactive) { + state->isInteractive = interactive; +} + +bool controlState_IsInteractive(ControlState *state) { + return state->isInteractive; +} + +int controlState_GetSockfd(ControlState *state) { + parcAssertNotNull(state, "Parameter state must be non-null"); + return state->sockfd; +} + +void *controlState_GetUserdata(ControlState *state) { + parcAssertNotNull(state, "Parameter state must be non-null"); + return state->userdata; +} + +bool controlState_isConfigFile(ControlState *state) { + parcAssertNotNull(state, "Parameter state must be non-null"); + if (state->sockfd != 2) { + return false; + } else { + return true; + } +}
\ No newline at end of file diff --git a/hicn-light/src/config/controlState.h b/hicn-light/src/config/controlState.h new file mode 100755 index 000000000..905c56c30 --- /dev/null +++ b/hicn-light/src/config/controlState.h @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file controlState.h + * @brief A control program for hicn-light using CLI commands + * + * Implements the state machine for the control program. It takes a "writeRead" + * function as part of the constructor. This abstracts out the backend. It + * could be a Portal from hicnLightControl program down to the forwarder or it + * could be an internal function within hicn-light. + * + */ + +#ifndef control_h +#define control_h + +#include <parc/algol/parc_List.h> +#include <src/config/commandParser.h> + +#include <src/utils/commands.h> + +struct controller_state; +typedef struct controller_state ControlState; + +/** + * controlState_Create + * + * Creates the global state for the Control program. The user provides the + * writeRead function for sending and receiving the message wrapping command + * arguments. For configuration file inside hicn-light, it would make direct + * calls to Configuration -> Dispatcher. + * + * @param [in] userdata A closure passed back to the user when calling + * writeRead. + * @param [in] writeRead The function to write then read configuration messages + * to hicn-light + * + * @return non-null The control state + * + * Example: + * @code + * <#example#> + * @endcode + */ + +ControlState *controlState_Create( + void *userdata, + struct iovec *(*writeRead)(ControlState *state, struct iovec *msg), + bool openControllerConnetion); + +/** + * Destroys the control state, closing all network connections + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +void controlState_Destroy(ControlState **statePtr); + +/** + * Registers a CommandOps with the system. + * + * Each command has its complete command prefix in the "command" field. + * RegisterCommand will put these command prefixes in to a tree and then match + * what a user types against the longest-matching prefix in the tree. If + * there's a match, it will call the "execute" function. + * + * @param [in] state An allocated ControlState + * @param [in] command The command to register with the system + * + * Example: + * @code + * static CommandReturn + * control_Root_Execute(CommandParser *parser, CommandOps *ops, PARCList + * *args) + * { + * printf("Root Command\n"); + * return CommandReturn_Success; + * } + * + * static CommandReturn + * control_FooBar_Execute(CommandParser *parser, CommandOps *ops, PARCList + * *args) + * { + * printf("Foo Bar Command\n"); + * return CommandReturn_Success; + * } + * + * const CommandOps control_Root = { + * .command = "", // empty string for root + * .init = NULL, + * .execute = control_Root_Execute + * }; + * + * const CommandOps control_FooBar = { + * .command = "foo bar", // empty string for root + * .init = NULL, + * .execute = control_FooBar_Execute + * }; + * + * void startup(void) + * { + * ControlState *state = controlState_Create("happy", "day"); + * controlState_RegisterCommand(state, control_FooBar); + * controlState_RegisterCommand(state, control_Root); + * + * // this executes "root" + * controlState_DispatchCommand(state, "foo"); + * controlState_Destroy(&state); + * } + * @endcode + */ +void controlState_RegisterCommand(ControlState *state, CommandOps *command); + +/** + * Performs a longest-matching prefix of the args to the command tree + * + * The command tree is created with controlState_RegisterCommand. + * + * @param [in] state The allocated ControlState + * @param [in] args Each command_line word parsed to the ordered list + * + * @return CommandReturn_Success the command was successful + * @return CommandReturn_Failure the command failed or was not found + * @return CommandReturn_Exit the command indicates that the interactive mode + * should exit + * + * Example: + * @code + * <#example#> + * @endcode + */ +CommandReturn controlState_DispatchCommand(ControlState *state, PARCList *args); + +/** + * Begin an interactive shell + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +int controlState_Interactive(ControlState *state); + +/** + * Write then Read a command + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +struct iovec *controlState_WriteRead(ControlState *state, struct iovec *msg); + +/** + * Sets the Debug mode, which will print out much more information. + * + * Prints out much more diagnostic information about what hicn-light controller + * is doing. yes, you would make a CommandOps to set and unset this :) + * + * @param [in] debugFlag true means to print debug info, false means to turn it + * off + * + * Example: + * @code + * <#example#> + * @endcode + */ +void controlState_SetDebug(ControlState *state, bool debugFlag); + +/** + * Returns the debug state of ControlState + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool controlState_GetDebug(ControlState *state); +#endif // control_h + +void controlState_SetCommandOutput(ControlState *state, char **commandData); + +void controlState_ReleaseCommandOutput(ControlState *state, char **commandData, + size_t commandLenght); + +char **controlState_GetCommandOutput(ControlState *state); + +void controlState_SetInteractiveFlag(ControlState *state, bool interactive); + +bool controlState_IsInteractive(ControlState *state); + +void *controlState_GetUserdata(ControlState *state); + +bool controlState_isConfigFile(ControlState *state); + +int controlState_GetSockfd(ControlState *state); diff --git a/hicn-light/src/config/controlUnset.c b/hicn-light/src/config/controlUnset.c new file mode 100755 index 000000000..2da6a6518 --- /dev/null +++ b/hicn-light/src/config/controlUnset.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/security/parc_Security.h> + +#include <src/config/controlUnset.h> +#include <src/config/controlUnsetDebug.h> + +static void _controlUnset_Init(CommandParser *parser, CommandOps *ops); + +static CommandReturn _controlUnset_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args); +static CommandReturn _controlUnset_HelpExecute(CommandParser *parser, + CommandOps *ops, PARCList *args); + +static const char *_commandUnset = "unset"; +static const char *_commandUnsetHelp = "help unset"; + +// =========================================================== + +CommandOps *controlUnset_Create(ControlState *state) { + return commandOps_Create(state, _commandUnset, _controlUnset_Init, + _controlUnset_Execute, commandOps_Destroy); +} + +CommandOps *controlUnset_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandUnsetHelp, NULL, + _controlUnset_HelpExecute, commandOps_Destroy); +} + +// =========================================================== + +static void _controlUnset_Init(CommandParser *parser, CommandOps *ops) { + ControlState *state = ops->closure; + controlState_RegisterCommand(state, controlUnsetDebug_Create(state)); + controlState_RegisterCommand(state, controlUnsetDebug_HelpCreate(state)); +} + +static CommandReturn _controlUnset_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + CommandOps *ops_help_unset_debug = controlUnsetDebug_HelpCreate(NULL); + + printf("Available commands:\n"); + printf(" %s\n", ops_help_unset_debug->command); + printf("\n"); + + commandOps_Destroy(&ops_help_unset_debug); + return CommandReturn_Success; +} + +static CommandReturn _controlUnset_Execute(CommandParser *parser, + CommandOps *ops, PARCList *args) { + return _controlUnset_HelpExecute(parser, ops, args); +} diff --git a/hicn-light/src/config/controlUnset.h b/hicn-light/src/config/controlUnset.h new file mode 100755 index 000000000..6eeb983f7 --- /dev/null +++ b/hicn-light/src/config/controlUnset.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_Unset.h + * @brief Implements the unset node of the CLI tree + * + * Implements the "unset" and "help unset" nodes of the command tree + * + */ +#ifndef Control_Unset_h +#define Control_Unset_h + +#include <src/config/controlState.h> +CommandOps *controlUnset_Create(ControlState *state); +CommandOps *controlUnset_HelpCreate(ControlState *state); +#endif // Control_Unset_h diff --git a/hicn-light/src/config/controlUnsetDebug.c b/hicn-light/src/config/controlUnsetDebug.c new file mode 100755 index 000000000..4892bd513 --- /dev/null +++ b/hicn-light/src/config/controlUnsetDebug.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_Memory.h> + +#include <src/config/controlUnsetDebug.h> +#include <src/core/dispatcher.h> +#include <src/core/forwarder.h> + +static CommandReturn _controlUnsetDebug_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args); +static CommandReturn _controlUnsetDebug_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args); + +static const char *_commandUnsetDebug = "unset debug"; +static const char *_commandUnsetDebugHelp = "help unset debug"; + +// ==================================================== + +CommandOps *controlUnsetDebug_Create(ControlState *state) { + return commandOps_Create(state, _commandUnsetDebug, NULL, + _controlUnsetDebug_Execute, commandOps_Destroy); +} + +CommandOps *controlUnsetDebug_HelpCreate(ControlState *state) { + return commandOps_Create(state, _commandUnsetDebugHelp, NULL, + _controlUnsetDebug_HelpExecute, commandOps_Destroy); +} + +// ==================================================== + +static CommandReturn _controlUnsetDebug_HelpExecute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + printf("unset debug: will disable the debug flag\n"); + printf("\n"); + return CommandReturn_Success; +} + +static CommandReturn _controlUnsetDebug_Execute(CommandParser *parser, + CommandOps *ops, + PARCList *args) { + if (parcList_Size(args) != 2) { + _controlUnsetDebug_HelpExecute(parser, ops, args); + return CommandReturn_Failure; + } + + ControlState *state = ops->closure; + controlState_SetDebug(state, false); + printf("Debug flag cleared\n\n"); + + return CommandReturn_Success; +} diff --git a/hicn-light/src/config/controlUnsetDebug.h b/hicn-light/src/config/controlUnsetDebug.h new file mode 100755 index 000000000..e34f8aa5f --- /dev/null +++ b/hicn-light/src/config/controlUnsetDebug.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file control_UnsetDebug.h + * @brief Unsets the debug flag for more verbose output + * + * Implements the "unset debug" and "help unset debug" nodes of the CLI tree + * + */ + +#ifndef Control_UnsetDebug_h +#define Control_UnsetDebug_h + +#include <src/config/controlState.h> +CommandOps *controlUnsetDebug_Create(ControlState *state); +CommandOps *controlUnsetDebug_HelpCreate(ControlState *state); +#endif // Control_UnsetDebug_h diff --git a/hicn-light/src/config/symbolicNameTable.c b/hicn-light/src/config/symbolicNameTable.c new file mode 100755 index 000000000..ccf416d67 --- /dev/null +++ b/hicn-light/src/config/symbolicNameTable.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ctype.h> +#include <parc/algol/parc_Hash.h> +#include <parc/algol/parc_HashCodeTable.h> +#include <parc/algol/parc_Memory.h> +#include <parc/assert/parc_Assert.h> +#include <src/config.h> +#include <stdio.h> + +#include <src/config/symbolicNameTable.h> + +struct symblic_name_table { + PARCHashCodeTable *symbolicNameTable; + PARCHashCodeTable *indexToNameTable; +}; + +// ======================================================================================== +// symbolic name table functions + +static bool _symbolicNameEquals(const void *keyA, const void *keyB) { + return (strcasecmp((const char *)keyA, (const char *)keyB) == 0); +} + +static HashCodeType _symbolicNameHash(const void *keyA) { + const char *str = (const char *)keyA; + size_t length = strlen(str); + return parcHash32_Data(str, length); +} + +static bool _connectionIdEquals(const void *keyA, const void *keyB) { + unsigned idA = *((unsigned *)keyA); + unsigned idB = *((unsigned *)keyB); + return (idA == idB); +} + +static HashCodeType _connectionIdHash(const void *keyA) { + unsigned idA = *((unsigned *)keyA); + return parcHash32_Int32(idA); +} + +// ======================================================================================== + +SymbolicNameTable *symbolicNameTable_Create(void) { + SymbolicNameTable *table = parcMemory_Allocate(sizeof(SymbolicNameTable)); + + if (table) { + // key = char * + // value = uint32_t * + table->symbolicNameTable = parcHashCodeTable_Create( + _symbolicNameEquals, _symbolicNameHash, parcMemory_DeallocateImpl, + parcMemory_DeallocateImpl); + table->indexToNameTable = parcHashCodeTable_Create( + _connectionIdEquals, _connectionIdHash, parcMemory_DeallocateImpl, + parcMemory_DeallocateImpl); + } + + return table; +} + +void symbolicNameTable_Destroy(SymbolicNameTable **tablePtr) { + SymbolicNameTable *table = *tablePtr; + parcHashCodeTable_Destroy(&table->symbolicNameTable); + // parcHashCodeTable_Destroy(&table->indexToNameTable); + parcMemory_Deallocate((void **)&table); + *tablePtr = NULL; +} + +static char *_createKey(const char *symbolicName) { + char *key = parcMemory_StringDuplicate(symbolicName, strlen(symbolicName)); + + // convert key to upper case + char *p = key; + + // keeps looping until the first null + while ((*p = toupper(*p))) { + p++; + } + return key; +} + +bool symbolicNameTable_Exists(SymbolicNameTable *table, + const char *symbolicName) { + parcAssertNotNull(table, "Parameter table must be non-null"); + parcAssertNotNull(symbolicName, "Parameter symbolicName must be non-null"); + + char *key = _createKey(symbolicName); + bool found = (parcHashCodeTable_Get(table->symbolicNameTable, key) != NULL); + parcMemory_Deallocate((void **)&key); + return found; +} + +void symbolicNameTable_Remove(SymbolicNameTable *table, + const char *symbolicName) { + parcAssertNotNull(table, "Parameter table must be non-null"); + parcAssertNotNull(symbolicName, "Parameter symbolicName must be non-null"); + + char *key = _createKey(symbolicName); + + unsigned id = symbolicNameTable_Get(table, symbolicName); + uint32_t *value = parcMemory_Allocate(sizeof(uint32_t)); + *value = id; + + parcHashCodeTable_Del(table->symbolicNameTable, key); + parcHashCodeTable_Del(table->indexToNameTable, value); + parcMemory_Deallocate((void **)&key); + parcMemory_Deallocate((void **)&value); +} + +bool symbolicNameTable_Add(SymbolicNameTable *table, const char *symbolicName, + unsigned connid) { + parcAssertNotNull(table, "Parameter table must be non-null"); + parcAssertNotNull(symbolicName, "Parameter symbolicName must be non-null"); + parcAssertTrue(connid < UINT32_MAX, "Parameter connid must be less than %u", + UINT32_MAX); + + char *key = _createKey(symbolicName); + + uint32_t *value = parcMemory_Allocate(sizeof(uint32_t)); + *value = connid; + + bool success = parcHashCodeTable_Add(table->symbolicNameTable, key, value); + success = parcHashCodeTable_Add(table->indexToNameTable, value, key); + if (!success) { + parcMemory_Deallocate((void **)&key); + parcMemory_Deallocate((void **)&value); + } + + return success; +} + +unsigned symbolicNameTable_Get(SymbolicNameTable *table, + const char *symbolicName) { + parcAssertNotNull(table, "Parameter table must be non-null"); + parcAssertNotNull(symbolicName, "Parameter symbolicName must be non-null"); + + unsigned connid = UINT32_MAX; + + char *key = _createKey(symbolicName); + + uint32_t *value = parcHashCodeTable_Get(table->symbolicNameTable, key); + if (value) { + connid = *value; + } + + parcMemory_Deallocate((void **)&key); + return connid; +} + +const char *symbolicNameTable_GetNameByIndex(SymbolicNameTable *table, + unsigned id) { + parcAssertNotNull(table, "Parameter table must be non-null"); + + uint32_t *value = parcMemory_Allocate(sizeof(uint32_t)); + *value = id; + + const char *name = parcHashCodeTable_Get(table->indexToNameTable, value); + if (name == NULL) name = ""; + + parcMemory_Deallocate((void **)&value); + return name; +} diff --git a/hicn-light/src/config/symbolicNameTable.h b/hicn-light/src/config/symbolicNameTable.h new file mode 100755 index 000000000..69919cf00 --- /dev/null +++ b/hicn-light/src/config/symbolicNameTable.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file symbolicNameTable.h + * @brief The symbolic name table maps a string name to a connection id + * + * When configuring tunnels/connections, the user provides a string name + * (symbolic name) that they will use to refer to that connection. The symblic + * name table translates that symbolic name to a connection id. + * + */ + +#ifndef symbolicNameTable_h +#define symbolicNameTable_h + +struct symblic_name_table; +typedef struct symblic_name_table SymbolicNameTable; + +#include <stdbool.h> + +/** + * Creates a symbolic name table + * + * Allocates a SymbolicNameTable, which will store the symbolic names + * in a hash table. + * + * @retval non-null An allocated SymbolicNameTable + * @retval null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +SymbolicNameTable *symbolicNameTable_Create(void); + +/** + * Destroys a name table + * + * All keys and data are released. + * + * @param [in,out] tablePtr A pointer to a SymbolicNameTable, which will be + * NULL'd + * + * Example: + * @code + * <#example#> + * @endcode + */ +void symbolicNameTable_Destroy(SymbolicNameTable **tablePtr); + +/** + * Checks if the name (case insensitive) is in the table + * + * Does a case-insensitive match to see if the name is in the table + * + * @param [in] table An allocated SymbolicNameTable + * @param [in] symbolicName The name to check for + * + * @retval true The name is in the table + * @retval false The name is not in the talbe + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool symbolicNameTable_Exists(SymbolicNameTable *table, + const char *symbolicName); + +/** + * Adds a (name, connid) pair to the table. + * + * The name is stored case insensitive. The value UINT_MAX is used to indicate + * a non-existent key, so it should not be stored as a value in the table. + * + * @param [in] table An allocated SymbolicNameTable + * @param [in] symbolicName The name to save (will make a copy) + * @param [in] connid The connection id to associate with the name + * + * @retval true The pair was added + * @retval false The pair was not added (likely duplicate key) + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool symbolicNameTable_Add(SymbolicNameTable *table, const char *symbolicName, + unsigned connid); + +/** + * Returns the connection id associated with the symbolic name + * + * This function will look for the given name (case insensitive) and return the + * corresponding connid. If the name is not in the table, the function will + * return UINT_MAX. + * + * @param [in] table An allocated SymbolicNameTable + * @param [in] symbolicName The name to retrieve + * + * @retval UINT_MAX symbolicName not found + * @retval number the corresponding connid. + * + * Example: + * @code + * <#example#> + * @endcode + */ +unsigned symbolicNameTable_Get(SymbolicNameTable *table, + const char *symbolicName); + +void symbolicNameTable_Remove(SymbolicNameTable *table, + const char *symbolicName); +const char *symbolicNameTable_GetNameByIndex(SymbolicNameTable *table, + unsigned id); + +#endif /* defined(symbolicNameTable_h) */ diff --git a/hicn-light/src/content_store/CMakeLists.txt b/hicn-light/src/content_store/CMakeLists.txt new file mode 100755 index 000000000..85643cf5e --- /dev/null +++ b/hicn-light/src/content_store/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/contentStoreEntry.h + ${CMAKE_CURRENT_SOURCE_DIR}/contentStoreInterface.h + ${CMAKE_CURRENT_SOURCE_DIR}/contentStoreLRU.h + ${CMAKE_CURRENT_SOURCE_DIR}/listTimeOrdered.h + ${CMAKE_CURRENT_SOURCE_DIR}/listLRU.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/contentStoreInterface.c + ${CMAKE_CURRENT_SOURCE_DIR}/contentStoreLRU.c + ${CMAKE_CURRENT_SOURCE_DIR}/listLRU.c + ${CMAKE_CURRENT_SOURCE_DIR}/listTimeOrdered.c + ${CMAKE_CURRENT_SOURCE_DIR}/contentStoreEntry.c +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE)
\ No newline at end of file diff --git a/hicn-light/src/content_store/contentStoreEntry.c b/hicn-light/src/content_store/contentStoreEntry.c new file mode 100755 index 000000000..d36ed61a0 --- /dev/null +++ b/hicn-light/src/content_store/contentStoreEntry.c @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> +#include <stdio.h> + +#include <parc/algol/parc_Memory.h> +#include <src/content_store/contentStoreEntry.h> + +#include <parc/assert/parc_Assert.h> + +const uint64_t contentStoreEntry_MaxExpiryTime = UINT64_MAX; + +struct contentstore_entry { + Message *message; + ListLruEntry *lruEntry; + unsigned refcount; + bool hasExpiryTimeTicks; + uint64_t expiryTimeTicks; +}; + +ContentStoreEntry *contentStoreEntry_Create(Message *contentMessage, + ListLru *listLRU) { + parcAssertNotNull(contentMessage, "Parameter objectMessage must be non-null"); + + ContentStoreEntry *entry = + parcMemory_AllocateAndClear(sizeof(ContentStoreEntry)); + parcAssertNotNull(entry, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ContentStoreEntry)); + entry->message = message_Acquire(contentMessage); + entry->refcount = 1; + + if (listLRU != NULL) { + entry->lruEntry = listLRU_NewHeadEntry(listLRU, entry); + } + + entry->hasExpiryTimeTicks = message_HasContentExpiryTime(contentMessage); + + if (entry->hasExpiryTimeTicks) { + entry->expiryTimeTicks = message_GetContentExpiryTimeTicks(contentMessage); + } + + return entry; +} + +ContentStoreEntry *contentStoreEntry_Acquire( + const ContentStoreEntry *original) { + parcAssertNotNull(original, "Parameter must be non-null"); + ((ContentStoreEntry *)original)->refcount++; + return (ContentStoreEntry *)original; +} + +void contentStoreEntry_Release(ContentStoreEntry **entryPtr) { + parcAssertNotNull(entryPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*entryPtr, + "Parameter must dereference to non-null pointer"); + + ContentStoreEntry *entry = *entryPtr; + parcAssertTrue(entry->refcount > 0, "Illegal state: has refcount of 0"); + + entry->refcount--; + if (entry->refcount == 0) { + if (entry->lruEntry) { + listLRU_EntryDestroy(&entry->lruEntry); + } + message_Release(&entry->message); + parcMemory_Deallocate((void **)&entry); + } + *entryPtr = NULL; +} + +Message *contentStoreEntry_GetMessage(const ContentStoreEntry *storeEntry) { + parcAssertNotNull(storeEntry, "Parameter must be non-null"); + return storeEntry->message; +} + +bool contentStoreEntry_HasExpiryTimeTicks(const ContentStoreEntry *storeEntry) { + parcAssertNotNull(storeEntry, "Parameter must be non-null"); + return storeEntry->hasExpiryTimeTicks; +} + +uint64_t contentStoreEntry_GetExpiryTimeTicks( + const ContentStoreEntry *storeEntry) { + parcAssertNotNull(storeEntry, "Parameter must be non-null"); + parcAssertTrue(storeEntry->hasExpiryTimeTicks, + "storeEntry has no ExpiryTimeTicks. Did you call " + "contentStoreEntry_HasExpiryTimeTicks() first?"); + return storeEntry->expiryTimeTicks; +} + +int contentStoreEntry_CompareExpiryTime(const ContentStoreEntry *value1, + const ContentStoreEntry *value2) { + // A signum comparison. negative if key 1 is smaller, 0 if key1 == key2, + // greater than 0 if key1 is bigger. + + ContentStoreEntry *v1 = (ContentStoreEntry *)value1; + ContentStoreEntry *v2 = (ContentStoreEntry *)value2; + + if (v1->expiryTimeTicks < v2->expiryTimeTicks) { + return -1; + } else if (v1->expiryTimeTicks > v2->expiryTimeTicks) { + return +1; + } else { + // At this point, the times are the same. Use the address of the message as + // the decider. This allows us to store multiple messages with the same + // expiry/cache time. + if (v1->message < v2->message) { + return -1; + } else if (v1->message > v2->message) { + return +1; + } + } + + return 0; // The same message has been encountered. +} + +void contentStoreEntry_MoveToHead(ContentStoreEntry *storeEntry) { + parcAssertNotNull(storeEntry, "Parameter must be non-null"); + parcAssertNotNull(storeEntry->lruEntry, + "ContentStoreEntry is not attached to an ListLru"); + if (storeEntry->lruEntry) { + listLRU_EntryMoveToHead(storeEntry->lruEntry); + } +} diff --git a/hicn-light/src/content_store/contentStoreEntry.h b/hicn-light/src/content_store/contentStoreEntry.h new file mode 100755 index 000000000..766cc15e4 --- /dev/null +++ b/hicn-light/src/content_store/contentStoreEntry.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef contentStoreEntry_h +#define contentStoreEntry_h + +#include <src/content_store/listLRU.h> +#include <src/core/message.h> + +struct contentstore_entry; +typedef struct contentstore_entry ContentStoreEntry; + +/** + * The max time allowed for an ExpiryTime. Will never be exceeded. + */ +extern const uint64_t contentStoreEntry_MaxExpiryTime; + +/** + * Creates a new `ContentStoreEntry` instance, acquiring a reference to the + * supplied `Message`. + * + * @param message the message to store + * @param listLRU the LRU list that this entry will be stored in. + * @return A newly created `ContentStoreEntry` instance that must eventually be + * released by calling + * {@link contentStoreEntry_Release}. + * + * @see contentStoreEntry_Release + */ +ContentStoreEntry *contentStoreEntry_Create(Message *objectMessage, + ListLru *listLRU); + +/** + * Returns a reference counted copy of the supplied `ContentStoreEntry`. + * + * @param original the ContentStoreEntry to return a reference to. + * @return Reference counted copy, must call + * <code>contentStoreEntry_Destroy()</code> on it. + */ +ContentStoreEntry *contentStoreEntry_Acquire(const ContentStoreEntry *original); + +/** + * Releases one reference count and destroys object when reaches zero + * + * @param [in,out] entryPtr A pointer to an allocated ContentStoreEntry + * + */ +void contentStoreEntry_Release(ContentStoreEntry **entryPtr); + +/** + * Returns a pointer to the contained {@link Message}. + * The caller must called {@link message_Acquire()} if they want to keep a + * reference to the returned message. + * + * @param storeEntry the ContentStoreEntry from which to retrieve the `Message` + * pointer. + * @return the address of the `Message` contained in the storeEntry. + * @see message_Acquire + */ +Message *contentStoreEntry_GetMessage(const ContentStoreEntry *storeEntry); + +/** + * Return true if the message stored in this `ContentStoreEntry` has an + * ExpiryTime. + * + * @param storeEntry the ContentStoreEntry containing the message. + * @return true if the referenced message has an ExpiryTime. False, otherwise. + */ +bool contentStoreEntry_HasExpiryTimeTicks(const ContentStoreEntry *storeEntry); + +/** + * Return the ExpiryTime stored in this `ContentStoreEntry`. + * + * @param storeEntry the ContentStoreEntry from which to retrieve the `Message` + * pointer. + * @return the address of the `Message` contained in the storeEntry. + */ +uint64_t contentStoreEntry_GetExpiryTimeTicks( + const ContentStoreEntry *storeEntry); + +/** + * A signum function comparing two `ContentStoreEntry` instances, using their + * ExpiryTime and, if necessary, the addresses of the referenced Message. In + * other words, if two ContentStoreEntries have the same ExpiryTime, the + * comparison will then be made on the memory addresses of the Messages + * referenced by the ContentStoreEntrys. So, the only way two ContentStoreEntrys + * will compare equally (0) is if they both have the same ExpiryTime and + * reference the same Message. + * + * Used to determine the ordering relationship of two `ContentStoreEntry` + * instances. This is used by the {@link ListTimeOrdered} to keep a list of + * ContentStoreEntrys, sorted by ExpiryTime. + * + * @param [in] storeEntry1 A pointer to a `ContentStoreEntry` instance. + * @param [in] storeEntry2 A pointer to a `ContentStoreEntry` instance to be + * compared to `storeEntry1`. + * + * @return 0 if `storeEntry1` and `storeEntry2` are equivalent + * @return < 0 if `storeEntry1` < `storeEntry2` + * @return > 0 if `storeEntry1` > `storeEntry2` + * + * Example: + * @code + * { + * ContentStoreEntry *entry1 = contentStoreEntry_Create(...); + * ContentStoreEntry *entry2 = contentStoreEntry_Create(...); + * + * int val = contentStoreEntry_CompareExpiryTime(entry1, entry2); + * if (val < 0) { + * // entry1 has a lower ExpiryTime, or the same ExpiryTime as entry2 + * and a different message. } else if (val > 0) { + * // entry2 has a lower ExpiryTime, or the same ExpiryTime as entry1 + * and a different message. } else { + * // entry1 and entry2 have the same ExpiryTime AND the same message. + * } + * + * contentStoreEntry_Release(&entry1); + * contentStoreEntry_Release(&entry2); + * + * } + * @endcode + */ +int contentStoreEntry_CompareExpiryTime(const ContentStoreEntry *storeEntry1, + const ContentStoreEntry *storeEntry2); + +/** + * Move this entry to the head of the LRU list + * + * Moves the entry to the head of the LRU list it was created with + * + * @param [in] storeEntry An allocated ContenstoreEntry + */ +void contentStoreEntry_MoveToHead(ContentStoreEntry *storeEntry); +#endif // contentStoreEntry_h diff --git a/hicn-light/src/content_store/contentStoreInterface.c b/hicn-light/src/content_store/contentStoreInterface.c new file mode 100755 index 000000000..a42041670 --- /dev/null +++ b/hicn-light/src/content_store/contentStoreInterface.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> +#include <stdio.h> + +#include <src/content_store/contentStoreInterface.h> + +void contentStoreInterface_Release(ContentStoreInterface **storeImplPtr) { + (*storeImplPtr)->release(storeImplPtr); +} + +bool contentStoreInterface_PutContent(ContentStoreInterface *storeImpl, + Message *content, + uint64_t currentTimeTicks) { + return storeImpl->putContent(storeImpl, content, currentTimeTicks); +} + +bool contentStoreInterface_RemoveContent(ContentStoreInterface *storeImpl, + Message *content) { + return storeImpl->removeContent(storeImpl, content); +} + +Message *contentStoreInterface_MatchInterest(ContentStoreInterface *storeImpl, + Message *interest, + uint64_t currentTimeTicks) { + return storeImpl->matchInterest(storeImpl, interest, currentTimeTicks); +} + +size_t contentStoreInterface_GetObjectCapacity( + ContentStoreInterface *storeImpl) { + return storeImpl->getObjectCapacity(storeImpl); +} + +size_t contentStoreInterface_GetObjectCount(ContentStoreInterface *storeImpl) { + return storeImpl->getObjectCount(storeImpl); +} + +void contentStoreInterface_Log(ContentStoreInterface *storeImpl) { + storeImpl->log(storeImpl); +} + +void *contentStoreInterface_GetPrivateData(ContentStoreInterface *storeImpl) { + return storeImpl->_privateData; +} diff --git a/hicn-light/src/content_store/contentStoreInterface.h b/hicn-light/src/content_store/contentStoreInterface.h new file mode 100755 index 000000000..d73c63019 --- /dev/null +++ b/hicn-light/src/content_store/contentStoreInterface.h @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef contentStoreInterface_h +#define contentStoreInterface_h + +#include <stdio.h> + +#include <src/core/message.h> + +typedef struct contentstore_config { + size_t objectCapacity; +} ContentStoreConfig; + +typedef struct contentstore_interface ContentStoreInterface; + +struct contentstore_interface { + /** + * Place a Message representing a ContentObject into the ContentStore. If + * necessary to make room, remove expired content or content that has exceeded + * the Recommended Cache Time. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + * @param content - a pointer to a `Message` to place in the store. + * @param currentTimeTicks - the current time, in hicn-light ticks, since the + * UTC epoch. + */ + bool (*putContent)(ContentStoreInterface *storeImpl, Message *content, + uint64_t currentTimeTicks); + + /** + * The function to call to remove content from the ContentStore. + * It will Release any references that were created when the content was + * placed into the ContentStore. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + * @param content - a pointer to a `Message` to remove from the store. + */ + bool (*removeContent)(ContentStoreInterface *storeImpl, Message *content); + + /** + * Given a Message that represents and Interest, try to find a matching + * ContentObject. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + * @param interest - a pointer to a `Message` representing the Interest to + * match. + * + * @return a pointer to a Message containing the matching ContentObject + * @return NULL if no matching ContentObject was found + */ + Message *(*matchInterest)(ContentStoreInterface *storeImpl, Message *interest, + uint64_t currentTimeTicks); + + /** + * Return the maximum number of ContentObjects that can be stored in this + * ContentStore. This is a raw count, not based on memory size. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + * + * @return the maximum number of ContentObjects that can be stored + */ + size_t (*getObjectCapacity)(ContentStoreInterface *storeImpl); + + /** + * Return the number of ContentObjects currently stored in the ContentStore. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + * + * @return the current number of ContentObjects in the ContentStore + */ + size_t (*getObjectCount)(ContentStoreInterface *storeImpl); + + /** + * Log a ContentStore implementation specific version of store-related + * information. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + */ + void (*log)(ContentStoreInterface *storeImpl); + + /** + * Acquire a new reference to the specified ContentStore instance. This + * reference will eventually need to be released by calling {@link + * contentStoreInterface_Release}. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + */ + ContentStoreInterface *(*acquire)(const ContentStoreInterface *storeImpl); + + /** + * Release the ContentStore, which will also Release any references held by + * it. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + */ + void (*release)(ContentStoreInterface **storeImpl); + + /** + * A pointer to opaque private data used by the ContentStore instance + * represented by this instance of ContentStoreInterface. + */ + void *_privateData; +}; + +/** + * Place a Message representing a ContentObject into the ContentStore. If + * necessary to make room, remove expired content or content that has exceeded + * the Recommended Cache Time. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + * @param content - a pointer to a `Message` to place in the store. + * + * @param currentTimeTicks - the current time, in hicn-light ticks, since the + * UTC epoch. + */ +bool contentStoreInterface_PutContent(ContentStoreInterface *storeImpl, + Message *content, + uint64_t currentTimeTicks); + +/** + * The function to call to remove content from the ContentStore. + * It will Release any references that were created when the content was placed + * into the ContentStore. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + * @param content - a pointer to a `Message` to remove from the store. + */ +bool contentStoreInterface_RemoveContent(ContentStoreInterface *storeImpl, + Message *content); + +/** + * Given a Message that represents and Interest, try to find a matching + * ContentObject. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + * @param interest - a pointer to a `Message` representing the Interest to + * match. + * + * @return a pointer to a Message containing the matching ContentObject + * @return NULL if no matching ContentObject was found + */ +Message *contentStoreInterface_MatchInterest(ContentStoreInterface *storeImpl, + Message *interest, + uint64_t currentTimeTicks); + +/** + * Return the maximum number of ContentObjects that can be stored in this + * ContentStore. This is a raw count, not based on memory size. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + * + * @return the maximum number of ContentObjects that can be stored + */ +size_t contentStoreInterface_GetObjectCapacity( + ContentStoreInterface *storeImpl); + +/** + * Return the number of ContentObjects currently stored in the ContentStore. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + * + * @return the current number of ContentObjects in the ContentStore + */ +size_t contentStoreInterface_GetObjectCount(ContentStoreInterface *storeImpl); + +/** + * Loga ContentStore implementation specific version of store-related + * information. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + */ +void contentStoreInterface_Log(ContentStoreInterface *storeImpl); + +/** + * Acquire a new reference to the specified ContentStore instance. This + * reference will eventually need to be released by calling {@link + * contentStoreInterface_Release}. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + */ +ContentStoreInterface *contentStoreInterface_Aquire( + const ContentStoreInterface *storeImpl); + +/** + * Release the ContentStore, which will also Release any references held by it. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + */ +void contentStoreInterface_Release(ContentStoreInterface **storeImplPtr); + +/** + * Return a pointer to the data private to this implementation of the + * ContentStore interface. + * + * @param storeImpl - a pointer to this ContentStoreInterface instance. + */ +void *contentStoreInterface_GetPrivateData(ContentStoreInterface *storeImpl); +#endif // contentStoreInterface_h diff --git a/hicn-light/src/content_store/contentStoreLRU.c b/hicn-light/src/content_store/contentStoreLRU.c new file mode 100755 index 000000000..9b69d51c0 --- /dev/null +++ b/hicn-light/src/content_store/contentStoreLRU.c @@ -0,0 +1,455 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> +#include <stdio.h> +#include <sys/queue.h> + +#include <parc/algol/parc_DisplayIndented.h> +#include <parc/algol/parc_HashCodeTable.h> +#include <parc/algol/parc_Object.h> + +#include <src/core/logger.h> + +#include <src/content_store/contentStoreLRU.h> + +#include <src/content_store/contentStoreEntry.h> +#include <src/content_store/contentStoreInterface.h> +#include <src/content_store/listLRU.h> +#include <src/content_store/listTimeOrdered.h> + +#include <parc/assert/parc_Assert.h> +#include <src/processor/hashTableFunction.h> + +typedef struct contentstore_stats { + uint64_t countExpiryEvictions; + uint64_t countRCTEvictions; + uint64_t countLruEvictions; + uint64_t countAdds; + uint64_t countHits; + uint64_t countMisses; +} _ContentStoreLRUStats; + +typedef struct contentstore_lru_data { + size_t objectCapacity; + size_t objectCount; + + Logger *logger; + + // This LRU is just for keeping track of insertion and access order. + ListLru *lru; + + ListTimeOrdered *indexByExpirationTime; + + PARCHashCodeTable *storageByName; + + _ContentStoreLRUStats stats; +} _ContentStoreLRU; + +static void _destroyIndexes(_ContentStoreLRU *store) { + if (store->indexByExpirationTime != NULL) { + listTimeOrdered_Release(&(store->indexByExpirationTime)); + } + + if (store->storageByName != NULL) { + parcHashCodeTable_Destroy(&(store->storageByName)); + } + + if (store->lru != NULL) { + listLRU_Destroy(&(store->lru)); + } +} + +static void _contentStoreInterface_Destroy( + ContentStoreInterface **storeImplPtr) { + _ContentStoreLRU *store = contentStoreInterface_GetPrivateData(*storeImplPtr); + + parcObject_Release((PARCObject **)&store); +} + +static bool _contentStoreLRU_Destructor(_ContentStoreLRU **storePtr) { + _ContentStoreLRU *store = *storePtr; + + _destroyIndexes(store); + logger_Release(&store->logger); + + return true; +} + +parcObject_Override(_ContentStoreLRU, PARCObject, + .destructor = (PARCObjectDestructor *) + _contentStoreLRU_Destructor); + +parcObject_ExtendPARCObject(ContentStoreInterface, + _contentStoreInterface_Destroy, NULL, NULL, NULL, + NULL, NULL, NULL); + +static parcObject_ImplementAcquire(_contentStoreLRU, ContentStoreInterface); +static parcObject_ImplementRelease(_contentStoreLRU, ContentStoreInterface); + +static void _hashTableFunction_ContentStoreEntryDestroyer(void **dataPtr) { + contentStoreEntry_Release((ContentStoreEntry **)dataPtr); +} + +static bool _contentStoreLRU_Init(_ContentStoreLRU *store, + ContentStoreConfig *config, Logger *logger) { + bool result = false; + + store->logger = logger_Acquire(logger); + + size_t initialSize = config->objectCapacity * 2; + memset(&store->stats, 0, sizeof(_ContentStoreLRUStats)); + + store->objectCapacity = config->objectCapacity; + store->objectCount = 0; + + // initial size must be at least 1 or else the data structures break. + initialSize = (initialSize == 0) ? 1 : initialSize; + + store->indexByExpirationTime = listTimeOrdered_Create( + (TimeOrderList_KeyCompare *)contentStoreEntry_CompareExpiryTime); + + store->storageByName = parcHashCodeTable_Create_Size( + hashTableFunction_MessageNameEquals, + hashTableFunction_MessageNameHashCode, NULL, + _hashTableFunction_ContentStoreEntryDestroyer, initialSize); + + store->lru = listLRU_Create(); + + // If any of the index tables couldn't be allocated, we can't continue. + if ((store->indexByExpirationTime == NULL) || + (store->storageByName == NULL) || (store->lru == NULL)) { + if (logger_IsLoggable(store->logger, LoggerFacility_Processor, + PARCLogLevel_Error)) { + logger_Log(store->logger, LoggerFacility_Processor, PARCLogLevel_Error, + __func__, + "ContentStoreLRU could not be created. Could not allocate all " + "index tables.", + (void *)store, store->objectCapacity); + } + + _destroyIndexes(store); + result = false; + } else { + result = true; + } + return result; +} + +/** + * Remove a ContentStoreEntry from all tables and indices. + */ +static void _contentStoreLRU_PurgeStoreEntry(_ContentStoreLRU *store, + ContentStoreEntry *entryToPurge) { + if (contentStoreEntry_HasExpiryTimeTicks(entryToPurge)) { + listTimeOrdered_Remove(store->indexByExpirationTime, entryToPurge); + } + + Message *content = contentStoreEntry_GetMessage(entryToPurge); + + // This _Del call will call the Release/Destroy on the ContentStoreEntry, + // which will remove it from the LRU as well. + parcHashCodeTable_Del(store->storageByName, content); + + store->objectCount--; +} + +static bool _contentStoreLRU_RemoveLeastUsed(_ContentStoreLRU *store) { + bool result = false; + + if (store->objectCount > 0) { + ListLruEntry *lruEntry = listLRU_PopTail(store->lru); + ContentStoreEntry *storeEntry = + (ContentStoreEntry *)listLRU_EntryGetData(lruEntry); + + if (logger_IsLoggable(store->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log( + store->logger, LoggerFacility_Processor, PARCLogLevel_Debug, __func__, + "ContentStore %p evict message %p by LRU (LRU evictions %" PRIu64 ")", + (void *)store, (void *)contentStoreEntry_GetMessage(storeEntry), + store->stats.countLruEvictions); + } + + _contentStoreLRU_PurgeStoreEntry(store, storeEntry); + + result = true; + } + return result; +} + +static void _evictByStorePolicy(_ContentStoreLRU *store, + uint64_t currentTimeInTicks) { + // We need to make room. Here's the plan: + // 1) Check to see if anything has expired. If so, remove it and we're done. + // If not, 2) Remove the least recently used item. + + ContentStoreEntry *entry = + listTimeOrdered_GetOldest(store->indexByExpirationTime); + if (entry && contentStoreEntry_HasExpiryTimeTicks(entry) && + (currentTimeInTicks > contentStoreEntry_GetExpiryTimeTicks(entry))) { + // Found an expired entry. Remove it, and we're done. + + store->stats.countExpiryEvictions++; + if (logger_IsLoggable(store->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(store->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, + "ContentStore %p evict message %p by ExpiryTime (ExpiryTime " + "evictions %" PRIu64 ")", + (void *)store, (void *)contentStoreEntry_GetMessage(entry), + store->stats.countExpiryEvictions); + } + + _contentStoreLRU_PurgeStoreEntry(store, entry); + } else { + store->stats.countLruEvictions++; + _contentStoreLRU_RemoveLeastUsed(store); + } +} + +static bool _contentStoreLRU_PutContent(ContentStoreInterface *storeImpl, + Message *content, + uint64_t currentTimeTicks) + +{ + bool result = false; + _ContentStoreLRU *store = + (_ContentStoreLRU *)contentStoreInterface_GetPrivateData(storeImpl); + parcAssertNotNull(store, "Parameter store must be non-null"); + parcAssertNotNull(content, "Parameter objectMessage must be non-null"); + + parcAssertTrue(message_GetType(content) == MessagePacketType_ContentObject, + "Parameter objectMessage must be a Content Object"); + + if (store->objectCapacity == 0) { + return false; + } + + uint64_t expiryTimeTicks = contentStoreEntry_MaxExpiryTime; + + if (message_HasContentExpiryTime(content)) { + expiryTimeTicks = message_GetContentExpiryTimeTicks(content); + } + // Don't add anything that's already expired or has exceeded RCT. + if (currentTimeTicks >= expiryTimeTicks) { + return false; + } + + if (store->objectCount >= store->objectCapacity) { + // Store is full. Need to make room. + _evictByStorePolicy(store, currentTimeTicks); + } + + // And now add a new entry to the head of the LRU. + + ContentStoreEntry *entry = contentStoreEntry_Create(content, store->lru); + + if (entry != NULL) { + if (parcHashCodeTable_Add(store->storageByName, content, entry)) { + if (contentStoreEntry_HasExpiryTimeTicks(entry)) { + listTimeOrdered_Add(store->indexByExpirationTime, entry); + } + + store->objectCount++; + store->stats.countAdds++; + + if (logger_IsLoggable(store->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(store->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, + "ContentStoreLRU %p saved message %p (object count %" PRIu64 + ")", + (void *)store, (void *)content, store->objectCount); + } + + result = true; + } else { + // Free what we just created, but did not add. 'entry' has ownership of + // 'copy', and so will call _Release() on it + contentStoreEntry_Release(&entry); + + if (logger_IsLoggable(store->logger, LoggerFacility_Processor, + PARCLogLevel_Warning)) { + logger_Log(store->logger, LoggerFacility_Processor, + PARCLogLevel_Warning, __func__, + "ContentStoreLRU %p failed to add message %p to hash table", + (void *)store, (void *)content); + } + } + } + + return result; +} + +static Message *_contentStoreLRU_MatchInterest(ContentStoreInterface *storeImpl, + Message *interest, + uint64_t currentTimeTicks) { + Message *result = NULL; + + _ContentStoreLRU *store = + (_ContentStoreLRU *)contentStoreInterface_GetPrivateData(storeImpl); + + parcAssertNotNull(store, "Parameter store must be non-null"); + parcAssertNotNull(interest, "Parameter interestMessage must be non-null"); + parcAssertTrue(message_GetType(interest) == MessagePacketType_Interest, + "Parameter interestMessage must be an Interest"); + + PARCHashCodeTable *table; + table = store->storageByName; + + ContentStoreEntry *storeEntry = parcHashCodeTable_Get(table, interest); + + bool foundEntry = false; + + if (storeEntry) { + if (contentStoreEntry_HasExpiryTimeTicks(storeEntry) && + contentStoreEntry_GetExpiryTimeTicks(storeEntry) < currentTimeTicks) { + // the entry is expired, we can remove it + _contentStoreLRU_PurgeStoreEntry(store, storeEntry); + } else { + foundEntry = true; + } + } + + if (foundEntry) { + contentStoreEntry_MoveToHead(storeEntry); + result = contentStoreEntry_GetMessage(storeEntry); + + store->stats.countHits++; + + if (logger_IsLoggable(store->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(store->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, + "ContentStoreLRU %p matched interest %p (hits %" PRIu64 + ", misses %" PRIu64 ")", + (void *)store, (void *)interest, store->stats.countHits, + store->stats.countMisses); + } + } else { + store->stats.countMisses++; + + if (logger_IsLoggable(store->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(store->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, + "ContentStoreLRU %p missed interest %p (hits %" PRIu64 + ", misses %" PRIu64 ")", + (void *)store, (void *)interest, store->stats.countHits, + store->stats.countMisses); + } + } + + return result; +} + +static bool _contentStoreLRU_RemoveContent(ContentStoreInterface *storeImpl, + Message *content) { + bool result = false; + _ContentStoreLRU *store = + (_ContentStoreLRU *)contentStoreInterface_GetPrivateData(storeImpl); + + ContentStoreEntry *storeEntry = + parcHashCodeTable_Get(store->storageByName, content); + + if (storeEntry != NULL) { + _contentStoreLRU_PurgeStoreEntry(store, storeEntry); + result = true; + } + + return result; +} + +static void _contentStoreLRU_Log(ContentStoreInterface *storeImpl) { + _ContentStoreLRU *store = + (_ContentStoreLRU *)contentStoreInterface_GetPrivateData(storeImpl); + + logger_Log(store->logger, LoggerFacility_Processor, PARCLogLevel_All, + __func__, + "ContentStoreLRU @%p {count = %zu, capacity = %zu {" + "stats = @%p {adds = %" PRIu64 ", hits = %" PRIu64 + ", misses = %" PRIu64 ", LRUEvictons = %" PRIu64 + ", ExpiryEvictions = %" PRIu64 ", RCTEvictions = %" PRIu64 "} }", + store, store->objectCount, store->objectCapacity, &store->stats, + store->stats.countAdds, store->stats.countHits, + store->stats.countMisses, store->stats.countLruEvictions, + store->stats.countExpiryEvictions, store->stats.countRCTEvictions); +} + +static size_t _contentStoreLRU_GetObjectCapacity( + ContentStoreInterface *storeImpl) { + _ContentStoreLRU *store = + (_ContentStoreLRU *)contentStoreInterface_GetPrivateData(storeImpl); + return store->objectCapacity; +} + +static size_t _contentStoreLRU_GetObjectCount( + ContentStoreInterface *storeImpl) { + _ContentStoreLRU *store = + (_ContentStoreLRU *)contentStoreInterface_GetPrivateData(storeImpl); + return store->objectCount; +} + +static size_t _contentStoreLRU_SetObjectCapacity( + ContentStoreInterface *storeImpl, size_t newCapacity) { + _ContentStoreLRU *store = + (_ContentStoreLRU *)contentStoreInterface_GetPrivateData(storeImpl); + return store->objectCapacity = newCapacity; +} + +ContentStoreInterface *contentStoreLRU_Create(ContentStoreConfig *config, + Logger *logger) { + ContentStoreInterface *storeImpl = NULL; + + parcAssertNotNull(logger, "ContentStoreLRU requires a non-NULL logger"); + + storeImpl = parcObject_CreateAndClearInstance(ContentStoreInterface); + + if (storeImpl != NULL) { + storeImpl->_privateData = + parcObject_CreateAndClearInstance(_ContentStoreLRU); + + if (_contentStoreLRU_Init(storeImpl->_privateData, config, logger)) { + storeImpl->putContent = &_contentStoreLRU_PutContent; + storeImpl->removeContent = &_contentStoreLRU_RemoveContent; + + storeImpl->matchInterest = &_contentStoreLRU_MatchInterest; + + storeImpl->getObjectCount = &_contentStoreLRU_GetObjectCount; + storeImpl->getObjectCapacity = &_contentStoreLRU_GetObjectCapacity; + + storeImpl->log = &_contentStoreLRU_Log; + + storeImpl->acquire = &_contentStoreLRU_Acquire; + storeImpl->release = &_contentStoreLRU_Release; + + // Initialize from the config passed to us. + _contentStoreLRU_SetObjectCapacity(storeImpl, config->objectCapacity); + + if (logger_IsLoggable(logger, LoggerFacility_Processor, + PARCLogLevel_Info)) { + logger_Log(logger, LoggerFacility_Processor, PARCLogLevel_Info, + __func__, "ContentStoreLRU %p created with capacity %zu", + (void *)storeImpl, + contentStoreInterface_GetObjectCapacity(storeImpl)); + } + } + } else { + parcObject_Release((void **)&storeImpl); + } + + return storeImpl; +} diff --git a/hicn-light/src/content_store/contentStoreLRU.h b/hicn-light/src/content_store/contentStoreLRU.h new file mode 100755 index 000000000..3c0815ebd --- /dev/null +++ b/hicn-light/src/content_store/contentStoreLRU.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef contentStoreLRU_h +#define contentStoreLRU_h + +#include <src/content_store/contentStoreInterface.h> +#include <src/core/logger.h> +#include <stdio.h> + +/** + * Create and Initialize an instance of contentStoreLRU. A newly allocated + * {@link ContentStoreInterface} object is initialized and returned. It must + * eventually be released by calling {@link contentStoreInterface_Release}. + * + * + * @param config An instance of `ContentStoreConfig`, specifying options to be + * applied by the underlying contentStoreLRU instance. + * @param logger An instance of a {@link Logger} to use for logging content + * store events. + * + * @return a newly created contentStoreLRU instance. + * + */ +ContentStoreInterface *contentStoreLRU_Create(ContentStoreConfig *config, + Logger *logger); +#endif // contentStoreLRU_h diff --git a/hicn-light/src/content_store/listLRU.c b/hicn-light/src/content_store/listLRU.c new file mode 100755 index 000000000..42b491d7c --- /dev/null +++ b/hicn-light/src/content_store/listLRU.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> +#include <stdbool.h> +#include <stdio.h> +#include <sys/queue.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/assert/parc_Assert.h> +#include <src/content_store/listLRU.h> + +struct list_lru_entry { + void *userData; + + // always set to the list + ListLru *parentList; + + // indicates if the Entry is currently in the list + bool inList; + + TAILQ_ENTRY(list_lru_entry) list; +}; + +// this defines the TAILQ structure so we can access the tail pointer +TAILQ_HEAD(lru_s, list_lru_entry); + +struct list_lru { + struct lru_s head; + size_t itemsInList; +}; + +void listLRU_EntryDestroy(ListLruEntry **entryPtr) { + parcAssertNotNull(entryPtr, + "Parameter entryPtr must be non-null double pointer"); + + ListLruEntry *entry = *entryPtr; + if (entry->inList) { + TAILQ_REMOVE(&entry->parentList->head, entry, list); + parcAssertTrue( + entry->parentList->itemsInList > 0, + "Invalid state, removed entry from list, but itemsInList is 0"); + entry->parentList->itemsInList--; + } + + parcMemory_Deallocate((void **)&entry); + *entryPtr = NULL; +} + +void listLRU_EntryMoveToHead(ListLruEntry *entry) { + parcAssertNotNull(entry, "Parameter entry must be non-null"); + + TAILQ_REMOVE(&entry->parentList->head, entry, list); + TAILQ_INSERT_HEAD(&entry->parentList->head, entry, list); +} + +void *listLRU_EntryGetData(ListLruEntry *entry) { return entry->userData; } + +ListLru *listLRU_Create() { + ListLru *list = parcMemory_AllocateAndClear(sizeof(ListLru)); + parcAssertNotNull(list, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ListLru)); + list->itemsInList = 0; + TAILQ_INIT(&list->head); + return list; +} + +void listLRU_Destroy(ListLru **lruPtr) { + parcAssertNotNull(lruPtr, "Parameter lruPtr must be non-null double pointer"); + + ListLru *lru = *lruPtr; + + ListLruEntry *entry = TAILQ_FIRST(&lru->head); + while (entry != NULL) { + ListLruEntry *next = TAILQ_NEXT(entry, list); + listLRU_EntryDestroy(&entry); + entry = next; + } + + parcMemory_Deallocate((void **)&lru); + *lruPtr = NULL; +} + +ListLruEntry *listLRU_NewHeadEntry(ListLru *lru, void *data) { + parcAssertNotNull(lru, "Parameter lru must be non-null"); + parcAssertNotNull(data, "Parameter data must be non-null"); + + ListLruEntry *entry = parcMemory_AllocateAndClear(sizeof(ListLruEntry)); + parcAssertNotNull(entry, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ListLruEntry)); + entry->userData = data; + entry->parentList = lru; + entry->inList = true; + + TAILQ_INSERT_HEAD(&lru->head, entry, list); + lru->itemsInList++; + + return entry; +} + +ListLruEntry *listLRU_PopTail(ListLru *lru) { + parcAssertNotNull(lru, "Parameter lru must be non-null"); + + ListLruEntry *entry = TAILQ_LAST(&lru->head, lru_s); + + if (entry) { + parcAssertTrue( + lru->itemsInList > 0, + "Invalid state, removed entry from list, but itemsInList is 0"); + lru->itemsInList--; + TAILQ_REMOVE(&lru->head, entry, list); + entry->inList = false; + } + + return entry; +} + +size_t listLRU_Length(const ListLru *lru) { + parcAssertNotNull(lru, "Parameter lru must be non-null"); + return lru->itemsInList; +} diff --git a/hicn-light/src/content_store/listLRU.h b/hicn-light/src/content_store/listLRU.h new file mode 100755 index 000000000..75f698b61 --- /dev/null +++ b/hicn-light/src/content_store/listLRU.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file listLRU.h + * @brief Maintains an LRU for the content store + * + * An LRU list is make up of LRU entries. The entries are bound to the list. + * The user of the list is reponsible for knowing when there's too many things + * and wants to remove one. The LRU list will grow without bound otherwise. + * + * The LRU list is meant to be used as an auxiliary data structure, not the + * primary storage of data elements. + * + */ + +#ifndef listLRU_h +#define listLRU_h + +struct list_lru_entry; +typedef struct list_lru_entry ListLruEntry; + +struct list_lru; +typedef struct list_lru ListLru; + +/** + * @function lruEntry_Destroy + * @abstract Destroys and element. This will also remove it from the list. + */ +void listLRU_EntryDestroy(ListLruEntry **entryPtr); + +/** + * @function listLRU_EntryMoveToHead + * @abstract move an element to head + */ +void listLRU_EntryMoveToHead(ListLruEntry *entry); + +/** + * @function lruEntry_GetData + * @abstract Returns the user-supplied opaque data when the entry was created + */ +void *listLRU_EntryGetData(ListLruEntry *entry); + +/** + * @function listLRU_Create + * @abstract Creates a new Least-Recently-Used list + */ +ListLru *listLRU_Create(); + +/** + * @function listLRU_Destroy + * @abstract Destroys a list and frees all the elements in it + */ +void listLRU_Destroy(ListLru **listPtr); + +/** + * Returns the number of items in the list + * + * @param [in] lru An allocated ListLru + * @retval number The number of items in the LRU list + */ +size_t listLRU_Length(const ListLru *lru); + +/** + * @function listLRU_NewHeadEntry + * @abstract Creates a new entry for the list. It is inserted at the head of + * the list. + */ +ListLruEntry *listLRU_NewHeadEntry(ListLru *lru, void *data); + +/** + * @function listLRU_PopTail + * @abstract Removes the tail element from the list and returns it to the user + * @discussion + * Pops the tail element. The user should examine its data to destroy their + * tail object, then call <code>LruEntry_Destroy()</code> to free the + * LRU entry. + * + * @return The tail element, or NULL for an empty list + */ +ListLruEntry *listLRU_PopTail(ListLru *list); +#endif // listLRU_h diff --git a/hicn-light/src/content_store/listTimeOrdered.c b/hicn-light/src/content_store/listTimeOrdered.c new file mode 100755 index 000000000..44697d202 --- /dev/null +++ b/hicn-light/src/content_store/listTimeOrdered.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <parc/assert/parc_Assert.h> +#include <src/config.h> +#include <src/content_store/listTimeOrdered.h> + +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_TreeRedBlack.h> + +/** + * A list of ContentStoreEntrys, kept in sorted order by time. The ordering is + * calculated by a key compare function (e.g. {@link TimeOrderList_KeyCompare}), + * passed in. + * + * This container does not hold references to the objects that it contains. In + * other words, it does not Acquire() the Messages that are placed in it. That + * reference count is managed by the owning ContentStore. This is purely an + * index, and provides an easy to way index Messages based on a specified time + * value. Typically, that would be the Expiration Time. + * + * It maintains a tree, sorted by the time values passed in to the Add() + * function. It does not manage capacity, and can grow uncontrollably if the + * owning ContentStore does not manage it. Items are indexed first by time, then + * address of the Message (just as a distringuishing attribute). This allows us + * to store multiple items with the same expiration time. + */ + +struct list_timeordered { + PARCTreeRedBlack *timeOrderedTree; +}; + +static void _finalRelease(ListTimeOrdered **listP) { + ListTimeOrdered *list = *listP; + parcTreeRedBlack_Destroy(&list->timeOrderedTree); +} + +parcObject_ExtendPARCObject(ListTimeOrdered, _finalRelease, NULL, NULL, NULL, + NULL, NULL, NULL); + +parcObject_ImplementAcquire(listTimeOrdered, ListTimeOrdered); + +parcObject_ImplementRelease(listTimeOrdered, ListTimeOrdered); + +ListTimeOrdered *listTimeOrdered_Create( + TimeOrderList_KeyCompare *keyCompareFunction) { + ListTimeOrdered *result = parcObject_CreateInstance(ListTimeOrdered); + if (NULL != result) { + result->timeOrderedTree = + parcTreeRedBlack_Create(keyCompareFunction, // keyCompare + NULL, // keyFree + NULL, // keyCopy + NULL, // valueEquals + NULL, // valueFree + NULL); // valueCopy + } + return result; +} + +void listTimeOrdered_Add(ListTimeOrdered *list, ContentStoreEntry *entry) { + parcTreeRedBlack_Insert(list->timeOrderedTree, entry, entry); +} + +ContentStoreEntry *listTimeOrdered_GetOldest(ListTimeOrdered *list) { + return parcTreeRedBlack_FirstKey(list->timeOrderedTree); +} + +bool listTimeOrdered_Remove(ListTimeOrdered *list, + ContentStoreEntry *storeEntry) { + bool result = false; + + ContentStoreEntry *entry = (ContentStoreEntry *)parcTreeRedBlack_Remove( + list->timeOrderedTree, storeEntry); + if (entry != NULL) { + result = true; + } + return result; +} + +size_t listTimeOrdered_Length(ListTimeOrdered *list) { + return (size_t)parcTreeRedBlack_Size(list->timeOrderedTree); +} diff --git a/hicn-light/src/content_store/listTimeOrdered.h b/hicn-light/src/content_store/listTimeOrdered.h new file mode 100755 index 000000000..b18bd16f7 --- /dev/null +++ b/hicn-light/src/content_store/listTimeOrdered.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef listTimeOrdered_h +#define listTimeOrdered_h + +#include <parc/algol/parc_TreeRedBlack.h> +#include <src/content_store/contentStoreEntry.h> +#include <src/core/message.h> +#include <stdio.h> + +struct list_timeordered; +typedef struct list_timeordered ListTimeOrdered; + +/** + * A signum function that takes two instances of ContentStoreEntrys and + * returns a value based on their relative values. + */ +typedef PARCTreeRedBlack_KeyCompare TimeOrderList_KeyCompare; + +/** + * Create a new instance of `ListTimeOrdered` that will maintain the order of + * its list items using the supplied `keyCompareFunction`. + * + * The newly created `ListTimeOrdered` must eventually be released by calling + * {@link listTimeOrdered_Release}. + * + * @param keyCompareFunction the signum comparison function to use to sort + * stored items. + * @return a new instance of `TimeOrderList`. + * @return NULL if the new instance couldn't be created. + * + */ +ListTimeOrdered *listTimeOrdered_Create( + TimeOrderList_KeyCompare *keyCompareFunction); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + */ +void listTimeOrdered_Release(ListTimeOrdered **listP); + +/** + * Add a {@link ContentStoreEntry} instance to the specified list. Note that a + * new refernece to the specified `storeEntry` is not acquired. + * + * @param list the list instance into which to add the specified storeEntry. + * @param storeEntry the storeEntry instance to add. + * + */ +void listTimeOrdered_Add(ListTimeOrdered *list, ContentStoreEntry *storeEntry); + +/** + * Remove a {@link ContentStoreEntry} instance from the specified list. + * + * @param list the list instance from which to remove the specified storeEntry. + * @param storeEntry the storeEntry instance to remove. + * @return true if the removal was succesful. + * @return false if the removal was not succesful. + * + */ +bool listTimeOrdered_Remove(ListTimeOrdered *list, + ContentStoreEntry *storeEntry); + +/** + * Return the oldest {@link ContentStoreEntry} instance in this list. That is, + * the one with the smallest time value. + * + * @param list the list instance from which to retrieve the oldest storeEntry. + * @param the oldest `ContentStoreEntry` in the list + * @param NULL if no `ContentStoreEntry` was available. + * + */ +ContentStoreEntry *listTimeOrdered_GetOldest(ListTimeOrdered *list); + +/** + * Return the number of items currently stored in the list. + * + * @param list the `ListTimeOrdered` instance from which to retrieve the count. + * @return the number of items in the list. + * + */ +size_t listTimeOrdered_Length(ListTimeOrdered *list); +#endif /* defined(listTimeOrdered_h) */ diff --git a/hicn-light/src/core/CMakeLists.txt b/hicn-light/src/core/CMakeLists.txt new file mode 100755 index 000000000..1d7dc03e9 --- /dev/null +++ b/hicn-light/src/core/CMakeLists.txt @@ -0,0 +1,55 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/connectionManager.h + ${CMAKE_CURRENT_SOURCE_DIR}/ticks.h + ${CMAKE_CURRENT_SOURCE_DIR}/connectionList.h + ${CMAKE_CURRENT_SOURCE_DIR}/connectionTable.h + ${CMAKE_CURRENT_SOURCE_DIR}/connection.h + ${CMAKE_CURRENT_SOURCE_DIR}/forwarder.h + ${CMAKE_CURRENT_SOURCE_DIR}/logger.h + ${CMAKE_CURRENT_SOURCE_DIR}/dispatcher.h + ${CMAKE_CURRENT_SOURCE_DIR}/message.h + ${CMAKE_CURRENT_SOURCE_DIR}/messagePacketType.h + ${CMAKE_CURRENT_SOURCE_DIR}/numberSet.h + ${CMAKE_CURRENT_SOURCE_DIR}/streamBuffer.h + ${CMAKE_CURRENT_SOURCE_DIR}/system.h + ${CMAKE_CURRENT_SOURCE_DIR}/mapMe.h + ${CMAKE_CURRENT_SOURCE_DIR}/wldr.h + ${CMAKE_CURRENT_SOURCE_DIR}/messageHandler.h + ${CMAKE_CURRENT_SOURCE_DIR}/nameBitvector.h + ${CMAKE_CURRENT_SOURCE_DIR}/name.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/connection.c + ${CMAKE_CURRENT_SOURCE_DIR}/connectionList.c + ${CMAKE_CURRENT_SOURCE_DIR}/connectionManager.c + ${CMAKE_CURRENT_SOURCE_DIR}/connectionTable.c + ${CMAKE_CURRENT_SOURCE_DIR}/dispatcher.c + ${CMAKE_CURRENT_SOURCE_DIR}/forwarder.c + ${CMAKE_CURRENT_SOURCE_DIR}/logger.c + ${CMAKE_CURRENT_SOURCE_DIR}/message.c + ${CMAKE_CURRENT_SOURCE_DIR}/numberSet.c + ${CMAKE_CURRENT_SOURCE_DIR}/streamBuffer.c + ${CMAKE_CURRENT_SOURCE_DIR}/mapMe.c + ${CMAKE_CURRENT_SOURCE_DIR}/wldr.c + ${CMAKE_CURRENT_SOURCE_DIR}/nameBitvector.c + ${CMAKE_CURRENT_SOURCE_DIR}/name.c +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE)
\ No newline at end of file diff --git a/hicn-light/src/core/connection.c b/hicn-light/src/core/connection.c new file mode 100755 index 000000000..073b7260f --- /dev/null +++ b/hicn-light/src/core/connection.c @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <limits.h> +#include <src/config.h> +#include <stdio.h> + +#include <src/core/connection.h> +#include <src/core/messageHandler.h> +#include <src/core/ticks.h> +#include <src/core/wldr.h> +#include <src/io/addressPair.h> +#include <src/io/ioOperations.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/assert/parc_Assert.h> + +struct connection { + const AddressPair *addressPair; + IoOperations *ops; + + unsigned refCount; + + bool probing_active; + unsigned probing_interval; + unsigned counter; + Ticks last_sent; + Ticks delay; + + bool wldrAutoStart; // if true, wldr can be set automatically + // by default this value is set to true. + // if wldr is activated using a command (config + // file/hicnLightControl) this value is set to false so + // that a base station can not disable wldr at the client + Wldr *wldr; +}; + +Connection *connection_Create(IoOperations *ops) { + parcAssertNotNull(ops, "Parameter ops must be non-null"); + Connection *conn = parcMemory_AllocateAndClear(sizeof(Connection)); + parcAssertNotNull(conn, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Connection)); + conn->addressPair = ioOperations_GetAddressPair(ops); + conn->ops = ops; + conn->refCount = 1; + conn->wldr = NULL; + conn->probing_active = false; + + conn->wldrAutoStart = true; + conn->probing_interval = 0; + conn->counter = 0; + conn->last_sent = 0; + conn->delay = INT_MAX; + return conn; +} + +Connection *connection_Acquire(Connection *connection) { + parcAssertNotNull(connection, "Parameter conn must be non-null"); + connection->refCount++; + return connection; +} + +void connection_Release(Connection **connectionPtr) { + parcAssertNotNull(connectionPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*connectionPtr, + "Parameter must dereference to non-null pointer"); + Connection *conn = *connectionPtr; + + parcAssertTrue( + conn->refCount > 0, + "Invalid state, connection reference count should be positive, got 0."); + conn->refCount--; + if (conn->refCount == 0) { + // don't destroy addressPair, its part of ops. + ioOperations_Release(&conn->ops); + if (conn->wldr != NULL) { + wldr_Destroy(&(conn->wldr)); + } + parcMemory_Deallocate((void **)&conn); + } + *connectionPtr = NULL; +} + +bool connection_Send(const Connection *conn, Message *message) { + parcAssertNotNull(conn, "Parameter conn must be non-null"); + parcAssertNotNull(message, "Parameter message must be non-null"); + + if (ioOperations_IsUp(conn->ops)) { + if (message_GetType(message) == MessagePacketType_ContentObject) { + uint8_t connectionId = (uint8_t)connection_GetConnectionId(conn); + message_UpdatePathLabel(message, connectionId); + } + if (conn->wldr != NULL) { + wldr_SetLabel(conn->wldr, message); + } else { + message_ResetWldrLabel(message); + } + return ioOperations_Send(conn->ops, NULL, message); + } + return false; +} + +static void _sendProbe(Connection *conn, unsigned probeType, uint8_t *message) { + parcAssertNotNull(conn, "Parameter conn must be non-null"); + + if (probeType == PACKET_TYPE_PROBE_REQUEST) { + Ticks now = ioOperations_SendProbe(conn->ops, probeType, message); + if (now != 0) { + conn->last_sent = now; + } + } else { + ioOperations_SendProbe(conn->ops, probeType, message); + } +} + +void connection_Probe(Connection *conn) { + _sendProbe(conn, PACKET_TYPE_PROBE_REQUEST, NULL); +} + +void connection_HandleProbe(Connection *conn, uint8_t *probe, + Ticks actualTime) { + parcAssertNotNull(conn, "Parameter conn must be non-null"); + parcAssertNotNull(probe, "Parameter pkt must be non-null"); + + uint8_t probeType = messageHandler_GetProbePacketType(probe); + if (probeType == PACKET_TYPE_PROBE_REQUEST) { + _sendProbe(conn, PACKET_TYPE_PROBE_REPLY, probe); + } else if (probeType == PACKET_TYPE_PROBE_REPLY) { + Ticks delay = actualTime - conn->last_sent; + if (delay == 0) { + delay = 1; + } + if (delay < conn->delay) { + conn->delay = delay; + } + } else { + printf("receivde unkwon probe type\n"); + } +} + +uint64_t connection_GetDelay(Connection *conn) { return (uint64_t)conn->delay; } + +IoOperations *connection_GetIoOperations(const Connection *conn) { + return conn->ops; +} + +unsigned connection_GetConnectionId(const Connection *conn) { + parcAssertNotNull(conn, "Parameter conn must be non-null"); + return ioOperations_GetConnectionId(conn->ops); +} + +const AddressPair *connection_GetAddressPair(const Connection *conn) { + parcAssertNotNull(conn, "Parameter conn must be non-null"); + return ioOperations_GetAddressPair(conn->ops); +} + +bool connection_IsUp(const Connection *conn) { + parcAssertNotNull(conn, "Parameter conn must be non-null"); + if (!conn->ops) return false; + return ioOperations_IsUp(conn->ops); +} + +bool connection_IsLocal(const Connection *conn) { + parcAssertNotNull(conn, "Parameter conn must be non-null"); + return ioOperations_IsLocal(conn->ops); +} + +const void *connection_Class(const Connection *conn) { + parcAssertNotNull(conn, "Parameter conn must be non-null"); + return ioOperations_Class(conn->ops); +} + +bool connection_ReSend(const Connection *conn, Message *message, + bool notification) { + parcAssertNotNull(conn, "Parameter conn must be non-null"); + parcAssertNotNull(message, "Parameter message must be non-null"); + bool res = false; + + if (connection_IsUp(conn)) { + // here the wldr header is alreay set: this message is a retransmission or a + // notification + + // we need to recompiute the path lable since we always store a pointer to + // the same message if this message will be sent again to someonelse, the new + // path label must be computed starting from the orignal labelorignal label. + // Notice that we heve the same problem in case of PIT aggregation. That case + // is handled insied the MessageProcessor. This is specific to WLDR + // retransmittions. This is done only for data packets + + if (message_GetType(message) == MessagePacketType_ContentObject) { + uint8_t connectionId = (uint8_t)connection_GetConnectionId(conn); + uint32_t old_path_label = message_GetPathLabel(message); + message_UpdatePathLabel(message, connectionId); + + res = ioOperations_Send(conn->ops, NULL, message); + + message_SetPathLabel(message, old_path_label); + } else { + res = ioOperations_Send(conn->ops, NULL, message); + } + } + + if (notification) { + // the notification is never destroyed + message_Release(&message); + } + + return res; +} + +void connection_AllowWldrAutoStart(Connection *conn, bool allow) { + conn->wldrAutoStart = allow; +} + +void connection_EnableWldr(Connection *conn) { + if (!connection_IsLocal(conn)) { + if (conn->wldr == NULL) { + printf("----------------- enable wldr\n"); + conn->wldr = wldr_Init(); + } + } +} + +void connection_DisableWldr(Connection *conn) { + if (!connection_IsLocal(conn)) { + if (conn->wldr != NULL) { + printf("----------------- disable wldr\n"); + wldr_Destroy(&(conn->wldr)); + conn->wldr = NULL; + } + } +} + +bool connection_HasWldr(const Connection *conn) { + if (conn->wldr == NULL) { + return false; + } else { + return true; + } +} + +bool connection_WldrAutoStartAllowed(const Connection *conn) { + return conn->wldrAutoStart; +} + +void connection_DetectLosses(Connection *conn, Message *message) { + if (conn->wldr != NULL) wldr_DetectLosses(conn->wldr, conn, message); +} + +void connection_HandleWldrNotification(Connection *conn, Message *message) { + if (conn->wldr != NULL) + wldr_HandleWldrNotification(conn->wldr, conn, message); +} diff --git a/hicn-light/src/core/connection.h b/hicn-light/src/core/connection.h new file mode 100755 index 000000000..b5c703527 --- /dev/null +++ b/hicn-light/src/core/connection.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file connection.h + * @brief Wrapper for different types of connections + * + * A connection wraps a specific set of {@link IoOperations}. Those operations + * allow for input and output. Connections get stored in the Connection Table. + * + */ + +#ifndef connection_h +#define connection_h +#include <src/config.h> +#include <src/io/ioOperations.h> +#include <src/utils/address.h> + +// packet types for probing +#define PACKET_TYPE_PROBE_REQUEST 5 +#define PACKET_TYPE_PROBE_REPLY 6 + +struct connection; +typedef struct connection Connection; + +/** + * Creates a connection object. + */ +Connection *connection_Create(IoOperations *ops); + +/** + * @function connection_Release + * @abstract Releases a reference count, destroying on last release + * @discussion + * Only frees the memory on the final reference count. The pointer will + * always be NULL'd. + */ +void connection_Release(Connection **connectionPtr); + +/** + * @function connection_Acquire + * @abstract A reference counted copy. + * @discussion + * A shallow copy, they share the same memory. + */ +Connection *connection_Acquire(Connection *connection); + +/** + * @function connection_Send + * @abstract Sends the message on the connection + * @return true if message sent, false if connection not up + */ +bool connection_Send(const Connection *conn, Message *message); + +/** + * Return the `IoOperations` instance associated with the specified `Connection` + * instance. + * @param [in] connection The allocated connection + * @return a pointer to the IoOperations instance associated by th specified + * connection. + */ +IoOperations *connection_GetIoOperations(const Connection *conn); + +/** + * Returns the unique identifier of the connection + * Calls the underlying IoOperations to fetch the connection id + * @param [in] connection The allocated connection + * @return unsigned The unique connection id + */ +unsigned connection_GetConnectionId(const Connection *conn); + +/** + * Returns the (remote, local) address pair that describes the connection + * @param [in] connection The allocated connection + * @return non-null The connection's remote and local address + * @return null Should never return NULL + */ +const AddressPair *connection_GetAddressPair(const Connection *conn); + +/** + * Checks if the connection is in the "up" state + * @param [in] connection The allocated connection + * @return true The connection is in the "up" state + * @return false The connection is not in the "up" state + */ +bool connection_IsUp(const Connection *conn); + +/** + * Checks if the connection is to a Local/Loopback address + * + * A local connection is PF_LOCAL (PF_UNIX) and a loopback connection is + * 127.0.0.0/8 or ::1 for IPv6. + * + * @param [in] connection The allocated connection + * + * @retval true The connection is local or loopback + * @retval false The connection is not local or loopback + */ +bool connection_IsLocal(const Connection *conn); + +/** + * Returns an opaque pointer representing the class of the Io Operations + * + * Returns an opaque pointer that an implementation can use to detect if + * the connection is based on that class. + * + * @param [in] conn The Connection to analyze + * + * @return non-null An opaque pointer for each concrete implementation + */ +const void *connection_Class(const Connection *conn); + +bool connection_ReSend(const Connection *conn, Message *message, + bool notification); + +void connection_Probe(Connection *conn); + +void connection_HandleProbe(Connection *conn, uint8_t *message, + Ticks actualTime); + +uint64_t connection_GetDelay(Connection *conn); + +void connection_AllowWldrAutoStart(Connection *conn, bool allow); + +void connection_EnableWldr(Connection *conn); + +void connection_DisableWldr(Connection *conn); + +bool connection_HasWldr(const Connection *conn); + +bool connection_WldrAutoStartAllowed(const Connection *conn); + +void connection_DetectLosses(Connection *conn, Message *message); + +void connection_HandleWldrNotification(Connection *conn, Message *message); +#endif // connection_h diff --git a/hicn-light/src/core/connectionList.c b/hicn-light/src/core/connectionList.c new file mode 100755 index 000000000..b2913fa05 --- /dev/null +++ b/hicn-light/src/core/connectionList.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> +#include <stdio.h> + +#include <parc/algol/parc_ArrayList.h> +#include <parc/algol/parc_Memory.h> + +#include <parc/assert/parc_Assert.h> +#include <src/core/connectionList.h> + +struct connection_list { + PARCArrayList *listOfConnections; +}; + +static void connectionList_ArrayDestroyer(void **voidPtr) { + Connection **entryPtr = (Connection **)voidPtr; + connection_Release(entryPtr); +} + +ConnectionList *connectionList_Create() { + ConnectionList *list = parcMemory_AllocateAndClear(sizeof(ConnectionList)); + parcAssertNotNull(list, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ConnectionList)); + list->listOfConnections = parcArrayList_Create(connectionList_ArrayDestroyer); + return list; +} + +void connectionList_Destroy(ConnectionList **listPtr) { + parcAssertNotNull(listPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*listPtr, "Parameter must dereference to non-null pointer"); + ConnectionList *list = *listPtr; + parcArrayList_Destroy(&list->listOfConnections); + parcMemory_Deallocate((void **)&list); + *listPtr = NULL; +} + +void connectionList_Append(ConnectionList *list, Connection *entry) { + parcAssertNotNull(list, "Parameter list must be non-null"); + parcAssertNotNull(entry, "Parameter entry must be non-null"); + + parcArrayList_Add(list->listOfConnections, connection_Acquire(entry)); +} + +size_t connectionList_Length(const ConnectionList *list) { + parcAssertNotNull(list, "Parameter list must be non-null"); + return parcArrayList_Size(list->listOfConnections); +} + +Connection *connectionList_Get(ConnectionList *list, size_t index) { + parcAssertNotNull(list, "Parameter list must be non-null"); + Connection *original = + (Connection *)parcArrayList_Get(list->listOfConnections, index); + return original; +} diff --git a/hicn-light/src/core/connectionList.h b/hicn-light/src/core/connectionList.h new file mode 100755 index 000000000..cdca12993 --- /dev/null +++ b/hicn-light/src/core/connectionList.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file connectionList.h + * @brief A typesafe list of Connection objects + * + * <#Detailed Description#> + * + */ + +#ifndef connectionList_h +#define connectionList_h + +struct connection_list; +typedef struct connection_list ConnectionList; + +#include <src/core/connection.h> + +/** + * Creates a lis of Connection + * + * @return non-null An allocated list + * @return null An error + */ +ConnectionList *connectionList_Create(void); + +/** + * Destroys the list and all objects inside it + */ +void connectionList_Destroy(ConnectionList **listPtr); + +/** + * @function connectionList_Append + * @abstract Adds a connection entry to the list. + * @discussion + * Acquires a reference to the passed entry and stores it in the list. + */ +void connectionList_Append(ConnectionList *list, Connection *entry); + +/** + * Returns the number of items on the list + * @param [in] list The allocated list to check + * @return number The number of items on the list + */ +size_t connectionList_Length(const ConnectionList *list); + +/** + * @function connectionList_Get + * @abstract Returns the connection entry. + * @discussion + * Caller must not destroy the returned value. If you will store the + * entry in your own data structure, you should acquire your own reference. + * Will assert if you go beyond the end of the list. + * + */ +Connection *connectionList_Get(ConnectionList *list, size_t index); +#endif // connectionList_h diff --git a/hicn-light/src/core/connectionManager.c b/hicn-light/src/core/connectionManager.c new file mode 100755 index 000000000..2089e1495 --- /dev/null +++ b/hicn-light/src/core/connectionManager.c @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * The Connection Manager sets itself up as a listener to the Messenger so it + * can take action based on system events. + * + * The Connection Manager queues and then processes in a later time slice the + * messages. + * + */ + +#include <src/config.h> +#include <src/core/connectionManager.h> +#include <src/core/forwarder.h> +#include <src/messenger/messenger.h> +#include <src/messenger/messengerRecipient.h> +#include <src/messenger/missiveDeque.h> +#include <stdio.h> + +#include <parc/algol/parc_Memory.h> + +#include <parc/assert/parc_Assert.h> + +struct connection_manager { + Forwarder *forwarder; + Logger *logger; + + MessengerRecipient *messengerRecipient; + + // we queue missives as they come in to process in our own + // event timeslice + MissiveDeque *missiveQueue; + + // for deferred queue processing + PARCEventTimer *timerEvent; +}; + +/** + * Receives missives from the messenger, queues them, and schedules our + * execution + * + * We defer processing of missives to a later time slice + */ +static void connectionManager_MessengerCallback(MessengerRecipient *recipient, + Missive *missive); + +/** + * Event callback + * + * This is our main run loop to process our queue of messages. It is scheduled + * in {@link connectionManager_MessengerCallback} when the queue becomes + * non-empty. + * + * When we are called here, we have exclusive use of the system, so we will not + * create any message loops + * + * @param [in] fd unused, required for compliance with function prototype + * @param [in] which_event unused, required for compliance with function + * prototype + * @param [in] connManagerVoidPtr A void* to ConnectionManager + * + */ +static void connectionManager_ProcessQueue(int fd, PARCEventType which_event, + void *connManagerVoidPtr); + +static void connectionManager_ProcessClosedMissive( + ConnectionManager *connManager, const Missive *missive); + +// ======================================================== +// Public API + +ConnectionManager *connectionManager_Create(Forwarder *forwarder) { + ConnectionManager *connManager = + parcMemory_AllocateAndClear(sizeof(ConnectionManager)); + parcAssertNotNull(connManager, + "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ConnectionManager)); + connManager->forwarder = forwarder; + connManager->missiveQueue = missiveDeque_Create(); + connManager->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + + Messenger *messenger = forwarder_GetMessenger(connManager->forwarder); + + // creates the timer, but does not start it + PARCEventScheduler *base = + dispatcher_GetEventScheduler(forwarder_GetDispatcher(forwarder)); + connManager->timerEvent = parcEventTimer_Create( + base, 0, connectionManager_ProcessQueue, connManager); + + connManager->messengerRecipient = messengerRecipient_Create( + connManager, connectionManager_MessengerCallback); + messenger_Register(messenger, connManager->messengerRecipient); + return connManager; +} + +void connectionManager_Destroy(ConnectionManager **managerPtr) { + parcAssertNotNull(managerPtr, "Double pointer must be non-null"); + parcAssertNotNull(*managerPtr, "Double pointer must dereference to non-null"); + + ConnectionManager *connManager = *managerPtr; + + Messenger *messenger = forwarder_GetMessenger(connManager->forwarder); + parcEventTimer_Destroy(&(connManager->timerEvent)); + messenger_Unregister(messenger, connManager->messengerRecipient); + messengerRecipient_Destroy(&connManager->messengerRecipient); + missiveDeque_Release(&connManager->missiveQueue); + logger_Release(&connManager->logger); + + parcMemory_Deallocate((void **)&connManager); + *managerPtr = NULL; +} + +// ======================================================== +// Internal Functions + +static void connectionManager_MessengerCallback(MessengerRecipient *recipient, + Missive *missive) { + ConnectionManager *connManager = + messengerRecipient_GetRecipientContext(recipient); + + // we do not release our reference count, we store it until later + // We are called with our own reference, so we do not need to acquire the + // missive here. + missiveDeque_Append(connManager->missiveQueue, missive); + + if (missiveDeque_Size(connManager->missiveQueue) == 1) { + // When it becomes non-empty, schedule {@link + // connectionManager_ProcessQueue} + struct timeval immediateTimeout = {0, 0}; + parcEventTimer_Start(connManager->timerEvent, &immediateTimeout); + } +} + +static void connectionManager_ProcessQueue(int fd, PARCEventType which_event, + void *connManagerVoidPtr) { + ConnectionManager *connManager = (ConnectionManager *)connManagerVoidPtr; + + Missive *missive; + while ((missive = missiveDeque_RemoveFirst(connManager->missiveQueue)) != + NULL) { + switch (missive_GetType(missive)) { + case MissiveType_ConnectionCreate: + // hook to signal that a new connection was created + break; + case MissiveType_ConnectionUp: + // hook to signal that a new connection is up + break; + case MissiveType_ConnectionDown: + // hook to signal that a connection is down + break; + case MissiveType_ConnectionClosed: + connectionManager_ProcessClosedMissive(connManager, missive); + break; + case MissiveType_ConnectionDestroyed: + // hook to signal that a connection was destroyed + break; + default: + parcTrapUnexpectedState("Missive %p of unknown type: %d", + (void *)missive, missive_GetType(missive)); + } + missive_Release(&missive); + } +} + +static void connectionManager_ProcessClosedMissive( + ConnectionManager *connManager, const Missive *missive) { + logger_Log(connManager->logger, LoggerFacility_Core, PARCLogLevel_Debug, + __func__, "Processing CLOSED message for connid %u", + missive_GetConnectionId(missive)); + + ConnectionTable *table = forwarder_GetConnectionTable(connManager->forwarder); + const Connection *conn = + connectionTable_FindById(table, missive_GetConnectionId(missive)); + + if (conn) { + // this will destroy the connection if its the last reference count + connectionTable_Remove(table, conn); + + // remove from FIB + forwarder_RemoveConnectionIdFromRoutes(connManager->forwarder, + missive_GetConnectionId(missive)); + } +} diff --git a/hicn-light/src/core/connectionManager.h b/hicn-light/src/core/connectionManager.h new file mode 100755 index 000000000..b77553e0d --- /dev/null +++ b/hicn-light/src/core/connectionManager.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file connectionManager.h + * @brief The connection manager handles connection events, such as going down + * + * The connection manager listens to the event notification system. Based on + * those events, the connection manager will take specific actions. This is + * expected to be a singleton instantiated by the forwarder. + * + */ + +#ifndef connectionManager_h +#define connectionManager_h + +#include <src/core/forwarder.h> + +struct connection_manager; +typedef struct connection_manager ConnectionManager; + +ConnectionManager *connectionManager_Create(Forwarder *forwarder); + +void connectionManager_Destroy(ConnectionManager **managerPtr); +#endif // connectionManager_h diff --git a/hicn-light/src/core/connectionTable.c b/hicn-light/src/core/connectionTable.c new file mode 100755 index 000000000..ba0942ddb --- /dev/null +++ b/hicn-light/src/core/connectionTable.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @header ConnectionTable + * @abstract Records all the current connections and references to them + * @discussion + * + */ + +#include <src/config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_ArrayList.h> +#include <parc/algol/parc_Hash.h> +#include <parc/algol/parc_HashCodeTable.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_TreeRedBlack.h> +#include <src/core/connectionTable.h> +#include <src/io/addressPair.h> + +struct connection_table { + // The main storage table that has a Destroy method. + // The key is an unsigned int pointer. We use an unsigned int pointer + // because we want to be able to lookup by the id alone, and not have to + // have the IoOperations everywhere. + PARCHashCodeTable *storageTableById; + + // The key is a AddressPair + // It does not have a destroy method for the data or key, + // as they are derived from the storage table. + PARCHashCodeTable *indexByAddressPair; + + // An iterable stucture organized by connection id. The keys and + // values are the same pointers as in storageTableById, so there + // are no destructors in the tree. + // The only reason to keep this tree is so we have an iterable list + // of connections, which the hash table does not give us. + PARCTreeRedBlack *listById; +}; + +static bool connectionTable_ConnectionIdEquals(const void *keyA, + const void *keyB) { + unsigned idA = *((unsigned *)keyA); + unsigned idB = *((unsigned *)keyB); + return (idA == idB); +} + +static int connectionTable_ConnectionIdCompare(const void *keyA, + const void *keyB) { + unsigned idA = *((unsigned *)keyA); + unsigned idB = *((unsigned *)keyB); + if (idA < idB) { + return -1; + } + if (idA > idB) { + return +1; + } + return 0; +} + +static bool connectionTable_AddressPairEquals(const void *keyA, + const void *keyB) { + const AddressPair *pairA = (const AddressPair *)keyA; + const AddressPair *pairB = (const AddressPair *)keyB; + + return addressPair_Equals(pairA, pairB); +} + +static HashCodeType connectionTable_ConnectionIdHashCode(const void *keyA) { + unsigned idA = *((unsigned *)keyA); + return parcHash32_Int32(idA); +} + +static HashCodeType connectionTable_AddressPairHashCode(const void *keyA) { + const AddressPair *pairA = (const AddressPair *)keyA; + return addressPair_HashCode(pairA); +} + +static void connectionTable_ConnectionIdDestroyer(void **dataPtr) { + unsigned *idA = (unsigned *)*dataPtr; + parcMemory_Deallocate((void **)&idA); + *dataPtr = NULL; +} + +static void connectionTable_ConnectionDestroyer(void **dataPtr) { + connection_Release((Connection **)dataPtr); +} + +ConnectionTable *connectionTable_Create() { + size_t initialSize = 16384; + + ConnectionTable *conntable = + parcMemory_AllocateAndClear(sizeof(ConnectionTable)); + parcAssertNotNull(conntable, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ConnectionTable)); + + conntable->storageTableById = parcHashCodeTable_Create_Size( + connectionTable_ConnectionIdEquals, connectionTable_ConnectionIdHashCode, + connectionTable_ConnectionIdDestroyer, + connectionTable_ConnectionDestroyer, initialSize); + + // no key or data destroyer, this is an index into storageByid. + conntable->indexByAddressPair = parcHashCodeTable_Create_Size( + connectionTable_AddressPairEquals, connectionTable_AddressPairHashCode, + NULL, NULL, initialSize); + + conntable->listById = + parcTreeRedBlack_Create(connectionTable_ConnectionIdCompare, + NULL, // key free + NULL, // key copy + NULL, // value equals + NULL, // value free + NULL); // value copy + + return conntable; +} + +void connectionTable_Destroy(ConnectionTable **conntablePtr) { + parcAssertNotNull(conntablePtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*conntablePtr, + "Parameter must dereference to non-null pointer"); + + ConnectionTable *conntable = *conntablePtr; + + parcTreeRedBlack_Destroy(&conntable->listById); + parcHashCodeTable_Destroy(&conntable->indexByAddressPair); + parcHashCodeTable_Destroy(&conntable->storageTableById); + parcMemory_Deallocate((void **)&conntable); + *conntablePtr = NULL; +} + +/** + * @function connectionTable_Add + * @abstract Add a connection, takes ownership of memory + */ +void connectionTable_Add(ConnectionTable *table, Connection *connection) { + parcAssertNotNull(table, "Parameter table must be non-null"); + parcAssertNotNull(connection, "Parameter connection must be non-null"); + + unsigned *connectionIdKey = parcMemory_Allocate(sizeof(unsigned)); + parcAssertNotNull(connectionIdKey, "parcMemory_Allocate(%zu) returned NULL", + sizeof(unsigned)); + *connectionIdKey = connection_GetConnectionId(connection); + + if (parcHashCodeTable_Add(table->storageTableById, connectionIdKey, + connection)) { + parcHashCodeTable_Add(table->indexByAddressPair, + (void *)connection_GetAddressPair(connection), + connection); + parcTreeRedBlack_Insert(table->listById, connectionIdKey, connection); + } else { + parcTrapUnexpectedState( + "Could not add connection id %u -- is it a duplicate?", + *connectionIdKey); + } +} + +/** + * @function connectionTable_Remove + * @abstract Removes the connection, calling Destroy on our copy + */ +void connectionTable_Remove(ConnectionTable *table, + const Connection *connection) { + parcAssertNotNull(table, "Parameter table must be non-null"); + parcAssertNotNull(connection, "Parameter connection must be non-null"); + + unsigned connid = connection_GetConnectionId(connection); + + parcTreeRedBlack_Remove(table->listById, &connid); + parcHashCodeTable_Del(table->indexByAddressPair, + connection_GetAddressPair(connection)); + parcHashCodeTable_Del(table->storageTableById, &connid); +} + +void connectionTable_RemoveById(ConnectionTable *table, unsigned id) { + parcAssertNotNull(table, "Parameter table must be non-null"); + const Connection *connection = connectionTable_FindById(table, id); + if (connection) { + connectionTable_Remove(table, connection); + } +} + +const Connection *connectionTable_FindByAddressPair(ConnectionTable *table, + const AddressPair *pair) { + parcAssertNotNull(table, "Parameter table must be non-null"); + return (Connection *)parcHashCodeTable_Get(table->indexByAddressPair, pair); +} + +const Connection *connectionTable_FindById(ConnectionTable *table, + unsigned id) { + parcAssertNotNull(table, "Parameter table must be non-null"); + return (Connection *)parcHashCodeTable_Get(table->storageTableById, &id); +} + +ConnectionList *connectionTable_GetEntries(const ConnectionTable *table) { + parcAssertNotNull(table, "Parameter table must be non-null"); + ConnectionList *list = connectionList_Create(); + + PARCArrayList *values = parcTreeRedBlack_Values(table->listById); + for (size_t i = 0; i < parcArrayList_Size(values); i++) { + Connection *original = parcArrayList_Get(values, i); + connectionList_Append(list, original); + } + parcArrayList_Destroy(&values); + return list; +} diff --git a/hicn-light/src/core/connectionTable.h b/hicn-light/src/core/connectionTable.h new file mode 100755 index 000000000..30517ae1d --- /dev/null +++ b/hicn-light/src/core/connectionTable.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#ifndef connectionTable_h +#define connectionTable_h + +#include <src/core/connection.h> +#include <src/core/connectionList.h> +#include <src/io/addressPair.h> +#include <src/io/ioOperations.h> + +struct connection_table; +typedef struct connection_table ConnectionTable; + +/** + * Creates an empty connection table + */ +ConnectionTable *connectionTable_Create(void); + +/** + * Destroys the connection table + * This will release the reference to all connections stored in the connection + * table. + * @param [in,out] conntablePtr Pointer to the allocated connection table, will + * be NULL'd + */ +void connectionTable_Destroy(ConnectionTable **conntablePtr); + +/** + * @function connectionTable_Add + * @abstract Add a connection, takes ownership of memory + */ +void connectionTable_Add(ConnectionTable *table, Connection *connection); + +/** + * @function connectionTable_Remove + * @abstract Removes the connection, calling Destroy on our copy + */ +void connectionTable_Remove(ConnectionTable *table, + const Connection *connection); + +/** + * Removes a connection from the connection table + * + * Looks up a connection by its connection ID and removes it from the connection + * table. Removing the connection will call connection_Release() on the + * connection object. + * + * @param [in] table The allocated connection table + * @param [in] id The connection ID + */ +void connectionTable_RemoveById(ConnectionTable *table, unsigned id); + +/** + * Lookup a connection by the (local, remote) addres pair + * + * @param [in] table The allocated connection table + * @param [in] pair The address pair to match, based on the inner values of the + * local and remote addresses + * + * @retval non-null The matched conneciton + * @retval null No match found or error + */ +const Connection *connectionTable_FindByAddressPair(ConnectionTable *table, + const AddressPair *pair); + +/** + * @function connectionTable_FindById + * @abstract Find a connection by its numeric id. + * @return NULL if not found + */ +const Connection *connectionTable_FindById(ConnectionTable *table, unsigned id); + +/** + * @function connectionTable_GetEntries + * @abstract Returns a list of connections. They are reference counted copies + * from the table. + * @discussion + * An allocated list of connections in the table. Each list entry is a + * reference counted copy of the connection in the table, thus they are "live" + * objects. + */ +ConnectionList *connectionTable_GetEntries(const ConnectionTable *table); +#endif // connectionTable_h diff --git a/hicn-light/src/core/dispatcher.c b/hicn-light/src/core/dispatcher.c new file mode 100755 index 000000000..078087c59 --- /dev/null +++ b/hicn-light/src/core/dispatcher.c @@ -0,0 +1,435 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @header dispatcher.c + * @abstract Event dispatcher for hicn-light. Uses parcEvent + * @discussion + * Wraps the functions we use in parcEvent, along with StreamBuffer and + * Message. The dispatcher is the event loop, so it manages things like signals, + * timers, and network events. + */ + +#include <errno.h> +#include <fcntl.h> +#include <pthread.h> +#include <signal.h> +#include <src/config.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <arpa/inet.h> +#include <sys/socket.h> + +#include <parc/algol/parc_EventQueue.h> +#include <parc/algol/parc_EventTimer.h> + +#include <parc/assert/parc_Assert.h> + +#include <src/core/dispatcher.h> + +#ifndef INPORT_ANY +#define INPORT_ANY 0 +#endif + +struct dispatcher { + PARCEventScheduler *Base; + Logger *logger; +}; + +// ========================================== +// Public API + +PARCEventScheduler *dispatcher_GetEventScheduler(Dispatcher *dispatcher) { + return dispatcher->Base; +} + +Dispatcher *dispatcher_Create(Logger *logger) { + Dispatcher *dispatcher = parcMemory_AllocateAndClear(sizeof(Dispatcher)); + parcAssertNotNull(dispatcher, + "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Dispatcher)); + + dispatcher->Base = parcEventScheduler_Create(); + dispatcher->logger = logger_Acquire(logger); + + parcAssertNotNull(dispatcher->Base, + "Got NULL from parcEventScheduler_Create()"); + + return dispatcher; +} + +void dispatcher_Destroy(Dispatcher **dispatcherPtr) { + parcAssertNotNull(dispatcherPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*dispatcherPtr, + "Parameter must dereference to non-null pointer"); + Dispatcher *dispatcher = *dispatcherPtr; + + logger_Release(&dispatcher->logger); + parcEventScheduler_Destroy(&(dispatcher->Base)); + parcMemory_Deallocate((void **)&dispatcher); + *dispatcherPtr = NULL; +} + +void dispatcher_Stop(Dispatcher *dispatcher) { + struct timeval delay = {0, 1000}; + + parcEventScheduler_Stop(dispatcher->Base, &delay); +} + +void dispatcher_Run(Dispatcher *dispatcher) { + parcAssertNotNull(dispatcher, "Parameter must be non-null"); + + parcEventScheduler_Start(dispatcher->Base, 0); +} + +void dispatcher_RunDuration(Dispatcher *dispatcher, struct timeval *duration) { + parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); + parcAssertNotNull(duration, "Parameter duration must be non-null"); + + parcEventScheduler_Stop(dispatcher->Base, duration); + parcEventScheduler_Start(dispatcher->Base, 0); +} + +void dispatcher_RunCount(Dispatcher *dispatcher, unsigned count) { + parcAssertNotNull(dispatcher, "Parameter must be non-null"); + + for (unsigned i = 0; i < count; i++) { + parcEventScheduler_Start(dispatcher->Base, + PARCEventSchedulerDispatchType_LoopOnce); + } +} + +PARCEventSocket *dispatcher_CreateListener(Dispatcher *dispatcher, + PARCEventSocket_Callback *callback, + void *user_data, int backlog, + const struct sockaddr *sa, + int socklen) { + PARCEventSocket *listener = parcEventSocket_Create( + dispatcher->Base, callback, NULL, user_data, sa, socklen); + if (listener == NULL) { + perror("Problem creating listener"); + } + return listener; +} + +void dispatcher_DestroyListener(Dispatcher *dispatcher, + PARCEventSocket **listenerPtr) { + parcAssertNotNull(listenerPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*listenerPtr, + "Parameter must dereference to non-null pointer"); + parcEventSocket_Destroy(listenerPtr); +} + +PARCEventQueue *dispatcher_CreateStreamBufferFromSocket(Dispatcher *dispatcher, + SocketType fd) { + parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); + PARCEventQueue *buffer = parcEventQueue_Create( + dispatcher->Base, fd, + PARCEventQueueOption_CloseOnFree | PARCEventQueueOption_DeferCallbacks); + parcAssertNotNull(buffer, + "Got null from parcEventBufver_Create for socket %d", fd); + return buffer; +} + +PARCEventTimer *dispatcher_CreateTimer(Dispatcher *dispatcher, bool isPeriodic, + PARCEvent_Callback *callback, + void *userData) { + parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); + parcAssertNotNull(callback, "Parameter callback must be non-null"); + + PARCEventType flags = 0; + if (isPeriodic) { + flags |= PARCEventType_Persist; + } + PARCEventTimer *event = + parcEventTimer_Create(dispatcher->Base, flags, callback, userData); + return event; +} + +void dispatcher_StartTimer(Dispatcher *dispatcher, PARCEventTimer *timerEvent, + struct timeval *delay) { + parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); + parcAssertNotNull(timerEvent, "Parameter timerEvent must be non-null"); + int failure = parcEventTimer_Start(timerEvent, delay); + parcAssertFalse(failure < 0, "Error starting timer event %p: (%d) %s", + (void *)timerEvent, errno, strerror(errno)); +} + +void dispatcher_StopTimer(Dispatcher *dispatcher, PARCEventTimer *event) { + parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); + parcAssertNotNull(event, "Parameter event must be non-null"); + + int failure = parcEventTimer_Stop(event); + parcAssertFalse(failure < 0, "Error stopping signal event %p: (%d) %s", + (void *)event, errno, strerror(errno)); +} + +void dispatcher_DestroyTimerEvent(Dispatcher *dispatcher, + PARCEventTimer **eventPtr) { + parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); + parcAssertNotNull(eventPtr, + "Parameter eventPtr must be non-null double pointer"); + parcAssertNotNull(*eventPtr, + "Paramter eventPtr must dereference to non-null pointer"); + + parcEventTimer_Destroy(eventPtr); + eventPtr = NULL; +} + +PARCEvent *dispatcher_CreateNetworkEvent(Dispatcher *dispatcher, + bool isPersistent, + PARCEvent_Callback *callback, + void *userData, int fd) { + short flags = PARCEventType_Timeout | PARCEventType_Read; + if (isPersistent) { + flags |= PARCEventType_Persist; + } + + PARCEvent *event = + parcEvent_Create(dispatcher->Base, fd, flags, callback, userData); + parcAssertNotNull(event, "Got null from parcEvent_Create for socket %d", fd); + return event; +} + +void dispatcher_DestroyNetworkEvent(Dispatcher *dispatcher, + PARCEvent **eventPtr) { + parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); + parcAssertNotNull(eventPtr, + "Parameter eventPtr must be non-null double pointer"); + parcAssertNotNull(*eventPtr, + "Paramter eventPtr must dereference to non-null pointer"); + + parcEvent_Destroy(eventPtr); + eventPtr = NULL; +} + +void dispatcher_StartNetworkEvent(Dispatcher *dispatcher, PARCEvent *event) { + parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); + parcAssertNotNull(event, "Parameter event must be non-null"); + + int failure = parcEvent_Start(event); + parcAssertFalse(failure < 0, "Error starting signal event %p: (%d) %s", + (void *)event, errno, strerror(errno)); +} + +void dispatcher_StopNetworkEvent(Dispatcher *dispatcher, PARCEvent *event) { + parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); + parcAssertNotNull(event, "Parameter event must be non-null"); + + int failure = parcEvent_Stop(event); + parcAssertFalse(failure < 0, "Error stopping signal event %p: (%d) %s", + (void *)event, errno, strerror(errno)); +} + +PARCEventSignal *dispatcher_CreateSignalEvent( + Dispatcher *dispatcher, PARCEventSignal_Callback *callback, void *userData, + int signal) { + parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); + parcAssertNotNull(callback, "Parameter callback must be non-null"); + + PARCEventSignal *event = parcEventSignal_Create( + dispatcher->Base, signal, PARCEventType_Signal | PARCEventType_Persist, + callback, userData); + parcAssertNotNull(event, + "Got null event when creating signal catcher for signal %d", + signal); + + return event; +} + +void dispatcher_DestroySignalEvent(Dispatcher *dispatcher, + PARCEventSignal **eventPtr) { + parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); + parcAssertNotNull(eventPtr, + "Parameter eventPtr must be non-null double pointer"); + parcAssertNotNull(*eventPtr, + "Paramter eventPtr must dereference to non-null pointer"); + + parcEventSignal_Destroy(eventPtr); + eventPtr = NULL; +} + +void dispatcher_StartSignalEvent(Dispatcher *dispatcher, + PARCEventSignal *event) { + parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); + parcAssertNotNull(event, "Parameter event must be non-null"); + + int failure = parcEventSignal_Start(event); + parcAssertFalse(failure < 0, "Error starting signal event %p: (%d) %s", + (void *)event, errno, strerror(errno)); +} + +void dispatcher_StopSignalEvent(Dispatcher *dispatcher, + PARCEventSignal *event) { + parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); + parcAssertNotNull(event, "Parameter event must be non-null"); + + int failure = parcEventSignal_Stop(event); + parcAssertFalse(failure < 0, "Error stopping signal event %p: (%d) %s", + (void *)event, errno, strerror(errno)); +} + +/** + * Bind to a local address/port then connect to peer. + */ +static bool dispatcher_StreamBufferBindAndConnect(Dispatcher *dispatcher, + PARCEventQueue *buffer, + struct sockaddr *localSock, + socklen_t localSockLength, + struct sockaddr *remoteSock, + socklen_t remoteSockLength) { + // we need to bind, then connect. Special operation, so we make our + // own fd then pass it off to the buffer event + + int fd = socket(localSock->sa_family, SOCK_STREAM, 0); + if (fd < 0) { + perror("socket"); + return -1; + } + + // Set non-blocking flag + int flags = fcntl(fd, F_GETFL, NULL); + if (flags < 0) { + perror("F_GETFL"); + close(fd); + return -1; + } + int failure = fcntl(fd, F_SETFL, flags | O_NONBLOCK); + if (failure) { + perror("F_SETFL"); + close(fd); + return -1; + } + + failure = bind(fd, localSock, localSockLength); + if (failure) { + perror("bind"); + close(fd); + return false; + } + + parcEventQueue_SetFileDescriptor(buffer, fd); + + failure = parcEventQueue_ConnectSocket(buffer, remoteSock, remoteSockLength); + if (failure && (errno != EINPROGRESS)) { + perror("connect"); + close(fd); + return false; + } + return true; +} + +/** + * Connect to an INET peer + * @return NULL on error, otherwise a streambuffer + */ +static PARCEventQueue *dispatcher_StreamBufferConnect_INET( + Dispatcher *dispatcher, const Address *localAddress, + const Address *remoteAddress) { + struct sockaddr_in localSock, remoteSock; + addressGetInet(localAddress, &localSock); + addressGetInet(remoteAddress, &remoteSock); + + PARCEventQueue *buffer = parcEventQueue_Create( + dispatcher->Base, -1, PARCEventQueueOption_CloseOnFree); + parcAssertNotNull(buffer, "got null buffer from parcEventQueue_Create()"); + + bool success = dispatcher_StreamBufferBindAndConnect( + dispatcher, buffer, (struct sockaddr *)&localSock, sizeof(localSock), + (struct sockaddr *)&remoteSock, sizeof(remoteSock)); + if (!success) { + parcEventQueue_Destroy(&buffer); + buffer = NULL; + } + + return buffer; +} + +/** + * Connect to an INET peer + * @return NULL on error, otherwise a streambuffer + */ +static PARCEventQueue * +// static StreamBuffer * +dispatcher_StreamBufferConnect_INET6(Dispatcher *dispatcher, + const Address *localAddress, + const Address *remoteAddress) { + struct sockaddr_in6 localSock, remoteSock; + addressGetInet6(localAddress, &localSock); + addressGetInet6(remoteAddress, &remoteSock); + + PARCEventQueue *buffer = parcEventQueue_Create( + dispatcher->Base, -1, PARCEventQueueOption_CloseOnFree); + parcAssertNotNull(buffer, "got null buffer from parcEventQueue_Create()"); + + bool success = dispatcher_StreamBufferBindAndConnect( + dispatcher, buffer, (struct sockaddr *)&localSock, sizeof(localSock), + (struct sockaddr *)&remoteSock, sizeof(remoteSock)); + if (!success) { + parcEventQueue_Destroy(&buffer); + buffer = NULL; + } + + return buffer; +} + +PARCEventQueue *dispatcher_StreamBufferConnect(Dispatcher *dispatcher, + const AddressPair *pair) { + const Address *localAddress = addressPair_GetLocal(pair); + const Address *remoteAddress = addressPair_GetRemote(pair); + + // they must be of the same address family + if (addressGetType(localAddress) != addressGetType(remoteAddress)) { + char message[2048]; + char *localAddressString = addressToString(localAddress); + char *remoteAddressString = addressToString(remoteAddress); + snprintf(message, 2048, + "Remote address not same type as local address, expected %d got " + "%d\nlocal %s remote %s", + addressGetType(localAddress), addressGetType(remoteAddress), + localAddressString, remoteAddressString); + + parcMemory_Deallocate((void **)&localAddressString); + parcMemory_Deallocate((void **)&remoteAddressString); + + parcAssertTrue( + addressGetType(localAddress) == addressGetType(remoteAddress), "%s", + message); + } + + address_type type = addressGetType(localAddress); + + PARCEventQueue *result = NULL; + + switch (type) { + case ADDR_INET: + return dispatcher_StreamBufferConnect_INET(dispatcher, localAddress, + remoteAddress); + break; + case ADDR_INET6: + return dispatcher_StreamBufferConnect_INET6(dispatcher, localAddress, + remoteAddress); + break; + default: + parcTrapIllegalValue(type, "local address unsupported address type: %d", + type); + } + return result; +} diff --git a/hicn-light/src/core/dispatcher.h b/hicn-light/src/core/dispatcher.h new file mode 100755 index 000000000..35d804a00 --- /dev/null +++ b/hicn-light/src/core/dispatcher.h @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @header hicn-light Dispatcher + * @abstract The dispatcher is the event loop run by Forwarder. + * @discussion + * These functions manage listeners, timers, and network events inside + * the event loop. + * + * Curently, it is a thin wrapper around an event so we don't have to + * expose that implementation detail to other modules. + * + */ + +#ifndef dispatcher_h +#define dispatcher_h + +#include <stdbool.h> +#include <sys/socket.h> + +struct dispatcher; +typedef struct dispatcher Dispatcher; + +#include <parc/algol/parc_Event.h> +#include <parc/algol/parc_EventQueue.h> +#include <parc/algol/parc_EventScheduler.h> +#include <parc/algol/parc_EventSignal.h> +#include <parc/algol/parc_EventSocket.h> +#include <parc/algol/parc_EventTimer.h> +#include <parc/algol/parc_Memory.h> + +#include <src/core/logger.h> + +PARCEventScheduler *dispatcher_GetEventScheduler(Dispatcher *dispatcher); +/** + * Creates an event dispatcher + * + * Event dispatcher based on PARCEvent + * + * @return non-null Allocated event dispatcher + * @return null An error + */ +Dispatcher *dispatcher_Create(Logger *logger); + +/** + * Destroys event dispatcher + * + * Caller is responsible for destroying call events before destroying + * the event dispatcher. + */ +void dispatcher_Destroy(Dispatcher **dispatcherPtr); + +/** + * @function dispatcher_Stop + * @abstract Called from a different thread, tells the dispatcher to stop + * @discussion + * Called from a user thread or from an interrupt handler. + * Does not block. Use <code>dispatcher_WaitForStopped()</code> to + * block until stopped after calling this. + */ +void dispatcher_Stop(Dispatcher *dispatcher); + +/** + * @function dispatcher_WaitForStopped + * @abstract Blocks until dispatcher in stopped state + * @discussion + * Used after <code>dispatcher_Stop()</code> to wait for stop. + */ +void dispatcher_WaitForStopped(Dispatcher *dispatcher); + +/** + * @function dispatcher_Run + * @abstract Runs the forwarder, blocks. + */ +void dispatcher_Run(Dispatcher *dispatcher); + +/** + * @function dispatcher_RunDuration + * @abstract Runs forwarder for at most duration, blocks. + * @discussion + * Blocks running the forwarder for a duration. May be called + * iteratively to keep running. Duration is a minimum, actual + * runtime may be slightly longer. + */ +void dispatcher_RunDuration(Dispatcher *dispatcher, struct timeval *duration); + +/** + * @header dispatcher_RunCount + * @abstract Run the event loop for the given count cycles + * @discussion + * Runs the event loop for the given number of cycles, blocking + * until done. May be called sequentially over and over. + * + */ +void dispatcher_RunCount(Dispatcher *dispatcher, unsigned count); + +typedef int SocketType; + +typedef struct evconnlistener Listener; + +/** + * @typedef ListenerCallback + * @abstract Callback function typedef for a stream listener + * + * @constant listener is the object created by <code>forwarder_NewBind()</code> + * that received the client connection + * @constant client_socket is the client socket + * @constant user_data is the user_data passed to + * <code>forwarder_NewBind()</code> + * @constant client_addr is the client address + * @constant socklen is the length of client_addr + * @discussion <#Discussion#> + */ +typedef void(ListenerCallback)(Listener *listener, SocketType client_socket, + struct sockaddr *client_addr, int socklen, + void *user_data); + +/** + * @header forwarder_NewBind + * @abstract Allocate a new stream listener + * @discussion + * The server socket will be freed when closed and will be reusable. + * + * @param forwarder that owns the event loop + * @param cb is the callback for a new connection + * @param user_data is opaque user data passed to the callback + * @param backlog is the listen() depth, may use -1 for a default value + * @param sa is the socket address to bind to (INET, INET6, LOCAL) + * @param socklen is the sizeof the actual sockaddr (e.g. sizeof(sockaddr_un)) + */ +PARCEventSocket *dispatcher_CreateListener(Dispatcher *dispatcher, + PARCEventSocket_Callback *callback, + void *user_data, int backlog, + const struct sockaddr *sa, + int socklen); + +void dispatcher_DestroyListener(Dispatcher *dispatcher, + PARCEventSocket **listenerPtr); + +typedef struct event TimerEvent; +typedef struct event NetworkEvent; +typedef struct event SignalEvent; + +/** + * @typedef EventCallback + * @abstract A network event or a timer callback + * @constant fd The file descriptor associated with the event, may be -1 for + * timers + * @constant which_event is a bitmap of the EventType + * @constant user_data is the user_data passed to + * <code>Forwarder_CreateEvent()</code> + */ +typedef void(EventCallback)(SocketType fd, short which_event, void *user_data); + +/** + * @function dispatcher_CreateTimer + * @abstract Creates a Event for use as a timer. + * @discussion + * + * When created, the timer is idle and you need to call + * <code>forwarder_StartTimer()</code> + * + * @param isPeriodic means the timer will fire repeatidly, otherwise it is a + * one-shot and needs to be set again with <code>dispatcher_StartTimer()</code> + */ +PARCEventTimer *dispatcher_CreateTimer(Dispatcher *dispatcher, bool isPeriodic, + PARCEvent_Callback *callback, + void *userData); + +/** + * @function dispatcher_StartTimer + * @abstract Starts the timer with the given delay. + * @discussion + * If the timer is periodic, it will keep firing with the given delay + */ +void dispatcher_StartTimer(Dispatcher *dispatcher, PARCEventTimer *timerEvent, + struct timeval *delay); + +void dispatcher_StopTimer(Dispatcher *dispatcher, PARCEventTimer *timerEvent); + +/** + * @function dispatcher_DestroyTimerEvent + * @abstract Cancels the timer and frees the event + */ +void dispatcher_DestroyTimerEvent(Dispatcher *dispatcher, + PARCEventTimer **eventPtr); + +/** + * @function dispatcher_CreateNetworkEvent + * @abstract Creates a network event callback on the socket + * @discussion + * May be used on any sort of file descriptor or socket. The event is edge + * triggered and non-reentrent. This means you need to drain the events off the + * socket, as the callback will not be called again until a new event arrives. + * + * When created, the event is idle and you need to call + * <code>forwarder_StartNetworkEvent()</code> + * + * @param isPersistent means the callback will keep firing with new events, + * otherwise its a one-shot + * @param fd is the socket to monitor + */ +PARCEvent *dispatcher_CreateNetworkEvent(Dispatcher *dispatcher, + bool isPersistent, + PARCEvent_Callback *callback, + void *userData, int fd); + +void dispatcher_StartNetworkEvent(Dispatcher *dispatcher, PARCEvent *event); +void dispatcher_StopNetworkEvent(Dispatcher *dispatcher, PARCEvent *event); + +void dispatcher_DestroyNetworkEvent(Dispatcher *dispatcher, + PARCEvent **eventPtr); + +/** + * @function dispatcher_CreateSignalEvent + * @abstract Creates a signal trap + * @discussion + * May be used on catchable signals. The event is edge triggered and + * non-reentrent. Signal events are persistent. + * + * When created, the signal trap is idle and you need to call + * <code>forwarder_StartSignalEvent()</code> + * + * @param signal is the system signal to monitor (e.g. SIGINT). + * @return <#return#> + */ +PARCEventSignal *dispatcher_CreateSignalEvent( + Dispatcher *dispatcher, PARCEventSignal_Callback *callback, void *userData, + int signal); + +void dispatcher_DestroySignalEvent(Dispatcher *dispatcher, + PARCEventSignal **eventPtr); + +void dispatcher_StartSignalEvent(Dispatcher *dispatcher, + PARCEventSignal *event); +void dispatcher_StopSignalEvent(Dispatcher *dispatcher, PARCEventSignal *event); + +// ============= +// stream buffers + +#include <src/core/streamBuffer.h> +#include <src/io/addressPair.h> + +/** + * @function dispatcher_CreateStreamBuffer + * @abstract Creates a high-function buffer around a stream socket + */ +PARCEventQueue *dispatcher_CreateStreamBufferFromSocket(Dispatcher *dispatcher, + SocketType fd); + +/** + * @function dispatcher_StreamBufferConnect + * @abstract Create a TCP tunnel to a remote peer + * @discussion + * For TCP, both address pairs need to be the same address family: both INET + * or both INET6. The remote address must have the complete socket information + * (address, port). The local socket could be wildcarded or may specify down to + * the (address, port) pair. + * + * If the local address is IPADDR_ANY and the port is 0, then it is a normal + * call to "connect" that will use whatever local IP address and whatever local + * port for the connection. If either the address or port is set, the local + * socket will first be bound (via bind(2)), and then call connect(). + * + * It is unlikely that the buffer will be connected by the time the function + * returns. The eventCallback will fire once the remote system accepts the + * conneciton. + * + * @return NULL on error, otherwise a streambuffer. + */ +PARCEventQueue *dispatcher_StreamBufferConnect(Dispatcher *dispatcher, + const AddressPair *pair); +#endif // dispatcher_h diff --git a/hicn-light/src/core/forwarder.c b/hicn-light/src/core/forwarder.c new file mode 100755 index 000000000..cb94af3b5 --- /dev/null +++ b/hicn-light/src/core/forwarder.c @@ -0,0 +1,499 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Event based router + * + * This module is the glue around the event scheduler. + * Its the packet i/o module. + * + * Packet processing is done in dispatcher.c, which is the actual wrapper around + * the event scheduler + */ + +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <src/config.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <arpa/inet.h> +#include <sys/socket.h> + +#define __STDC_FORMAT_MACROS +#include <inttypes.h> + +#include <parc/algol/parc_ArrayList.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> +#include <parc/logging/parc_LogReporterTextStdout.h> + +#include <src/core/connectionManager.h> +#include <src/core/connectionTable.h> +#include <src/core/dispatcher.h> +#include <src/core/forwarder.h> +#include <src/core/messagePacketType.h> +#ifdef WITH_MAPME +#include <src/core/mapMe.h> +#endif /* WITH_MAPME */ +#include <src/config/configuration.h> +#include <src/config/configurationFile.h> +#include <src/config/configurationListeners.h> +#include <src/processor/messageProcessor.h> + +#include <src/core/wldr.h> + +#include <parc/assert/parc_Assert.h> + +// the router's clock frequency (we now use the monotonic clock) +#define HZ 1000 + +// these will all be a little off because its all integer division +#define MSEC_PER_TICK (1000 / HZ) +#define USEC_PER_TICK (1000000 / HZ) +#define NSEC_PER_TICK ((1000000000ULL) / HZ) +#define MSEC_TO_TICKS(msec) \ + ((msec < FC_MSEC_PER_TICK) ? 1 : msec / FC_MSEC_PER_TICK) +#define NSEC_TO_TICKS(nsec) ((nsec < NSEC_PER_TICK) ? 1 : nsec / NSEC_PER_TICK) + +struct forwarder { + Dispatcher *dispatcher; + + uint16_t server_port; + + PARCEventSignal *signal_int; + PARCEventSignal *signal_term; + PARCEventSignal *signal_usr1; + PARCEventTimer *keepalive_event; + + // will skew the virtual clock forward. In normal operaiton, it is 0. + Ticks clockOffset; + + unsigned nextConnectionid; + Messenger *messenger; + ConnectionManager *connectionManager; + ConnectionTable *connectionTable; + ListenerSet *listenerSet; + Configuration *config; + + // we'll eventually want to setup a threadpool of these + MessageProcessor *processor; + + Logger *logger; + + PARCClock *clock; + + hicn_socket_helper_t + *hicnSocketHelper; // state required to manage hicn connections + + // used by seed48 and nrand48 + unsigned short seed[3]; + +#ifdef WITH_MAPME + MapMe *mapme; +#endif /* WITH_MAPME */ +}; + +// signal traps through the event scheduler +static void _signal_cb(int, PARCEventType, void *); + +// A no-op keepalive to prevent Libevent from exiting the dispatch loop +static void _keepalive_cb(int, PARCEventType, void *); + +/** + * Reseed our pseudo-random number generator. + */ +static void forwarder_Seed(Forwarder *forwarder) { + int fd; + ssize_t res; + + res = -1; + fd = open("/dev/urandom", O_RDONLY); + if (fd != -1) { + res = read(fd, forwarder->seed, sizeof(forwarder->seed)); + close(fd); + } + if (res != sizeof(forwarder->seed)) { + forwarder->seed[1] = (unsigned short)getpid(); /* better than no entropy */ + forwarder->seed[2] = (unsigned short)time(NULL); + } + /* + * The call to seed48 is needed by cygwin, and should be harmless + * on other platforms. + */ + seed48(forwarder->seed); +} + +Logger *forwarder_GetLogger(const Forwarder *forwarder) { + return forwarder->logger; +} + +// ============================================================================ +// Setup and destroy section + +Forwarder *forwarder_Create(Logger *logger) { + Forwarder *forwarder = parcMemory_AllocateAndClear(sizeof(Forwarder)); + parcAssertNotNull(forwarder, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Forwarder)); + memset(forwarder, 0, sizeof(Forwarder)); + forwarder_Seed(forwarder); + + forwarder->clock = parcClock_Monotonic(); + forwarder->clockOffset = 0; + + if (logger) { + forwarder->logger = logger_Acquire(logger); + logger_SetClock(forwarder->logger, forwarder->clock); + } else { + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + forwarder->logger = logger_Create(reporter, forwarder->clock); + parcLogReporter_Release(&reporter); + } + + forwarder->nextConnectionid = 1; + forwarder->dispatcher = dispatcher_Create(forwarder->logger); + forwarder->messenger = messenger_Create(forwarder->dispatcher); + forwarder->connectionManager = connectionManager_Create(forwarder); + forwarder->connectionTable = connectionTable_Create(); + forwarder->listenerSet = listenerSet_Create(); + forwarder->config = configuration_Create(forwarder); + forwarder->processor = messageProcessor_Create(forwarder); + + forwarder->signal_term = dispatcher_CreateSignalEvent( + forwarder->dispatcher, _signal_cb, forwarder, SIGTERM); + dispatcher_StartSignalEvent(forwarder->dispatcher, forwarder->signal_term); + + forwarder->signal_int = dispatcher_CreateSignalEvent( + forwarder->dispatcher, _signal_cb, forwarder, SIGINT); + dispatcher_StartSignalEvent(forwarder->dispatcher, forwarder->signal_int); + + forwarder->signal_usr1 = dispatcher_CreateSignalEvent( + forwarder->dispatcher, _signal_cb, forwarder, SIGPIPE); + dispatcher_StartSignalEvent(forwarder->dispatcher, forwarder->signal_usr1); + +#ifndef __APPLE__ + forwarder->hicnSocketHelper = hicn_create(); + if (forwarder->hicnSocketHelper == NULL) return NULL; +#endif /* __APPLE__ */ + /* ignore child */ + signal(SIGCHLD, SIG_IGN); + + /* ignore tty signals */ + signal(SIGTSTP, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + signal(SIGTTIN, SIG_IGN); + + // We no longer use this for ticks, but we need to have at least one event + // schedule to keep Libevent happy. + + struct timeval wtnow_timeout; + timerclear(&wtnow_timeout); + + wtnow_timeout.tv_sec = 0; + wtnow_timeout.tv_usec = 50000; // 20 Hz keepalive + +#ifdef WITH_MAPME + if (!(mapMe_Init(&forwarder->mapme, forwarder))) return NULL; +#endif /* WITH_MAPME */ + + PARCEventScheduler *base = + dispatcher_GetEventScheduler(forwarder->dispatcher); + forwarder->keepalive_event = parcEventTimer_Create( + base, PARCEventType_Persist, _keepalive_cb, (void *)forwarder); + parcEventTimer_Start(forwarder->keepalive_event, &wtnow_timeout); + + return forwarder; +} + +void forwarder_Destroy(Forwarder **ptr) { + parcAssertNotNull(ptr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*ptr, "Parameter must dereference to non-null pointer"); + Forwarder *forwarder = *ptr; +#if !defined(__APPLE__) && !defined(__ANDROID__) + hicn_destroy(); +#endif + parcEventTimer_Destroy(&(forwarder->keepalive_event)); + + listenerSet_Destroy(&(forwarder->listenerSet)); + connectionManager_Destroy(&(forwarder->connectionManager)); + connectionTable_Destroy(&(forwarder->connectionTable)); + messageProcessor_Destroy(&(forwarder->processor)); + configuration_Destroy(&(forwarder->config)); + + // the messenger is used by many of the other pieces, so destroy it last + messenger_Destroy(&(forwarder->messenger)); + + dispatcher_DestroySignalEvent(forwarder->dispatcher, + &(forwarder->signal_int)); + dispatcher_DestroySignalEvent(forwarder->dispatcher, + &(forwarder->signal_term)); + dispatcher_DestroySignalEvent(forwarder->dispatcher, + &(forwarder->signal_usr1)); + + parcClock_Release(&forwarder->clock); + logger_Release(&forwarder->logger); + + // do the dispatcher last + dispatcher_Destroy(&(forwarder->dispatcher)); + + parcMemory_Deallocate((void **)&forwarder); + *ptr = NULL; +} + +void forwarder_SetupAllListeners(Forwarder *forwarder, uint16_t port, + const char *localPath) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + + configurationListeners_SetupAll(forwarder->config, port, localPath); +} + +void forwarder_SetupFromConfigFile(Forwarder *forwarder, const char *filename) { + ConfigurationFile *configFile = configurationFile_Create(forwarder, filename); + if (configFile) { + configurationFile_Process(configFile); + configurationFile_Release(&configFile); + } +} + +Configuration *forwarder_GetConfiguration(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return forwarder->config; +} + +// ============================================================================ + +unsigned forwarder_GetNextConnectionId(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return forwarder->nextConnectionid++; +} + +Messenger *forwarder_GetMessenger(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return forwarder->messenger; +} + +Dispatcher *forwarder_GetDispatcher(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return forwarder->dispatcher; +} + +ConnectionTable *forwarder_GetConnectionTable(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return forwarder->connectionTable; +} + +ListenerSet *forwarder_GetListenerSet(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return forwarder->listenerSet; +} + +void forwarder_SetChacheStoreFlag(Forwarder *forwarder, bool val) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + messageProcessor_SetCacheStoreFlag(forwarder->processor, val); +} + +bool forwarder_GetChacheStoreFlag(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return messageProcessor_GetCacheStoreFlag(forwarder->processor); +} + +void forwarder_SetChacheServeFlag(Forwarder *forwarder, bool val) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + messageProcessor_SetCacheServeFlag(forwarder->processor, val); +} + +bool forwarder_GetChacheServeFlag(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return messageProcessor_GetCacheServeFlag(forwarder->processor); +} + +void forwarder_ReceiveCommand(Forwarder *forwarder, command_id command, + struct iovec *message, unsigned ingressId) { + configuration_ReceiveCommand(forwarder->config, command, message, ingressId); +} + +void forwarder_Receive(Forwarder *forwarder, Message *message) { + parcAssertNotNull(forwarder, "Parameter hicn-light must be non-null"); + parcAssertNotNull(message, "Parameter message must be non-null"); + + // this takes ownership of the message, so we're done here + + // this are the checks needed to implement WLDR. We set wldr only on the STAs + // and we let the AP to react according to choise of the client. + // if the STA enables wldr using the set command, the AP enable wldr as well + // otherwise, if the STA disable it the AP remove wldr + // WLDR should be enabled only on the STAs using the command line + // TODO + // disable WLDR command line on the AP + const Connection *conn = connectionTable_FindById( + forwarder->connectionTable, message_GetIngressConnectionId(message)); + + if (!conn) { + return; + } + + if (message_HasWldr(message)) { + if (connection_HasWldr(conn)) { + // case 1: WLDR is enabled + connection_DetectLosses((Connection *)conn, message); + } else if (!connection_HasWldr(conn) && + connection_WldrAutoStartAllowed(conn)) { + // case 2: We are on an AP. We enable WLDR + connection_EnableWldr((Connection *)conn); + connection_DetectLosses((Connection *)conn, message); + } + // case 3: Ignore WLDR + } else { + if (connection_HasWldr(conn) && connection_WldrAutoStartAllowed(conn)) { + // case 1: STA do not use WLDR, we disable it + connection_DisableWldr((Connection *)conn); + } + } + + messageProcessor_Receive(forwarder->processor, message); +} + +Ticks forwarder_GetTicks(const Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return parcClock_GetTime(forwarder->clock) + forwarder->clockOffset; +} + +Ticks forwarder_NanosToTicks(uint64_t nanos) { return NSEC_TO_TICKS(nanos); } + +uint64_t forwarder_TicksToNanos(Ticks ticks) { + return (1000000000ULL) * ticks / HZ; +} + +bool forwarder_AddOrUpdateRoute(Forwarder *forwarder, + add_route_command *control, unsigned ifidx) { + parcAssertNotNull(forwarder, "Parameter hicn-light must be non-null"); + parcAssertNotNull(control, "Parameter route must be non-null"); + + // we only have one message processor + bool res = + messageProcessor_AddOrUpdateRoute(forwarder->processor, control, ifidx); + + return res; +} + +bool forwarder_RemoveRoute(Forwarder *forwarder, remove_route_command *control, + unsigned ifidx) { + parcAssertNotNull(forwarder, "Parameter hicn-light must be non-null"); + parcAssertNotNull(control, "Parameter route must be non-null"); + + // we only have one message processor + return messageProcessor_RemoveRoute(forwarder->processor, control, ifidx); +} + +void forwarder_RemoveConnectionIdFromRoutes(Forwarder *forwarder, + unsigned connectionId) { + parcAssertNotNull(forwarder, "Parameter hicn-light must be non-null"); + messageProcessor_RemoveConnectionIdFromRoutes(forwarder->processor, + connectionId); +} + +void forwarder_SetStrategy(Forwarder *forwarder, Name *prefix, + strategy_type strategy) { + parcAssertNotNull(forwarder, "Parameter hicn-light must be non-null"); + parcAssertNotNull(prefix, "Parameter prefix must be non-null"); + + // if (strategy == NULL) { + // strategy = SET_STRATEGY_RANDOM; + // } + + processor_SetStrategy(forwarder->processor, prefix, strategy); +} + +FibEntryList *forwarder_GetFibEntries(Forwarder *forwarder) { + return messageProcessor_GetFibEntries(forwarder->processor); +} + +void forwarder_SetContentObjectStoreSize(Forwarder *forwarder, + size_t maximumContentStoreSize) { + messageProcessor_SetContentObjectStoreSize(forwarder->processor, + maximumContentStoreSize); +} + +void forwarder_ClearCache(Forwarder *forwarder) { + messageProcessor_ClearCache(forwarder->processor); +} + +PARCClock *forwarder_GetClock(const Forwarder *forwarder) { + return forwarder->clock; +} + +hicn_socket_helper_t *forwarder_GetHIcnSocketHelper(Forwarder *forwarder) { + return forwarder->hicnSocketHelper; +} + +// ======================================================= + +static void _signal_cb(int sig, PARCEventType events, void *user_data) { + Forwarder *forwarder = (Forwarder *)user_data; + + logger_Log(forwarder->logger, LoggerFacility_Core, PARCLogLevel_Warning, + __func__, "signal %d events %d", sig, events); + + switch ((int)sig) { + case SIGTERM: + logger_Log(forwarder->logger, LoggerFacility_Core, PARCLogLevel_Warning, + __func__, "Caught an terminate signal; exiting cleanly."); + dispatcher_Stop(forwarder->dispatcher); + break; + + case SIGINT: + logger_Log(forwarder->logger, LoggerFacility_Core, PARCLogLevel_Warning, + __func__, "Caught an interrupt signal; exiting cleanly."); + dispatcher_Stop(forwarder->dispatcher); + break; + + case SIGUSR1: + // dump stats + break; + + default: + break; + } +} + +static void _keepalive_cb(int fd, PARCEventType what, void *user_data) { + parcAssertTrue(what & PARCEventType_Timeout, "Got unexpected tick_cb: %d", + what); + // function is just a keepalive for hicn-light, does not do anything +} + +#ifdef WITH_MAPME +FIB *forwarder_getFib(Forwarder *forwarder) { + return messageProcessor_getFib(forwarder->processor); +} + +void forwarder_onConnectionAdded(Forwarder *forwarder, const Connection *conn) { + mapMe_onConnectionAdded(forwarder->mapme, conn); +} + +void forwarder_onConnectionRemoved(Forwarder *forwarder, + const Connection *conn) {} + +void forwarder_ProcessMapMe(Forwarder *forwarder, uint8_t *msgBuffer, + unsigned conn_id) { + mapMe_Process(forwarder->mapme, msgBuffer, conn_id); +} + +#endif /* WITH_MAPME */ diff --git a/hicn-light/src/core/forwarder.h b/hicn-light/src/core/forwarder.h new file mode 100755 index 000000000..ad3f9756b --- /dev/null +++ b/hicn-light/src/core/forwarder.h @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * The methods in this header are for the non-threaded forwarder. They should + * only be called within the forwarders thread of execution. + */ + +#ifndef forwarder_h +#define forwarder_h + +#include <stdlib.h> +#include <sys/time.h> + +#include <src/core/connectionTable.h> +#include <src/core/dispatcher.h> +#include <src/messenger/messenger.h> + +#include <src/core/message.h> + +#include <src/config/configuration.h> + +#ifdef WITH_MAPME +#include <src/processor/fib.h> +#endif /* WITH_MAPME */ + +#include <src/core/logger.h> +#include <src/core/ticks.h> +#include <src/io/listenerSet.h> + +#include <src/processor/fibEntryList.h> + +#include <parc/algol/parc_Clock.h> + +#include <src/socket/api.h> + +#define PORT_NUMBER 9695 +#define PORT_NUMBER_AS_STRING "9695" + +#include <src/utils/commands.h> + +// ============================================== + +struct forwarder; +typedef struct forwarder Forwarder; + + +/** + * @function forwarder_Create + * @abstract Create the forwarder and use the provided logger for diagnostic + * output + * @discussion + * If the logger is null, hicn-light will create a STDOUT logger. + * + * @param logger may be NULL + */ +Forwarder *forwarder_Create(Logger *logger); + +/** + * @function forwarder_Destroy + * @abstract Destroys the forwarder, stopping all traffic and freeing all memory + */ +void forwarder_Destroy(Forwarder **ptr); + +/** + * @function forwarder_SetupAllListeners + * @abstract Setup all listeners (tcp, udp, local, ether, ip multicast) on all + * interfaces + * @discussion + * Sets up all listeners on all running interfaces. This provides a quick and + * easy startup, rather than providing a configuration file or programmatic + * commands. + * + * @param port is used by TCP and UDP listeners, in host byte order + * @param localPath is the AF_UNIX path to use, if NULL no AF_UNIX listener is + * setup + */ +void forwarder_SetupAllListeners(Forwarder *forwarder, uint16_t port, + const char *localPath); + +/** + * Configure hicn-light via a configuration file + * + * The configuration file is a set of lines, just like used in hicnLightControl. + * You need to have "add listener" lines in the file to receive connections. No + * default listeners are configured. + * + * @param [in] forwarder An alloated Forwarder + * @param [in] filename The path to the configuration file + */ +void forwarder_SetupFromConfigFile(Forwarder *forwarder, const char *filename); + +/** + * Returns the logger used by this forwarder + * + * If you will store the logger, you should acquire a reference to it. + * + * @param [in] forwarder An allocated hicn-light forwarder + * + * @retval non-null The logger used by hicn-light + * @retval null An error + */ +Logger *forwarder_GetLogger(const Forwarder *forwarder); + +/** + * @function forwarder_SetLogLevel + * @abstract Sets the minimum level to log + */ +void forwarder_SetLogLevel(Forwarder *forwarder, PARCLogLevel level); + +/** + * @function forwarder_GetNextConnectionId + * @abstract Get the next identifier for a new connection + */ +unsigned forwarder_GetNextConnectionId(Forwarder *forwarder); + +Messenger *forwarder_GetMessenger(Forwarder *forwarder); + +Dispatcher *forwarder_GetDispatcher(Forwarder *forwarder); + +/** + * Returns the set of currently active listeners + * + * @param [in] forwarder An allocated hicn-light forwarder + * + * @retval non-null The set of active listeners + * @retval null An error + */ +ListenerSet *forwarder_GetListenerSet(Forwarder *forwarder); + +/** + * Returns the forwrder's connection table + * + * @param [in] forwarder An allocated hicn-light forwarder + * + * @retval non-null The connection tabler + * @retval null An error + * + */ +ConnectionTable *forwarder_GetConnectionTable(Forwarder *forwarder); + +/** + * Returns a Tick-based clock + * + * Runs at approximately 1 msec per tick (see HZ in forwarder.c). + * Do not Release this clock. If you save a copy of it, create your own + * reference to it with parcClock_Acquire(). + * + * @param [in] forwarder An allocated hicn-light forwarder + * + * @retval non-null An allocated hicn-light Clock based on the Tick counter + * @retval null An error + */ +PARCClock *forwarder_GetClock(const Forwarder *forwarder); + +/** + * Direct call to get the Tick clock + * + * Runs at approximately 1 msec per tick (see HZ in forwarder.c) + * + * @param [in] forwarder An allocated hicn-light forwarder + */ +Ticks forwarder_GetTicks(const Forwarder *forwarder); + +/** + * Convert nano seconds to Ticks + * + * Converts nano seconds to Ticks, based on HZ (in forwarder.c) + */ +Ticks forwarder_NanosToTicks(uint64_t nanos); + +uint64_t forwarder_TicksToNanos(Ticks ticks); + +void forwarder_ReceiveCommand(Forwarder *forwarder, command_id command, + struct iovec *message, unsigned ingressId); + +void forwarder_Receive(Forwarder *forwarder, Message *mesage); + +/** + * @function forwarder_AddOrUpdateRoute + * @abstract Adds or updates a route on all the message processors + */ +bool forwarder_AddOrUpdateRoute(Forwarder *forwarder, + add_route_command *control, unsigned ifidx); + +/** + * @function forwarder_RemoveRoute + * @abstract Removes a route from all the message processors + */ +bool forwarder_RemoveRoute(Forwarder *forwarder, remove_route_command *control, + unsigned ifidx); + +/** + * Removes a connection id from all routes + */ +void forwarder_RemoveConnectionIdFromRoutes(Forwarder *forwarder, + unsigned connectionId); + +/** + * @function forwarder_GetConfiguration + * @abstract The configuration object + * @discussion + * The configuration contains all user-issued commands. It does not include + * dynamic state. + */ +Configuration *forwarder_GetConfiguration(Forwarder *forwarder); + +FibEntryList *forwarder_GetFibEntries(Forwarder *forwarder); + +/** + * Sets the maximum number of content objects in the content store + * + * Implementation dependent - may wipe the cache. + */ +void forwarder_SetContentObjectStoreSize(Forwarder *forwarder, + size_t maximumContentStoreSize); + +void forwarder_SetChacheStoreFlag(Forwarder *forwarder, bool val); + +bool forwarder_GetChacheStoreFlag(Forwarder *forwarder); + +void forwarder_SetChacheServeFlag(Forwarder *forwarder, bool val); + +bool forwarder_GetChacheServeFlag(Forwarder *forwarder); + +void forwarder_ClearCache(Forwarder *forwarder); + +void forwarder_SetStrategy(Forwarder *forwarder, Name *prefix, + strategy_type strategy); + +hicn_socket_helper_t *forwarder_GetHIcnSocketHelper(Forwarder *forwarder); + +#ifdef WITH_MAPME + +/** + * @function forwarder_getFib + * @abstract Returns the hICN forwarder's FIB. + * @param [in] forwarder - Pointer to the hICN forwarder. + * @returns Pointer to the hICN FIB. + */ +FIB *forwarder_getFib(Forwarder *forwarder); + +/** + * @function forwarder_onConnectionAdded + * @abstract Callback fired upon addition of a new connection through the + * control protocol. + * @param [in] forwarder - Pointer to the hICN forwarder. + * @param [in] conn - Pointer to the newly added connection. + */ +void forwarder_onConnectionAdded(Forwarder *forwarder, const Connection *conn); + +/** + * @function forwarder_onConnectionRemoved + * @abstract Callback fired upon removal of a connection through the control + * protocol. + * @param [in] forwarder - Pointer to the hICN forwarder. + * @param [in] conn - Pointer to the removed connection. + */ +void forwarder_onConnectionRemoved(Forwarder *forwarder, + const Connection *conn); + +/** + * @function forwarder_ProcessMapMe + * @abstract Callback fired by an hICN listener upon reception of a MAP-Me + * message. + * @param [in] forwarder - Pointer to the hICN forwarder. + * @param [in] msgBuffer - MAP-Me buffer + * @param [in] conn_id - Ingress connection id + */ +void forwarder_ProcessMapMe(Forwarder *forwarder, uint8_t *msgBuffer, + unsigned conn_id); + +#endif /* WITH_MAPME */ + +#endif // forwarder_h diff --git a/hicn-light/src/core/logger.c b/hicn-light/src/core/logger.c new file mode 100755 index 000000000..cac3000e2 --- /dev/null +++ b/hicn-light/src/core/logger.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <src/config.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> + +#include <parc/logging/parc_Log.h> + +#include <src/core/forwarder.h> +#include <src/core/logger.h> + +struct logger { + PARCClock *clock; + + PARCLogReporter *reporter; + PARCLog *loggerArray[LoggerFacility_END]; +}; + +static const struct facility_to_string { + LoggerFacility facility; + const char *string; +} _facilityToString[] = { + {.facility = LoggerFacility_Config, .string = "Config"}, + {.facility = LoggerFacility_Core, .string = "Core"}, + {.facility = LoggerFacility_IO, .string = "IO"}, + {.facility = LoggerFacility_Message, .string = "Message"}, + {.facility = LoggerFacility_Processor, .string = "Processor"}, + {.facility = 0, .string = NULL}}; + +const char *logger_FacilityString(LoggerFacility facility) { + for (int i = 0; _facilityToString[i].string != NULL; i++) { + if (_facilityToString[i].facility == facility) { + return _facilityToString[i].string; + } + } + return "Unknown"; +} + +static void _allocateLoggers(Logger *logger, PARCLogReporter *reporter) { + parcTrapUnexpectedStateIf( + logger->reporter != NULL, + "Trying to allocate a reporter when the previous one is not null"); + logger->reporter = parcLogReporter_Acquire(reporter); + + char hostname[255]; + int gotHostName = gethostname(hostname, 255); + if (gotHostName < 0) { + snprintf(hostname, 255, "unknown"); + } + + for (int i = 0; i < LoggerFacility_END; i++) { + logger->loggerArray[i] = parcLog_Create(hostname, logger_FacilityString(i), + "forwarder", logger->reporter); + parcLog_SetLevel(logger->loggerArray[i], PARCLogLevel_Error); + } +} + +static void _releaseLoggers(Logger *logger) { + for (int i = 0; i < LoggerFacility_END; i++) { + parcLog_Release(&logger->loggerArray[i]); + } + parcLogReporter_Release(&logger->reporter); +} + +static void _destroyer(Logger **loggerPtr) { + Logger *logger = *loggerPtr; + _releaseLoggers(logger); + parcClock_Release(&(*loggerPtr)->clock); +} + +parcObject_ExtendPARCObject(Logger, _destroyer, NULL, NULL, NULL, NULL, NULL, + NULL); + +parcObject_ImplementAcquire(logger, Logger); + +parcObject_ImplementRelease(logger, Logger); + +Logger *logger_Create(PARCLogReporter *reporter, const PARCClock *clock) { + parcAssertNotNull(reporter, "Parameter reporter must be non-null"); + parcAssertNotNull(clock, "Parameter clock must be non-null"); + + Logger *logger = parcObject_CreateAndClearInstance(Logger); + if (logger) { + logger->clock = parcClock_Acquire(clock); + _allocateLoggers(logger, reporter); + } + + return logger; +} + +void logger_SetReporter(Logger *logger, PARCLogReporter *reporter) { + parcAssertNotNull(logger, "Parameter logger must be non-null"); + + // save the log level state + PARCLogLevel savedLevels[LoggerFacility_END]; + for (int i = 0; i < LoggerFacility_END; i++) { + savedLevels[i] = parcLog_GetLevel(logger->loggerArray[i]); + } + + _releaseLoggers(logger); + + _allocateLoggers(logger, reporter); + + // restore log level state + for (int i = 0; i < LoggerFacility_END; i++) { + parcLog_SetLevel(logger->loggerArray[i], savedLevels[i]); + } +} + +void logger_SetClock(Logger *logger, PARCClock *clock) { + parcAssertNotNull(logger, "Parameter logger must be non-null"); + parcClock_Release(&logger->clock); + logger->clock = parcClock_Acquire(clock); +} + +static void _assertInvariants(const Logger *logger, LoggerFacility facility) { + parcAssertNotNull(logger, "Parameter logger must be non-null"); + parcTrapOutOfBoundsIf(facility >= LoggerFacility_END, "Invalid facility %d", + facility); +} + +void logger_SetLogLevel(Logger *logger, LoggerFacility facility, + PARCLogLevel minimumLevel) { + _assertInvariants(logger, facility); + PARCLog *log = logger->loggerArray[facility]; + parcLog_SetLevel(log, minimumLevel); +} + +bool logger_IsLoggable(const Logger *logger, LoggerFacility facility, + PARCLogLevel level) { + _assertInvariants(logger, facility); + PARCLog *log = logger->loggerArray[facility]; + return parcLog_IsLoggable(log, level); +} + +void logger_Log(Logger *logger, LoggerFacility facility, PARCLogLevel level, + const char *module, const char *format, ...) { + if (logger_IsLoggable(logger, facility, level)) { + // this is logged as the messageid + uint64_t logtime = parcClock_GetTime(logger->clock); + + // logger_IsLoggable asserted invariants so we know facility is in bounds + PARCLog *log = logger->loggerArray[facility]; + + va_list va; + va_start(va, format); + + parcLog_MessageVaList(log, level, logtime, format, va); + + va_end(va); + } +} diff --git a/hicn-light/src/core/logger.h b/hicn-light/src/core/logger.h new file mode 100755 index 000000000..e2ab7e147 --- /dev/null +++ b/hicn-light/src/core/logger.h @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file logger.h + * @brief Logger for the hicn-light forwarder + * + * A facility based logger to allow selective logging from different parts of + * hicn-light + * + */ + +#ifndef logger_h +#define logger_h + +#include <parc/algol/parc_Buffer.h> +#include <parc/logging/parc_LogLevel.h> +#include <parc/logging/parc_LogReporter.h> +#include <stdarg.h> +#include <sys/time.h> + +#include <parc/algol/parc_Clock.h> + +struct logger; +typedef struct logger Logger; + +/** + * CONFIG faciilty concerns anything in the /config directory + * CORE concerns anything in the /core directory + * IO concerns anything in the /io directory (listeners, connectors, tcp, + * ethernet, etc.) PROCESSOR concerns FIB, PIT, CS MESSAGE concerns message + * events, like parsing + */ +typedef enum { + LoggerFacility_Config, + LoggerFacility_Core, + LoggerFacility_IO, + LoggerFacility_Processor, + LoggerFacility_Message, + LoggerFacility_END // sentinel value +} LoggerFacility; + +/** + * Returns a string representation of a facility + * + * Do not free the returned value. + * + * @param [in] facility The facility to change to a string + * + * @retval string A string representation of the facility + */ +const char *logger_FacilityString(LoggerFacility facility); + +/** + * Returns a string representation of a log level + * + * Do not free the returned value. + * + * @param [in] level The level to change to a string + * + * @retval string A string representation of the level + */ +const char *logger_LevelString(PARCLogLevel level); + +/** + * Create a logger that uses a given writer and clock + * + * <#Paragraphs Of Explanation#> + * + * @param [in] writer The output writer + * @param [in] clock The clock to use for log messages + * + * @retval non-null An allocated logger + * @retval null An error + */ +Logger *logger_Create(PARCLogReporter *reporter, const PARCClock *clock); + +/** + * Release logger + */ +void logger_Release(Logger **loggerPtr); + +/** + * Acquire logger + */ +Logger *logger_Acquire(const Logger *logger); + +/** + * Sets the minimum log level for a facility + * + * The default log level is ERROR. For a message to be logged, it must be of + * equal or higher log level. + * + * @param [in] logger An allocated logger + * @param [in] facility The facility to set the log level for + * @param [in] The minimum level to log + * + */ +void logger_SetLogLevel(Logger *logger, LoggerFacility facility, + PARCLogLevel minimumLevel); + +/** + * Tests if the log level would be logged + * + * If the facility would log the given level, returns true. May be used as a + * guard around expensive logging functions. + * + * @param [in] logger An allocated logger + * @param [in] facility The facility to test + * @param [in] The level to test + * + * @retval true The given facility would log the given level + * @retval false A message of the given level would not be logged + * + */ +bool logger_IsLoggable(const Logger *logger, LoggerFacility facility, + PARCLogLevel level); + +/** + * Log a message + * + * The message will only be logged if it is loggable (logger_IsLoggable returns + * true). + * + * @param [in] logger An allocated Logger + * @param [in] facility The facility to log under + * @param [in] level The log level of the message + * @param [in] module The specific module logging the message + * @param [in] format The message with varargs + * + */ +void logger_Log(Logger *logger, LoggerFacility facility, PARCLogLevel level, + const char *module, const char *format, ...); + +/** + * Switch the logger to a new reporter + * + * Will close the old reporter and re-setup the internal loggers to use the new + * reporter. All current log level settings are preserved. + * + * @param [in] logger An allocated Logger + * @param [in] reporter An allocated PARCLogReporter + */ +void logger_SetReporter(Logger *logger, PARCLogReporter *reporter); + +/** + * Set a new clock to use with the logger + * + * The logger will start getting the time (logged as the messageid) from the + * specified clock + * + * @param [in] logger An allocated Logger + * @param [in] clock An allocated PARCClock + */ +void logger_SetClock(Logger *logger, PARCClock *clock); +#endif // logger_h diff --git a/hicn-light/src/core/mapMe.c b/hicn-light/src/core/mapMe.c new file mode 100755 index 000000000..4444bcf15 --- /dev/null +++ b/hicn-light/src/core/mapMe.c @@ -0,0 +1,816 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file mapMe.c + * @brief MAP-Me : AnchorLess Producer Mobility Management. + */ + +#ifdef WITH_MAPME + +#include <hicn/hicn.h> +#include <src/core/mapMe.h> +#include <stdio.h> // printf + +#include <src/core/connectionList.h> +#include <src/core/forwarder.h> +#include <src/core/logger.h> +#include <src/core/message.h> +#include <src/core/messagePacketType.h> // packet types +#include <src/core/ticks.h> +#include <src/processor/fibEntry.h> +#include <src/processor/pitEntry.h> + +#include <parc/algol/parc_HashMap.h> +#include <parc/algol/parc_Iterator.h> +#include <parc/algol/parc_Unsigned.h> +#include <parc/assert/parc_Assert.h> + +#define MS2NS(x) x * 1000000 +#define T2NS(x) forwarder_TicksToNanos(x) + +#define MAPME_DEFAULT_TU 5000 /* ms */ +#define MAPME_DEFAULT_RETX 500 /* ms */ +#define MAX_RETX 3 + +#define NOT_A_NOTIFICATION false +#define NO_INGRESS 0 +#define TIMER_NO_REPEAT false + +#define DO_DISCOVERY 1 +#define MAPME_INVALID_DICOVERY_SEQ -1 + +#define LOG_FACILITY LoggerFacility_Core + +#define LOG(mapme, log_level, fmt, ...) \ + do { \ + Logger *logger = forwarder_GetLogger(mapme->forwarder); \ + if (logger_IsLoggable(logger, LOG_FACILITY, log_level)) { \ + logger_Log(logger, LOG_FACILITY, log_level, __func__, fmt, \ + ##__VA_ARGS__); \ + } \ + } while (0) + +#define WARN(mapme, fmt, ...) \ + LOG(mapme, PARCLogLevel_Warning, fmt, ##__VA_ARGS__) +#define ERROR(mapme, fmt, ...) \ + LOG(mapme, PARCLogLevel_Error, fmt, ##__VA_ARGS__) +#define INFO(mapme, fmt, ...) LOG(mapme, PARCLogLevel_Info, fmt, ##__VA_ARGS__) +#define DEBUG(mapme, fmt, ...) \ + LOG(mapme, PARCLogLevel_Debug, fmt, ##__VA_ARGS__) + +/** + * MAP-Me state data structure + */ +struct mapme { + uint32_t retx; /* ms */ + uint32_t Tu; /* ms */ + bool removeFibEntries; + + Forwarder *forwarder; +}; + +static MapMe MapMeDefault = {.retx = MAPME_DEFAULT_RETX, + .Tu = MAPME_DEFAULT_TU, + .removeFibEntries = false}; + +/******************************************************************************/ + +#include <src/core/connection.h> + +bool mapMe_Init(MapMe **mapme, Forwarder *forwarder) { + *mapme = malloc(sizeof(MapMe)); + if (!mapme) goto ERR_MALLOC; + + /* Internal state : set default values */ + memcpy(*mapme, &MapMeDefault, sizeof(MapMe)); + + (*mapme)->forwarder = forwarder; + + /* Install hook on Face events to onConnectionAdded */ + // see. config/configuration.c + + /* Install hook for signalization processing. See : + * - io/hicnListener.c + * - src/core/connection.{c,h} + */ + + ERROR((*mapme), "MapMe"); + + return true; + +ERR_MALLOC: + return false; +} + +/****************************************************************************** + * TFIB + ******************************************************************************/ + +#define INVALID_SEQ 0 +#define INIT_SEQ 1 + +typedef struct { + uint32_t seq; + PARCHashMap *nexthops; + /* Update/Notification heuristic */ + Ticks lastAckedUpdate; // XXX This is only for producer !!! +} MapMeTFIB; + +static MapMeTFIB *mapMeTFIB_Create() { + MapMeTFIB *tfib; + tfib = malloc(sizeof(MapMeTFIB)); + if (!tfib) goto ERR_MALLOC; + tfib->seq = 0; + tfib->lastAckedUpdate = 0; + tfib->nexthops = parcHashMap_Create(); + if (!tfib->nexthops) goto ERR_HASHMAP; + + return tfib; + +ERR_HASHMAP: + free(tfib); +ERR_MALLOC: + return NULL; +} + +void mapMeTFIB_Release(MapMeTFIB **tfibPtr) { + MapMeTFIB *tfib = *tfibPtr; + parcHashMap_Release(&tfib->nexthops); + free(tfib); + *tfibPtr = NULL; +} + +/** + * @function mapMe_CreateTFIB + * @abstract Associate a new TFIB entry to a FIB entry. + * @param [in] - Pointer to the FIB entry. + * @return Boolean indicating the success of the operation. + */ +static void mapMe_CreateTFIB(FibEntry *fibEntry) { + MapMeTFIB *tfib; + + /* Make sure we don't already have an associated TFIB entry */ + tfib = fibEntry_getUserData(fibEntry); + // assertNull(tfib); + + tfib = mapMeTFIB_Create(); + fibEntry_setUserData(fibEntry, tfib, (void (*)(void **))mapMeTFIB_Release); +} + +#define TFIB(fibEntry) ((MapMeTFIB *)fibEntry_getUserData(fibEntry)) + +static const PARCEventTimer *mapMeTFIB_Get(const MapMeTFIB *tfib, + unsigned conn_id) { + const PARCEventTimer *timer; + const PARCBuffer *buffer; + PARCUnsigned *cid = parcUnsigned_Create(conn_id); + buffer = parcHashMap_Get(tfib->nexthops, cid); + if (!buffer) return NULL; + PARCByteArray *array = parcBuffer_Array(buffer); + timer = *((PARCEventTimer **)parcByteArray_Array(array)); + parcUnsigned_Release(&cid); + return timer; +} + +static void mapMeTFIB_Put(MapMeTFIB *tfib, unsigned conn_id, + const PARCEventTimer *timer) { + /* NOTE: Timers are not objects (the only class not being an object in + * fact), and as such, we cannot use them as values for the HashMap. + * Just like for unsigned we needed the PARC wrapper. + * There is no wrapper for pointers, so we use Arrays, which has an ubly + * syntax... + */ + PARCUnsigned *cid = parcUnsigned_Create(conn_id); + PARCBuffer *buffer = + parcBuffer_CreateFromArray(&timer, sizeof(PARCEventTimer *)); + parcHashMap_Put(tfib->nexthops, cid, buffer); + parcUnsigned_Release(&cid); + parcBuffer_Release(&buffer); +} + +static void mapMeTFIB_Remove(MapMeTFIB *tfib, unsigned conn_id) { + // Who releases the timer ? + PARCUnsigned *cid = parcUnsigned_Create(conn_id); + parcHashMap_Remove(tfib->nexthops, cid); + parcUnsigned_Release(&cid); +} + +static PARCIterator *mapMeTFIB_CreateKeyIterator(const MapMeTFIB *tfib) { + return parcHashMap_CreateKeyIterator(tfib->nexthops); +} + +int hicn_prefix_from_name(const Name *name, hicn_prefix_t *prefix) { + NameBitvector *bv = name_GetContentName(name); + ip_address_t ip_address; + nameBitvector_ToIPAddress(bv, &ip_address); + + /* The name length will be equal to ip address' prefix length */ + return hicn_prefix_create_from_ip_address(&ip_address, prefix); +} + +static Message *mapMe_createMessage(const MapMe *mapme, const Name *name, + mapme_params_t *params) { + Ticks now = forwarder_GetTicks(mapme->forwarder); + Logger *logger = logger_Acquire(forwarder_GetLogger(mapme->forwarder)); + + INFO(mapme, "[MAP-Me] CreateMessage type=%d seq=%d", params->type, + params->seq); + + size_t size = (params->protocol == IPPROTO_IPV6) ? HICN_MAPME_V6_HDRLEN + : HICN_MAPME_V4_HDRLEN; + uint8_t *icmp_pkt = parcMemory_AllocateAndClear(size); + + hicn_prefix_t prefix; + int rc = hicn_prefix_from_name(name, &prefix); + if (rc < 0) { + ERROR(mapme, "[MAP-Me] Failed to create lib's name"); + goto ERR_NAME; + } + + INFO(mapme, "[MAP-Me] Creating MAP-Me packet"); + size_t len = hicn_mapme_create_packet(icmp_pkt, &prefix, params); + if (len != 0) { + ERROR(mapme, "[MAP-Me] Failed to create mapme packet through lib"); + goto ERR_CREATE; + } + + // hicn_packet_dump(icmp_pkt, MAPME_HDRLEN); + + return message_CreateFromByteArray(NO_INGRESS, icmp_pkt, + MessagePacketType_Interest, now, logger); + +ERR_CREATE: +ERR_NAME: + return NULL; +} + +static Message *mapMe_createAckMessage(const MapMe *mapme, + const uint8_t *msgBuffer, + const mapme_params_t *params) { + Ticks now = forwarder_GetTicks(mapme->forwarder); + Logger *logger = logger_Acquire(forwarder_GetLogger(mapme->forwarder)); + + size_t size = (params->protocol == IPPROTO_IPV6) ? HICN_MAPME_V6_HDRLEN + : HICN_MAPME_V4_HDRLEN; + uint8_t *icmp_pkt = parcMemory_AllocateAndClear(size); + memcpy(icmp_pkt, msgBuffer, size); + + size_t len = hicn_mapme_create_ack(icmp_pkt, params); + if (len != size) { + ERROR(mapme, "[MAP-Me] Failed to create mapme ack packet through lib"); + return NULL; + } + + return message_CreateFromByteArray( + NO_INGRESS, icmp_pkt, MessagePacketType_ContentObject, now, logger); +} + +struct setFacePendingArgs { + const MapMe *mapme; + const Name *name; + FibEntry *fibEntry; + unsigned conn_id; + bool send; + bool is_first; + uint32_t num_retx; +}; + +static bool mapMe_setFacePending(const MapMe *mapme, const Name *name, + FibEntry *fibEntry, unsigned conn_id, + bool send, bool is_first, uint32_t num_retx); + +static void mapMe_setFacePendingCallback(int fd, PARCEventType which_event, + void *data) { + struct setFacePendingArgs *args = (struct setFacePendingArgs *)data; + + parcAssertTrue(which_event & PARCEventType_Timeout, + "Event incorrect, expecting %X set, got %X", + PARCEventType_Timeout, which_event); + + INFO(args->mapme, "Timeout during retransmission. Re-sending"); + mapMe_setFacePending(args->mapme, args->name, args->fibEntry, args->conn_id, + args->send, args->is_first, args->num_retx); +} + +/** + * @brief Update/Notification heuristic: + * + * NOTE: IN are currently disabled until the proper placeholder is agreed in the + * interest header. + */ +static hicn_mapme_type_t mapMe_getTypeFromHeuristic(const MapMe *mapme, + FibEntry *fibEntry) { +#if 0 /* interplay of IU/IN */ + if (TFIB(fibEntry)->lastAckedUpdate == 0) { + return UPDATE; + } else { + Ticks interval = now - TFIB(fibEntry)->lastAckedUpdate; + return (T2NS(interval) > MS2NS(mapme->Tu)) ? UPDATE : NOTIFICATION; + } +#else /* Always send IU */ + return UPDATE; +#endif +} + +static bool mapMe_setFacePending(const MapMe *mapme, const Name *name, + FibEntry *fibEntry, unsigned conn_id, + bool send, bool is_first, uint32_t num_retx) { + int rc; + + INFO(mapme, "[MAP-Me] SetFacePending connection=%d prefix=XX retx=%d", + conn_id, num_retx); + + /* NOTE: if the face is pending an we receive an IN, maybe we should not + * cancel the timer + */ + Dispatcher *dispatcher = forwarder_GetDispatcher(mapme->forwarder); + PARCEventTimer *timer; + + // NOTE + // - at producer, send always true, we always send something reliably so we + // set the timer. + // - in the network, we always forward an IU, and never an IN + if (is_first || send) { + // XXX + mapme_params_t params = { + .protocol = IPPROTO_IPV6, + .type = is_first ? mapMe_getTypeFromHeuristic(mapme, fibEntry) : UPDATE, + .seq = TFIB(fibEntry)->seq}; + Message *special_interest = mapMe_createMessage(mapme, name, ¶ms); + if (!special_interest) { + INFO(mapme, "[MAP-Me] Could not create special interest"); + return false; + } + + const ConnectionTable *table = + forwarder_GetConnectionTable(mapme->forwarder); + const Connection *conn = + connectionTable_FindById((ConnectionTable *)table, conn_id); + if (conn) { + INFO(mapme, "[MAP-Me] Sending MAP-Me packet"); + connection_ReSend(conn, special_interest, NOT_A_NOTIFICATION); + } else { + INFO(mapme, "[MAP-Me] Stopped retransmissions as face went down"); + } + + if (num_retx < MAX_RETX) { + INFO(mapme, "[MAP-Me] - Scheduling retransmission\n"); + /* Schedule retransmission */ + struct setFacePendingArgs *args = + malloc(sizeof(struct setFacePendingArgs)); + if (!args) goto ERR_MALLOC; + args->mapme = mapme; + args->name = name; + args->fibEntry = fibEntry; + args->conn_id = conn_id; + args->send = send; + args->is_first = is_first; + args->num_retx = num_retx + 1; + + timer = dispatcher_CreateTimer(dispatcher, TIMER_NO_REPEAT, + mapMe_setFacePendingCallback, args); + struct timeval timeout = {mapme->retx / 1000, + (mapme->retx % 1000) * 1000}; + rc = parcEventTimer_Start(timer, &timeout); + if (rc < 0) goto ERR_TIMER; + } else { + INFO(mapme, "[MAP-Me] Last retransmission."); + timer = NULL; + } + } else { + INFO(mapme, "[MAP-Me] - not forwarding as send is False"); + timer = NULL; + } + + PARCEventTimer *oldTimer = + (PARCEventTimer *)mapMeTFIB_Get(TFIB(fibEntry), conn_id); + if (oldTimer) { + INFO(mapme, "[MAP-Me] - Found old timer, would need to cancel !"); + // parcEventTimer_Stop(oldTimer); + } + INFO(mapme, "[MAP-Me] - Putting new timer in TFIB"); + if (timer) mapMeTFIB_Put(TFIB(fibEntry), conn_id, timer); + + return true; + +ERR_MALLOC: +ERR_TIMER: + return false; +} + +/*------------------------------------------------------------------------------ + * Event handling + *----------------------------------------------------------------------------*/ + +/* + * Return true if we have at least one local connection as next hop + */ +static bool mapMe_hasLocalNextHops(const MapMe *mapme, + const FibEntry *fibEntry) { + const NumberSet *nexthops = fibEntry_GetNexthops(fibEntry); + const ConnectionTable *table = forwarder_GetConnectionTable(mapme->forwarder); + + for (size_t j = 0; j < fibEntry_NexthopCount(fibEntry); j++) { + /* Retrieve Nexthop #j */ + unsigned conn_id = numberSet_GetItem(nexthops, j); + const Connection *conn = + connectionTable_FindById((ConnectionTable *)table, conn_id); + + /* Ignore non-local connections */ + if (!connection_IsLocal(conn)) continue; + /* We don't need to test against conn_added since we don't + * expect it to have any entry in the FIB */ + + return true; + } + return false; +} + +/* + * Callback called everytime a new connection is created by the control protocol + */ +void mapMe_onConnectionAdded(const MapMe *mapme, const Connection *conn_added) { + /* bool ret; */ + FibEntryList *fiblist; + + /* Ignore local connections corresponding to applications for now */ + if (connection_IsLocal(conn_added)) return; + + unsigned conn_added_id = connection_GetConnectionId(conn_added); + INFO(mapme, "[MAP-Me] New connection %d", conn_added_id); + + /* + * Iterate on FIB to find locally served prefix + * Ideally, we want to avoid a FIB scan everytime a face is added/removed + */ + fiblist = forwarder_GetFibEntries(mapme->forwarder); + for (size_t i = 0; i < fibEntryList_Length(fiblist); i++) { + FibEntry *fibEntry = (FibEntry *)fibEntryList_Get(fiblist, i); + const Name *name = fibEntry_GetPrefix(fibEntry); + + /* Skip entries that have no local connection as next hop */ + if (!mapMe_hasLocalNextHops(mapme, fibEntry)) continue; + + /* This entry corresponds to a locally served prefix, set + * Special Interest */ + if (!TFIB(fibEntry)) /* Create TFIB associated to FIB entry */ + mapMe_CreateTFIB(fibEntry); + TFIB(fibEntry)->seq++; + + char *name_str = name_ToString(name); + INFO(mapme, "[MAP-Me] sending IU/IN for name %s on connection %d", name_str, + conn_added_id); + free(name_str); + + mapMe_setFacePending(mapme, name, fibEntry, conn_added_id, true, true, 0); + } +} + +/*------------------------------------------------------------------------------ + * Special Interest handling + *----------------------------------------------------------------------------*/ + +/** + * @discussion This function is way too long and should be cut out + */ +static bool mapMe_onSpecialInterest(const MapMe *mapme, + const uint8_t *msgBuffer, + unsigned conn_in_id, hicn_prefix_t *prefix, + mapme_params_t *params) { + const ConnectionTable *table = forwarder_GetConnectionTable(mapme->forwarder); + /* The cast is needed since connectionTable_FindById miss the + * const qualifier for the first parameter */ + const Connection *conn_in = + connectionTable_FindById((ConnectionTable *)table, conn_in_id); + seq_t fibSeq, seq = params->seq; + bool send = (params->type == UPDATE); + bool rv; + + Name *name = name_CreateFromPacket(msgBuffer, MessagePacketType_Interest); + char *name_str = name_ToString(name); + INFO(mapme, + "[MAP-Me] Ack'ed Special Interest on connection %d - prefix=%s type=XX " + "seq=%d", + conn_in_id, name_str, seq); + free(name_str); + + /* + * Immediately send an acknowledgement back on the ingress connection + * We always ack, even duplicates. + */ + Message *ack = mapMe_createAckMessage(mapme, msgBuffer, params); + if (!ack) goto ERR_ACK_CREATE; + rv = connection_ReSend(conn_in, ack, NOT_A_NOTIFICATION); + if (!rv) goto ERR_ACK_SEND; + message_Release(&ack); + + /* EPM on FIB */ + /* only the processor has access to the FIB */ + FIB *fib = forwarder_getFib(mapme->forwarder); + + FibEntry *fibEntry = fib_Contains(fib, name); + if (!fibEntry) { + INFO(mapme, + "[MAP-Me] - Re-creating FIB entry with next hop on connection %d", + conn_in_id); + /* + * This might happen for a node hosting a producer which has moved. + * Destroying the face has led to removing all corresponding FIB + * entries. In that case, we need to correctly restore the FIB entries. + */ + strategy_type fwdStrategy = LAST_STRATEGY_VALUE; + + fibEntry = fibEntry_Create(name, fwdStrategy); + fibEntry_AddNexthopByConnectionId(fibEntry, conn_in_id); + mapMe_CreateTFIB(fibEntry); + TFIB(fibEntry)->seq = seq; // INIT_SEQ; + fib_Add(fib, fibEntry); + return true; // with proper seq, we are done + + } else if (!TFIB(fibEntry)) { + /* Create TFIB associated to FIB entry */ + INFO(mapme, + "[MAP-Me] - Creating TFIB entry with default sequence number"); + mapMe_CreateTFIB(fibEntry); + } + + fibSeq = TFIB(fibEntry)->seq; + if (seq > fibSeq) { + INFO(mapme, + "[MAP-Me] - Higher sequence number than FIB %d, updating seq and " + "next hops", + fibSeq); + /* This has to be done first to allow processing SpecialInterestAck's */ + TFIB(fibEntry)->seq = seq; + + /* Reliably forward the IU on all prevHops */ + INFO(mapme, "[MAP-Me] - (1/3) processing prev hops"); + if (params->type == UPDATE) { + PARCIterator *iterator = mapMeTFIB_CreateKeyIterator(TFIB(fibEntry)); + while (parcIterator_HasNext(iterator)) { + PARCUnsigned *cid = parcIterator_Next(iterator); + unsigned conn_id = parcUnsigned_GetUnsigned(cid); + INFO(mapme, "[MAP-Me] - Re-sending IU to pending connection %d", + conn_id); + mapMe_setFacePending(mapme, fibEntry_GetPrefix(fibEntry), fibEntry, + conn_id, false, false, 0); + } + parcIterator_Release(&iterator); + } + + /* nextHops -> prevHops + * + * We add to the list of pendingUpdates the current next hops, and + * eventually forward them an IU too. + * + * Exception: nextHops -> nextHops + * Because of retransmission issues, it is possible that a second interest + * (with same of higher sequence number) is receive from a next-hop + * interface. In that case, the face remains a next hop. + */ + const NumberSet *nexthops_old = fibEntry_GetNexthops(fibEntry); + + /* We make a copy to be able to send IU _after_ updating next hops */ + NumberSet *nexthops = numberSet_Create(); + numberSet_AddSet(nexthops, nexthops_old); + + /* We are considering : * -> nextHops + * + * If inFace was a previous hop, we need to cancel the timer and remove + * the entry. Also, the face should be added to next hops. + * + * Optimization : nextHops -> nextHops + * - no next hop to add + * - we know that inFace was not a previous hop since it was a next hop and + * this forms a partition. No need for a search + */ + + INFO(mapme, "[MAP-Me] - (3/3) next hops ~~> prev hops"); + PARCEventTimer *oldTimer = + (PARCEventTimer *)mapMeTFIB_Get(TFIB(fibEntry), conn_in_id); + if (oldTimer) { + /* This happens if we receive an IU while we are still sending + * one in the other direction + */ + INFO(mapme, "[MAP-Me] - Canceled pending timer"); + parcEventTimer_Stop(oldTimer); + mapMeTFIB_Remove(TFIB(fibEntry), conn_in_id); + } + + /* Remove all next hops */ + for (size_t k = 0; k < numberSet_Length(nexthops_old); k++) { + unsigned conn_id = numberSet_GetItem(nexthops_old, k); + INFO(mapme, "[MAP-Me] - Replaced next hops by connection %d", conn_id); + fibEntry_RemoveNexthopByConnectionId(fibEntry, conn_id); + } + fibEntry_AddNexthopByConnectionId(fibEntry, conn_in_id); + + INFO(mapme, "[MAP-Me] - (2/3) processing next hops"); + bool complete = true; + for (size_t k = 0; k < numberSet_Length(nexthops); k++) { + unsigned conn_id = numberSet_GetItem(nexthops, k); + INFO(mapme, " - Next hop connection %d", conn_id); + if (conn_id == conn_in_id) { + INFO(mapme, " . Ignored this next hop since equal to ingress face"); + continue; + } + + INFO(mapme, "[MAP-Me] - Sending IU on current next hop connection %d", + conn_id); + mapMe_setFacePending(mapme, fibEntry_GetPrefix(fibEntry), fibEntry, + conn_id, send, false, 0); + complete = false; + } + + /* + * The update is completed when the IU could not be sent to any + * other next hop. + */ + if (complete) INFO(mapme, "[MAP-Me] - Update completed !"); + + numberSet_Release(&nexthops); + + } else if (seq == fibSeq) { + /* + * Multipath, multihoming, multiple producers or duplicate interest + * + * In all cases, we assume the propagation was already done when the first + * interest with the same sequence number was received, so we stop here + * + * It might happen that the previous AP has still a connection to the + * producer and that we received back our own IU. In that case, we just + * need to Ack and ignore it. + */ + if (mapMe_hasLocalNextHops(mapme, fibEntry)) { + INFO(mapme, "[MAP-Me] - Received original interest... Update complete"); + return true; + } + + INFO(mapme, "[MAP-Me] - Adding multipath next hop on connection %d", + conn_in_id); + fibEntry_AddNexthopByConnectionId(fibEntry, conn_in_id); + + } else { // seq < fibSeq + /* + * Face is propagating outdated information, we can just + * consider it as a prevHops. Send the special interest backwards with + * the new sequence number to reconciliate this outdated part of the + * arborescence. + */ + INFO( + mapme, + "[MAP-Me] - Update interest %d -> %d sent backwards on connection %d", + seq, fibSeq, conn_in_id); + mapMe_setFacePending(mapme, fibEntry_GetPrefix(fibEntry), fibEntry, + conn_in_id, send, false, 0); + } + + return true; + +ERR_ACK_SEND: + message_Release(&ack); +ERR_ACK_CREATE: + return false; +} + +void mapMe_onSpecialInterestAck(const MapMe *mapme, const uint8_t *msgBuffer, + unsigned conn_in_id, hicn_prefix_t *prefix, + mapme_params_t *params) { + INFO(mapme, "[MAP-Me] Receive IU/IN Ack on connection %d", conn_in_id); + + const Name *name = + name_CreateFromPacket(msgBuffer, MessagePacketType_Interest); + + FIB *fib = forwarder_getFib(mapme->forwarder); + FibEntry *fibEntry = fib_Contains(fib, name); + parcAssertNotNull(fibEntry, + "No corresponding FIB entry for name contained in IU Ack"); + + /* Test if the latest pending update has been ack'ed, otherwise just ignore */ + seq_t seq = params->seq; + if (seq != INVALID_SEQ) { + seq_t fibSeq = TFIB(fibEntry)->seq; + + if (seq < fibSeq) { + INFO(mapme, + "[MAP-Me] - Ignored special interest Ack with seq=%u, expected %u", + seq, fibSeq); + return; + } + } + + /* + * Ignore the Ack if no TFIB is present, or it has no corresponding entry + * with the ingress face. + * Note: previously, we were creating the TFIB entry + */ + if (!TFIB(fibEntry)) { + INFO(mapme, "[MAP-Me] - Ignored ACK for prefix with no TFIB entry"); + return; + } + + PARCEventTimer *timer = + (PARCEventTimer *)mapMeTFIB_Get(TFIB(fibEntry), conn_in_id); + if (!timer) { + INFO(mapme, + "[MAP-Me] - Ignored ACK for prefix not having the Connection in " + "TFIB entry. Possible duplicate ?"); + return; + } + + /* Stop timer and remove entry from TFIB */ + parcEventTimer_Stop(timer); + mapMeTFIB_Remove(TFIB(fibEntry), conn_in_id); + + INFO(mapme, "[MAP-Me] - Removing TFIB entry for ack on connection %d", + conn_in_id); + + /* We need to update the timestamp only for IU Acks, not for IN Acks */ + if (params->type == UPDATE_ACK) { + INFO(mapme, "[MAP-Me] - Updating LastAckedUpdate"); + TFIB(fibEntry)->lastAckedUpdate = forwarder_GetTicks(mapme->forwarder); + } +} + +/*----------------------------------------------------------------------------- + * Overloaded functions + *----------------------------------------------------------------------------*/ + +/* + * @abstract returns where to forward a normal interests(nexthops) defined by + * mapme, it also set the sequnence number properly if needed + */ + +/****************************************************************************** + * Public functions (exposed in the .h) + ******************************************************************************/ + +/* + * Returns true iif the message corresponds to a MAP-Me packet + */ +bool mapMe_isMapMe(const uint8_t *msgBuffer) { + uint8_t next_header = messageHandler_NextHeaderType(msgBuffer); + + const uint8_t *icmp_ptr; + if (next_header == IPPROTO_ICMP) { + icmp_ptr = msgBuffer + IPV4_HDRLEN; + } else if (next_header == IPPROTO_ICMPV6) { + icmp_ptr = msgBuffer + IPV6_HDRLEN; + } else { + return false; + } + + uint8_t type = ((_icmp_header_t *)icmp_ptr)->type; + uint8_t code = ((_icmp_header_t *)icmp_ptr)->code; + if (HICN_IS_MAPME(type, code)) return true; + + return false; +} + +/** + * @discussion The exact type of the MapMe message is determined after + * reception. In hICN, Interest Update and Notifications look like regular + * Interest packets, and are first punted from the normal path by the forwarder, + * then treated as such in the Listener to reach this function. Acknowledgements + * are received as Content (Data) packets and will land here too. + * + * This function is in charge of abstracting the low-level implementation of + * MAP-Me (eg. ICMP packets) and return higher level messages that can be + * processed by MAP-Me core. + */ +void mapMe_Process(const MapMe *mapme, const uint8_t *msgBuffer, + unsigned conn_id) { + hicn_prefix_t prefix; + mapme_params_t params; + hicn_mapme_parse_packet(msgBuffer, &prefix, ¶ms); + + // XXX Dispatch message dependenging on type + switch (params.type) { + case UPDATE: + case NOTIFICATION: + mapMe_onSpecialInterest(mapme, msgBuffer, conn_id, &prefix, ¶ms); + break; + case UPDATE_ACK: + case NOTIFICATION_ACK: + mapMe_onSpecialInterestAck(mapme, msgBuffer, conn_id, &prefix, ¶ms); + break; + default: + printf("E:Unknown message\n"); + break; + } +} + +#endif /* WITH_MAPME */ diff --git a/hicn-light/src/core/mapMe.h b/hicn-light/src/core/mapMe.h new file mode 100755 index 000000000..39edd0bd7 --- /dev/null +++ b/hicn-light/src/core/mapMe.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file mapMe.h + * @brief MAP-Me : AnchorLess Producer Mobility Management + */ + +#ifndef mapMe_h +#define mapMe_h + +#ifdef WITH_MAPME + +#include <stdbool.h> +#include <stdint.h> + +#include <src/io/hicnListener.h> + +#include <hicn/hicn.h> +#include <src/utils/commands.h> + +struct mapme; +typedef struct mapme MapMe; + +/** + * @function MapMe_Init + * @abstract Initializes MAP-Me state in the forwarder. + * @return bool - Boolean informing about the success of MAP-Me initialization. + */ +bool mapMe_Init(MapMe **mapme, Forwarder *Forwarder); + +/** + * @function messageHandler_isMapMe + * @abstract Identifies MAP-Me messages + * @discussion This function can be used by the forwarder to dispatch MAP-Me + * message to the appropriate processing function. Ideally this would be + * done through hooks defined in the Init function. + * @param [in] msgBuffer - The buffer to match + * @return A boolean indicating whether message is a MAP-Me control message. + */ +bool mapMe_isMapMe(const uint8_t *msgBuffer); + +/** + * @function mapMe_handleMapMeMessage + * @abstract Process a MAP-Me message. + * @param [in] mapme - Pointer to the MAP-Me data structure. + * @param [in] message - MAP-Me buffer + * @param [in] conn_id - Ingress connection id + */ +void mapMe_Process(const MapMe *mapme, const uint8_t *msgBuffer, + unsigned conn_id); + +/** + * @function mapMe_onConnectionAdded + * @abstract Callback following the addition of the face though the control + * protocol. + * @discussion This callback triggers the sending of control packets by MAP-Me. + * @param [in] mapme - Pointer to the MAP-Me data structure. + * @param [in] conn - The newly added connection. + */ +void mapMe_onConnectionAdded(const MapMe *mapme, const Connection *conn); + +/** + * @function mapMe_getNextHops + * @abstract return the nexthops to forward interests defined by mapme, it + * covers also the case where local discovery mechanisms are trriggered. + */ +NumberSet *mapMe_getNextHops(const MapMe *mapme, FibEntry *fibEntry, + const Message *interest); + +hicn_mapme_type_t mapMe_PktType_To_LibHicnPktType(MessagePacketType type); + +MessagePacketType mapMe_LibHicnPktType_To_PktType(hicn_mapme_type_t type); + +#endif /* WITH_MAPME */ + +#endif // mapMe_h diff --git a/hicn-light/src/core/message.c b/hicn-light/src/core/message.c new file mode 100755 index 000000000..6c0e916d2 --- /dev/null +++ b/hicn-light/src/core/message.c @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <src/config.h> +#include <stdio.h> +#include <string.h> + +#include <src/core/forwarder.h> +#include <src/core/message.h> +#include <src/core/wldr.h> + +#include <src/core/messageHandler.h> + +#include <parc/algol/parc_Hash.h> +#include <parc/algol/parc_Memory.h> +#include <src/core/messagePacketType.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_EventBuffer.h> + +struct message { + Logger *logger; + + Ticks receiveTime; + unsigned ingressConnectionId; + + Name *name; + + uint8_t *messageHead; + + unsigned length; + + uint8_t packetType; + + unsigned refcount; +}; + +Message *message_Acquire(const Message *message) { + Message *copy = (Message *)message; + copy->refcount++; + return copy; +} + +Message *message_CreateFromEventBuffer(PARCEventBuffer *data, size_t dataLength, + unsigned ingressConnectionId, + Ticks receiveTime, Logger *logger) { + // used by applications, we can get only interest or data packets + Message *message = parcMemory_AllocateAndClear(sizeof(Message)); + parcAssertNotNull(message, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Message)); + + message->logger = logger_Acquire(logger); + message->receiveTime = receiveTime; + message->ingressConnectionId = ingressConnectionId; + message->length = dataLength; + + message->messageHead = parcMemory_AllocateAndClear(dataLength); + parcAssertNotNull(message->messageHead, + "parcMemory_AllocateAndClear(%zu) returned NULL", + dataLength); + + // copy the data because *data is destroyed in the connection. + int res = parcEventBuffer_Read(data, message->messageHead, dataLength); + if (res == -1) { + return NULL; + } + + if (messageHandler_IsInterest(message->messageHead)) { + message->packetType = MessagePacketType_Interest; + } else if (messageHandler_IsData(message->messageHead)) { + message->packetType = MessagePacketType_ContentObject; + } else { + printf("Got a packet that is not a data nor an interest, drop it!\n"); + return NULL; + } + message->name = + name_CreateFromPacket(message->messageHead, message->packetType); + + message->refcount = 1; + + return message; +} + +Message *message_CreateFromByteArray(unsigned connid, uint8_t *pckt, + MessagePacketType type, Ticks receiveTime, + Logger *logger) { + Message *message = parcMemory_AllocateAndClear(sizeof(Message)); + parcAssertNotNull(message, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Message)); + + message->logger = logger_Acquire(logger); + message->receiveTime = receiveTime; + message->ingressConnectionId = connid; + message->messageHead = pckt; + message->length = messageHandler_GetTotalPacketLength(pckt); + message->packetType = type; + + if (messageHandler_IsWldrNotification(pckt)) { + message->name = NULL; + } else { + message->name = + name_CreateFromPacket(message->messageHead, message->packetType); + } + + message->refcount = 1; + + return message; +} + +void message_Release(Message **messagePtr) { + parcAssertNotNull(messagePtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*messagePtr, + "Parameter must dereference to non-null pointer"); + + Message *message = *messagePtr; + parcAssertTrue( + message->refcount > 0, + "Invalid state: message_Release called on message with 0 references %p", + (void *)message); + + message->refcount--; + if (message->refcount == 0) { + if (logger_IsLoggable(message->logger, LoggerFacility_Message, + PARCLogLevel_Debug)) { + logger_Log(message->logger, LoggerFacility_Message, PARCLogLevel_Debug, + __func__, "Message %p destroyed", (void *)message); + } + + logger_Release(&message->logger); + if (message->name != NULL) name_Release(&message->name); + parcMemory_Deallocate((void **)&message->messageHead); + parcMemory_Deallocate((void **)&message); + } + *messagePtr = NULL; +} + +bool message_Write(PARCEventQueue *parcEventQueue, const Message *message) { + parcAssertNotNull(message, "Message parameter must be non-null"); + parcAssertNotNull(parcEventQueue, "Buffer parameter must be non-null"); + + return parcEventQueue_Write(parcEventQueue, message->messageHead, + message_Length(message)); +} + +size_t message_Length(const Message *message) { + parcAssertNotNull(message, "Parameter must be non-null"); + return message->length; +} + +bool message_HasWldr(const Message *message) { + parcAssertNotNull(message, "Parameter must be non-null"); + return messageHandler_HasWldr(message->messageHead); +} + +bool message_IsWldrNotification(const Message *message) { + parcAssertNotNull(message, "Parameter must be non-null"); + return messageHandler_IsWldrNotification(message->messageHead); +} + +void message_ResetWldrLabel(Message *message) { + parcAssertNotNull(message, "Parameter must be non-null"); + return messageHandler_ResetWldrLabel(message->messageHead); +} + +unsigned message_GetWldrLabel(const Message *message) { + parcAssertNotNull(message, "Parameter must be non-null"); + return messageHandler_GetWldrLabel(message->messageHead); +} + +unsigned message_GetWldrExpectedLabel(const Message *message) { + parcAssertNotNull(message, "Parameter must be non-null"); + return messageHandler_GetExpectedWldrLabel(message->messageHead); +} + +unsigned message_GetWldrLastReceived(const Message *message) { + parcAssertNotNull(message, "Parameter must be non-null"); + return messageHandler_GetWldrLastReceived(message->messageHead); +} + +void message_SetWldrLabel(Message *message, uint16_t label) { + parcAssertNotNull(message, "Parameter must be non-null"); + messageHandler_SetWldrLabel(message->messageHead, label); +} + +Message *message_CreateWldrNotification(Message *original, uint16_t expected, + uint16_t lastReceived) { + parcAssertNotNull(original, "Parameter original must be non-null"); + Message *message = parcMemory_AllocateAndClear(sizeof(Message)); + parcAssertNotNull(message, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Message)); + message->receiveTime = original->receiveTime; + message->ingressConnectionId = original->ingressConnectionId; + message->refcount = 1; + message->logger = logger_Acquire(original->logger); + + message->length = messageHandler_GetICMPPacketSize( + messageHandler_GetIPPacketType(original->messageHead)); + message->messageHead = parcMemory_AllocateAndClear(message->length); + parcAssertNotNull(message->messageHead, + "parcMemory_AllocateAndClear returned NULL"); + + message->packetType = MessagePacketType_WldrNotification; + message->name = NULL; // nobody will use the name in a notification packet, + // so we can simply set it to NULL + + // set notification stuff. + messageHandler_SetWldrNotification( + message->messageHead, original->messageHead, expected, lastReceived); + // XXX: what about the checksum? + return message; +} + +unsigned message_GetIngressConnectionId(const Message *message) { + parcAssertNotNull(message, "Parameter must be non-null"); + return message->ingressConnectionId; +} + +void message_SetIngressConnectionId(Message *message, unsigned conn) { + parcAssertNotNull(message, "Parameter must be non-null"); + message->ingressConnectionId = conn; +} + +Ticks message_GetReceiveTime(const Message *message) { + parcAssertNotNull(message, "Parameter must be non-null"); + return message->receiveTime; +} + +uint32_t message_GetPathLabel(const Message *message) { + parcAssertNotNull(message, "Parameter must be non-null"); + return messageHandler_GetPathLabel(message->messageHead); +} + +void message_SetPathLabel(Message *message, uint32_t label) { + parcAssertNotNull(message, "Parameter must be non-null"); + return messageHandler_SetPathLabel(message->messageHead, label); +} + +void message_UpdatePathLabel(Message *message, uint8_t outFace) { + parcAssertNotNull(message, "Parameter must be non-null"); + messageHandler_UpdatePathLabel(message->messageHead, outFace); +} + +void message_ResetPathLabel(Message *message) { + parcAssertNotNull(message, "Parameter must be non-null"); + messageHandler_ResetPathLabel(message->messageHead); +} + +MessagePacketType message_GetType(const Message *message) { + parcAssertNotNull(message, "Parameter message must be non-null"); + return message->packetType; +} + +Name *message_GetName(const Message *message) { + parcAssertNotNull(message, "Parameter message must be non-null"); + return message->name; +} + +bool message_HasInterestLifetime(const Message *message) { + parcAssertNotNull(message, "Parameter message must be non-null"); + return messageHandler_HasInterestLifetime(message->messageHead); +} + +uint64_t message_GetInterestLifetimeTicks(const Message *message) { + parcAssertNotNull(message, "Parameter message must be non-null"); + uint64_t lifetime = messageHandler_GetInterestLifetime(message->messageHead); + return forwarder_NanosToTicks(lifetime * 1000000ULL); +} + +bool message_HasContentExpiryTime(const Message *message) { + parcAssertNotNull(message, "Parameter message must be non-null"); + return messageHandler_HasContentExpiryTime(message->messageHead); +} + +uint64_t message_GetContentExpiryTimeTicks(const Message *message) { + parcAssertNotNull(message, "Parameter message must be non-null"); + uint64_t expire = messageHandler_GetContentExpiryTime(message->messageHead); + return message->receiveTime + forwarder_NanosToTicks(expire * 1000000ULL); +} + +const uint8_t *message_FixedHeader(const Message *message) { + parcAssertNotNull(message, "Parameter message must be non-null"); + return message->messageHead; +} diff --git a/hicn-light/src/core/message.h b/hicn-light/src/core/message.h new file mode 100755 index 000000000..88aa32480 --- /dev/null +++ b/hicn-light/src/core/message.h @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file message.h + * @brief Message is the unit of forwarding, i.e. the packets being switched + * + */ +#ifndef message_h +#define message_h + +#include <src/config.h> +#include <src/core/logger.h> +#include <src/core/messagePacketType.h> +#include <src/core/streamBuffer.h> + +#include <src/core/name.h> + +#include <parc/algol/parc_EventBuffer.h> +#include <parc/algol/parc_EventQueue.h> + +#include <src/utils/address.h> + +#include <src/core/ticks.h> + +struct message; +typedef struct message Message; + +/** + * @function message_CreateFromBuffer + * @abstract Takes ownership of the input buffer, which comprises one complete + * message + */ + +Message *message_CreateFromEventBuffer(PARCEventBuffer *data, size_t dataLength, + unsigned ingressConnectionId, + Ticks receiveTime, Logger *logger); + +/** + * @function message_CreateFromByteArray + * @abstract create a message from a byte array + */ + +Message *message_CreateFromByteArray(unsigned connid, uint8_t *pckt, + MessagePacketType type, Ticks receiveTime, + Logger *logger); + +/** + * @function message_Copy + * @abstract Get a reference counted copy + */ + +Message *message_Acquire(const Message *message); + +/** + * Releases the message and frees the memory + */ +void message_Release(Message **messagePtr); + +/** + * Writes the message to the queue + */ + +bool message_Write(PARCEventQueue *parcEventQueue, const Message *message); + +/** + * Returns the total byte length of the message + */ +size_t message_Length(const Message *message); + +bool message_HasWldr(const Message *message); + +bool message_IsWldrNotification(const Message *message); + +void message_ResetWldrLabel(Message *message); + +unsigned message_GetWldrLabel(const Message *message); + +unsigned message_GetWldrExpectedLabel(const Message *message); + +unsigned message_GetWldrLastReceived(const Message *message); + +void message_SetWldrLabel(Message *message, uint16_t label); + +Message *message_CreateWldrNotification(Message *original, uint16_t expected, + uint16_t lastReceived); +/** + * Returns the connection id of the packet input + */ +unsigned message_GetIngressConnectionId(const Message *message); + +void message_SetIngressConnectionId(Message *message, unsigned conn); + +/** + * Returns the receive time (in router ticks) of the message + */ +Ticks message_GetReceiveTime(const Message *message); + +/** + * Returns the PacketType + */ +MessagePacketType message_GetType(const Message *message); + +uint32_t message_GetPathLabel(const Message *message); +void message_SetPathLabel(Message *message, uint32_t label); +void message_UpdatePathLabel(Message *message, uint8_t outFace); +void message_ResetPathLabel(Message *message); + +// =========================================================== +// Accessors used to index and compare messages + +/** + * @function message_GetName + * @abstract The name in the message + * @discussion + * The name of the Interest or Content Object. If the caller will store the + * name, he should make a reference counted copy. + * @return The name as stored in the message object. + */ + +Name *message_GetName(const Message *message); + +/** + * Determines if the message has an Interest Lifetime parameter + * + * @param [in] message An allocated and parsed Message + * + * @retval true If an Intrerest Lifetime field exists + * @retval false If no Interest Lifetime exists + */ + +bool message_HasInterestLifetime(const Message *message); + +/** + * Returns the Interest lifetime in hicn-light Ticks + * + * the interest expires after now + returned ticks + * + * @param [in] message An allocated and parsed Message + * + * @retval integer Lifetime in forwarder Ticks + * + */ + +uint64_t message_GetInterestLifetimeTicks(const Message *message); + +/** + * checks if the expiry time is set inside the content object + */ +bool message_HasContentExpiryTime(const Message *message); + +/** + * returns the moment (in hicn-light ticks) when the content object will expire + */ +uint64_t message_GetContentExpiryTimeTicks(const Message *message); + +/** + * Returns a pointer to the beginning of the FixedHeader + * + * @param [in] message An allocated and parsed Message + * + * @return non-null The fixed header memory + * @return null No fixed header or an error + */ + +const uint8_t *message_FixedHeader(const Message *message); + +#endif // message_h diff --git a/hicn-light/src/core/messageHandler.h b/hicn-light/src/core/messageHandler.h new file mode 100755 index 000000000..d63656461 --- /dev/null +++ b/hicn-light/src/core/messageHandler.h @@ -0,0 +1,580 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef messageHandler +#define messageHandler + +#include <stdlib.h> + +#include <hicn/hicn.h> +#include <src/core/messagePacketType.h> + +#define H(packet) ((hicn_header_t *)packet) +#define H6(packet) (H(packet)->v6.ip) +#define H6T(packet) (H(packet)->v6.tcp) +#define H4(packet) (H(packet)->v4.ip) +#define H4T(packet) (H(packet)->v4.tcp) + +#define HICN_V6_LEN(packet) (H6(packet).len) +#define HICN_V4_LEN(packet) (H4(packet).len) + +/*** codes and types ***/ +#define IPv6_TYPE 6 +#define IPv4_TYPE 4 +#define ICMP_WLDR_TYPE 42 +#define ICMP_WLDR_CODE 0 +#define ICMP_LB_TYPE 43 + +/*** masks and constants ***/ +#define PATH_LABEL_MASK 0x8000 // 1000 0000 0000 0000 +#define NOT_PATH_LABEL_MASK 0x7fff // 0111 0000 0000 0000 +#define UINT16_T_MASK 0x0000ffff // 1111 1111 1111 1111 +#define NEVER_EXPIRE \ + 16777216 // 2^16 (max urgent pointer) * 2^8 (max reserved + NS bits) + +/*** HICN ALLOWED PORTS ***/ +#define CONTROL_PORT 9695 +#define HTTP_PORT 8080 + +#define IPV6_DEFAULT_VERSION 6 +#define IPV6_DEFAULT_TRAFFIC_CLASS 0 +#define IPV6_DEFAULT_FLOW_LABEL 0 + +#define expected_lbl wldr_notification_lbl.expected_lbl +#define received_lbl wldr_notification_lbl.received_lbl + +static inline uint8_t messageHandler_GetIPPacketType(const uint8_t *message) { + return HICN_IP_VERSION(message); +} + +static inline void messageHandler_UpdateTCPCheckSum(uint8_t *message, + uint16_t *old_val, + uint16_t *new_val, + uint8_t size) { + switch (messageHandler_GetIPPacketType(message)) { + case IPv4_TYPE: + for (uint8_t i = 0; i < size; i++) { + uint16_t old_csum = ~(H4T(message).csum); + uint16_t not_old_val = ~(*old_val); + uint32_t sum = (uint32_t)old_csum + not_old_val + *new_val; + + while (sum >> 16) { + sum = (sum >> 16) + (sum & UINT16_T_MASK); + } + + H4T(message).csum = ~sum; + ++old_val; + ++new_val; + } + break; + case IPv6_TYPE: + for (uint8_t i = 0; i < size; i++) { + uint16_t old_csum = ~(H6T(message).csum); + uint16_t not_old_val = ~(*old_val); + uint32_t sum = (uint32_t)old_csum + not_old_val + *new_val; + + while (sum >> 16) { + sum = (sum >> 16) + (sum & UINT16_T_MASK); + } + + H6T(message).csum = ~sum; + ++old_val; + ++new_val; + } + break; + default: + return; + } +} + +static inline void messageHandler_UpdateIPv4CheckSum(uint8_t *message, + uint16_t *old_val, + uint16_t *new_val, + uint8_t size) { + for (uint8_t i = 0; i < size; i++) { + uint16_t old_csum = ~(H4(message).csum); + uint16_t not_old_val = ~(*old_val); + uint32_t sum = (uint32_t)old_csum + not_old_val + *new_val; + + while (sum >> 16) { + sum = (sum >> 16) + (sum & UINT16_T_MASK); + } + + H4(message).csum = ~sum; + ++old_val; + ++new_val; + } +} + +static inline size_t messageHandler_GetEmptyTCPPacketSize(unsigned ipVersion) { + if (ipVersion == IPv4_TYPE) + return IPV4_HDRLEN + TCP_HDRLEN; + else if (ipVersion == IPv6_TYPE) + return IPV6_HDRLEN + TCP_HDRLEN; + else + return 0; +} + +static inline size_t messageHandler_GetICMPPacketSize(unsigned ipVersion) { + if (ipVersion == IPv4_TYPE) + return IPV4_HDRLEN + ICMP_HDRLEN; + else if (ipVersion == IPv6_TYPE) + return IPV6_HDRLEN + ICMP_HDRLEN; + else + return 0; +} + +static inline size_t messageHandler_GetIPHeaderLength(unsigned ipVersion) { + if (ipVersion == IPv4_TYPE) + return IPV4_HDRLEN; + else if (ipVersion == IPv6_TYPE) + return IPV6_HDRLEN; + else + return 0; +} + +static inline bool messageHandler_IsValidHIcnPacket(const uint8_t *message) { + uint8_t version = messageHandler_GetIPPacketType(message); + if (version == IPv6_TYPE || version == IPv4_TYPE) { + return true; + } + return false; +} + +static inline uint8_t messageHandler_NextHeaderType(const uint8_t *message) { + switch (messageHandler_GetIPPacketType(message)) { + case IPv6_TYPE: + return (uint8_t)H6(message).nxt; + case IPv4_TYPE: + return (uint8_t)H4(message).protocol; + default: + return 0; + } +} + +static inline bool messageHandler_IsTCP(const uint8_t *message) { + if (messageHandler_NextHeaderType(message) != IPPROTO_TCP) return false; + return true; +} + +static inline bool messageHandler_IsInterest(const uint8_t *message) { + if (!messageHandler_IsTCP(message)) return false; + + bool flag; + hicn_packet_test_ece((hicn_header_t *)message, + &flag); // ECE flag is set to 0 in interest packets + if (flag == false) return true; + return false; +} + +static inline bool messageHandler_IsData(const uint8_t *message) { + if (!messageHandler_IsTCP(message)) return false; + + bool flag; + hicn_packet_test_ece((hicn_header_t *)message, + &flag); // ECE flag is set to 1 in data packets + if (flag == true) return true; + return false; +} + +static inline bool messageHandler_IsWldrNotification(const uint8_t *message) { + // this function returns true only if the packet is an ICMP packet in Wldr + // form. type must be equal to ICMP_WLDR_TYPE and code equal to ICMP_WLDR_CODE + uint8_t next_header = messageHandler_NextHeaderType(message); + + const uint8_t *icmp_ptr; + if (next_header == IPPROTO_ICMP) { + icmp_ptr = message + IPV4_HDRLEN; + } else if (next_header == IPPROTO_ICMPV6) { + icmp_ptr = message + IPV6_HDRLEN; + } else { + return false; + } + + uint8_t type = ((_icmp_header_t *)icmp_ptr)->type; + uint8_t code = ((_icmp_header_t *)icmp_ptr)->code; + if (type == ICMP_WLDR_TYPE && code == ICMP_WLDR_CODE) { + return true; + } + + return false; +} + +static inline bool messageHandler_IsLoadBalancerProbe(const uint8_t *message) { + uint8_t next_header = messageHandler_NextHeaderType(message); + + const uint8_t *icmp_ptr; + if (next_header == IPPROTO_ICMP) { + icmp_ptr = message + IPV4_HDRLEN; + } else if (next_header == IPPROTO_ICMPV6) { + icmp_ptr = message + IPV6_HDRLEN; + } else { + return false; + } + + uint8_t type = ((_icmp_header_t *)icmp_ptr)->type; + if (type == ICMP_LB_TYPE) { + return true; + } + + return false; +} + +static inline uint16_t messageHandler_GetTotalPacketLength( + const uint8_t *message) { + switch (messageHandler_GetIPPacketType(message)) { + case IPv6_TYPE: + return ntohs((uint16_t)HICN_V6_LEN(message)) + IPV6_HDRLEN; + case IPv4_TYPE: + return ntohs((uint16_t)HICN_V4_LEN(message)); + default: + return 0; + } +} + +static inline uint32_t messageHandler_GetSegment(const uint8_t *message) { + if (!messageHandler_IsTCP(message)) return 0; + + switch (messageHandler_GetIPPacketType(message)) { + case IPv6_TYPE: + return ntohl((uint32_t)H6T(message).seq); + case IPv4_TYPE: + return ntohl((uint32_t)H4T(message).seq); + default: + return 0; + } +} + +static inline uint16_t messageHandler_GetExpectedWldrLabel( + const uint8_t *message) { + const uint8_t *icmp_ptr; + switch (messageHandler_GetIPPacketType(message)) { + case IPv6_TYPE: + icmp_ptr = message + IPV6_HDRLEN; + break; + case IPv4_TYPE: + icmp_ptr = message + IPV4_HDRLEN; + break; + default: + return 0; + } + + return ntohs(((_icmp_wldr_header_t *)icmp_ptr)->expected_lbl); +} + +static inline uint16_t messageHandler_GetWldrLastReceived( + const uint8_t *message) { + const uint8_t *icmp_ptr; + switch (messageHandler_GetIPPacketType(message)) { + case IPv6_TYPE: + icmp_ptr = message + IPV6_HDRLEN; + break; + case IPv4_TYPE: + icmp_ptr = message + IPV4_HDRLEN; + break; + default: + return 0; + } + + return ntohs(((_icmp_wldr_header_t *)icmp_ptr)->received_lbl); +} + +static inline uint16_t messageHandler_GetWldrLabel(const uint8_t *message) { + switch (messageHandler_GetIPPacketType(message)) { + case IPv6_TYPE: + return ntohs((uint16_t)H6T(message).window); + case IPv4_TYPE: + return ntohs((uint16_t)H4T(message).window); + default: + return 0; + } +} + +static inline void messageHandler_SetWldrLabel(uint8_t *message, + uint16_t label) { + uint16_t old_val = messageHandler_GetWldrLabel(message); + + switch (messageHandler_GetIPPacketType(message)) { + case IPv6_TYPE: + H6T(message).window = htons(label); + break; + case IPv4_TYPE: + H4T(message).window = htons(label); + break; + default: + break; + } + + messageHandler_UpdateTCPCheckSum(message, &old_val, &label, 1); +} + +static inline void messageHandler_ResetWldrLabel(uint8_t *message) { + messageHandler_SetWldrLabel(message, 0); +} + +static inline bool messageHandler_HasWldr(const uint8_t *message) { + if (messageHandler_IsTCP(message)) { + uint16_t lbl = messageHandler_GetWldrLabel(message); + if (lbl != 0) { + return true; + } + } + return false; +} + +static inline uint8_t messageHandler_GetProbePacketType( + const uint8_t *message) { + const uint8_t *icmp_ptr; + switch (messageHandler_GetIPPacketType(message)) { + case IPv6_TYPE: + icmp_ptr = message + IPV6_HDRLEN; + break; + case IPv4_TYPE: + icmp_ptr = message + IPV4_HDRLEN; + break; + default: + return 0; + } + + return ((_icmp_header_t *)icmp_ptr)->code; +} + +static inline uint32_t messageHandler_GetPathLabel(const uint8_t *message) { + if (!messageHandler_IsTCP(message)) return 0; + + uint32_t path_label; + int res = hicn_data_get_path_label((hicn_header_t *)message, &path_label); + if (res < 0) return 0; + return path_label; +} + +static inline void messageHandler_SetPathLabel(uint8_t *message, + uint32_t new_path_label) { + if (!messageHandler_IsTCP(message)) return; + + uint32_t old_path_label; + int res = hicn_data_get_path_label((hicn_header_t *)message, &old_path_label); + if (res < 0) return; + + hicn_data_set_path_label((hicn_header_t *)message, new_path_label); + + messageHandler_UpdateTCPCheckSum(message, (uint16_t *)&old_path_label, + (uint16_t *)&new_path_label, 2); +} + +static inline void messageHandler_UpdatePathLabel(uint8_t *message, + uint8_t outFace) { + if (!messageHandler_IsTCP(message)) return; + + uint32_t pl_old_32bit = messageHandler_GetPathLabel(message); + uint8_t pl_old_8bit = (uint8_t)(pl_old_32bit >> 24UL); + uint32_t pl_new_32bit = + (uint32_t)((((pl_old_8bit << 1) | (pl_old_8bit >> 7)) ^ outFace) << 24UL); + + hicn_data_set_path_label((hicn_header_t *)message, pl_new_32bit); + + messageHandler_UpdateTCPCheckSum(message, (uint16_t *)&pl_old_32bit, + (uint16_t *)&pl_new_32bit, 2); +} + +static inline void messageHandler_ResetPathLabel(uint8_t *message) { + if (!messageHandler_IsTCP(message)) return; + + uint32_t pl_old_32bit = messageHandler_GetPathLabel(message); + uint32_t pl_new_32bit = 0; + hicn_data_set_path_label((hicn_header_t *)message, pl_new_32bit); + messageHandler_UpdateTCPCheckSum(message, (uint16_t *)&pl_old_32bit, + (uint16_t *)&pl_new_32bit, 2); +} + +static inline uint16_t messageHandler_GetInterestLifetime( + const uint8_t *message) { + if (!messageHandler_IsTCP(message)) return 0; + + hicn_lifetime_t lifetime; + int res = hicn_interest_get_lifetime((hicn_header_t *)message, &lifetime); + if (res < 0) return 0; + return lifetime; +} + +static inline bool messageHandler_HasInterestLifetime(const uint8_t *message) { + if (!messageHandler_IsTCP(message)) return false; + + if (messageHandler_GetInterestLifetime(message) == 0) return false; + return true; +} + +static inline uint32_t messageHandler_GetContentExpiryTime( + const uint8_t *message) { + if (!messageHandler_IsTCP(message)) return 0; + + uint32_t expirationTime; + int res = + hicn_data_get_expiry_time((hicn_header_t *)message, &expirationTime); + if (res < 0) return 0; + return expirationTime; +} + +static inline bool messageHandler_HasContentExpiryTime(const uint8_t *message) { + if (!messageHandler_IsTCP(message)) return 0; + + uint32_t expirationTime; + int res = + hicn_data_get_expiry_time((hicn_header_t *)message, &expirationTime); + if (res < 0) return false; + + if (expirationTime == NEVER_EXPIRE) return false; + + return true; +} + +static inline void *messageHandler_GetSource(const uint8_t *message) { + switch (messageHandler_GetIPPacketType(message)) { + case IPv6_TYPE: + return &H6(message).saddr; + break; + case IPv4_TYPE: + return &H4(message).saddr; + break; + default: + return NULL; + } +} + +static inline void *messageHandler_GetDestination(const uint8_t *message) { + switch (messageHandler_GetIPPacketType(message)) { + case IPv6_TYPE: + return &H6(message).daddr; + break; + case IPv4_TYPE: + return &H4(message).daddr; + break; + default: + return NULL; + } +} + +static inline void messageHandler_SetSource_IPv6(uint8_t *message, + struct in6_addr *address) { + if (messageHandler_IsTCP(message)) { + uint16_t *old_src = (uint16_t *)messageHandler_GetSource(message); + messageHandler_UpdateTCPCheckSum(message, old_src, (uint16_t *)address, 8); + } + H6(message).saddr.as_in6addr = *address; +} + +static inline void messageHandler_SetDestination_IPv6( + uint8_t *message, struct in6_addr *address) { + if (messageHandler_IsTCP(message)) { + uint16_t *old_dst = (uint16_t *)messageHandler_GetDestination(message); + messageHandler_UpdateTCPCheckSum(message, old_dst, (uint16_t *)address, 8); + } + H6(message).daddr.as_in6addr = *address; +} + +static inline void messageHandler_SetSource_IPv4(uint8_t *message, + uint32_t *address) { + // update tcp checksum + uint16_t *old_src = (uint16_t *)messageHandler_GetSource(message); + if (messageHandler_IsTCP(message)) { + messageHandler_UpdateTCPCheckSum(message, old_src, (uint16_t *)address, 2); + } + // update IPv4 cheksum + // the IPv4 checksum is not part of the psudo header for TCP checksum + // calculation we can update them separetelly + messageHandler_UpdateIPv4CheckSum(message, old_src, (uint16_t *)address, 2); + + H4(message).saddr.as_u32 = *address; +} + +static inline void messageHandler_SetDestination_IPv4(uint8_t *message, + uint32_t *address) { + uint16_t *old_dst = (uint16_t *)messageHandler_GetDestination(message); + if (messageHandler_IsTCP(message)) { + messageHandler_UpdateTCPCheckSum(message, old_dst, (uint16_t *)address, 2); + } + messageHandler_UpdateIPv4CheckSum(message, old_dst, (uint16_t *)address, 2); + H4(message).daddr.as_u32 = *address; +} + +static inline void messageHandler_SetWldrNotification(uint8_t *notification, + uint8_t *original, + uint16_t expected, + uint16_t received) { + hicn_header_t *h = (hicn_header_t *)notification; + switch (messageHandler_GetIPPacketType(original)) { + case IPv6_TYPE: { + *h = (hicn_header_t){.v6 = { + .ip = + { + .version_class_flow = htonl( + (IPV6_DEFAULT_VERSION << 28) | + (IPV6_DEFAULT_TRAFFIC_CLASS << 20) | + (IPV6_DEFAULT_FLOW_LABEL & 0xfffff)), + .len = htons(ICMP_HDRLEN), + .nxt = IPPROTO_ICMPV6, + .hlim = 5, + }, + .wldr = + { + .type = ICMP_WLDR_TYPE, + .code = ICMP_WLDR_CODE, + .expected_lbl = htons(expected), + .received_lbl = htons(received), + }, + }}; + messageHandler_SetSource_IPv6( + notification, + (struct in6_addr *)messageHandler_GetDestination(original)); + messageHandler_SetDestination_IPv6( + notification, (struct in6_addr *)messageHandler_GetSource(original)); + break; + } + case IPv4_TYPE: { + break; + } + default: + break; + } +} + +static inline void messageHandler_SetProbePacket(uint8_t *message, + uint8_t probeType, + struct in6_addr *src, + struct in6_addr *dst) { + hicn_header_t *h = (hicn_header_t *)message; + *h = (hicn_header_t){ + .v6 = { + .ip = + { + .version_class_flow = + htonl((IPV6_DEFAULT_VERSION << 28) | + (IPV6_DEFAULT_TRAFFIC_CLASS << 20) | + (IPV6_DEFAULT_FLOW_LABEL & 0xfffff)), + .len = htons(ICMP_HDRLEN), + .nxt = IPPROTO_ICMPV6, + .hlim = 5, // this should be 1, but ... just to be safe + }, + .icmp = + { + .type = ICMP_LB_TYPE, + .code = probeType, + }, + }}; + messageHandler_SetSource_IPv6(message, src); + messageHandler_SetDestination_IPv6(message, dst); +} + +#endif // Metis_metis_MessageHandler diff --git a/hicn-light/src/core/messagePacketType.h b/hicn-light/src/core/messagePacketType.h new file mode 100755 index 000000000..dfbb12342 --- /dev/null +++ b/hicn-light/src/core/messagePacketType.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file message_packet_type_h + * @brief Defines the packet type for a HICN message + * + */ + +#ifndef message_packet_type_h +#define message_packet_type_h + +typedef enum message_type { + MessagePacketType_Unknown, + MessagePacketType_Interest, + MessagePacketType_ContentObject, + MessagePacketType_WldrNotification +} MessagePacketType; + +#endif // message_packet_type_h diff --git a/hicn-light/src/core/name.c b/hicn-light/src/core/name.c new file mode 100755 index 000000000..f6a452d27 --- /dev/null +++ b/hicn-light/src/core/name.c @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <limits.h> +#include <src/config.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> + +#include <parc/algol/parc_BufferComposer.h> +#include <parc/algol/parc_Hash.h> +#include <parc/algol/parc_Memory.h> + +#include <src/core/messageHandler.h> +#include <src/core/name.h> + +#include <parc/algol/parc_Hash.h> + +#include <parc/assert/parc_Assert.h> + +#define IPv6_TYPE 6 +#define IPv4_TYPE 4 + +// assumption: the IPv6 address is the name, the TCP segment number is the ICN +// segment + +struct name { + NameBitvector *content_name; + uint32_t segment; + uint32_t name_hash; + // the refcount is shared between all copies + unsigned *refCountPtr; +}; + +// ===================================================== + +static unsigned _getRefCount(const Name *name) { return *name->refCountPtr; } + +static void _incrementRefCount(Name *name) { + parcAssertTrue(*name->refCountPtr > 0, + "Illegal State: Trying to increment a 0 refcount!"); + (*name->refCountPtr)++; +} + +static void _decrementRefCount(Name *name) { + parcAssertTrue(*name->refCountPtr > 0, + "Illegal State: Trying to decrement a 0 refcount!"); + (*name->refCountPtr)--; +} + +static uint32_t _computeHash(Name *name) { + parcAssertNotNull(name, "Parameter must be non-null pointer"); + + uint32_t hash1 = nameBitvector_GetHash32(name->content_name); + return parcHash32_Data_Cumulative((const uint8_t *)&name->segment, 4, hash1); +} + +// ============================================================================ + +Name *name_CreateFromPacket(const uint8_t *packet, MessagePacketType type) { + Name *name = parcMemory_AllocateAndClear(sizeof(Name)); + parcAssertNotNull(name, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Name)); + + if (messageHandler_GetIPPacketType(packet) == IPv6_TYPE) { + if (type == MessagePacketType_Interest) { + name->content_name = nameBitvector_CreateFromIn6Addr( + (struct in6_addr *)messageHandler_GetDestination(packet), 128); + } else if (type == MessagePacketType_ContentObject) { + name->content_name = nameBitvector_CreateFromIn6Addr( + (struct in6_addr *)messageHandler_GetSource(packet), 128); + } else { + parcMemory_Deallocate((void **)&name); + return NULL; + } + } else if (messageHandler_GetIPPacketType(packet) == IPv4_TYPE) { + if (type == MessagePacketType_Interest) { + name->content_name = nameBitvector_CreateFromInAddr( + *((uint32_t *)messageHandler_GetDestination(packet)), 32); + } else if (type == MessagePacketType_ContentObject) { + name->content_name = nameBitvector_CreateFromInAddr( + *((uint32_t *)messageHandler_GetSource(packet)), 32); + } else { + parcMemory_Deallocate((void **)&name); + return NULL; + } + } else { + printf("Error: unknown message type\n"); + parcMemory_Deallocate((void **)&name); + return NULL; + } + + name->segment = messageHandler_GetSegment(packet); + name->name_hash = _computeHash(name); + + name->refCountPtr = parcMemory_Allocate(sizeof(unsigned)); + parcAssertNotNull(name->refCountPtr, "parcMemory_Allocate(%zu) returned NULL", + sizeof(unsigned)); + *name->refCountPtr = 1; + return name; +} + +Name *name_CreateFromAddress(address_type addressType, union commandAddr addr, + uint8_t len) { + Name *name = parcMemory_AllocateAndClear(sizeof(Name)); + parcAssertNotNull(name, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Name)); + if (addressType == ADDR_INET) { + name->content_name = nameBitvector_CreateFromInAddr(addr.ipv4, len); + } else if (addressType == ADDR_INET6) { + name->content_name = nameBitvector_CreateFromIn6Addr(&addr.ipv6, len); + } else { + parcTrapNotImplemented("Unkown packet type"); + } + + name->segment = 0; + name->name_hash = _computeHash(name); + + name->refCountPtr = parcMemory_Allocate(sizeof(unsigned)); + parcAssertNotNull(name->refCountPtr, "parcMemory_Allocate(%zu) returned NULL", + sizeof(unsigned)); + *name->refCountPtr = 1; + + return name; +} + +void name_Release(Name **namePtr) { + parcAssertNotNull(namePtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*namePtr, "Parameter must dereference to non-null pointer"); + + Name *name = *namePtr; + _decrementRefCount(name); + if (_getRefCount(name) == 0) { + parcMemory_Deallocate((void **)&(name->refCountPtr)); + nameBitvector_Destroy(&(name->content_name)); + } + parcMemory_Deallocate((void **)&name); + *namePtr = NULL; +} + +Name *name_Acquire(const Name *original) { + parcAssertNotNull(original, "Parameter must be non-null"); + Name *copy = parcMemory_AllocateAndClear(sizeof(Name)); + parcAssertNotNull(copy, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Name)); + + memcpy(copy, original, sizeof(Name)); + _incrementRefCount(copy); + + return copy; +} + +uint32_t name_HashCode(const Name *name) { + parcAssertNotNull(name, "Parameter must be non-null"); + return name->name_hash; +} + +NameBitvector *name_GetContentName(const Name *name) { + parcAssertNotNull(name, "Parameter must be non-null"); + return name->content_name; +} + +bool name_Equals(const Name *a, const Name *b) { + parcAssertNotNull(a, "Parameter a must be non-null"); + parcAssertNotNull(b, "Parameter b must be non-null"); + + if ((nameBitvector_Equals(a->content_name, b->content_name) && + a->segment == b->segment)) + return true; + return false; +} + +int name_Compare(const Name *a, const Name *b) { + parcAssertNotNull(a, "Parameter a must be non-null"); + parcAssertNotNull(b, "Parameter b must be non-null"); + + if (a == NULL && b == NULL) { + return 0; + } + if (a == NULL) { + return -1; + } + if (b == NULL) { + return +1; + } + + int res = nameBitvector_Compare(a->content_name, b->content_name); + + if (res != 0) { + return res; + } else { + if (a->segment < b->segment) { + return -1; + } else if (a->segment > b->segment) { + return +1; + } else { + return 0; + } + } +} + +bool name_StartsWith(const Name *name, const Name *prefix) { + parcAssertNotNull(name, "Parameter name must be non-null"); + parcAssertNotNull(prefix, "Parameter prefix must be non-null"); + + return nameBitvector_StartsWith(name->content_name, prefix->content_name); +} + +char *name_ToString(const Name *name) { + char *output = malloc(128); + + Address *packetAddr = nameBitvector_ToAddress(name_GetContentName(name)); + + sprintf(output, "name: %s seq: %u", addressToString(packetAddr), + name->segment); + + addressDestroy(&packetAddr); + + return output; +} + +void name_setLen(const Name *name, uint8_t len) { + nameBitvector_setLen(name->content_name, len); +} diff --git a/hicn-light/src/core/name.h b/hicn-light/src/core/name.h new file mode 100755 index 000000000..fb4ad7a56 --- /dev/null +++ b/hicn-light/src/core/name.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef name_h +#define name_h + +#include <stdbool.h> +#include <stdlib.h> + +#include <src/core/messagePacketType.h> +#include <src/core/nameBitvector.h> +#include <src/utils/address.h> + +#include <src/utils/commands.h> + +struct name; +typedef struct name Name; + +/** + * Creates a name from packet + * + */ +Name *name_CreateFromPacket(const uint8_t *memory, MessagePacketType type); + +/** + * Releases one reference count, and frees memory after last reference + */ +void name_Release(Name **namePtr); + +/** + * Acquires a reference to the name so that a reference count increments. + * Notice however that this * function is used only when a new fib entry is + * created (mostly configuration time) probably here performance are not + * critical. + */ +Name *name_Acquire(const Name *original); + +/** + * A hash value for use in hash tables + * + */ +uint32_t name_HashCode(const Name *name); + +/** + * Returns the content name without the segment value + * + */ +NameBitvector *name_GetContentName(const Name *name); + +/** + * Determine if two HicnName instances are equal. + */ +bool name_Equals(const Name *a, const Name *b); + +/** + * Compares two names and returns their ordering + * + */ +int name_Compare(const Name *a, const Name *b); + +/** + * @function metsName_StartsWith + * @abstract Checks if name starts with prefix + * @discussion + * Byte-by-byte prefix comparison + * + * @return True if the name is equal to or begins with prefix + */ + +bool name_StartsWith(const Name *name, const Name *prefix); + +/** + * return the name in string format (bitvector + segment number) + * + */ +char *name_ToString(const Name *name); + +/** + * @function message_setNameLen + * @abstract Sets a message name length + * @param [in] message - Interest message + * @param [in] len - Name length + */ +void name_setLen(const Name *name, uint8_t len); + +/** + * Creates a name from a Address + * + */ +Name *name_CreateFromAddress(address_type addressType, union commandAddr addr, + uint8_t len); + +#endif // name_h diff --git a/hicn-light/src/core/nameBitvector.c b/hicn-light/src/core/nameBitvector.c new file mode 100755 index 000000000..66f3eae20 --- /dev/null +++ b/hicn-light/src/core/nameBitvector.c @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/assert/parc_Assert.h> + +#include <src/core/messageHandler.h> +#include <src/core/nameBitvector.h> + +#include <parc/algol/parc_Hash.h> + +#include <src/utils/commands.h> + +#define BLOCKS 2 + +const uint64_t BLOCK_SIZE = 64; +const uint64_t WIDTH = 128; +const uint64_t BLOCK_ONE = 0x1; + +// the bits are encoded in the following order: +// 00100101001---101010 00100011---110100100 +// [bits[0] (uint64_t)] [bits[1] (uint64_t)] +// ^ ^ ^ ^ +// 0 63 64 127 +// address 2200::0011 is encoded as: +// 1000 1000 0000 0010 00000 ....0100 0100 +// ^ ^ +// 0 127 + +struct name_bitvector { + uint64_t bits[BLOCKS]; + uint8_t len; + uint8_t IPversion; +}; + +NameBitvector *nameBitvector_CreateFromInAddr(uint32_t s_addr, uint8_t len) { + NameBitvector *bitvector = parcMemory_AllocateAndClear(sizeof(NameBitvector)); + parcAssertNotNull(bitvector, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(NameBitvector)); + + bitvector->bits[0] = 0; + bitvector->bits[1] = 0; + + uint8_t addr_1 = (s_addr & 0xff000000) >> 24; + uint8_t addr_2 = (s_addr & 0x00ff0000) >> 16; + uint8_t addr_3 = (s_addr & 0x0000ff00) >> 8; + uint8_t addr_4 = (s_addr & 0x000000ff); + + bitvector->bits[1] = (bitvector->bits[1] | addr_4) << 8; + bitvector->bits[1] = (bitvector->bits[1] | addr_3) << 8; + bitvector->bits[1] = (bitvector->bits[1] | addr_2) << 8; + bitvector->bits[1] = (bitvector->bits[1] | addr_1); + bitvector->bits[1] = bitvector->bits[1] << 32; + + bitvector->len = len; + + bitvector->IPversion = IPv4_TYPE; + + return bitvector; +} + +NameBitvector *nameBitvector_CreateFromIn6Addr(struct in6_addr *addr, + uint8_t len) { + parcAssertNotNull(addr, "addr cannot be null"); + + NameBitvector *bitvector = parcMemory_AllocateAndClear(sizeof(NameBitvector)); + parcAssertNotNull(bitvector, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(NameBitvector)); + + bitvector->bits[0] = 0; + bitvector->bits[1] = 0; + + for (int i = 0; i < 8; ++i) { + bitvector->bits[1] = (bitvector->bits[1] << 8) | addr->s6_addr[i]; + } + + for (int i = 8; i < 16; ++i) { + bitvector->bits[0] = (bitvector->bits[0] << 8) | addr->s6_addr[i]; + } + + bitvector->len = len; + + bitvector->IPversion = IPv6_TYPE; + + return bitvector; +} + +NameBitvector *nameBitvector_CreateFromAddress(const Address *prefix, + uint8_t len) { + parcAssertNotNull(prefix, "prefix cannot be null"); + + NameBitvector *bitvector = NULL; + switch (addressGetType(prefix)) { + case ADDR_INET: { + struct sockaddr_in addr; + addressGetInet(prefix, &addr); + bitvector = nameBitvector_CreateFromInAddr(addr.sin_addr.s_addr, len); + break; + } + case ADDR_INET6: { + struct sockaddr_in6 addr; + addressGetInet6(prefix, &addr); + bitvector = nameBitvector_CreateFromIn6Addr(&addr.sin6_addr, len); + break; + } + default: + parcTrapNotImplemented("Unkown packet type"); + break; + } + + return bitvector; +} + +NameBitvector *nameBitvector_Copy(const NameBitvector *original) { + parcAssertNotNull(original, "original cannot be null"); + + NameBitvector *copy = parcMemory_AllocateAndClear(sizeof(NameBitvector)); + parcAssertNotNull(copy, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(NameBitvector)); + + copy->bits[0] = original->bits[0]; + copy->bits[1] = original->bits[1]; + copy->len = original->len; + + return copy; +} + +void nameBitvector_Destroy(NameBitvector **bitvectorPtr) { + parcAssertNotNull(bitvectorPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*bitvectorPtr, + "Parameter must dereference to non-null pointer"); + + NameBitvector *bv = *bitvectorPtr; + parcMemory_Deallocate((void **)&(bv)); + *bitvectorPtr = NULL; +} + +uint8_t nameBitvector_GetLength(const NameBitvector *name) { return name->len; } + +uint32_t nameBitvector_GetHash32(const NameBitvector *name) { + return parcHash32_Data_Cumulative((const uint8_t *)name->bits, 16, 0); +} + +bool nameBitvector_Equals(const NameBitvector *a, const NameBitvector *b) { + if (a->bits[0] == b->bits[0] && a->bits[1] == b->bits[1] && a->len == b->len) + return true; + return false; +} + +int nameBitvector_Compare(const NameBitvector *a, const NameBitvector *b) { + if (a == NULL && b == NULL) { + return 0; + } + if (a == NULL) { + return -1; + } + if (b == NULL) { + return +1; + } + + if (a->bits[0] < b->bits[0]) { + return -1; + } else if (a->bits[0] > b->bits[0]) { + return +1; + } else if (a->bits[1] < b->bits[1]) { + return -1; + } else if (a->bits[1] > b->bits[1]) { + return +1; + } else if (a->len < b->len) { + return -1; + } else if (a->len > b->len) { + return +1; + } else { + return 0; + } +} + +bool nameBitvector_StartsWith(const NameBitvector *name, + const NameBitvector *prefix) { + parcAssertNotNull(name, "name cannot be NULL"); + parcAssertNotNull(prefix, "prefix cannot be NULL"); + parcAssertTrue(prefix->len > 0, "prefix length can not be 0"); + + if (prefix->len > BLOCK_SIZE) + return (name->bits[1] == prefix->bits[1]) && + ((name->bits[0] ^ prefix->bits[0]) >> + (BLOCK_SIZE - (prefix->len - BLOCK_SIZE)) == + 0); + + return ((name->bits[1] ^ prefix->bits[1]) >> (BLOCK_SIZE - prefix->len) == 0); +} + +bool nameBitvector_testBit(const NameBitvector *name, uint8_t pos) { + if (pos == WIDTH) pos = 127; + + uint8_t final_pos = WIDTH - name->len; + + // the bit to test is inside the name/prefix len + if (pos > final_pos) { + return (name->bits[pos / BLOCK_SIZE] & (BLOCK_ONE << (pos % BLOCK_SIZE))); + } + + // the bit to test is outside the name/prefix len + if (pos < final_pos) { + return false; + } + + // pos is equal to the name/prefix len + return true; +} + +uint64_t _diff_bit_log2(uint64_t val) { + // base 2 log of an uint64_t. This is the same as get the position of + // the highest bit set (or most significant bit set, MSB) + uint64_t result = 0; + + if (val & 0xFFFFFFFF00000000) { + val = val >> 32; + result = result | 32; + } + if (val & 0xFFFF0000) { + val = val >> 16; + result = result | 16; + } + if (val & 0xFF00) { + val = val >> 8; + result = result | 8; + } + if (val & 0xF0) { + val = val >> 4; + result = result | 4; + } + if (val & 0xC) { + val = val >> 2; + result = result | 2; + } + if (val & 0x2) { + val = val >> 1; + result = result | 1; + } + return result; +} + +uint8_t nameBitvector_firstDiff(const NameBitvector *a, + const NameBitvector *b) { + uint8_t res = 0; + uint64_t diff = a->bits[1] ^ b->bits[1]; + if (diff) + res = 64 + _diff_bit_log2(diff); + else + res = _diff_bit_log2(a->bits[0] ^ b->bits[0]); + + // res is computed over the bitvector which is composed by 128 bit all the + // times however the prefixes may be diffrent just because the have different + // lengths example: prefix 1: 0::/30 prefix 2: 0::/20 at this point of the + // function res would be 0 since both the bitvectors are composed by 0s but the + // function will return 127-20, which is the position at which the two prefix + // are different, since prefix 2 has only 20 bits + + uint8_t len_diff; + if (a->len < b->len) + len_diff = WIDTH - a->len; + else + len_diff = WIDTH - b->len; + + if (len_diff > res) res = len_diff; + + return res; +} + +int nameBitvector_ToIPAddress(const NameBitvector *name, + ip_address_t *ip_address) { + if (name->IPversion == IPv4_TYPE) { + struct in_addr *addr = (struct in_addr *)(&ip_address->buffer); + ip_address->family = AF_INET; + ip_address->prefix_len = IPV4_ADDR_LEN_BITS; + + uint32_t tmp_addr = name->bits[1] >> 32ULL; + uint8_t addr_1 = (tmp_addr & 0xff000000) >> 24; + uint8_t addr_2 = (tmp_addr & 0x00ff0000) >> 16; + uint8_t addr_3 = (tmp_addr & 0x0000ff00) >> 8; + uint8_t addr_4 = (tmp_addr & 0x000000ff); + + addr->s_addr = 0; + addr->s_addr = (addr->s_addr | addr_4) << 8; + addr->s_addr = (addr->s_addr | addr_3) << 8; + addr->s_addr = (addr->s_addr | addr_2) << 8; + addr->s_addr = (addr->s_addr | addr_1); + + } else { + struct in6_addr *addr = (struct in6_addr *)(&ip_address->buffer); + ip_address->family = AF_INET6; + ip_address->prefix_len = name->len; // IPV6_ADDR_LEN_BITS; + + for (int i = 0; i < 8; i++) { + addr->s6_addr[i] = (uint8_t)((name->bits[1] >> 8 * (7 - i)) & 0xFF); + } + + int x = 0; + for (int i = 8; i < 16; ++i) { + addr->s6_addr[i] = (uint8_t)((name->bits[0] >> 8 * (7 - x)) & 0xFF); + x++; + } + } + return true; +} + +void nameBitvector_setLen(NameBitvector *name, uint8_t len) { name->len = len; } + +Address *nameBitvector_ToAddress(const NameBitvector *name) { + if (name->IPversion == IPv4_TYPE) { + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(1234); + + uint32_t tmp_addr = name->bits[1] >> 32ULL; + uint8_t addr_1 = (tmp_addr & 0xff000000) >> 24; + uint8_t addr_2 = (tmp_addr & 0x00ff0000) >> 16; + uint8_t addr_3 = (tmp_addr & 0x0000ff00) >> 8; + uint8_t addr_4 = (tmp_addr & 0x000000ff); + + addr.sin_addr.s_addr = 0; + addr.sin_addr.s_addr = (addr.sin_addr.s_addr | addr_4) << 8; + addr.sin_addr.s_addr = (addr.sin_addr.s_addr | addr_3) << 8; + addr.sin_addr.s_addr = (addr.sin_addr.s_addr | addr_2) << 8; + addr.sin_addr.s_addr = (addr.sin_addr.s_addr | addr_1); + + Address *packetAddr = addressCreateFromInet(&addr); + + return packetAddr; + + } else { + struct sockaddr_in6 addr; + addr.sin6_family = AF_INET6; + addr.sin6_port = htons(1234); + addr.sin6_scope_id = 0; + addr.sin6_flowinfo = 0; + + for (int i = 0; i < 8; i++) { + addr.sin6_addr.s6_addr[i] = + (uint8_t)((name->bits[1] >> 8 * (7 - i)) & 0xFF); + } + + int x = 0; + for (int i = 8; i < 16; ++i) { + addr.sin6_addr.s6_addr[i] = + (uint8_t)((name->bits[0] >> 8 * (7 - x)) & 0xFF); + x++; + } + + Address *packetAddr = addressCreateFromInet6(&addr); + + return packetAddr; + } +} + +char *nameBitvector_ToString(const NameBitvector *name) { + char *output = malloc(WIDTH); + + Address *packetAddr = nameBitvector_ToAddress(name); + + sprintf(output, "prefix: %s len: %u", addressToString(packetAddr), name->len); + + addressDestroy(&packetAddr); + + return output; +}
\ No newline at end of file diff --git a/hicn-light/src/core/nameBitvector.h b/hicn-light/src/core/nameBitvector.h new file mode 100755 index 000000000..28a31dc26 --- /dev/null +++ b/hicn-light/src/core/nameBitvector.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef name_bitvector_h +#define name_bitvector_h + +#include <hicn/hicn.h> +#include <stdint.h> +#include <stdlib.h> + +#include <src/utils/address.h> + +struct name_bitvector; +typedef struct name_bitvector NameBitvector; + +NameBitvector *nameBitvector_CreateFromInAddr(uint32_t s_addr, uint8_t len); + +NameBitvector *nameBitvector_CreateFromIn6Addr(struct in6_addr *addr, + uint8_t len); + +NameBitvector *nameBitvector_CreateFromAddress(const Address *prefix, + uint8_t len); + +NameBitvector *nameBitvector_Copy(const NameBitvector *original); + +void nameBitvector_Destroy(NameBitvector **bitvectorPtr); + +uint8_t nameBitvector_GetLength(const NameBitvector *name); + +uint32_t nameBitvector_GetHash32(const NameBitvector *name); + +bool nameBitvector_Equals(const NameBitvector *a, const NameBitvector *b); + +int nameBitvector_Compare(const NameBitvector *a, const NameBitvector *b); + +bool nameBitvector_StartsWith(const NameBitvector *name, + const NameBitvector *prefix); + +bool nameBitvector_testBit(const NameBitvector *name, uint8_t pos); + +uint8_t nameBitvector_firstDiff(const NameBitvector *a, const NameBitvector *b); + +int nameBitvector_ToIPAddress(const NameBitvector *name, + ip_address_t *ip_address); +void nameBitvector_setLen(NameBitvector *name, uint8_t len); + +Address *nameBitvector_ToAddress(const NameBitvector *name); + +char *nameBitvector_ToString(const NameBitvector *name); + +#endif // name_bitvector_h diff --git a/hicn-light/src/core/numberSet.c b/hicn-light/src/core/numberSet.c new file mode 100755 index 000000000..75fec1524 --- /dev/null +++ b/hicn-light/src/core/numberSet.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <parc/algol/parc_ArrayList.h> +#include <parc/algol/parc_Memory.h> +#include <src/config.h> +#include <src/core/numberSet.h> +#include <stdio.h> + +#include <parc/assert/parc_Assert.h> + +struct number_set { + Number *arrayOfNumbers; + size_t length; + size_t limit; + unsigned refcount; +}; + +static void numberSet_Expand(NumberSet *set); + +NumberSet *numberSet_Create() { + NumberSet *set = parcMemory_AllocateAndClear(sizeof(NumberSet)); + parcAssertNotNull(set, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(NumberSet)); + set->arrayOfNumbers = parcMemory_AllocateAndClear(sizeof(Number) * 16); + parcAssertNotNull((set->arrayOfNumbers), + "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Number) * 16); + set->length = 0; + set->limit = 16; + set->refcount = 1; + return set; +} + +NumberSet *numberSet_Acquire(const NumberSet *original) { + parcAssertNotNull(original, "Parameter original must be non-null"); + NumberSet *copy = (NumberSet *)original; + copy->refcount++; + return copy; +} + +void numberSet_Release(NumberSet **setPtr) { + parcAssertNotNull(setPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*setPtr, "Parameter must dereference to non-null pointer"); + + NumberSet *set = *setPtr; + parcAssertTrue( + set->refcount > 0, + "Invalid state: calling destroy on an object with 0 reference count"); + set->refcount--; + + if (set->refcount == 0) { + parcMemory_Deallocate((void **)&(set->arrayOfNumbers)); + parcMemory_Deallocate((void **)&set); + *setPtr = NULL; + } +} + +/** + * @function numberSet_AddNoChecks + * @abstract Add a number we know is not already in the set + * @discussion + * Used by other functions that already know the number is unique in the set, + * Does not do the expensive Contains check. + */ +static void numberSet_AddNoChecks(NumberSet *set, Number number) { + if (set->length == set->limit) { + numberSet_Expand(set); + } + + set->arrayOfNumbers[set->length] = number; + set->length++; +} + +bool numberSet_Add(NumberSet *set, Number number) { + parcAssertNotNull(set, "Parameter set must be non-null"); + if (numberSet_Contains(set, number)) { + return false; + } + + numberSet_AddNoChecks(set, number); + return true; +} + +size_t numberSet_Length(const NumberSet *set) { + parcAssertNotNull(set, "Parameter set must be non-null"); + return set->length; +} + +Number numberSet_GetItem(const NumberSet *set, size_t ordinalIndex) { + parcAssertNotNull(set, "Parameter set must be non-null"); + parcAssertTrue(ordinalIndex < set->length, + "Limit beyond end of set, length %zu got %zu", set->length, + ordinalIndex); + + return set->arrayOfNumbers[ordinalIndex]; +} + +bool numberSet_Contains(const NumberSet *set, Number number) { + parcAssertNotNull(set, "Parameter set must be non-null"); + for (size_t i = 0; i < set->length; i++) { + if (set->arrayOfNumbers[i] == number) { + return true; + } + } + return false; +} + +void numberSet_AddSet(NumberSet *destinationSet, const NumberSet *setToAdd) { + parcAssertNotNull(destinationSet, + "Parameter destinationSet must be non-null"); + parcAssertNotNull(setToAdd, "Parameter setToAdd must be non-null"); + + for (size_t i = 0; i < setToAdd->length; i++) { + numberSet_Add(destinationSet, setToAdd->arrayOfNumbers[i]); + } +} + +NumberSet *numberSet_Subtract(const NumberSet *minuend, + const NumberSet *subtrahend) { + // because the underlying ADT is not sorted, this is pretty ineffient, could + // be O(n^2). + + NumberSet *difference = numberSet_Create(); + + for (size_t i = 0; i < minuend->length; i++) { + bool unique = true; + for (size_t j = 0; j < subtrahend->length && unique; j++) { + if (minuend->arrayOfNumbers[i] == subtrahend->arrayOfNumbers[j]) { + unique = false; + } + } + + if (unique) { + numberSet_AddNoChecks(difference, minuend->arrayOfNumbers[i]); + } + } + return difference; +} + +bool numberSet_Equals(const NumberSet *a, const NumberSet *b) { + if (a == NULL && b == NULL) { + return true; + } + + if (a == NULL || b == NULL) { + return false; + } + + if (a->length == b->length) { + for (size_t i = 0; i < a->length; i++) { + bool found = false; + for (size_t j = 0; j < b->length && !found; j++) { + if (a->arrayOfNumbers[i] == b->arrayOfNumbers[j]) { + found = true; + } + } + if (!found) { + return false; + } + } + return true; + } + + return false; +} + +void numberSet_Remove(NumberSet *set, Number number) { + parcAssertNotNull(set, "Parameter set must be non-null"); + for (size_t i = 0; i < set->length; i++) { + if (set->arrayOfNumbers[i] == number) { + set->length--; + if (set->length > 0) { + // move the last element to the removed element to keep the array + // packed. + set->arrayOfNumbers[i] = set->arrayOfNumbers[set->length]; + } + return; + } + } +} + +// ===================================================== + +static void numberSet_Expand(NumberSet *set) { + size_t newlimit = set->limit * 2; + size_t newbytes = newlimit * sizeof(Number); + + set->arrayOfNumbers = parcMemory_Reallocate(set->arrayOfNumbers, newbytes); + set->limit = newlimit; +} diff --git a/hicn-light/src/core/numberSet.h b/hicn-light/src/core/numberSet.h new file mode 100755 index 000000000..91a965d7b --- /dev/null +++ b/hicn-light/src/core/numberSet.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief Stores a set of numbers. + * + * Useful for things like the reverse path of a PIT + * or the forward paths of a FIB. Does not allow duplicates. + * + */ + +#ifndef numberSet_h +#define numberSet_h + +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> + +struct number_set; +typedef struct number_set NumberSet; + +typedef uint32_t Number; + +/** + * @function numberList_Create + * @abstract A new list of numbers + */ +NumberSet *numberSet_Create(void); + +/** + * Obtains a reference counted copy of the original + * The reference count is increased by one. It must be released with + * NumberSet_Release(). + * @param [in] original An allocated NumberSet + * @return non-null The reference counted copy + */ +NumberSet *numberSet_Acquire(const NumberSet *original); + +/** + * Releases one reference count and destroys the memory after last release + * The pointer will be NULLed after release regardless if the memory was + * destroyed. + * @param [in,out] setPtr A pointer to a NumberSet. Will be NULL'd after + * release. + */ +void numberSet_Release(NumberSet **setPtr); + +/** + * @function numberList_Append + * @abstract Add a number to the end of the list + * @discussion + * No check for duplicates is done + * @return true if added, false if a duplicate + */ +bool numberSet_Add(NumberSet *set, Number number); + +/** + * @function numberList_Length + * @abstract The count of numbers in the list + */ +size_t numberSet_Length(const NumberSet *set); + +/** + * @function numberSet_GetItem + * @abstract Retrieves an item based on the ordinal index + * @discussion + * Will assert if the ordinalIndex is out of bounds. + */ +Number numberSet_GetItem(const NumberSet *set, size_t ordinalIndex); + +/** + * @function numberSet_Contains + * @abstract Checks for set membership + * @return true if the set contains the number, false otherwise + */ +bool numberSet_Contains(const NumberSet *set, Number number); + +/** + * @function numberSet_AddSet + * @abstract Adds one set to another set + * @discussion + * Adds <code>setToAdd</code> to <code>destinationSet</code> + * @return true if the set contains the number, false otherwise + */ +void numberSet_AddSet(NumberSet *destinationSet, const NumberSet *setToAdd); + +/** + * @function numberSet_Subtract + * @abstract Computes set difference <code>difference = minuend - + * subtrahend</code>, returns a new number set. + * @discussion + * <code>minuend</code> and <code>subtrahend</code> are not modified. A new + * difference set is created. + * + * Returns the elements in <code>minuend</code> that are not in + * <code>subtrahend</code>. + * + * @param minuend The set from which to subtract + * @param subrahend The set begin removed from minuend + * @return The set difference. May be empty, but will not be NULL. + */ +NumberSet *numberSet_Subtract(const NumberSet *minuend, + const NumberSet *subtrahend); + +/** + * Determine if two NumberSet instances are equal. + * + * Two NumberSet instances are equal if, and only if, + * they are the same size and contain the same elements. Empty sets are + * equal. NULL equals NULL, but does not equal non-NULL. + * + * The following equivalence relations on non-null `NumberSet` instances are + * maintained: + * + * * It is reflexive: for any non-null reference value x, `NumberSet_Equals(x, + * x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `numberSet_Equals(x, y)` must return true if and only if + * `numberSet_Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `numberSet_Equals(x, y)` returns true and + * `numberSet_Equals(y, z)` returns true, + * then `numberSet_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `numberSet_Equals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `numberSet_Equals(x, NULL)` must + * return false. + * + * @param a A pointer to a `NumberSet` instance. + * @param b A pointer to a `NumberSet` instance. + * @return true if the two `NumberSet` instances are equal. + */ +bool numberSet_Equals(const NumberSet *a, const NumberSet *b); + +/** + * @function numberSet_Remove + * @abstract Removes the number from the set + */ +void numberSet_Remove(NumberSet *set, Number number); +#endif // numberSet_h diff --git a/hicn-light/src/core/streamBuffer.c b/hicn-light/src/core/streamBuffer.c new file mode 100755 index 000000000..7aebb5edb --- /dev/null +++ b/hicn-light/src/core/streamBuffer.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> +#include <stdio.h> + +#include <parc/assert/parc_Assert.h> + +#include <src/core/streamBuffer.h> + +void streamBuffer_Destroy(PARCEventQueue **bufferPtr) { + parcAssertNotNull(bufferPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*bufferPtr, + "Parameter must dereference to non-null pointer"); + parcEventQueue_Destroy(bufferPtr); + *bufferPtr = NULL; +} + +void streamBuffer_SetWatermark(PARCEventQueue *buffer, bool setRead, + bool setWrite, size_t low, size_t high) { + parcAssertNotNull(buffer, "Parameter buffer must be non-null"); + + short flags = 0; + if (setRead) { + flags |= PARCEventType_Read; + } + + if (setWrite) { + flags |= PARCEventType_Write; + } + + parcEventQueue_SetWatermark(buffer, flags, low, high); +} + +int streamBuffer_Flush(PARCEventQueue *buffer, bool flushRead, + bool flushWrite) { + parcAssertNotNull(buffer, "Parameter buffer must be non-null"); + + short flags = 0; + if (flushRead) { + flags |= PARCEventType_Read; + } + + if (flushWrite) { + flags |= PARCEventType_Write; + } + + return parcEventQueue_Flush(buffer, flags); +} + +int streamBuffer_FlushCheckpoint(PARCEventQueue *buffer, bool flushRead, + bool flushWrite) { + parcAssertNotNull(buffer, "Parameter buffer must be non-null"); + + short flags = 0; + if (flushRead) { + flags |= PARCEventType_Read; + } + + if (flushWrite) { + flags |= PARCEventType_Write; + } + + return parcEventQueue_Flush(buffer, flags); +} + +int streamBuffer_FlushFinished(PARCEventQueue *buffer, bool flushRead, + bool flushWrite) { + parcAssertNotNull(buffer, "Parameter buffer must be non-null"); + + short flags = 0; + if (flushRead) { + flags |= PARCEventType_Read; + } + + if (flushWrite) { + flags |= PARCEventType_Write; + } + + return parcEventQueue_Flush(buffer, flags); +} + +void streamBuffer_SetCallbacks(PARCEventQueue *buffer, + PARCEventQueue_Callback *readCallback, + PARCEventQueue_Callback *writeCallback, + PARCEventQueue_EventCallback *eventCallback, + void *user_data) { + parcAssertNotNull(buffer, "Parameter buffer must be non-null"); + + parcEventQueue_SetCallbacks(buffer, readCallback, writeCallback, + eventCallback, user_data); +} + +void streamBuffer_EnableCallbacks(PARCEventQueue *buffer, bool enableRead, + bool enableWrite) { + parcAssertNotNull(buffer, "Parameter buffer must be non-null"); + short flags = 0; + if (enableRead) { + flags |= PARCEventType_Read; + } + if (enableWrite) { + flags |= PARCEventType_Write; + } + + parcEventQueue_Enable(buffer, flags); +} + +/** + * @function StreamBuffer_DisableCallbacks + * @abstract Disables specified callbacks. Does not affect others. + * @discussion + * Disables enabled callbacks. If a callback is already disabled, has no + * effect. A "false" value does not enable it. + * + * @param <#param1#> + * @return <#return#> + */ +void streamBuffer_DisableCallbacks(PARCEventQueue *buffer, bool disableRead, + bool disableWrite) { + parcAssertNotNull(buffer, "Parameter buffer must be non-null"); + short flags = 0; + if (disableRead) { + flags |= PARCEventType_Read; + } + if (disableWrite) { + flags |= PARCEventType_Write; + } + + parcEventQueue_Disable(buffer, flags); +} diff --git a/hicn-light/src/core/streamBuffer.h b/hicn-light/src/core/streamBuffer.h new file mode 100755 index 000000000..27e793176 --- /dev/null +++ b/hicn-light/src/core/streamBuffer.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Wrapper around event scheduler + */ + +#ifndef streamBuffer_h +#define streamBuffer_h + +#include <parc/algol/parc_EventQueue.h> +#include <stdbool.h> + +void streamBuffer_Destroy(PARCEventQueue **bufferPtr); + +/** + * @function streamBuffer_SetWatermark + * @abstract Sets the read and/or write watermarks + * @discussion + * For a read watermark, when there is at least <code>low</code> bytes + * available to read, the read callback will be fired. If the bytes in the + * buffer exceed <code>high</code>, the stream buffer will stop reading from the + * network. + * + * For a write watermark, when the bytes in the buffer fall below + * <code>low</code>, the write callback is fired. The <code>high</code> + * watermark limits stream filters and shapers from exceeding that threashold on + * what they write to the buffer. + * + */ +void streamBuffer_SetWatermark(PARCEventQueue *buffer, bool setRead, + bool setWrite, size_t low, size_t high); + +/** + * @function streamBuffer_Flush + * @abstract The buffer will read/write more data if available + * + * @return -1 error, 0 no more data, 1 more data + */ +int streamBuffer_Flush(PARCEventQueue *buffer, bool flushRead, bool flushWrite); + +/** + * @function streamBuffer_FlushCheckpoint + * @abstract Flushes the stream, checkpointing all data in the buffer + */ +int streamBuffer_FlushCheckpoint(PARCEventQueue *buffer, bool flushRead, + bool flushWrite); + +/** + * @function streamBuffer_FlushFinished + * @abstract Flush the stream and indicate the end of new data + */ +int streamBuffer_FlushFinished(PARCEventQueue *buffer, bool flushRead, + bool flushWrite); + +/** + * @typedef StreamBufferReadWriteCallback + * @abstract Callback when data is available or write space available + * @constant user_data opaque data passed to + * <code>StreamBuffer_SetCallbacks()</code> + */ +typedef void(StreamBufferReadWriteCallback)(PARCEventQueue *buffer, + void *user_data); + +/** + * @typedef StreamBufferEventCallback + * @abstract Callback on error or other event on the stream buffer + * @constant what logical or of STREAM events. STREAM_READING and + * STREAM_WRITING indicate if the error was on the read or write direction. The + * conditions may be STREAM_ERROR, STREAM_EOF, STREAM_TIMEOUT, or + * STREAM_CONNECTED. + * @constant user_data opaque data passed to + * <code>StreamBuffer_SetCallbacks()</code> + */ +typedef void(StreamBufferEventCallback)(PARCEventQueue *buffer, short what, + void *user_data); + +/** + * Changes the callbacks for a buffer event. + * + * @param bufev the buffer event object for which to change callbacks + * @param readcb callback to invoke when there is data to be read, or NULL if + * no callback is desired + * @param writecb callback to invoke when the file descriptor is ready for + * writing, or NULL if no callback is desired + * @param eventcb callback to invoke when there is an event on the file + * descriptor + * @param cbarg an argument that will be supplied to each of the callbacks + * (readcb, writecb, and errorcb) + * @see parcEventQueue_Create() + */ +void streamBuffer_SetCallbacks(PARCEventQueue *buffer, + PARCEventQueue_Callback *readCallback, + PARCEventQueue_Callback *writeCallback, + PARCEventQueue_EventCallback *eventCallback, + void *user_data); + +/** + * @function StreamBuffer_EnableCallbacks + * @abstract Enables specified callbacks. Does not affect others. + * @discussion + * Enables disabled callbacks. If a callback is already enabled, has no + * effect. A "false" value does not disable it. + */ +void streamBuffer_EnableCallbacks(PARCEventQueue *buffer, bool enableRead, + bool enableWrite); + +/** + * @function StreamBuffer_DisableCallbacks + * @abstract Disables specified callbacks. Does not affect others. + * @discussion + * Disables enabled callbacks. If a callback is already disabled, has no + * effect. A "false" value does not enable it. + */ +void streamBuffer_DisableCallbacks(PARCEventQueue *buffer, bool disableRead, + bool disableWrite); +#endif // streamBuffer_h diff --git a/hicn-light/src/core/system.h b/hicn-light/src/core/system.h new file mode 100755 index 000000000..3c5c8cba2 --- /dev/null +++ b/hicn-light/src/core/system.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @header system.h + * @abstract System-level properties + * @discussion + * <#Discussion#> + * + */ + +#ifndef system_h +#define system_h + +#include <src/core/forwarder.h> +#include <src/utils/interfaceSet.h> + +/** + * @function system_Interfaces + * @abstract The system network interfaces + */ +InterfaceSet *system_Interfaces(Forwarder *forwarder); + +/** + * Returns the MTU of the named interface + * + * @param [in] an allocated hicn-light forwarder + * @param [in] interfaceName The system interface name, e.g. "eth0" + * + * @return 0 Interface does not exist + * @return positive the MTU the kernel reports + * + */ +unsigned system_InterfaceMtu(Forwarder *forwarder, const char *interfaceName); + +/** + * Returns the LINK address of the specified interface + * + * @param [in] an allocated hicn-light forwarder + * @param [in] interfaceName The system interface name, e.g. "eth0" + * + * @retval non-null The MAC address of the interface + * @retval null The interface does not exist + * + */ +Address *system_GetMacAddressByName(Forwarder *forwarder, + const char *interfaceName); +#endif diff --git a/hicn-light/src/core/ticks.h b/hicn-light/src/core/ticks.h new file mode 100755 index 000000000..8750abde5 --- /dev/null +++ b/hicn-light/src/core/ticks.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief The router periodically measures time in units of Ticks + * + * See forwarder.c HZ which specifies the tick rate. forwarder.h has functions + * to convert between ticks and milliseconds. + * + */ +#ifndef ticks_h +#define ticks_h + +#define __STDC_FORMAT_MACROS +#include <stdint.h> + +typedef uint64_t Ticks; + +#endif // ticks_h diff --git a/hicn-light/src/core/wldr.c b/hicn-light/src/core/wldr.c new file mode 100755 index 000000000..b94ae76e5 --- /dev/null +++ b/hicn-light/src/core/wldr.c @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <parc/assert/parc_Assert.h> +#include <parc/logging/parc_LogReporterTextStdout.h> +#include <src/core/connection.h> +#include <src/core/forwarder.h> +#include <src/core/wldr.h> +#include <stdint.h> +#include <stdio.h> + +struct wldr_buffer { + Message *message; + uint8_t rtx_counter; +}; + +typedef struct wldr_buffer WldrBuffer; + +struct wldr_state { + uint16_t expected_label; + uint16_t next_label; + WldrBuffer *buffer[BUFFER_SIZE]; +}; + +Wldr *wldr_Init() { + Wldr *wldr = parcMemory_AllocateAndClear(sizeof(Wldr)); + parcAssertNotNull(wldr, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Wldr)); + wldr->expected_label = 1; + wldr->next_label = 1; + for (int i = 0; i < BUFFER_SIZE; i++) { + WldrBuffer *entry = parcMemory_AllocateAndClear(sizeof(WldrBuffer)); + parcAssertNotNull( + entry, + "WldrBuffer init: parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(WldrBuffer)); + entry->message = NULL; + entry->rtx_counter = 0; + wldr->buffer[i] = entry; + } + return wldr; +} + +void wldr_ResetState(Wldr *wldr) { + wldr->expected_label = 1; + wldr->next_label = 1; + for (int i = 0; i < BUFFER_SIZE; i++) { + wldr->buffer[i]->message = NULL; + wldr->buffer[i]->rtx_counter = 0; + } +} + +void wldr_Destroy(Wldr **wldrPtr) { + Wldr *wldr = *wldrPtr; + for (unsigned i = 0; i < BUFFER_SIZE; i++) { + if (wldr->buffer[i]->message != NULL) { + message_Release(&(wldr->buffer[i]->message)); + parcMemory_Deallocate((void **)&(wldr->buffer[i])); + } + } + parcMemory_Deallocate((void **)&wldr); + *wldrPtr = NULL; +} + +static void _wldr_RetransmitPacket(Wldr *wldr, const Connection *conn, + uint16_t label) { + if (wldr->buffer[label % BUFFER_SIZE]->message == NULL) { + // the required message for retransmission is not in the buffer + return; + } + + if (wldr->buffer[label % BUFFER_SIZE]->rtx_counter < MAX_RTX) { + Message *msg = wldr->buffer[label % BUFFER_SIZE]->message; + message_SetWldrLabel(msg, wldr->next_label); + + if (wldr->buffer[wldr->next_label % BUFFER_SIZE]->message != NULL) { + message_Release(&(wldr->buffer[wldr->next_label % BUFFER_SIZE]->message)); + } + + wldr->buffer[wldr->next_label % BUFFER_SIZE]->message = msg; + wldr->buffer[wldr->next_label % BUFFER_SIZE]->rtx_counter = + wldr->buffer[label % BUFFER_SIZE]->rtx_counter + 1; + message_Acquire(wldr->buffer[wldr->next_label % BUFFER_SIZE]->message); + wldr->next_label++; + connection_ReSend(conn, msg, false); + } +} + +static void _wldr_SendWldrNotificaiton(Wldr *wldr, const Connection *conn, + Message *message, uint16_t expected_lbl, + uint16_t received_lbl) { + // here we need to create a new packet that is used to send the wldr + // notification to the prevoius hop. the destionation address of the + // notification is the source address of the message for which we want to + // create a notification. in fact, if message is an interest the prevoius hop + // is identified by the src. if message is a data, we need to send the + // notification message with the content name has a source address in this way + // the message will be trapped by the pounting rules in the next hop We define + // the notification as an interest message so that the NAT in the send function + // will set the src address of the local connection. Notice that in this way + // the notification packet will be dispaced to the right connection at the next + // hop. + + Message *notification = + message_CreateWldrNotification(message, expected_lbl, received_lbl); + parcAssertNotNull(notification, "Got null from CreateWldrNotification"); + connection_ReSend(conn, notification, true); +} + +void wldr_SetLabel(Wldr *wldr, Message *message) { + // in this function we send the packet for the first time + // 1) we set the wldr label + message_SetWldrLabel(message, wldr->next_label); + + // 2) we store the pointer to packet in the buffer + if (wldr->buffer[wldr->next_label % BUFFER_SIZE]->message != NULL) { + // release an old message if necessary + message_Release(&(wldr->buffer[wldr->next_label % BUFFER_SIZE]->message)); + } + + // we need to acquire the message to avoid that it gets destroyed + message_Acquire(message); + + wldr->buffer[wldr->next_label % BUFFER_SIZE]->message = message; + wldr->buffer[wldr->next_label % BUFFER_SIZE]->rtx_counter = 0; + wldr->next_label++; + if (wldr->next_label == + 0) // we alwasy skip label 0 beacause it means that wldr is not active + wldr->next_label++; +} + +void wldr_DetectLosses(Wldr *wldr, const Connection *conn, Message *message) { + if (message_HasWldr(message)) { + // this is a normal wldr packet + uint16_t pkt_lbl = (uint16_t)message_GetWldrLabel(message); + if (pkt_lbl != wldr->expected_label) { + // if the received packet label is 1 and the expected packet label > + // pkt_lbl usually we are in the case where a remote note disconnected for + // a while and reconnected on this same connection, so the two nodes are + // out of synch for this reason we do not send any notification, we just + // synch the labels + + if ((pkt_lbl != 1) || (wldr->expected_label < pkt_lbl)) { + _wldr_SendWldrNotificaiton(wldr, conn, message, wldr->expected_label, + pkt_lbl); + } + + // here we always synch + wldr->expected_label = (uint16_t)(pkt_lbl + 1); + } else { + wldr->expected_label++; + if (wldr->expected_label == 0) + wldr->expected_label++; // for the next_label we want to skip 0 + } + } +} + +void wldr_HandleWldrNotification(Wldr *wldr, const Connection *conn, + Message *message) { + uint16_t expected_lbl = (uint16_t)message_GetWldrExpectedLabel(message); + uint16_t received_lbl = (uint16_t)message_GetWldrLastReceived(message); + if ((wldr->next_label - expected_lbl) > BUFFER_SIZE) { + // the packets are not in the buffer anymore + return; + } + while (expected_lbl < received_lbl) { + _wldr_RetransmitPacket(wldr, conn, expected_lbl); + expected_lbl++; + } +} diff --git a/hicn-light/src/core/wldr.h b/hicn-light/src/core/wldr.h new file mode 100755 index 000000000..1666b4d3f --- /dev/null +++ b/hicn-light/src/core/wldr.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef wldr_h +#define wldr_h + +#include <src/config.h> +#include <src/core/connection.h> +#include <src/core/message.h> + +#define BUFFER_SIZE 8192 +#define MAX_RTX 3 +#define WLDR_LBL 13 +#define WLDR_NOTIFICATION 14 +#define WLDR_UNKNOWN 15 + +// NORMAL PACKET or RETRASMISSION +// WLDR_LBL: label = window size in the TCP header +// NOTIFICATION +// WLDR_NOTIFICATION: expected_label = window size in the TCP header, +// last_received_label = urgent pointer in the TCP header +// ATTENTION!!! in order to detect a notificaiton the +// source and destination ports must be set to 0 + +struct wldr_state; +typedef struct wldr_state Wldr; + +Wldr *wldr_Init(); + +void wldr_Destroy(Wldr **wldrPtr); + +void wldr_ResetState(Wldr *wldr); + +void wldr_SetLabel(Wldr *wldr, Message *message); + +void wldr_DetectLosses(Wldr *wldr, const Connection *conn, Message *message); + +void wldr_HandleWldrNotification(Wldr *wldr, const Connection *conn, + Message *message); +#endif // wldr_h diff --git a/hicn-light/src/io/CMakeLists.txt b/hicn-light/src/io/CMakeLists.txt new file mode 100755 index 000000000..f65f0b580 --- /dev/null +++ b/hicn-light/src/io/CMakeLists.txt @@ -0,0 +1,53 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/addressPair.h + ${CMAKE_CURRENT_SOURCE_DIR}/ioOperations.h + ${CMAKE_CURRENT_SOURCE_DIR}/listener.h + ${CMAKE_CURRENT_SOURCE_DIR}/listenerSet.h + ${CMAKE_CURRENT_SOURCE_DIR}/tcpListener.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicnListener.h + ${CMAKE_CURRENT_SOURCE_DIR}/udpTunnel.h + ${CMAKE_CURRENT_SOURCE_DIR}/tcpTunnel.h + ${CMAKE_CURRENT_SOURCE_DIR}/udpConnection.h + ${CMAKE_CURRENT_SOURCE_DIR}/udpListener.h + ${CMAKE_CURRENT_SOURCE_DIR}/streamConnection.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicnTunnel.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicnConnection.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/addressPair.c + ${CMAKE_CURRENT_SOURCE_DIR}/ioOperations.c + ${CMAKE_CURRENT_SOURCE_DIR}/listenerSet.c + ${CMAKE_CURRENT_SOURCE_DIR}/streamConnection.c + ${CMAKE_CURRENT_SOURCE_DIR}/tcpListener.c + ${CMAKE_CURRENT_SOURCE_DIR}/tcpTunnel.c + ${CMAKE_CURRENT_SOURCE_DIR}/udpConnection.c + ${CMAKE_CURRENT_SOURCE_DIR}/udpListener.c + ${CMAKE_CURRENT_SOURCE_DIR}/udpTunnel.c +) + +if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + list(APPEND SOURCE_FILES + io/hicnTunnel.c + io/hicnConnection.c + io/hicnListener.c + ) +endif() + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE)
\ No newline at end of file diff --git a/hicn-light/src/io/addressPair.c b/hicn-light/src/io/addressPair.c new file mode 100755 index 000000000..5d2017a3d --- /dev/null +++ b/hicn-light/src/io/addressPair.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> +#include <stdio.h> + +#include <parc/algol/parc_Hash.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> +#include <parc/assert/parc_Assert.h> +#include <src/io/addressPair.h> + +struct address_pair { + Address *local; + Address *remote; +}; + +static void _addressPair_Destroy(AddressPair **addressPairPtr) { + AddressPair *pair = *addressPairPtr; + + addressDestroy(&pair->local); + addressDestroy(&pair->remote); +} + +parcObject_ExtendPARCObject(AddressPair, _addressPair_Destroy, NULL, + addressPair_ToString, addressPair_Equals, NULL, + addressPair_HashCode, NULL); + +parcObject_ImplementAcquire(addressPair, AddressPair); + +parcObject_ImplementRelease(addressPair, AddressPair); + +AddressPair *addressPair_Create(const Address *local, const Address *remote) { + parcAssertNotNull(local, "Parameter local must be non-null"); + parcAssertNotNull(remote, "Parameter remote must be non-null"); + + AddressPair *pair = parcObject_CreateInstance(AddressPair); + parcAssertNotNull(pair, "Got null from parcObject_Create()"); + + pair->local = addressCopy(local); + pair->remote = addressCopy(remote); + + return pair; +} + +bool addressPair_Equals(const AddressPair *a, const AddressPair *b) { + if (a == b) { + return true; + } + if (a == NULL || b == NULL) { + return false; + } + + if (addressEquals(a->local, b->local)) { + if (addressEquals(a->remote, b->remote)) { + return true; + } + } + + return false; +} + +bool addressPair_EqualsAddresses(const AddressPair *a, const Address *local, + const Address *remote) { + if (a == NULL || local == NULL || remote == NULL) { + return false; + } + + if (addressEquals(a->local, local)) { + if (addressEquals(a->remote, remote)) { + return true; + } + } + + return false; +} + +char *addressPair_ToString(const AddressPair *pair) { + parcAssertNotNull(pair, "Parameter pair must be non-null"); + + char *local = addressToString(pair->local); + char *remote = addressToString(pair->remote); + + char *output; + int failure = asprintf(&output, "{ .local=%s, .remote=%s }", local, remote); + parcAssertTrue(failure > -1, "Error on asprintf"); + + parcMemory_Deallocate((void **)&local); + parcMemory_Deallocate((void **)&remote); + + return output; +} + +const Address *addressPair_GetLocal(const AddressPair *pair) { + parcAssertNotNull(pair, "Parameter pair must be non-null"); + return pair->local; +} + +const Address *addressPair_GetRemote(const AddressPair *pair) { + parcAssertNotNull(pair, "Parameter pair must be non-null"); + return pair->remote; +} + +/** + * @function addressPair_HashCode + * @abstract Hash useful for tables. Consistent with Equals. + * @discussion + * Returns a non-cryptographic hash that is consistent with equals. That is, + * if a == b, then hash(a) == hash(b). + * + */ +PARCHashCode addressPair_HashCode(const AddressPair *pair) { + PARCHashCode hashpair[2]; + hashpair[0] = addressHashCode(pair->local); + hashpair[1] = addressHashCode(pair->remote); + return parcHashCode_Hash((const uint8_t *)hashpair, sizeof(hashpair)); +} diff --git a/hicn-light/src/io/addressPair.h b/hicn-light/src/io/addressPair.h new file mode 100755 index 000000000..5152267b6 --- /dev/null +++ b/hicn-light/src/io/addressPair.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Used to identify a connection between a specific local address and + * a specific remote address. + */ + +#ifndef address_Pair_h +#define address_Pair_h + +#include <src/utils/address.h> + +struct address_pair; +typedef struct address_pair AddressPair; + +/** + * @function addressPair_Create + * @abstract Creates and address pair. There is no restriction on the address + * types. + * @discussion + * Creates an ordered pair of addresses, where the first is considered the + * "local" address and the second is the "remote" address. Those designations + * are purely a convention used to name them, and does not imply any specifici + * types of operations. + * + * The two addresses may be of any address types (e.g. IPv4, IPv6, Local, + * Ethernet). However, some functions that use an AddressPair may require that + * the local and remote addresses be the same type. + * + */ +AddressPair *addressPair_Create(const Address *local, const Address *remote); + +/** + * Returns a reference counted copy of the address pair + * + * Increments the reference count and returns the same address pair + * + * @param [in] addressPair An allocated address pair + * + * @retval non-null A reference counted copy + * @retval null An error + */ +AddressPair *addressPair_Acquire(const AddressPair *addressPair); + +/** + * Releases a reference count to the object + * + * Decrements the reference count and destroys the object when it reaches 0. + */ +void addressPair_Release(AddressPair **pairPtr); + +/** + * Determine if two AddressPair instances are equal. + * + * Two AddressPair instances are equal if, and only if, the local and remote + * addresses are identical. Equality is determined by addressEquals(a->local, + * b->local) and Adress_Equals(a->remote, b->remote). + * + * The following equivalence relations on non-null `AddressPair` instances are + * maintained: + * + * * It is reflexive: for any non-null reference value x, + * `AddressPair_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `addressPair_Equals(x, y)` must return true if and only if + * `addressPair_Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `addressPair_Equals(x, y)` returns true and + * `addressPair_Equals(y, z)` returns true, + * then `addressPair_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `addressPair_Equals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `addressPair_Equals(x, NULL)` must + * return false. + * + * @param a A pointer to a `AddressPair` instance. + * @param b A pointer to a `AddressPair` instance. + * @return true if the two `AddressPair` instances are equal. + */ +bool addressPair_Equals(const AddressPair *a, const AddressPair *b); + +/** + * @function addressPair_EqualsAddresses + * @abstract As AddressEquals, but "b" is broken out + * @discussion + * Equality is determined by addressEquals(a->local, local) and + * Adress_Equals(a->remote, remote). + */ +bool addressPair_EqualsAddresses(const AddressPair *a, const Address *local, + const Address *remote); + +const Address *addressPair_GetLocal(const AddressPair *pair); + +const Address *addressPair_GetRemote(const AddressPair *pair); + +/** + * @function addressPair_HashCode + * @abstract Hash useful for tables. Consistent with Equals. + * @discussion + * Returns a non-cryptographic hash that is consistent with equals. That is, + * if a == b, then hash(a) == hash(b). + */ +PARCHashCode addressPair_HashCode(const AddressPair *pair); + +/** + * @function addressPair_ToString + * @abstract Human readable string representation. Caller must use free(3). + */ +char *addressPair_ToString(const AddressPair *pair); +#endif // address_Pair_h diff --git a/hicn-light/src/io/hicnConnection.c b/hicn-light/src/io/hicnConnection.c new file mode 100755 index 000000000..85cf50921 --- /dev/null +++ b/hicn-light/src/io/hicnConnection.c @@ -0,0 +1,517 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Embodies the reader/writer for a HIcn connection + * + * NB The Send() function may overflow the output buffer + * + */ + +#include <errno.h> +#include <src/config.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <src/core/message.h> +#include <src/io/hicnConnection.h> + +#include <src/core/messageHandler.h> + +#include <parc/algol/parc_Hash.h> +#include <parc/algol/parc_Memory.h> +#include <parc/assert/parc_Assert.h> +#include <src/core/connection.h> +#include <src/core/forwarder.h> + +typedef struct hicn_state { + Forwarder *forwarder; + Logger *logger; + + // the hicn listener socket we receive packets on + int hicnListenerSocket; + + AddressPair *addressPair; + + // We need to access this all the time, so grab it out + // of the addressPair; + struct sockaddr *peerAddress; + socklen_t peerAddressLength; + + struct sockaddr *localAddress; + socklen_t localAddressLength; + + // this address contains one of the content names reachable + // throught the connection peer. We need this address beacuse it is + // the only way we have to conntact the next hop, since the main tun + // does not have address. Notice that a connection that sends probes + // is a connection that sends interest. In a "data" connection this + // value will remain NULL. We refresh the content address every time + // we send a probe, in this way we don't need to waste to much time in + // copy the address, but we can also react to the routing changes + struct sockaddr *probeDestAddress; + socklen_t probeDestAddressLength; + bool refreshProbeDestAddress; + + bool isLocal; + bool isUp; + unsigned id; + + unsigned delay; +} _HicnState; + +// Prototypes +static bool _send(IoOperations *ops, const Address *nexthop, Message *message); +static const Address *_getRemoteAddress(const IoOperations *ops); +static const AddressPair *_getAddressPair(const IoOperations *ops); +static unsigned _getConnectionId(const IoOperations *ops); +static bool _isUp(const IoOperations *ops); +static bool _isLocal(const IoOperations *ops); +static void _destroy(IoOperations **opsPtr); +static list_connections_type _getConnectionType(const IoOperations *ops); +static Ticks _sendProbe(IoOperations *ops, unsigned probeType, + uint8_t *message); + +/* + * This assigns a unique pointer to the void * which we use + * as a GUID for this class. + */ +static const void *_ioOperationsGuid = __FILE__; + +/* + * Return our GUID + */ +static const void *_streamConnection_Class(const IoOperations *ops) { + return _ioOperationsGuid; +} + +static IoOperations _template = {.closure = NULL, + .send = &_send, + .getRemoteAddress = &_getRemoteAddress, + .getAddressPair = &_getAddressPair, + .getConnectionId = &_getConnectionId, + .isUp = &_isUp, + .isLocal = &_isLocal, + .destroy = &_destroy, + .class = &_streamConnection_Class, + .getConnectionType = &_getConnectionType, + .sendProbe = &_sendProbe}; + +// ================================================================= + +static void _setConnectionState(_HicnState *HIcn, bool isUp); +static bool _saveSockaddr(_HicnState *hicnConnState, const AddressPair *pair); +static void _refreshProbeDestAddress(_HicnState *hicnConnState, + const uint8_t *message); + +IoOperations *hicnConnection_Create(Forwarder *forwarder, int fd, + const AddressPair *pair, bool isLocal) { + IoOperations *io_ops = NULL; + + _HicnState *hicnConnState = parcMemory_AllocateAndClear(sizeof(_HicnState)); + parcAssertNotNull(hicnConnState, + "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(_HicnState)); + + hicnConnState->forwarder = forwarder; + hicnConnState->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + + bool saved = _saveSockaddr(hicnConnState, pair); + if (saved) { + hicnConnState->hicnListenerSocket = fd; + hicnConnState->id = forwarder_GetNextConnectionId(forwarder); + hicnConnState->addressPair = addressPair_Acquire(pair); + hicnConnState->isLocal = isLocal; + + // allocate a connection + io_ops = parcMemory_AllocateAndClear(sizeof(IoOperations)); + parcAssertNotNull(io_ops, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(IoOperations)); + memcpy(io_ops, &_template, sizeof(IoOperations)); + io_ops->closure = hicnConnState; + + _setConnectionState(hicnConnState, true); + + if (logger_IsLoggable(hicnConnState->logger, LoggerFacility_IO, + PARCLogLevel_Info)) { + char *str = addressPair_ToString(hicnConnState->addressPair); + logger_Log(hicnConnState->logger, LoggerFacility_IO, PARCLogLevel_Info, + __func__, + "HIcnConnection %p created for address %s (isLocal %d)", + (void *)hicnConnState, str, hicnConnState->isLocal); + free(str); + } + + messenger_Send( + forwarder_GetMessenger(forwarder), + missive_Create(MissiveType_ConnectionCreate, hicnConnState->id)); + messenger_Send(forwarder_GetMessenger(forwarder), + missive_Create(MissiveType_ConnectionUp, hicnConnState->id)); + } else { + // _saveSockaddr will already log an error, no need for extra log message + // here + logger_Release(&hicnConnState->logger); + parcMemory_Deallocate((void **)&hicnConnState); + } + + return io_ops; +} + +// ================================================================= +// I/O Operations implementation + +static void _destroy(IoOperations **opsPtr) { + parcAssertNotNull(opsPtr, "Parameter opsPtr must be non-null double pointer"); + parcAssertNotNull(*opsPtr, + "Parameter opsPtr must dereference to non-null pointer"); + + IoOperations *ops = *opsPtr; + parcAssertNotNull(ioOperations_GetClosure(ops), + "ops->context must not be null"); + + _HicnState *hicnConnState = (_HicnState *)ioOperations_GetClosure(ops); + addressPair_Release(&hicnConnState->addressPair); + parcMemory_Deallocate((void **)&(hicnConnState->peerAddress)); + parcMemory_Deallocate((void **)&(hicnConnState->localAddress)); + if (hicnConnState->probeDestAddress != NULL) + parcMemory_Deallocate((void **)&(hicnConnState->probeDestAddress)); + + messenger_Send( + forwarder_GetMessenger(hicnConnState->forwarder), + missive_Create(MissiveType_ConnectionDestroyed, hicnConnState->id)); + + if (logger_IsLoggable(hicnConnState->logger, LoggerFacility_IO, + PARCLogLevel_Info)) { + logger_Log(hicnConnState->logger, LoggerFacility_IO, PARCLogLevel_Info, + __func__, "HIcnConnection %p destroyed", (void *)hicnConnState); + } + + // XXX + // do not close hicListenerSocket, the listener will close + // that when its done + // should I say something to libhicn? + + logger_Release(&hicnConnState->logger); + parcMemory_Deallocate((void **)&hicnConnState); + parcMemory_Deallocate((void **)&ops); + + *opsPtr = NULL; +} + +static bool _isUp(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _HicnState *hicnConnState = + (const _HicnState *)ioOperations_GetClosure(ops); + return hicnConnState->isUp; +} + +static bool _isLocal(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _HicnState *hicnConnState = + (const _HicnState *)ioOperations_GetClosure(ops); + return hicnConnState->isLocal; +} + +static const Address *_getRemoteAddress(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _HicnState *hicnConnState = + (const _HicnState *)ioOperations_GetClosure(ops); + return addressPair_GetRemote(hicnConnState->addressPair); +} + +static const AddressPair *_getAddressPair(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _HicnState *hicnConnState = + (const _HicnState *)ioOperations_GetClosure(ops); + return hicnConnState->addressPair; +} + +static unsigned _getConnectionId(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _HicnState *hicnConnState = + (const _HicnState *)ioOperations_GetClosure(ops); + return hicnConnState->id; +} + +/** + * @function hicnConnection_Send + * @abstract Non-destructive send of the message. + * @discussion + * sends a message to the peer. + * + * @param dummy is ignored. . + * @return <#return#> + */ +static bool _send(IoOperations *ops, const Address *dummy, Message *message) { + parcAssertNotNull(ops, "Parameter ops must be non-null"); + parcAssertNotNull(message, "Parameter message must be non-null"); + _HicnState *hicnConnState = (_HicnState *)ioOperations_GetClosure(ops); + + // NAT for HICN + // XXX + if (message_GetType(message) == MessagePacketType_ContentObject) { + // this is a data packet. We need to put the remote address in the + // destination field + + if (messageHandler_GetIPPacketType(message_FixedHeader(message)) == + IPv6_TYPE) { + messageHandler_SetDestination_IPv6( + (uint8_t *)message_FixedHeader(message), + &((struct sockaddr_in6 *)hicnConnState->peerAddress)->sin6_addr); + } else { + messageHandler_SetDestination_IPv4( + (uint8_t *)message_FixedHeader(message), + &(((struct sockaddr_in *)hicnConnState->peerAddress) + ->sin_addr.s_addr)); + } + } else if (message_GetType(message) == MessagePacketType_Interest) { + // this si an interest packet. We need to put the local address in the + // source field + if (messageHandler_GetIPPacketType(message_FixedHeader(message)) == + IPv6_TYPE) { + messageHandler_SetSource_IPv6( + (uint8_t *)message_FixedHeader(message), + &((struct sockaddr_in6 *)hicnConnState->localAddress)->sin6_addr); + } else { + messageHandler_SetSource_IPv4( + (uint8_t *)message_FixedHeader(message), + &(((struct sockaddr_in *)hicnConnState->localAddress) + ->sin_addr.s_addr)); + } + + // only in this case we may need to set the probeDestAddress + if (hicnConnState->refreshProbeDestAddress) { + _refreshProbeDestAddress(hicnConnState, message_FixedHeader(message)); + } + + } else if (message_GetType(message) == MessagePacketType_WldrNotification) { + // here we don't need to do anything for now + } else { + // unkown packet + if (logger_IsLoggable(hicnConnState->logger, LoggerFacility_IO, + PARCLogLevel_Debug)) { + logger_Log(hicnConnState->logger, LoggerFacility_IO, PARCLogLevel_Debug, + __func__, "connid %u can't parse the message", + hicnConnState->id); + } + return false; + } + + ssize_t writeLength = + write(hicnConnState->hicnListenerSocket, message_FixedHeader(message), + message_Length(message)); + + if (writeLength < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return false; + } else { + // this print is for debugging + printf("Incorrect write length %zd, expected %zd: (%d) %s\n", writeLength, + message_Length(message), errno, strerror(errno)); + return false; + } + } + + return true; +} + +static list_connections_type _getConnectionType(const IoOperations *ops) { + return CONN_HICN; +} + +static Ticks _sendProbe(IoOperations *ops, unsigned probeType, + uint8_t *message) { + parcAssertNotNull(ops, "Parameter ops must be non-null"); + _HicnState *hicnConnState = (_HicnState *)ioOperations_GetClosure(ops); + + if ((hicnConnState->peerAddressLength == sizeof(struct sockaddr_in)) || + (hicnConnState->localAddressLength == sizeof(struct sockaddr_in))) + return false; + + if (hicnConnState->probeDestAddress == NULL && + probeType == PACKET_TYPE_PROBE_REPLY) { + uint8_t *pkt = parcMemory_AllocateAndClear( + messageHandler_GetICMPPacketSize(IPv6_TYPE)); + messageHandler_SetProbePacket( + pkt, probeType, + (struct in6_addr *)messageHandler_GetDestination(message), + (struct in6_addr *)messageHandler_GetSource(message)); + + ssize_t writeLength = write(hicnConnState->hicnListenerSocket, pkt, + messageHandler_GetICMPPacketSize(IPv6_TYPE)); + + parcMemory_Deallocate((void **)&pkt); + + if (writeLength < 0) { + return 0; + } + + } else if (hicnConnState->probeDestAddress != NULL && + probeType == PACKET_TYPE_PROBE_REQUEST) { + hicnConnState->refreshProbeDestAddress = true; + + uint8_t *pkt = parcMemory_AllocateAndClear( + messageHandler_GetICMPPacketSize(IPv6_TYPE)); + messageHandler_SetProbePacket( + pkt, probeType, + &((struct sockaddr_in6 *)hicnConnState->localAddress)->sin6_addr, + &((struct sockaddr_in6 *)hicnConnState->probeDestAddress)->sin6_addr); + + ssize_t writeLength = write(hicnConnState->hicnListenerSocket, pkt, + messageHandler_GetICMPPacketSize(IPv6_TYPE)); + + parcMemory_Deallocate((void **)&pkt); + + if (writeLength < 0) { + return 0; + } + + } else { + if (hicnConnState->probeDestAddress == NULL && + probeType == PACKET_TYPE_PROBE_REQUEST) { + // this happen for the first probe + hicnConnState->refreshProbeDestAddress = true; + } + // do nothing + return 0; + } + + return forwarder_GetTicks(hicnConnState->forwarder); +} + +// ================================================================= +// Internal API + +static bool _saveSockaddr(_HicnState *hicnConnState, const AddressPair *pair) { + bool success = false; + const Address *remoteAddress = addressPair_GetRemote(pair); + const Address *localAddress = addressPair_GetLocal(pair); + switch (addressGetType(remoteAddress)) { // local must be of the same type + + case ADDR_INET: { + size_t bytes = sizeof(struct sockaddr_in); + hicnConnState->peerAddress = parcMemory_Allocate(bytes); + parcAssertNotNull(hicnConnState->peerAddress, + "parcMemory_Allocate(%zu) returned NULL", bytes); + + addressGetInet(remoteAddress, + (struct sockaddr_in *)hicnConnState->peerAddress); + hicnConnState->peerAddressLength = (socklen_t)bytes; + + hicnConnState->localAddress = parcMemory_Allocate(bytes); + parcAssertNotNull(hicnConnState->localAddress, + "parcMemory_Allocate(%zu) returned NULL", bytes); + + addressGetInet(localAddress, + (struct sockaddr_in *)hicnConnState->localAddress); + hicnConnState->localAddressLength = (socklen_t)bytes; + + hicnConnState->probeDestAddress = NULL; + hicnConnState->probeDestAddressLength = (socklen_t)bytes; + hicnConnState->refreshProbeDestAddress = false; + + success = true; + break; + } + + case ADDR_INET6: { + size_t bytes = sizeof(struct sockaddr_in6); + hicnConnState->peerAddress = parcMemory_Allocate(bytes); + parcAssertNotNull(hicnConnState->peerAddress, + "parcMemory_Allocate(%zu) returned NULL", bytes); + + addressGetInet6(remoteAddress, + (struct sockaddr_in6 *)hicnConnState->peerAddress); + hicnConnState->peerAddressLength = (socklen_t)bytes; + + hicnConnState->localAddress = parcMemory_Allocate(bytes); + parcAssertNotNull(hicnConnState->localAddress, + "parcMemory_Allocate(%zu) returned NULL", bytes); + + addressGetInet6(localAddress, + (struct sockaddr_in6 *)hicnConnState->localAddress); + hicnConnState->localAddressLength = (socklen_t)bytes; + + hicnConnState->probeDestAddress = NULL; + hicnConnState->probeDestAddressLength = (socklen_t)bytes; + hicnConnState->refreshProbeDestAddress = false; + + success = true; + break; + } + + default: + if (logger_IsLoggable(hicnConnState->logger, LoggerFacility_IO, + PARCLogLevel_Error)) { + char *str = addressToString(remoteAddress); + logger_Log(hicnConnState->logger, LoggerFacility_IO, PARCLogLevel_Error, + __func__, "Remote address is not INET or INET6: %s", str); + parcMemory_Deallocate((void **)&str); + } + break; + } + return success; +} + +static void _refreshProbeDestAddress(_HicnState *hicnConnState, + const uint8_t *message) { + if ((hicnConnState->peerAddressLength == sizeof(struct sockaddr_in)) || + (hicnConnState->localAddressLength == sizeof(struct sockaddr_in))) + return; + + if (hicnConnState->probeDestAddress == NULL) { + hicnConnState->probeDestAddress = + parcMemory_AllocateAndClear(sizeof(struct sockaddr_in6)); + parcAssertNotNull(hicnConnState->probeDestAddress, + "parcMemory_Allocate(%zu) returned NULL", + sizeof(struct sockaddr_in6)); + } + + ((struct sockaddr_in6 *)hicnConnState->probeDestAddress)->sin6_family = + AF_INET6; + ((struct sockaddr_in6 *)hicnConnState->probeDestAddress)->sin6_port = + htons(1234); + ((struct sockaddr_in6 *)hicnConnState->probeDestAddress)->sin6_scope_id = 0; + ((struct sockaddr_in6 *)hicnConnState->probeDestAddress)->sin6_flowinfo = 0; + ((struct sockaddr_in6 *)hicnConnState->probeDestAddress)->sin6_addr = + *((struct in6_addr *)messageHandler_GetDestination(message)); + hicnConnState->refreshProbeDestAddress = false; +} + +static void _setConnectionState(_HicnState *hicnConnState, bool isUp) { + parcAssertNotNull(hicnConnState, "Parameter HICN must be non-null"); + + Messenger *messenger = forwarder_GetMessenger(hicnConnState->forwarder); + + bool oldStateIsUp = hicnConnState->isUp; + hicnConnState->isUp = isUp; + + if (oldStateIsUp && !isUp) { + // bring connection DOWN + Missive *missive = + missive_Create(MissiveType_ConnectionDown, hicnConnState->id); + messenger_Send(messenger, missive); + return; + } + + if (!oldStateIsUp && isUp) { + // bring connection UP + Missive *missive = + missive_Create(MissiveType_ConnectionUp, hicnConnState->id); + messenger_Send(messenger, missive); + return; + } +} diff --git a/hicn-light/src/io/hicnConnection.h b/hicn-light/src/io/hicnConnection.h new file mode 100755 index 000000000..757e534ba --- /dev/null +++ b/hicn-light/src/io/hicnConnection.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file hicnConnection.h + * @brief Represents a HIcn connection for the connection table + * + * <#Detailed Description#> + * + */ + +#ifndef hicnConnection_h +#define hicnConnection_h + +#include <src/core/forwarder.h> +#include <src/io/addressPair.h> +#include <src/io/ioOperations.h> +#include <src/utils/address.h> + +/** + * Creates a HIcn connection that can send to the remote address + * + * The address pair must both be same type (i.e. INET or INET6). + * + * @param [in] an allocated hicn-light Forwarder (saves reference) + * @param [in] fd The socket to use + * @param [in] pair An allocated address pair for the connection (saves + * reference) + * @param [in] isLocal determines if the remote address is on the current system + * + * @retval non-null An allocated Io operations + * @retval null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +IoOperations *hicnConnection_Create(Forwarder *forwarder, int fd, + const AddressPair *pair, bool isLocal); +#endif // hicnConnection_h diff --git a/hicn-light/src/io/hicnListener.c b/hicn-light/src/io/hicnListener.c new file mode 100755 index 000000000..161f5b317 --- /dev/null +++ b/hicn-light/src/io/hicnListener.c @@ -0,0 +1,725 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <fcntl.h> +#include <src/config.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <unistd.h> + +#include <src/io/hicnConnection.h> +#include <src/io/hicnListener.h> + +#include <src/core/connection.h> +#include <src/core/connectionTable.h> +#include <src/core/forwarder.h> +#ifdef WITH_MAPME +#include <src/config/symbolicNameTable.h> +#include <src/core/mapMe.h> +#include <src/core/message.h> +#include <src/io/hicnTunnel.h> +#endif /* WITH_MAPME */ +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Network.h> +#include <parc/assert/parc_Assert.h> +#include <src/core/mapMe.h> +#include <src/core/messagePacketType.h> +#include <src/io/listener.h> +#include <src/socket/api.h> + +#define IPv6 6 +#define IPv4 4 +#define MTU_SIZE 1500 // bytes +#define MAX_HICN_RETRY 5 + +struct hicn_listener { + Forwarder *forwarder; + Logger *logger; + + PARCEvent *hicn_event; + int hicn_fd; // this is the file descriptor got from hicn library + + Address *localAddress; // this is the local address or 0::0 in case of the + // main listener this is the address used inside + // forwarder to identify the listener. Notice that this + // address is the same as the fisical interfaces on + // which we create the TUN. it is NOT the TUN address + // which is given by libhicn after the bind operation + // However the user alway uses this address since is + // the only one available at configuration time + + unsigned inetFamily; + + int connection_id; // this is used only if the listener is used to receive + // data packets we assume that 1 connection is associated + // to one listener in this case so we set the connection_id + // we the connection is create. if this id is not set and a + // data packet is received, the packet is dropped + + unsigned conn_id; +}; + +static void _destroy(ListenerOps **listenerOpsPtr); +static unsigned _getInterfaceIndex(const ListenerOps *ops); +static const Address *_getListenAddress(const ListenerOps *ops); +static EncapType _getEncapType(const ListenerOps *ops); +static int _getSocket(const ListenerOps *ops); + +static ListenerOps _hicnTemplate = {.context = NULL, + .destroy = &_destroy, + .getInterfaceIndex = &_getInterfaceIndex, + .getListenAddress = &_getListenAddress, + .getEncapType = &_getEncapType, + .getSocket = &_getSocket}; + +static void _hicnListener_readcb(int fd, PARCEventType what, void *hicnVoid); + +static bool _isEmptyAddressIPv6(Address *address) { + struct sockaddr_in6 *addr6 = + parcMemory_AllocateAndClear(sizeof(struct sockaddr_in6)); + parcAssertNotNull(addr6, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(addr6)); + + addressGetInet6(address, addr6); + + bool res = true; + for (int i = 0; i < 16; ++i) { + if (addr6->sin6_addr.s6_addr[i] != 0) { + res = false; + } + } + + parcMemory_Deallocate((void **)&addr6); + + return res; +} + +static bool _isEmptyAddressIPv4(Address *address) { + bool res = false; + + if (strcmp("inet4://0.0.0.0:1234", addressToString(address)) == 0) res = true; + return res; +} + +ListenerOps *hicnListener_CreateInet(Forwarder *forwarder, char *symbolic, + Address *address) { + HIcnListener *hicn = parcMemory_AllocateAndClear(sizeof(HIcnListener)); + parcAssertNotNull(hicn, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(HIcnListener)); + + hicn->forwarder = forwarder; + hicn->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + + hicn->conn_id = forwarder_GetNextConnectionId(forwarder); + hicn->localAddress = addressCopy(address); + + hicn->inetFamily = IPv4; + + hicn->connection_id = -1; + + hicn_socket_helper_t *hicnSocketHelper = + forwarder_GetHIcnSocketHelper(forwarder); + + if (_isEmptyAddressIPv4(address)) { + hicn->hicn_fd = hicn_socket(hicnSocketHelper, symbolic, NULL); + } else { + struct sockaddr_in *tmpAddr = + parcMemory_AllocateAndClear(sizeof(struct sockaddr_in)); + parcAssertNotNull(tmpAddr, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(tmpAddr)); + addressGetInet(address, tmpAddr); + char *local_addr = parcMemory_AllocateAndClear(INET_ADDRSTRLEN); + inet_ntop(AF_INET, &(tmpAddr->sin_addr), local_addr, INET_ADDRSTRLEN); + parcMemory_Deallocate((void **)&tmpAddr); + + hicn->hicn_fd = hicn_socket(hicnSocketHelper, symbolic, local_addr); + + parcMemory_Deallocate((void **)&local_addr); + } + + if (hicn->hicn_fd < 0) { + if (logger_IsLoggable(hicn->logger, LoggerFacility_IO, + PARCLogLevel_Debug)) { + logger_Log( + hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "HIcnListener %s: error while creating an hicn listener in lib_hicn", + symbolic); + } + logger_Release(&hicn->logger); + addressDestroy(&hicn->localAddress); + parcMemory_Deallocate((void **)&hicn); + return NULL; + } + + // Set non-blocking flag + int flags = fcntl(hicn->hicn_fd, F_GETFL, NULL); + parcAssertTrue(flags != -1, + "fcntl failed to obtain file descriptor flags (%d)", errno); + int failure = fcntl(hicn->hicn_fd, F_SETFL, flags | O_NONBLOCK); + parcAssertFalse(failure, "fcntl failed to set file descriptor flags (%d)", + errno); + + hicn->hicn_event = dispatcher_CreateNetworkEvent( + forwarder_GetDispatcher(forwarder), true, _hicnListener_readcb, + (void *)hicn, hicn->hicn_fd); + dispatcher_StartNetworkEvent(forwarder_GetDispatcher(forwarder), + hicn->hicn_event); + + ListenerOps *ops = parcMemory_AllocateAndClear(sizeof(ListenerOps)); + parcAssertNotNull(ops, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ListenerOps)); + + memcpy(ops, &_hicnTemplate, sizeof(ListenerOps)); + ops->context = hicn; + + if (logger_IsLoggable(hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + logger_Log(hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "HIcnListener %s created", symbolic); + } + + return ops; + return NULL; +} + +ListenerOps *hicnListener_CreateInet6(Forwarder *forwarder, char *symbolic, + Address *address) { + HIcnListener *hicn = parcMemory_AllocateAndClear(sizeof(HIcnListener)); + parcAssertNotNull(hicn, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(HIcnListener)); + + hicn->forwarder = forwarder; + hicn->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + + hicn->conn_id = forwarder_GetNextConnectionId(forwarder); + hicn->localAddress = addressCopy(address); + + hicn->inetFamily = IPv6; + + hicn->connection_id = -1; + + // the call to libhicn is the same both for the main and the normal listeners + // in both cases we need to set only the identifier. In the case of normal + // listener (listener for data packet) we let the library select the right ip + //address we just need to set the right type of packet + + hicn_socket_helper_t *hicnSocketHelper = + forwarder_GetHIcnSocketHelper(forwarder); + + if (_isEmptyAddressIPv6(address)) { + // create main listener + hicn->hicn_fd = hicn_socket(hicnSocketHelper, symbolic, NULL); + } else { + // create listener for the connetion + struct sockaddr_in6 *tmpAddr = + parcMemory_AllocateAndClear(sizeof(struct sockaddr_in6)); + + parcAssertNotNull(tmpAddr, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(tmpAddr)); + addressGetInet6(address, tmpAddr); + + char *local_addr = parcMemory_AllocateAndClear(INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, &(tmpAddr->sin6_addr), local_addr, INET6_ADDRSTRLEN); + + parcMemory_Deallocate((void **)&tmpAddr); + + hicn->hicn_fd = hicn_socket(hicnSocketHelper, symbolic, local_addr); + + parcMemory_Deallocate((void **)&local_addr); + } + + if (hicn->hicn_fd < 0) { + if (logger_IsLoggable(hicn->logger, LoggerFacility_IO, + PARCLogLevel_Debug)) { + logger_Log( + hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "HIcnListener %s: error while creating an hicn listener in lib_hicn", + symbolic); + } + logger_Release(&hicn->logger); + addressDestroy(&hicn->localAddress); + parcMemory_Deallocate((void **)&hicn); + return NULL; + } + + // Set non-blocking flag + int flags = fcntl(hicn->hicn_fd, F_GETFL, NULL); + parcAssertTrue(flags != -1, + "fcntl failed to obtain file descriptor flags (%d)", errno); + int failure = fcntl(hicn->hicn_fd, F_SETFL, flags | O_NONBLOCK); + parcAssertFalse(failure, "fcntl failed to set file descriptor flags (%d)", + errno); + + hicn->hicn_event = dispatcher_CreateNetworkEvent( + forwarder_GetDispatcher(forwarder), true, _hicnListener_readcb, + (void *)hicn, hicn->hicn_fd); + dispatcher_StartNetworkEvent(forwarder_GetDispatcher(forwarder), + hicn->hicn_event); + + ListenerOps *ops = parcMemory_AllocateAndClear(sizeof(ListenerOps)); + parcAssertNotNull(ops, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ListenerOps)); + + memcpy(ops, &_hicnTemplate, sizeof(ListenerOps)); + ops->context = hicn; + + if (logger_IsLoggable(hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + logger_Log(hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "HIcnListener %s created", symbolic); + } + + return ops; +} + +bool _hicnListener_BindInet6(ListenerOps *ops, const Address *remoteAddress) { + HIcnListener *hicn = (HIcnListener *)ops->context; + hicn_socket_helper_t *hicnSocketHelper = + forwarder_GetHIcnSocketHelper(hicn->forwarder); + + struct sockaddr_in6 *tmpAddr = + parcMemory_AllocateAndClear(sizeof(struct sockaddr_in6)); + parcAssertNotNull(tmpAddr, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(tmpAddr)); + addressGetInet6(remoteAddress, tmpAddr); + char *remote_addr = parcMemory_AllocateAndClear(INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, &(tmpAddr->sin6_addr), remote_addr, INET6_ADDRSTRLEN); + parcMemory_Deallocate((void **)&tmpAddr); + + int res = hicn_bind(hicnSocketHelper, hicn->hicn_fd, remote_addr); + + bool result = false; + if (res < 0) { + if (logger_IsLoggable(hicn->logger, LoggerFacility_IO, + PARCLogLevel_Debug)) { + logger_Log(hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "hicn_bild failed %d %s", res, hicn_socket_strerror(res)); + } + } else { + result = true; + } + + parcMemory_Deallocate((void **)&remote_addr); + + return result; +} + +bool _hicnListener_BindInet(ListenerOps *ops, const Address *remoteAddress) { + HIcnListener *hicn = (HIcnListener *)ops->context; + hicn_socket_helper_t *hicnSocketHelper = + forwarder_GetHIcnSocketHelper(hicn->forwarder); + + struct sockaddr_in *tmpAddr = + parcMemory_AllocateAndClear(sizeof(struct sockaddr_in)); + parcAssertNotNull(tmpAddr, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(tmpAddr)); + addressGetInet(remoteAddress, tmpAddr); + char *remote_addr = parcMemory_AllocateAndClear(INET_ADDRSTRLEN); + inet_ntop(AF_INET, &(tmpAddr->sin_addr), remote_addr, INET_ADDRSTRLEN); + parcMemory_Deallocate((void **)&tmpAddr); + + int res = hicn_bind(hicnSocketHelper, hicn->hicn_fd, remote_addr); + bool result = false; + + if (res < 0) { + if (logger_IsLoggable(hicn->logger, LoggerFacility_IO, + PARCLogLevel_Debug)) { + logger_Log(hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "hicn_bild failed %d %s", res, hicn_socket_strerror(res)); + } + } else { + result = true; + } + + parcMemory_Deallocate((void **)&remote_addr); + + return result; +} + +bool hicnListener_Bind(ListenerOps *ops, const Address *remoteAddress) { + if (addressGetType(remoteAddress) == ADDR_INET) { + return _hicnListener_BindInet(ops, remoteAddress); + } else if (addressGetType(remoteAddress) == ADDR_INET6) { + return _hicnListener_BindInet6(ops, remoteAddress); + } else { + printf("Bind failed: Invalid address\n"); + return false; + } +} + +bool hicnListener_Punting(ListenerOps *ops, const char *prefix) { + HIcnListener *hicn = (HIcnListener *)ops->context; + hicn_socket_helper_t *hicnSocketHelper = + forwarder_GetHIcnSocketHelper(hicn->forwarder); + + int res = hicn_listen(hicnSocketHelper, hicn->hicn_fd, prefix); + int retry = 0; + + while (res < 0 && retry < MAX_HICN_RETRY) { + sleep(1); + res = hicn_listen(hicnSocketHelper, hicn->hicn_fd, prefix); + retry++; + } + + if (res < 0) { + if (logger_IsLoggable(hicn->logger, LoggerFacility_IO, + PARCLogLevel_Debug)) { + logger_Log(hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "hicn_listen failed %d %s", res, hicn_socket_strerror(res)); + } + return false; + } + + return true; +} + +bool hicnListener_SetConnectionId(ListenerOps *ops, unsigned connId) { + HIcnListener *hicn = (HIcnListener *)ops->context; + if (hicn) { + hicn->connection_id = connId; + return true; + } + return false; +} + +static void _hicnListener_Destroy(HIcnListener **listenerPtr) { + parcAssertNotNull(listenerPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*listenerPtr, + "Parameter must derefernce to non-null pointer"); + + HIcnListener *hicn = *listenerPtr; + + // close(hicn->hicn_fd); //XXX close the fd in the hicnlib (detroy listener?) + dispatcher_DestroyNetworkEvent(forwarder_GetDispatcher(hicn->forwarder), + &hicn->hicn_event); + logger_Release(&hicn->logger); + addressDestroy(&hicn->localAddress); + parcMemory_Deallocate((void **)&hicn); + *listenerPtr = NULL; +} + +static void _destroy(ListenerOps **listenerOpsPtr) { + ListenerOps *ops = *listenerOpsPtr; + HIcnListener *hicn = (HIcnListener *)ops->context; + _hicnListener_Destroy(&hicn); + parcMemory_Deallocate((void **)&ops); + *listenerOpsPtr = NULL; +} + +static unsigned _getInterfaceIndex(const ListenerOps *ops) { + HIcnListener *hicn = (HIcnListener *)ops->context; + return hicn->conn_id; +} + +static const Address *_getListenAddress(const ListenerOps *ops) { + HIcnListener *hicn = (HIcnListener *)ops->context; + return hicn->localAddress; +} + +static EncapType _getEncapType(const ListenerOps *ops) { return ENCAP_HICN; } + +static int _getSocket(const ListenerOps *ops) { + HIcnListener *hicn = (HIcnListener *)ops->context; + return hicn->hicn_fd; +} + +// =============================== + +static void _readFrameToDiscard(HIcnListener *hicn, int fd) { + // we need to discard the frame. Read 1 byte. This will clear it off the + // stack. + uint8_t buffer; + int nread = read(fd, &buffer, 1); + + if (nread > 0) { + if (logger_IsLoggable(hicn->logger, LoggerFacility_IO, + PARCLogLevel_Debug)) { + logger_Log(hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "Discarded frame from fd %d", fd); + } + } else if (nread < 0) { + printf("Error trying to discard frame from fd %d: (%d) %s", fd, errno, + strerror(errno)); + if (logger_IsLoggable(hicn->logger, LoggerFacility_IO, + PARCLogLevel_Error)) { + logger_Log(hicn->logger, LoggerFacility_IO, PARCLogLevel_Error, __func__, + "Error trying to discard frame from fd %d: (%d) %s", fd, errno, + strerror(errno)); + } + } +} + +static unsigned _createNewConnection(HIcnListener *hicn, int fd, + const AddressPair *pair) { + bool isLocal = false; + + // udpConnection_Create takes ownership of the pair + IoOperations *ops = hicnConnection_Create(hicn->forwarder, fd, pair, isLocal); + Connection *conn = connection_Create(ops); + + connectionTable_Add(forwarder_GetConnectionTable(hicn->forwarder), conn); + unsigned connid = ioOperations_GetConnectionId(ops); + + return connid; +} + +const Connection *_findConnectionFromPacket(HIcnListener *hicn, + Address *packetSourceAddress) { + const Connection *conn = NULL; + if (hicn->connection_id != -1) { + conn = connectionTable_FindById( + forwarder_GetConnectionTable(hicn->forwarder), hicn->connection_id); + } else { + if (packetSourceAddress != NULL) { + // in this first check we try to retrieve the standard connection + // generated by the hicn-light + AddressPair *pair = + addressPair_Create(hicn->localAddress, packetSourceAddress); + conn = connectionTable_FindByAddressPair( + forwarder_GetConnectionTable(hicn->forwarder), pair); + addressPair_Release(&pair); + } + } + + return conn; +} + +static Address *_createAddressFromPacket(uint8_t *msgBuffer) { + Address *packetAddr = NULL; + if (messageHandler_GetIPPacketType(msgBuffer) == IPv6_TYPE) { + struct sockaddr_in6 addr_in6; + addr_in6.sin6_family = AF_INET6; + addr_in6.sin6_port = htons(1234); + addr_in6.sin6_flowinfo = 0; + addr_in6.sin6_scope_id = 0; + memcpy(&addr_in6.sin6_addr, + (struct in6_addr *)messageHandler_GetSource(msgBuffer), 16); + packetAddr = addressCreateFromInet6(&addr_in6); + } else if (messageHandler_GetIPPacketType(msgBuffer) == IPv4_TYPE) { + struct sockaddr_in addr_in; + addr_in.sin_family = AF_INET; + addr_in.sin_port = htons(1234); + memcpy(&addr_in.sin_addr, + (struct in_addr *)messageHandler_GetSource(msgBuffer), 4); + packetAddr = addressCreateFromInet(&addr_in); + } + return packetAddr; +} + +static void _handleProbeMessage(HIcnListener *hicn, uint8_t *msgBuffer) { + Address *packetAddr = _createAddressFromPacket(msgBuffer); + + if (packetAddr != NULL) { + const Connection *conn = _findConnectionFromPacket(hicn, packetAddr); + if (conn != NULL) { + // we drop all the probes for a connection that does not exists + connection_HandleProbe((Connection *)conn, msgBuffer, + forwarder_GetTicks(hicn->forwarder)); + } + } + + addressDestroy(&packetAddr); + parcMemory_Deallocate((void **)&msgBuffer); +} + +static void _handleWldrNotification(HIcnListener *hicn, uint8_t *msgBuffer) { + Address *packetAddr = _createAddressFromPacket(msgBuffer); + + if (packetAddr == NULL) { + parcMemory_Deallocate((void **)&msgBuffer); + return; + } + + const Connection *conn = _findConnectionFromPacket(hicn, packetAddr); + if (conn == NULL) { + addressDestroy(&packetAddr); + return; + } + + addressDestroy(&packetAddr); + + Message *message = message_CreateFromByteArray( + connection_GetConnectionId(conn), msgBuffer, + MessagePacketType_WldrNotification, forwarder_GetTicks(hicn->forwarder), + forwarder_GetLogger(hicn->forwarder)); + + connection_HandleWldrNotification((Connection *)conn, message); + + message_Release(&message); +} + +#ifdef WITH_MAPME +static void _handleMapMe(HIcnListener *hicn, int fd, uint8_t *msgBuffer) { + Address *packetAddr = _createAddressFromPacket(msgBuffer); + + if (packetAddr == NULL) { + parcMemory_Deallocate((void **)&msgBuffer); + return; + } + + const Connection *conn = _findConnectionFromPacket(hicn, packetAddr); + unsigned conn_id; + if (conn == NULL) { + /* Unlike the interest path, we don't create virtual connections bound + * on the listener, whose only interest is to send data, but full + * tunnels to be able to route interests + * + * packetAddr is the remote address, we need to ask the lib for our + * local address + * hicn->localAddress is None as the interest is received by the main + * listener. + */ + printf("MapMe, connection did not exist, creating\n"); + + /* Populate remote_address through packetAddr */ + struct sockaddr_in6 sockaddr; // XXX IPv6 only + addressGetInet6(packetAddr, &sockaddr); + ip_address_t remote_address = {.family = AF_INET6, + .prefix_len = IPV6_ADDR_LEN_BITS}; + memcpy(&remote_address.buffer, &sockaddr.sin6_addr, + ip_address_len(&remote_address)); + + /* Get local address through libhicn */ + ip_address_t local_address; + int rc = hicn_get_local_address(&remote_address, &local_address); + if (rc < 0) { + printf("Error getting local address. Discarded mapme packet.\n"); + return; + } + + struct sockaddr_in6 addr_in6; + addr_in6.sin6_family = AF_INET6; + addr_in6.sin6_port = htons(1234); + addr_in6.sin6_flowinfo = 0; + addr_in6.sin6_scope_id = 0; + memcpy(&addr_in6.sin6_addr, (struct in6_addr *)&(local_address.buffer), 16); + + Address *localAddr = addressCreateFromInet6(&addr_in6); + IoOperations *ops = + hicnTunnel_Create(hicn->forwarder, localAddr, packetAddr); + + if (!ops) { + printf("Error creating tunnel. Discarded mapme packet.\n"); + return; + } + + conn = connection_Create(ops); + + connectionTable_Add(forwarder_GetConnectionTable(hicn->forwarder), + (Connection *)conn); + } + conn_id = connection_GetConnectionId(conn); + + addressDestroy(&packetAddr); + + forwarder_ProcessMapMe(hicn->forwarder, msgBuffer, conn_id); +} +#endif /* WITH_MAPME */ + +static Message *_readMessage(HIcnListener *hicn, int fd, uint8_t *msgBuffer) { + Message *message = NULL; + + ssize_t readLength = read(fd, msgBuffer, MTU_SIZE); + + if (readLength < 0) { + printf("read failed %d: (%d) %s\n", fd, errno, strerror(errno)); + return message; + } + + size_t packetLength = messageHandler_GetTotalPacketLength(msgBuffer); + + if (readLength != packetLength) { + parcMemory_Deallocate((void **)&msgBuffer); + return message; + } + + if (messageHandler_IsTCP(msgBuffer)) { + MessagePacketType pktType; + unsigned connid = 0; + if (messageHandler_IsData(msgBuffer)) { + pktType = MessagePacketType_ContentObject; + if (hicn->connection_id == -1) { + parcMemory_Deallocate((void **)&msgBuffer); + return message; + } else { + connid = hicn->connection_id; + } + } else if (messageHandler_IsInterest(msgBuffer)) { + // notice that the connections for the interest (the one that we create at + // run time) uses as a local address 0::0, so the main tun + pktType = MessagePacketType_Interest; + Address *packetAddr = _createAddressFromPacket(msgBuffer); + const Connection *conn = _findConnectionFromPacket(hicn, packetAddr); + + if (conn == NULL) { + AddressPair *pair = addressPair_Create(hicn->localAddress, packetAddr); + connid = _createNewConnection(hicn, fd, pair); + addressPair_Release(&pair); + } else { + connid = connection_GetConnectionId(conn); + } + addressDestroy(&packetAddr); + } else { + printf("Got a packet that is not a data nor an interest, drop it!\n"); + parcMemory_Deallocate((void **)&msgBuffer); + return message; + } + + message = message_CreateFromByteArray(connid, msgBuffer, pktType, + forwarder_GetTicks(hicn->forwarder), + forwarder_GetLogger(hicn->forwarder)); + if (message == NULL) { + parcMemory_Deallocate((void **)&msgBuffer); + } + } else if (messageHandler_IsWldrNotification(msgBuffer)) { + _handleWldrNotification(hicn, msgBuffer); + } else if (messageHandler_IsLoadBalancerProbe(msgBuffer)) { + _handleProbeMessage(hicn, msgBuffer); + } +#ifdef WITH_MAPME + else if (mapMe_isMapMe(msgBuffer)) { + /* This function triggers the handling of the MAP-Me message, and we + * will return NULL so as to terminate the processing of this + * msgBuffer. */ + _handleMapMe(hicn, fd, msgBuffer); + } +#endif /* WITH_MAPME */ + + return message; +} + +static void _receivePacket(HIcnListener *hicn, int fd) { + Message *msg = NULL; + uint8_t *msgBuffer = parcMemory_AllocateAndClear(MTU_SIZE); + msg = _readMessage(hicn, fd, msgBuffer); + + if (msg) { + forwarder_Receive(hicn->forwarder, msg); + } +} + +static void _hicnListener_readcb(int fd, PARCEventType what, void *hicnVoid) { + HIcnListener *hicn = (HIcnListener *)hicnVoid; + + if (hicn->inetFamily == IPv4 || hicn->inetFamily == IPv6) { + if (what & PARCEventType_Read) { + _receivePacket(hicn, fd); + } + } else { + _readFrameToDiscard(hicn, fd); + } +} diff --git a/hicn-light/src/io/hicnListener.h b/hicn-light/src/io/hicnListener.h new file mode 100755 index 000000000..98c5387c9 --- /dev/null +++ b/hicn-light/src/io/hicnListener.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file hicnListener.h + * @brief Listens for in coming HIcn connections + * + * + */ + +#ifndef hicnListener_h +#define hicnListener_h + +#include <src/core/forwarder.h> +#include <src/core/messageHandler.h> +#include <src/io/listener.h> +#include <stdlib.h> + +struct hicn_listener; +typedef struct hicn_listener HIcnListener; + +ListenerOps *hicnListener_CreateInet(Forwarder *forwarder, char *symbolic, + Address *address); +ListenerOps *hicnListener_CreateInet6(Forwarder *forwarder, char *symbolic, + Address *address); +bool hicnListener_Punting(ListenerOps *ops, const char *prefix); +bool hicnListener_Bind(ListenerOps *ops, const Address *remoteAddress); +bool hicnListener_SetConnectionId(ListenerOps *ops, unsigned connId); +// const Address *hicnListener_GetTunAddress(const ListenerOps *ops); +#endif // hicnListener_h diff --git a/hicn-light/src/io/hicnTunnel.c b/hicn-light/src/io/hicnTunnel.c new file mode 100755 index 000000000..e55393137 --- /dev/null +++ b/hicn-light/src/io/hicnTunnel.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <src/config.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> + +#include <parc/assert/parc_Assert.h> +#include <src/io/hicnConnection.h> +#include <src/io/hicnListener.h> +#include <src/io/hicnTunnel.h> + +IoOperations *hicnTunnel_CreateOnListener(Forwarder *forwarder, + ListenerOps *localListener, + const Address *remoteAddress) { + parcAssertNotNull(forwarder, "Parameter hicn-light must be non-null"); + parcAssertNotNull(localListener, "Parameter localListener must be non-null"); + parcAssertNotNull(remoteAddress, "Parameter remoteAddress must be non-null"); + + Logger *logger = forwarder_GetLogger(forwarder); + + IoOperations *ops = NULL; + if (localListener->getEncapType(localListener) == ENCAP_HICN) { + const Address *localAddress = + localListener->getListenAddress(localListener); + address_type localType = addressGetType(localAddress); + address_type remoteType = addressGetType(remoteAddress); + + if (localType == remoteType) { + bool res = hicnListener_Bind(localListener, remoteAddress); + if (res == false) { + if (logger_IsLoggable(logger, LoggerFacility_IO, PARCLogLevel_Error)) { + logger_Log(logger, LoggerFacility_IO, PARCLogLevel_Error, __func__, + "Unable to bind local listener to remote node"); + } + return ops; + } + + // localAddress = hicnListener_GetTunAddress(localListener); //This is the + // true local address + + AddressPair *pair = addressPair_Create(localAddress, remoteAddress); + bool isLocal = false; + int fd = localListener->getSocket(localListener); + ops = hicnConnection_Create(forwarder, fd, pair, isLocal); + + addressPair_Release(&pair); + } else { + if (logger_IsLoggable(logger, LoggerFacility_IO, PARCLogLevel_Error)) { + logger_Log(logger, LoggerFacility_IO, PARCLogLevel_Error, __func__, + "Local listener of type %s and remote type %s, cannot " + "establish tunnel", + addressTypeToString(localType), + addressTypeToString(remoteType)); + } + } + } else { + if (logger_IsLoggable(logger, LoggerFacility_IO, PARCLogLevel_Error)) { + logger_Log(logger, LoggerFacility_IO, PARCLogLevel_Error, __func__, + "Local listener %p is not type UDP, cannot establish tunnel", + (void *)localListener); + } + } + + return ops; +} + +IoOperations *hicnTunnel_Create(Forwarder *forwarder, + const Address *localAddress, + const Address *remoteAddress) { + ListenerSet *set = forwarder_GetListenerSet(forwarder); + ListenerOps *listener = listenerSet_Find(set, ENCAP_HICN, localAddress); + IoOperations *ops = NULL; + if (listener) { + ops = hicnTunnel_CreateOnListener(forwarder, listener, remoteAddress); + } else { + if (logger_IsLoggable(forwarder_GetLogger(forwarder), LoggerFacility_IO, + PARCLogLevel_Error)) { + char *str = addressToString(localAddress); + logger_Log(forwarder_GetLogger(forwarder), LoggerFacility_IO, + PARCLogLevel_Error, __func__, + "Could not find listener to match address %s", str); + parcMemory_Deallocate((void **)&str); + } + } + + if (ops) { + hicnListener_SetConnectionId(listener, ops->getConnectionId(ops)); + } + + return ops; +} diff --git a/hicn-light/src/io/hicnTunnel.h b/hicn-light/src/io/hicnTunnel.h new file mode 100755 index 000000000..70295797c --- /dev/null +++ b/hicn-light/src/io/hicnTunnel.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file hicnTunnel.h + * @brief Establish a tunnel to a remote system + * + * Creates a "hicn tunnel" to a remote system. There must already be a local + * HICN listener for the local side of the connection. + * + */ + +#ifndef hicnTunnel_h +#define hicnTunnel_h + +#include <src/core/forwarder.h> +#include <src/io/ioOperations.h> +#include <src/io/listener.h> +#include <src/utils/address.h> + +/** + * Establishes a connection to a remote system over HICN + * + * The remoteAddress must be of the same type (i.e. v4 or v6) as the + * localAddress. There must be an existing HICN listener on the local address. + * If either of these are not true, will return NULL. + * + * The connection will go in the table immediately, and will be in the "up" + * state. + * + * @param [in] an allocated hicn-light Forwarder + * @param [in] localAddress The local IP address and port to use for the + * connection + * @param [in] remote Address the remote IP address for the connection, must + * include a destination port. + * + * @retval non-null An allocated Io Operations structure for the connection + * @retval null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +IoOperations *hicnTunnel_Create(Forwarder *forwarder, + const Address *localAddress, + const Address *remoteAddress); + +IoOperations *hicnTunnel_CreateOnListener(Forwarder *forwarder, + ListenerOps *localListener, + const Address *remoteAddress); + +#endif // hicnTunnel_h diff --git a/hicn-light/src/io/ioOperations.c b/hicn-light/src/io/ioOperations.c new file mode 100755 index 000000000..bbc8cec91 --- /dev/null +++ b/hicn-light/src/io/ioOperations.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <parc/assert/parc_Assert.h> +#include <src/config.h> +#include <src/io/ioOperations.h> +#include <stdio.h> + +void *ioOperations_GetClosure(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter ops must be non-null"); + return ops->closure; +} + +bool ioOperations_Send(IoOperations *ops, const Address *nexthop, + Message *message) { + return ops->send(ops, nexthop, message); +} + +const Address *ioOperations_GetRemoteAddress(const IoOperations *ops) { + return ops->getRemoteAddress(ops); +} + +const AddressPair *ioOperations_GetAddressPair(const IoOperations *ops) { + return ops->getAddressPair(ops); +} + +bool ioOperations_IsUp(const IoOperations *ops) { return ops->isUp(ops); } + +bool ioOperations_IsLocal(const IoOperations *ops) { return ops->isLocal(ops); } + +unsigned ioOperations_GetConnectionId(const IoOperations *ops) { + return ops->getConnectionId(ops); +} + +void ioOperations_Release(IoOperations **opsPtr) { + IoOperations *ops = *opsPtr; + ops->destroy(opsPtr); +} + +const void *ioOperations_Class(const IoOperations *ops) { + return ops->class(ops); +} + +list_connections_type ioOperations_GetConnectionType(const IoOperations *ops) { + return ops->getConnectionType(ops); +} + +Ticks ioOperations_SendProbe(IoOperations *ops, unsigned probeType, + uint8_t *message) { + return ops->sendProbe(ops, probeType, message); +} diff --git a/hicn-light/src/io/ioOperations.h b/hicn-light/src/io/ioOperations.h new file mode 100755 index 000000000..dee66030d --- /dev/null +++ b/hicn-light/src/io/ioOperations.h @@ -0,0 +1,394 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Defines the interface all connections use to communicate with the forwarder. + * + * @code + * + * static IoOperations _template = { + * .closure = NULL, + * .send = &_etherConnection_Send, + * .getRemoteAddress = &_etherConnection_GetRemoteAddress, + * .getAddressPair = &_etherConnection_GetAddressPair, + * .getConnectionId = &_etherConnection_GetConnectionId, + * .isUp = &_etherConnection_IsUp, + * .isLocal = &_etherConnection_IsLocal, + * .destroy = &_etherConnection_DestroyOperations, + * .class = &_etherConnection_Class, + * .getConnectionType = &_etherConnection_getConnectionType + * }; + * + * IoOperations * + * etherConnection_Create(Forwarder *forwarder, GenericEther *ether, + * AddressPair *pair) + * { + * _EtherState *etherConnState = parcMemory_Allocate(sizeof(_EtherState)); + * // Fill in etherConnState with instance variables + * + * IoOperations *io_ops = parcMemory_Allocate(sizeof(IoOperations)); + * memcpy(io_ops, &_template, sizeof(IoOperations)); + * io_ops->closure = etherConnState; + * // Add to connection table, send missives about connection state + * + * return op_ops; + * } + * @endcode + * + */ + +/** + * I/O is built around a callback structure. The connection table contains an + * operations structure built around function pointers. These allow the + * connection table to be agnostic about underlying connections. + */ + +#ifndef io_h +#define io_h + +#include <src/core/message.h> +#include <src/core/ticks.h> +#include <src/io/addressPair.h> +#include <src/utils/address.h> + +// packet types for probing +#define PACKET_TYPE_PROBE_REQUEST 5 +#define PACKET_TYPE_PROBE_REPLY 6 + +struct io_ops; +typedef struct io_ops IoOperations; + +/** + * @typedef IoOperations + * @abstract The IO Operations structure abstracts an connection's properties + * and send() method + * @constant context Implementation specific opaque data, passed back on each + * call + * @constant send function pointer to send a message, does not destroy the + * message + * @constant getRemoteAddress function pointer to return the "to" address + * associated with the connection. Some connections might not have a specific + * peer, such as multicast, where its the group address. + * @constant isUp test if the connection is up, ready to send a message. + * @constant isLocal test if the connection is local to the host. + * @constant getConnectionId returns the hicn-light id for the connection. + * @constant destroy releases a refernce count on the connection and possibly + * destroys the connection. + * @constant class A unique identifier for each class that instantiates + * IoOperations. + * @constant getConnectionType Returns the type of connection (TCP, UDP, L2, + * etc.) of the underlying connection. + * @discussion <#Discussion#> + */ +struct io_ops { + void *closure; + bool (*send)(IoOperations *ops, const Address *nexthop, Message *message); + const Address *(*getRemoteAddress)(const IoOperations *ops); + const AddressPair *(*getAddressPair)(const IoOperations *ops); + bool (*isUp)(const IoOperations *ops); + bool (*isLocal)(const IoOperations *ops); + unsigned (*getConnectionId)(const IoOperations *ops); + void (*destroy)(IoOperations **opsPtr); + const void *(*class)(const IoOperations *ops); + list_connections_type (*getConnectionType)(const IoOperations *ops); + Ticks (*sendProbe)(IoOperations *ops, unsigned probeType, uint8_t *message); +}; + +/** + * Returns the closure of the interface + * + * The creator of the closure sets this parameter to store its state. + * + * @param [in] ops A concrete instance of the interface + * + * @return The value set by the concrete instance of the interface. + * + * Example: + * @clode + * { + + * } + * @endcode + */ +void *ioOperations_GetClosure(const IoOperations *ops); + +/** + * Release all memory related to the interface and implementation + * + * This function must release all referenced memory in the concrete + * implementation and memory related to the IoOperations. It should NULL the + * input parameter. + * + * @param [in,out] opsPtr Pointer to interface. Will be NULLed. + * + * Example: + * @code + * + * static void + * _etherConnection_InternalRelease(_EtherState *etherConnState) + * { + * // release internal state of _EtherState + * } + * + * static void + * _etherConnection_Release(IoOperations **opsPtr) + * { + * IoOperations *ops = *opsPtr; + * + * _EtherState *etherConnState = (_EtherState *) + * ioOperations_GetClosure(ops); + * _etherConnection_InternalRelease(etherConnState); + * + * parcMemory_Deallocate((void **) &ops); + * } + * + * IoOperations * + * etherConnection_Create(Forwarder *forwarder, GenericEther *ether, + * AddressPair *pair) + * { + * size_t allocationSize = sizeof(_EtherState) + sizeof(IoOperations); + * IoOperations *ops = parcMemory_AllocateAndClear(allocationSize); + * if (ops) { + * // fill in other interface functions + * ops->destroy = &_etherConnection_Release; + * ops->closure = (uint8_t *) ops + sizeof(IoOperations); + * + * _EtherState *etherConnState = ioOperations_GetClosure(ops); + * // fill in Ethernet state + * } + * return ops; + * } + * @endcode + */ +void ioOperations_Release(IoOperations **opsPtr); + +/** + * Sends the specified Message out this connection + * + * The the implementation of send may queue the message, it must acquire a + * reference to it. + * + * @param [in] ops The connection implementation. + * @param [in] nexthop On multiple access networks, this parameter might be + * used, usually NULL. + * @param [in] message The message to send. If the message will be queued, it + * will be acquired. + * + * @return true The message was sent or queued + * @retrun false An error occured and the message will not be sent or queued + * + * Example: + * @code + * { + * if (ioOperations_IsUp(conn->ops)) { + * return ioOperations_Send(conn->ops, NULL, message); + * } + * } + * @endcode + */ +bool ioOperations_Send(IoOperations *ops, const Address *nexthop, + Message *message); + +/** + * A connection is made up of a local and a remote address. This function + * returns the remote address. + * + * Represents the destination endpoint of the communication. + * + * @param [in] ops The connection implementation. + * + * @return non-null The remote address + * @return null The connection does not have a remote address + * + * Example: + * @code + * { + * Address *local = addressCreateFromLink((uint8_t []) { 0x01, 0x02, 0x03, + * 0x04, 0x05, 0x06 }, 6); Address *remote = addressCreateFromLink((uint8_t []) + * { 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F }, 6); AddressPair *pair = + * addressPair_Create(local, remote); IoOperations *ops = + * etherConnection_Create(forwarder, ether, pair); + * + * const Address *test = ioOperations_GetRemoteAddress(ops); + * parcAssertTrue(addressEquals(test, remote), "Wrong remote address"); + * ioOperations_Release(&ops); + * addressPair_Release(&pair); + * addressDestroy(&local); + * addressDestroy(&remote); + * } + * @endcode + */ +const Address *ioOperations_GetRemoteAddress(const IoOperations *ops); + +/** + * A connection is made up of a local and a remote address. This function + * returns the address pair. + * + * Represents the destination endpoint of the communication. + * + * @param [in] ops The connection implementation. + * + * @return non-null The address pair + * @return null An error. + * + * Example: + * @code + * { + * Address *local = addressCreateFromLink((uint8_t []) { 0x01, 0x02, 0x03, + * 0x04, 0x05, 0x06 }, 6); Address *remote = addressCreateFromLink((uint8_t []) + * { 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F }, 6); AddressPair *pair = + * addressPair_Create(local, remote); IoOperations *ops = + * etherConnection_Create(forwarder, ether, pair); + * + * const AddressPair *test = ioOperations_GetAddressPair(ops); + * parcAssertTrue(addressPair(test, pair), "Wrong address pair"); + * ioOperations_Release(&ops); + * addressPair_Release(&pair); + * addressDestroy(&local); + * addressDestroy(&remote); + * } + * @endcode + */ +const AddressPair *ioOperations_GetAddressPair(const IoOperations *ops); + +/** + * Returns true if the underlying connection is in operation + * + * An UP connection is able to send and receive packets. If a subsystem needs to + * take actions when a connection goes UP or DOWN, it should subscribe as a + * Missive listener. + * + * @param [in] ops The connection implementation. + * + * @return true The connection is UP + * @return false The connection is not UP + * + * Example: + * @code + * { + * if (ioOperations_IsUp(conn->ops)) { + * return ioOperations_Send(conn->ops, NULL, message); + * } + * } + * @endcode + */ +bool ioOperations_IsUp(const IoOperations *ops); + +/** + * If the remote address is local to this system, returns true + * + * Will return true if an INET or INET6 connection is on localhost. Will return + * true for AF_UNIX. An Ethernet connection is not local. + * + * @param [in] ops The connection implementation. + * + * @return true The remote address is local to the system + * @return false The remote address is not local + * + * Example: + * @code + * { + * // Is the ingress connection remote? If so check for non-zero and + * decrement if (!ioOperations(ingressConnectionOps) { uint8_t hoplimit = + * message_GetHopLimit(interestMessage); if (hoplimit == 0) { + * // error + * } else { + * hoplimit--; + * } + * // take actions on hoplimit + * } + * } + * @endcode + */ +bool ioOperations_IsLocal(const IoOperations *ops); + +/** + * Returns the connection ID represented by this IoOperations in the + * ConnectionTable. + * + * <#Paragraphs Of Explanation#> + * + * @param [in] ops The connection implementation. + * + * @return number The connection ID in the connection table. + * + * Example: + * @code + * { + * unsigned id = ioOperations_GetConnectionId(ingressIoOps); + * const Connection *conn = + * connectionTable_FindById(forwarder->connectionTable, id); + * } + * @endcode + */ +unsigned ioOperations_GetConnectionId(const IoOperations *ops); + +/** + * A pointer that represents the class of the connection + * + * Each concrete implementation has a class pointer that is unique to the + * implementation (not instance). Each implementation is free to choose how to + * determine the value, so long as it is unique on the system. This is a + * system-local value. + * + * @param [in] ops The connection implementation. + * + * @return non-null A pointer value unique to the implementation (not instance). + * + * Example: + * @code + * bool + * etherConnection_IsInstanceOf(const Connection *conn) + * { + * bool result = false; + * if (conn != NULL) { + * IoOperations *ops = connection_GetIoOperations(conn); + * const void *class = ioOperations_Class(ops); + * result = (class == _etherConnection_Class(ops)); + * } + * return result; + * } + * @endcode + */ +const void *ioOperations_Class(const IoOperations *ops); + +/** + * Returns the transport type of the connection (TCP, UDP, L2, etc.). + * + * TCP and AF_UNIX are both stream connections and will both return + * "Connection_TCP". Ethernet will return "Connection_L2". + * + * @param [in] ops The connection implementation. + * + * @return Connection_TCP A TCP4, TCP6, or AF_UNIX connection + * @return Connection_UDP A UDP4 or UDP6 connection + * @return Connection_L2 An Ethernet connection + * + * Example: + * @code + * { + * ConnectionType type = + * ioOperations_GetConnectionType(connection_GetIoOperations(connection)); + * Connection *Conn = + * Connection_Create(connection_GetConnectionId(connection), localAddress, + * remoteAddress, type); + * } + * @endcode + */ +list_connections_type ioOperations_GetConnectionType(const IoOperations *ops); + +Ticks ioOperations_SendProbe(IoOperations *ops, unsigned probeType, + uint8_t *message); +#endif // io_h diff --git a/hicn-light/src/io/listener.h b/hicn-light/src/io/listener.h new file mode 100755 index 000000000..ffbb513fa --- /dev/null +++ b/hicn-light/src/io/listener.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file listener.h + * @brief Provides the function abstraction of all Listeners. + * + * A listener accepts in coming packets. A Stream listener will accept the + * connection then pass it off to the {@link StreamConnection} class. A + * datagram listener will have to have its own way to multiplex packets. + * + */ + +#ifndef listener_h +#define listener_h + +#include <src/utils/address.h> + +struct listener_ops; +typedef struct listener_ops ListenerOps; + +typedef enum { + ENCAP_TCP, /**< TCP encapsulation type */ + ENCAP_UDP, /**< UDP encapsulation type */ + ENCAP_ETHER, /**< Ethernet encapsulation type */ + ENCAP_LOCAL, /**< A connection to a local protocol stack */ + ENCAP_HICN +} EncapType; + +struct listener_ops { + /** + * A user-defined parameter + */ + void *context; + + /** + * Called to destroy the Listener. + * + * @param [in] listenerOpsPtr Double pointer to this structure + */ + void (*destroy)(ListenerOps **listenerOpsPtr); + + /** + * Returns the interface index of the listener. + * + * @param [in] ops Pointer to this structure + * + * @return the interface index of the listener + */ + unsigned (*getInterfaceIndex)(const ListenerOps *ops); + + /** + * Returns the address pair that defines the listener (local, remote) + * + * @param [in] ops Pointer to this structure + * + * @return the (local, remote) pair of addresses + */ + const Address *(*getListenAddress)(const ListenerOps *ops); + + /** + * Returns the encapsulation type of the listener (e.g. TCP, UDP, HICN) + * + * @param [in] ops Pointer to this structure + * + * @return the listener encapsulation type + */ + EncapType (*getEncapType)(const ListenerOps *ops); + + /** + * Returns the underlying socket associated with the listener + * + * Not all listeners are capable of returning a useful socket. In those + * cases, this function pointer is NULL. + * + * TCP does not support this operation (function is NULL). UDP returns its + * local socket. + * + * The caller should never close this socket, the listener will do that when + * its destroy method is called. + * + * @param [in] ops Pointer to this structure + * + * @retval integer The socket descriptor + * + * Example: + * @code + * <#example#> + * @endcode + */ + int (*getSocket)(const ListenerOps *ops); +}; +#endif // listener_h diff --git a/hicn-light/src/io/listenerSet.c b/hicn-light/src/io/listenerSet.c new file mode 100755 index 000000000..a890cd5b8 --- /dev/null +++ b/hicn-light/src/io/listenerSet.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> +#include <stdio.h> + +#include <parc/algol/parc_ArrayList.h> +#include <parc/algol/parc_Memory.h> +#include <parc/assert/parc_Assert.h> +#include <src/io/listenerSet.h> + +struct listener_set { + PARCArrayList *listOfListeners; +}; + +static void listenerSet_DestroyListenerOps(void **opsPtr) { + ListenerOps *ops = *((ListenerOps **)opsPtr); + ops->destroy(&ops); +} + +ListenerSet *listenerSet_Create() { + ListenerSet *set = parcMemory_AllocateAndClear(sizeof(ListenerSet)); + parcAssertNotNull(set, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ListenerSet)); + set->listOfListeners = parcArrayList_Create(listenerSet_DestroyListenerOps); + + return set; +} + +void listenerSet_Destroy(ListenerSet **setPtr) { + parcAssertNotNull(setPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*setPtr, "Parameter must dereference to non-null pointer"); + + ListenerSet *set = *setPtr; + parcArrayList_Destroy(&set->listOfListeners); + parcMemory_Deallocate((void **)&set); + *setPtr = NULL; +} + +/** + * @function listenerSet_Add + * @abstract Adds the listener to the set + * @discussion + * Unique set based on pair (EncapType, localAddress) + * + * @param <#param1#> + * @return <#return#> + */ +bool listenerSet_Add(ListenerSet *set, ListenerOps *ops) { + parcAssertNotNull(set, "Parameter set must be non-null"); + parcAssertNotNull(ops, "Parameter ops must be non-null"); + + int opsEncap = ops->getEncapType(ops); + const Address *opsAddress = ops->getListenAddress(ops); + + // make sure its not in the set + size_t length = parcArrayList_Size(set->listOfListeners); + for (size_t i = 0; i < length; i++) { + ListenerOps *entry = parcArrayList_Get(set->listOfListeners, i); + + int entryEncap = entry->getEncapType(entry); + const Address *entryAddress = entry->getListenAddress(entry); + + if (opsEncap == entryEncap && addressEquals(opsAddress, entryAddress)) { + // duplicate + return false; + } + } + + parcArrayList_Add(set->listOfListeners, ops); + return true; +} + +size_t listenerSet_Length(const ListenerSet *set) { + parcAssertNotNull(set, "Parameter set must be non-null"); + return parcArrayList_Size(set->listOfListeners); +} + +/** + * Returns the listener at the given index + * + * <#Paragraphs Of Explanation#> + * + * @param [in] set An allocated listener set + * @param [in] index The index position (0 <= index < listenerSet_Count) + * + * @retval non-null The listener at index + * @retval null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +ListenerOps *listenerSet_Get(const ListenerSet *set, size_t index) { + parcAssertNotNull(set, "Parameter set must be non-null"); + return parcArrayList_Get(set->listOfListeners, index); +} + +ListenerOps *listenerSet_Find(const ListenerSet *set, EncapType encapType, + const Address *localAddress) { + parcAssertNotNull(set, "Parameter set must be non-null"); + parcAssertNotNull(localAddress, "Parameter localAddress must be non-null"); + + ListenerOps *match = NULL; + + for (size_t i = 0; i < parcArrayList_Size(set->listOfListeners) && !match; + i++) { + ListenerOps *ops = parcArrayList_Get(set->listOfListeners, i); + parcAssertNotNull(ops, "Got null listener ops at index %zu", i); + + if (ops->getEncapType(ops) == encapType) { + if (addressEquals(localAddress, ops->getListenAddress(ops))) { + match = ops; + } + } + } + + return match; +} diff --git a/hicn-light/src/io/listenerSet.h b/hicn-light/src/io/listenerSet.h new file mode 100755 index 000000000..671e68479 --- /dev/null +++ b/hicn-light/src/io/listenerSet.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file listenerSet.h + * @brief A listener set is unique on (EncapType, localAddress) + * + * Keeps track of all the running listeners. The set is unique on the + * encapsulation type and the local address. For example, with TCP + * encapsulation and local address 127.0.0.1 or Ethernet encapsulation and MAC + * address 00:11:22:33:44:55. + * + * NOTE: This does not allow multiple EtherType on the same interface because + * the Address for a LINK address does not include an EtherType. + * + */ + +#ifndef listenerSet_h +#define listenerSet_h + +#include <src/io/listener.h> + +struct listener_set; +typedef struct listener_set ListenerSet; + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @retval <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +ListenerSet *listenerSet_Create(void); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @retval <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +void listenerSet_Destroy(ListenerSet **setPtr); + +/** + * @function listenerSet_Add + * @abstract Adds the listener to the set + * @discussion + * Unique set based on pair (EncapType, localAddress). + * Takes ownership of the ops memory if added. + * + * @param <#param1#> + * @return true if added, false if not + */ +bool listenerSet_Add(ListenerSet *set, ListenerOps *ops); + +/** + * The number of listeners in the set + * + * <#Paragraphs Of Explanation#> + * + * @param [in] set An allocated listener set + * + * @retval <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t listenerSet_Length(const ListenerSet *set); +size_t listenerSet_Length(const ListenerSet *set); + +/** + * Returns the listener at the given index + * + * <#Paragraphs Of Explanation#> + * + * @param [in] set An allocated listener set + * @param [in] index The index position (0 <= index < listenerSet_Lenght) + * + * @retval non-null The listener at index + * @retval null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +ListenerOps *listenerSet_Get(const ListenerSet *set, size_t index); + +/** + * Looks up a listener by its key (EncapType, LocalAddress) + * + * <#Paragraphs Of Explanation#> + * + * @param [in] set An allocated listener set + * @param [in] encapType the listener type + * @param [in] localAddress The local bind address (e.g. MAC address or TCP + * socket) + * + * @retval non-null The listener matching the query + * @retval null Does not exist + * + * Example: + * @code + * + * @endcode + */ +ListenerOps *listenerSet_Find(const ListenerSet *set, EncapType encapType, + const Address *localAddress); +#endif diff --git a/hicn-light/src/io/streamConnection.c b/hicn-light/src/io/streamConnection.c new file mode 100755 index 000000000..fedbb157a --- /dev/null +++ b/hicn-light/src/io/streamConnection.c @@ -0,0 +1,714 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Common activity for STREAM based listeners. + */ + +#include <errno.h> +#include <src/config.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> + +#include <parc/algol/parc_Hash.h> +#include <src/core/connection.h> +#include <src/core/forwarder.h> +#include <src/core/message.h> +#include <src/io/streamConnection.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/assert/parc_Assert.h> +#include <src/core/messageHandler.h> + +#include <src/utils/commands.h> + +#include <hicn/hicn.h> +// 128 KB output queue +#define OUTPUT_QUEUE_BYTES (128 * 1024) + +static void _conn_readcb(PARCEventQueue *bufferEventVector, PARCEventType type, + void *ioOpsVoid); + +static void _conn_eventcb(PARCEventQueue *bufferEventVector, + PARCEventQueueEventType events, void *ioOpsVoid); + +typedef struct stream_state { + Forwarder *forwarder; + Logger *logger; + + int fd; + + AddressPair *addressPair; + PARCEventQueue *bufferEventVector; + + bool isLocal; + bool isUp; + bool isClosed; + unsigned id; + + size_t nextMessageLength; +} _StreamState; + +// Prototypes +static bool _streamConnection_Send(IoOperations *ops, const Address *nexthop, + Message *message); +static const Address *_streamConnection_GetRemoteAddress( + const IoOperations *ops); +static const AddressPair *_streamConnection_GetAddressPair( + const IoOperations *ops); +static unsigned _streamConnection_GetConnectionId(const IoOperations *ops); +static bool _streamConnection_IsUp(const IoOperations *ops); +static bool _streamConnection_IsLocal(const IoOperations *ops); +static void _streamConnection_DestroyOperations(IoOperations **opsPtr); + +static void _setConnectionState(_StreamState *stream, bool isUp); +static list_connections_type _streamConnection_GetConnectionType( + const IoOperations *ops); +static Ticks _sendProbe(IoOperations *ops, unsigned probeType, + uint8_t *message); + +// REMINDER: when a new_command is added, the following array has to be updated +// with the sizeof(new_command). It allows to allocate the buffer for receiving +// the payload of the CONTROLLER REQUEST after the header has beed read. Each +// command identifier (typedef enum command_id) corresponds to a position in the +// following array. +static int payloadLengthDaemon[LAST_COMMAND_VALUE] = { + sizeof(add_listener_command), + sizeof(add_connection_command), + 0, // list connections: payload always 0 when get controller request + sizeof(add_route_command), + 0, // list routes: payload always 0 when get controller request + sizeof(remove_connection_command), + sizeof(remove_route_command), + sizeof(cache_store_command), + sizeof(cache_serve_command), + 0, // cache clear + sizeof(set_strategy_command), + sizeof(set_wldr_command), + sizeof(add_punting_command), + 0, // list listeners: payload always 0 when get controller request + sizeof(mapme_activator_command), + sizeof(mapme_activator_command), + sizeof(mapme_timing_command), + sizeof(mapme_timing_command)}; + +/* + * This assigns a unique pointer to the void * which we use + * as a GUID for this class. + */ +static const void *_ioOperationsGuid = __FILE__; + +/* + * Return our GUID + */ +static const void *_streamConnection_Class(const IoOperations *ops) { + return _ioOperationsGuid; +} + +static IoOperations _template = { + .closure = NULL, + .send = &_streamConnection_Send, + .getRemoteAddress = &_streamConnection_GetRemoteAddress, + .getAddressPair = &_streamConnection_GetAddressPair, + .getConnectionId = &_streamConnection_GetConnectionId, + .isUp = &_streamConnection_IsUp, + .isLocal = &_streamConnection_IsLocal, + .destroy = &_streamConnection_DestroyOperations, + .class = &_streamConnection_Class, + .getConnectionType = &_streamConnection_GetConnectionType, + .sendProbe = &_sendProbe, +}; + +IoOperations *streamConnection_AcceptConnection(Forwarder *forwarder, int fd, + AddressPair *pair, + bool isLocal) { + _StreamState *stream = parcMemory_AllocateAndClear(sizeof(_StreamState)); + parcAssertNotNull(stream, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(_StreamState)); + + Dispatcher *dispatcher = forwarder_GetDispatcher(forwarder); + PARCEventScheduler *eventBase = dispatcher_GetEventScheduler(dispatcher); + stream->bufferEventVector = parcEventQueue_Create( + eventBase, fd, + PARCEventQueueOption_CloseOnFree | PARCEventQueueOption_DeferCallbacks); + + stream->forwarder = forwarder; + stream->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + stream->fd = fd; + stream->id = forwarder_GetNextConnectionId(forwarder); + stream->addressPair = pair; + stream->isClosed = false; + + // allocate a connection + IoOperations *io_ops = parcMemory_AllocateAndClear(sizeof(IoOperations)); + parcAssertNotNull(io_ops, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(IoOperations)); + memcpy(io_ops, &_template, sizeof(IoOperations)); + io_ops->closure = stream; + stream->isLocal = isLocal; + + parcEventQueue_SetCallbacks(stream->bufferEventVector, _conn_readcb, NULL, + _conn_eventcb, (void *)io_ops); + parcEventQueue_Enable(stream->bufferEventVector, PARCEventType_Read); + + messenger_Send(forwarder_GetMessenger(stream->forwarder), + missive_Create(MissiveType_ConnectionCreate, stream->id)); + + // As we are acceting a connection, we begin in the UP state + _setConnectionState(stream, true); + + if (logger_IsLoggable(stream->logger, LoggerFacility_IO, + PARCLogLevel_Debug)) { + char *pair_str = addressPair_ToString(pair); + logger_Log(stream->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "StreamConnection %p accept for address pair %s", (void *)stream, + pair_str); + free(pair_str); + } + + return io_ops; +} + +IoOperations *streamConnection_OpenConnection(Forwarder *forwarder, + AddressPair *pair, bool isLocal) { + parcAssertNotNull(forwarder, "Parameter hicn-light must be non-null"); + parcAssertNotNull(pair, "Parameter pair must be non-null"); + + // if there's an error on the bind or connect, will return NULL + PARCEventQueue *bufferEventVector = + dispatcher_StreamBufferConnect(forwarder_GetDispatcher(forwarder), pair); + if (bufferEventVector == NULL) { + // error opening connection + return NULL; + } + + _StreamState *stream = parcMemory_AllocateAndClear(sizeof(_StreamState)); + parcAssertNotNull(stream, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(_StreamState)); + + stream->forwarder = forwarder; + stream->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + stream->fd = parcEventQueue_GetFileDescriptor(bufferEventVector); + stream->bufferEventVector = bufferEventVector; + stream->id = forwarder_GetNextConnectionId(forwarder); + stream->addressPair = pair; + stream->isClosed = false; + + // allocate a connection + IoOperations *io_ops = parcMemory_AllocateAndClear(sizeof(IoOperations)); + parcAssertNotNull(io_ops, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(IoOperations)); + memcpy(io_ops, &_template, sizeof(IoOperations)); + io_ops->closure = stream; + stream->isLocal = isLocal; + + parcEventQueue_SetCallbacks(stream->bufferEventVector, _conn_readcb, NULL, + _conn_eventcb, (void *)io_ops); + parcEventQueue_Enable(stream->bufferEventVector, PARCEventType_Read); + + // we start in DOWN state, until remote side answers + messenger_Send(forwarder_GetMessenger(stream->forwarder), + missive_Create(MissiveType_ConnectionCreate, stream->id)); + _setConnectionState(stream, false); + + if (logger_IsLoggable(stream->logger, LoggerFacility_IO, PARCLogLevel_Info)) { + char *pair_str = addressPair_ToString(pair); + logger_Log(stream->logger, LoggerFacility_IO, PARCLogLevel_Info, __func__, + "StreamConnection %p connect for address pair %s", + (void *)stream, pair_str); + free(pair_str); + } + + return io_ops; +} + +static void _streamConnection_DestroyOperations(IoOperations **opsPtr) { + parcAssertNotNull(opsPtr, "Parameter opsPtr must be non-null double pointer"); + parcAssertNotNull(*opsPtr, + "Parameter opsPtr must dereference to non-null pointer"); + + IoOperations *ops = *opsPtr; + parcAssertNotNull(ioOperations_GetClosure(ops), + "ops->context must not be null"); + + _StreamState *stream = (_StreamState *)ioOperations_GetClosure(ops); + + parcEventQueue_Destroy(&stream->bufferEventVector); + + addressPair_Release(&stream->addressPair); + + if (!stream->isClosed) { + stream->isClosed = true; + messenger_Send(forwarder_GetMessenger(stream->forwarder), + missive_Create(MissiveType_ConnectionClosed, stream->id)); + } + + messenger_Send(forwarder_GetMessenger(stream->forwarder), + missive_Create(MissiveType_ConnectionDestroyed, stream->id)); + + if (logger_IsLoggable(stream->logger, LoggerFacility_IO, PARCLogLevel_Info)) { + logger_Log(stream->logger, LoggerFacility_IO, PARCLogLevel_Info, __func__, + "StreamConnection %p destroyed", (void *)stream); + } + + logger_Release(&stream->logger); + parcMemory_Deallocate((void **)&stream); + parcMemory_Deallocate((void **)&ops); + + *opsPtr = NULL; +} + +static bool _streamConnection_IsUp(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _StreamState *stream = + (const _StreamState *)ioOperations_GetClosure(ops); + return stream->isUp; +} + +static bool _streamConnection_IsLocal(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _StreamState *stream = + (const _StreamState *)ioOperations_GetClosure(ops); + return stream->isLocal; +} + +static const Address *_streamConnection_GetRemoteAddress( + const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _StreamState *stream = + (const _StreamState *)ioOperations_GetClosure(ops); + return addressPair_GetRemote(stream->addressPair); +} + +static const AddressPair *_streamConnection_GetAddressPair( + const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _StreamState *stream = + (const _StreamState *)ioOperations_GetClosure(ops); + return stream->addressPair; +} + +static unsigned _streamConnection_GetConnectionId(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _StreamState *stream = + (const _StreamState *)ioOperations_GetClosure(ops); + return stream->id; +} + +bool streamState_SendCommandResponse(IoOperations *ops, + struct iovec *response) { + parcAssertNotNull(ops, "Parameter ops must be non-null"); + parcAssertNotNull(response, "Parameter message must be non-null"); + _StreamState *conn = (_StreamState *)ioOperations_GetClosure(ops); + + bool success = false; + if (conn->isUp) { + PARCEventBuffer *buffer = + parcEventBuffer_GetQueueBufferOutput(conn->bufferEventVector); + size_t buffer_backlog = parcEventBuffer_GetLength(buffer); + parcEventBuffer_Destroy(&buffer); + + if (buffer_backlog < OUTPUT_QUEUE_BYTES) { + if (logger_IsLoggable(conn->logger, LoggerFacility_IO, + PARCLogLevel_Debug)) { + logger_Log( + conn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "connid %u Writing %zu bytes to buffer with backlog %zu bytes", + conn->id, + (response[0].iov_len + + response[1].iov_len), // NEW: take total lenght + buffer_backlog); + } + + // NEW: write directly ino the parcEventQueue without passing through + // message + int failure = + parcEventQueue_Write(conn->bufferEventVector, response[0].iov_base, + response[0].iov_len) + + parcEventQueue_Write(conn->bufferEventVector, response[1].iov_base, + response[1].iov_len); + + if (failure == 0) { + success = true; + } + } else { + if (logger_IsLoggable(conn->logger, LoggerFacility_IO, + PARCLogLevel_Warning)) { + logger_Log(conn->logger, LoggerFacility_IO, PARCLogLevel_Warning, + __func__, + "connid %u Writing to buffer backlog %zu bytes DROP MESSAGE", + conn->id, buffer_backlog); + } + } + } else { + if (logger_IsLoggable(conn->logger, LoggerFacility_IO, + PARCLogLevel_Error)) { + logger_Log( + conn->logger, LoggerFacility_IO, PARCLogLevel_Error, __func__, + "connid %u tried to send to down connection (isUp %d isClosed %d)", + conn->id, conn->isUp, conn->isClosed); + } + } + + return success; +} + +/** + * @function streamConnection_Send + * @abstract Non-destructive send of the message. + * @discussion + * Send uses message_CopyToStreamBuffer, which is a non-destructive write. + * The send may fail if there's no buffer space in the output queue. + * + * @param dummy is ignored. A stream has only one peer. + * @return <#return#> + */ +static bool _streamConnection_Send(IoOperations *ops, const Address *dummy, + Message *message) { + parcAssertNotNull(ops, "Parameter ops must be non-null"); + parcAssertNotNull(message, "Parameter message must be non-null"); + _StreamState *stream = (_StreamState *)ioOperations_GetClosure(ops); + + bool success = false; + if (stream->isUp) { + PARCEventBuffer *buffer = + parcEventBuffer_GetQueueBufferOutput(stream->bufferEventVector); + size_t buffer_backlog = parcEventBuffer_GetLength(buffer); + parcEventBuffer_Destroy(&buffer); + + if (buffer_backlog < OUTPUT_QUEUE_BYTES) { + if (logger_IsLoggable(stream->logger, LoggerFacility_IO, + PARCLogLevel_Debug)) { + logger_Log( + stream->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "connid %u Writing %zu bytes to buffer with backlog %zu bytes", + stream->id, message_Length(message), buffer_backlog); + } + + int failure = message_Write(stream->bufferEventVector, message); + if (failure == 0) { + success = true; + } + } else { + if (logger_IsLoggable(stream->logger, LoggerFacility_IO, + PARCLogLevel_Warning)) { + logger_Log(stream->logger, LoggerFacility_IO, PARCLogLevel_Warning, + __func__, + "connid %u Writing to buffer backlog %zu bytes DROP MESSAGE", + stream->id, buffer_backlog); + } + } + } else { + if (logger_IsLoggable(stream->logger, LoggerFacility_IO, + PARCLogLevel_Error)) { + logger_Log( + stream->logger, LoggerFacility_IO, PARCLogLevel_Error, __func__, + "connid %u tried to send to down connection (isUp %d isClosed %d)", + stream->id, stream->isUp, stream->isClosed); + } + } + + return success; +} + +list_connections_type _streamConnection_GetConnectionType( + const IoOperations *ops) { + return CONN_TCP; +} + +static Ticks _sendProbe(IoOperations *ops, unsigned probeType, + uint8_t *message) { + // we don't need to implemet this here, it is a local connection + return 0; +} + +// ================================================================= +// the actual I/O functions + +int _isACommand(PARCEventBuffer *input) { + size_t bytesAvailable = parcEventBuffer_GetLength(input); + parcAssertTrue(bytesAvailable >= sizeof(header_control_message), + "Called with too short an input: %zu", bytesAvailable); + + uint8_t *msg = parcEventBuffer_Pullup(input, bytesAvailable); + // read first byte of the header + + // first byte: must be a REQUEST_LIGHT + if (msg[0] != 100) { + return LAST_COMMAND_VALUE; + } + + // second byte: must be a command_id + if (msg[1] < 0 || msg[1] >= LAST_COMMAND_VALUE) { + return LAST_COMMAND_VALUE; + } + + return msg[1]; +} + +PARCEventBuffer *_tryReadControlMessage(_StreamState *stream, + PARCEventBuffer *input, + command_id command, + struct iovec **request) { + size_t bytesAvailable = parcEventBuffer_GetLength(input); + + if (stream->nextMessageLength == 0) { + stream->nextMessageLength = + sizeof(header_control_message) + + payloadLengthDaemon[command]; // consider the whole packet. + } + + if (bytesAvailable >= stream->nextMessageLength) { + PARCEventBuffer *message = parcEventBuffer_Create(); + int bytesRead = parcEventBuffer_ReadIntoBuffer(input, message, + stream->nextMessageLength); + parcAssertTrue(bytesRead == stream->nextMessageLength, + "Partial read, expected %zu got %d", + stream->nextMessageLength, bytesRead); + + uint8_t *control = + parcEventBuffer_Pullup(message, stream->nextMessageLength); + if (!(*request = (struct iovec *)parcMemory_AllocateAndClear( + sizeof(struct iovec) * 2))) { + return NULL; + } + (*request)[0].iov_base = control; // header + (*request)[0].iov_len = sizeof(header_control_message); + if (payloadLengthDaemon[command] > 0) { + (*request)[1].iov_base = + control + sizeof(header_control_message); // payload + } else { + (*request)[1].iov_base = NULL; + } + (*request)[1].iov_len = payloadLengthDaemon[command]; + // now reset message length for next packet + + stream->nextMessageLength = 0; + + return message; + } + + return NULL; +} + +static bool _isAnHIcnPacket(PARCEventBuffer *input) { + size_t bytesAvailable = parcEventBuffer_GetLength(input); + parcAssertTrue(bytesAvailable >= sizeof(header_control_message), + "Called with too short an input: %zu", bytesAvailable); + + uint8_t *fh = parcEventBuffer_Pullup(input, sizeof(header_control_message)); + return messageHandler_IsValidHIcnPacket(fh); +} + +static Message *_readMessage(_StreamState *stream, Ticks time, + PARCEventBuffer *input) { + Message *message = message_CreateFromEventBuffer( + input, stream->nextMessageLength, stream->id, time, stream->logger); + + return message; +} + +static void _startNewMessage(_StreamState *stream, PARCEventBuffer *input, + size_t inputBytesAvailable) { + parcAssertTrue(stream->nextMessageLength == 0, + "Invalid state, nextMessageLength not zero: %zu", + stream->nextMessageLength); + parcAssertTrue(inputBytesAvailable >= sizeof(header_control_message), + "read_length not a whole fixed header!: %zd", + inputBytesAvailable); + + // this linearizes the first messageHandler_GetIPv6HeaderLength() bytes of the + // input buffer's iovecs and returns a pointer to it. + uint8_t *fh = parcEventBuffer_Pullup(input, sizeof(header_control_message)); + + // Calculate the total message size based on the fixed header + stream->nextMessageLength = messageHandler_GetTotalPacketLength(fh); +} + +static Message *_tryReadMessage(PARCEventBuffer *input, _StreamState *stream) { + size_t bytesAvailable = parcEventBuffer_GetLength(input); + parcAssertTrue(bytesAvailable >= sizeof(header_control_message), + "Called with too short an input: %zu", bytesAvailable); + + if (stream->nextMessageLength == 0) { + _startNewMessage(stream, input, bytesAvailable); + } + + // This is not an ELSE statement. We can both start a new message then + // check if there's enough bytes to read the whole thing. + + if (bytesAvailable >= stream->nextMessageLength) { + Message *message = + _readMessage(stream, forwarder_GetTicks(stream->forwarder), input); + + // now reset message length for next packet + stream->nextMessageLength = 0; + + return message; + } + + return NULL; +} + +/** + * @function conn_readcb + * @abstract Event callback for reads + * @discussion + * Will read messages off the input. Continues reading as long as we + * can get a header to determine the next message length or as long as we + * can read a complete message. + * + * This function manipulates the read low water mark. (1) read a fixed header + * plus complete message, then set the low water mark to FIXED_HEADER_LEN. (2) + * read a fixed header, but not a complete message, then set low water mark to + * the total mesage length. Using the low water mark like this means the buffer + * event will only trigger on meaningful byte boundaries when we can get actual + * work done. + * + * @param <#param1#> + * @return <#return#> + */ +static void _conn_readcb(PARCEventQueue *event, PARCEventType type, + void *ioOpsVoid) { + command_id command; + IoOperations *ops = (IoOperations *)ioOpsVoid; + _StreamState *stream = (_StreamState *)ioOperations_GetClosure(ops); + + PARCEventBuffer *input = parcEventBuffer_GetQueueBufferInput(event); + + // drain the input buffer + + // notice that we always try to read at least 8 bytes + // (sizeof(header_control_message)). This is enough to read the length of all + // kind of packets + while (parcEventBuffer_GetLength(input) >= sizeof(header_control_message) && + parcEventBuffer_GetLength(input) >= stream->nextMessageLength) { + if ((command = _isACommand(input)) != LAST_COMMAND_VALUE) { + struct iovec *rx; + // Get message from the stream and set the stream->nextMessageLength + PARCEventBuffer *message = + _tryReadControlMessage(stream, input, command, &rx); + // If received correctly the whole message, send to dispatcher + if (message) { + forwarder_ReceiveCommand(stream->forwarder, command, rx, stream->id); + parcEventBuffer_Destroy(&message); + } + + } else if (_isAnHIcnPacket(input)) { + // this is an HIcn packet (here we should distinguish between IPv4 and + // IPv6 tryReadMessage may set nextMessageLength + Message *message = _tryReadMessage(input, stream); + + if (message) { + forwarder_Receive(stream->forwarder, message); + } + + } else { + parcAssertTrue(false, + "(Local stream connection) malformend packet received"); + } + } + + if (stream->nextMessageLength == 0) { + // we don't have the next header, so set it to the header length + streamBuffer_SetWatermark(event, true, false, + sizeof(header_control_message), 0); + } else { + // set it to the packet length + streamBuffer_SetWatermark(event, true, false, stream->nextMessageLength, 0); + } + parcEventBuffer_Destroy(&input); +} + +static void _setConnectionState(_StreamState *stream, bool isUp) { + parcAssertNotNull(stream, "Parameter stream must be non-null"); + + Messenger *messenger = forwarder_GetMessenger(stream->forwarder); + + bool oldStateIsUp = stream->isUp; + stream->isUp = isUp; + + if (oldStateIsUp && !isUp) { + // bring connection DOWN + Missive *missive = missive_Create(MissiveType_ConnectionDown, stream->id); + messenger_Send(messenger, missive); + return; + } + + if (!oldStateIsUp && isUp) { + // bring connection UP + Missive *missive = missive_Create(MissiveType_ConnectionUp, stream->id); + messenger_Send(messenger, missive); + return; + } +} + +static void _conn_eventcb(PARCEventQueue *event, PARCEventQueueEventType events, + void *ioOpsVoid) { + IoOperations *ops = (IoOperations *)ioOpsVoid; + _StreamState *stream = (_StreamState *)ioOperations_GetClosure(ops); + + if (events & PARCEventQueueEventType_Connected) { + if (logger_IsLoggable(stream->logger, LoggerFacility_IO, + PARCLogLevel_Info)) { + logger_Log(stream->logger, LoggerFacility_IO, PARCLogLevel_Info, __func__, + "Connection %u is connected", stream->id); + } + + // if the stream was closed, do not transition to an UP state + if (!stream->isClosed) { + _setConnectionState(stream, true); + } + } else if (events & PARCEventQueueEventType_EOF) { + if (logger_IsLoggable(stream->logger, LoggerFacility_IO, + PARCLogLevel_Info)) { + logger_Log(stream->logger, LoggerFacility_IO, PARCLogLevel_Info, __func__, + "connid %u closed.", stream->id); + } + + parcEventQueue_Disable(stream->bufferEventVector, PARCEventType_Read); + + _setConnectionState(stream, false); + + if (!stream->isClosed) { + stream->isClosed = true; + // this will cause the connection manager to destroy the connection later + messenger_Send(forwarder_GetMessenger(stream->forwarder), + missive_Create(MissiveType_ConnectionClosed, stream->id)); + } + } else if (events & PARCEventQueueEventType_Error) { + if (logger_IsLoggable(stream->logger, LoggerFacility_IO, + PARCLogLevel_Error)) { + logger_Log(stream->logger, LoggerFacility_IO, PARCLogLevel_Error, + __func__, "Got an error on the connection %u: %s", stream->id, + strerror(errno)); + } + + parcEventQueue_Disable(stream->bufferEventVector, + PARCEventType_Read | PARCEventType_Write); + + _setConnectionState(stream, false); + + if (!stream->isClosed) { + stream->isClosed = true; + // this will cause the connection manager to destroy the connection later + messenger_Send(forwarder_GetMessenger(stream->forwarder), + missive_Create(MissiveType_ConnectionClosed, stream->id)); + } + } + /* None of the other events can happen here, since we haven't enabled + * timeouts */ +} diff --git a/hicn-light/src/io/streamConnection.h b/hicn-light/src/io/streamConnection.h new file mode 100755 index 000000000..8eb63a094 --- /dev/null +++ b/hicn-light/src/io/streamConnection.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Methods common to TCP and PF_LOCAL stream-based listeners + */ + +#ifndef streamConnection_h +#define streamConnection_h + +#include <src/core/forwarder.h> +#include <src/core/messagePacketType.h> +#include <src/io/addressPair.h> +#include <src/io/ioOperations.h> +#include <src/utils/address.h> + +/** + * @function streamConnection_AcceptConnection + * @abstract Receive a connection from a remote peer + * @discussion + * We are the "server side" of the stream connection, so we need to accept the + * client connection and setup state for her. + * + * @param <#param1#> + * @return <#return#> + */ +IoOperations *streamConnection_AcceptConnection(Forwarder *forwarder, int fd, + AddressPair *pair, + bool isLocal); + +/** + * @function streamConnection_OpenConnection + * @abstract Initiate a connection to a remote peer + * @discussion + * We are the "client side" of the stream connection. We'll create state for + * the peer, but it will be in the "down" state until the connection + * establishes. + * + * For TCP, both address pairs need to be the same address family: both INET + * or both INET6. The remote address must have the complete socket information + * (address, port). The local socket could be wildcarded or may specify down to + * the (address, port) pair. + * + * If the local address is IPADDR_ANY and the port is 0, then it is a normal + * call to "connect" that will use whatever local IP address and whatever local + * port for the connection. If either the address or port is set, the local + * socket will first be bound (via bind(2)), and then call connect(). + * + * AF_UNIX is not yet supported + * + * If there's an error binding to the specified address or connecting to the + * remote address, will return NULL. + * + * @param pair (takes ownership of this) is the complete socket pair of + * (address, port) for each end, if INET or INET6. + * @return NULL on error, otherwise the connections IO operations. + */ +IoOperations *streamConnection_OpenConnection(Forwarder *forwarder, + AddressPair *pair, bool isLocal); + +bool streamState_SendCommandResponse(IoOperations *ops, struct iovec *response); + +#endif // streamConnection_h diff --git a/hicn-light/src/io/tcpListener.c b/hicn-light/src/io/tcpListener.c new file mode 100755 index 000000000..6f0477f5b --- /dev/null +++ b/hicn-light/src/io/tcpListener.c @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <src/config.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> + +#include <src/core/connectionTable.h> +#include <src/core/forwarder.h> +#include <src/io/listener.h> +#include <src/io/streamConnection.h> +#include <src/io/tcpListener.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Network.h> +#include <parc/assert/parc_Assert.h> + +typedef struct tcp_listener { + Forwarder *forwarder; + Logger *logger; + + PARCEventSocket *listener; + + Address *localAddress; + + unsigned id; + + // is the localAddress as 127.0.0.0 address? + bool isLocalAddressLocal; +} _TcpListener; + +static void _tcpListener_Destroy(_TcpListener **listenerPtr); + +static void _tcpListener_OpsDestroy(ListenerOps **listenerOpsPtr); +static unsigned _tcpListener_OpsGetInterfaceIndex(const ListenerOps *ops); +static const Address *_tcpListener_OpsGetListenAddress(const ListenerOps *ops); +static EncapType _tcpListener_OpsGetEncapType(const ListenerOps *ops); + +static ListenerOps _tcpTemplate = { + .context = NULL, + .destroy = &_tcpListener_OpsDestroy, + .getInterfaceIndex = &_tcpListener_OpsGetInterfaceIndex, + .getListenAddress = &_tcpListener_OpsGetListenAddress, + .getEncapType = &_tcpListener_OpsGetEncapType, + .getSocket = NULL}; + +// STREAM daemon listener callback +static void _tcpListener_Listen(int, struct sockaddr *, int socklen, + void *tcpVoid); + +ListenerOps *tcpListener_CreateInet6(Forwarder *forwarder, + struct sockaddr_in6 sin6) { + _TcpListener *tcp = parcMemory_AllocateAndClear(sizeof(_TcpListener)); + parcAssertNotNull(tcp, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(_TcpListener)); + + tcp->forwarder = forwarder; + tcp->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + + tcp->listener = dispatcher_CreateListener( + forwarder_GetDispatcher(forwarder), _tcpListener_Listen, (void *)tcp, -1, + (struct sockaddr *)&sin6, sizeof(sin6)); + + if (tcp->listener == NULL) { + logger_Log(tcp->logger, LoggerFacility_IO, PARCLogLevel_Error, __func__, + "dispatcher_CreateListener failed to create listener (%d) %s", + errno, strerror(errno)); + logger_Release(&tcp->logger); + parcMemory_Deallocate((void **)&tcp); + return NULL; + } + + tcp->localAddress = addressCreateFromInet6(&sin6); + tcp->id = forwarder_GetNextConnectionId(forwarder); + tcp->isLocalAddressLocal = + parcNetwork_IsSocketLocal((struct sockaddr *)&sin6); + + ListenerOps *ops = parcMemory_AllocateAndClear(sizeof(ListenerOps)); + parcAssertNotNull(ops, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ListenerOps)); + + memcpy(ops, &_tcpTemplate, sizeof(ListenerOps)); + ops->context = tcp; + + if (logger_IsLoggable(tcp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + char *str = addressToString(tcp->localAddress); + logger_Log(tcp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "TcpListener %p created for address %s (isLocal %d)", + (void *)tcp, str, tcp->isLocalAddressLocal); + parcMemory_Deallocate((void **)&str); + } + + return ops; +} + +ListenerOps *tcpListener_CreateInet(Forwarder *forwarder, + struct sockaddr_in sin) { + _TcpListener *tcp = parcMemory_AllocateAndClear(sizeof(_TcpListener)); + parcAssertNotNull(tcp, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(_TcpListener)); + + tcp->forwarder = forwarder; + tcp->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + tcp->listener = dispatcher_CreateListener( + forwarder_GetDispatcher(forwarder), _tcpListener_Listen, (void *)tcp, -1, + (struct sockaddr *)&sin, sizeof(sin)); + + if (tcp->listener == NULL) { + logger_Log(tcp->logger, LoggerFacility_IO, PARCLogLevel_Error, __func__, + "dispatcher_CreateListener failed to create listener (%d) %s", + errno, strerror(errno)); + + logger_Release(&tcp->logger); + parcMemory_Deallocate((void **)&tcp); + return NULL; + } + + tcp->localAddress = addressCreateFromInet(&sin); + tcp->id = forwarder_GetNextConnectionId(forwarder); + tcp->isLocalAddressLocal = parcNetwork_IsSocketLocal((struct sockaddr *)&sin); + + ListenerOps *ops = parcMemory_AllocateAndClear(sizeof(ListenerOps)); + parcAssertNotNull(ops, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ListenerOps)); + + memcpy(ops, &_tcpTemplate, sizeof(ListenerOps)); + ops->context = tcp; + + if (logger_IsLoggable(tcp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + char *str = addressToString(tcp->localAddress); + logger_Log(tcp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "TcpListener %p created for address %s (isLocal %d)", + (void *)tcp, str, tcp->isLocalAddressLocal); + parcMemory_Deallocate((void **)&str); + } + + return ops; +} + +static void _tcpListener_Destroy(_TcpListener **listenerPtr) { + parcAssertNotNull(listenerPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*listenerPtr, + "Parameter must derefernce to non-null pointer"); + _TcpListener *tcp = *listenerPtr; + + if (logger_IsLoggable(tcp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + char *str = addressToString(tcp->localAddress); + logger_Log(tcp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "TcpListener %p destroyed", (void *)tcp); + parcMemory_Deallocate((void **)&str); + } + + logger_Release(&tcp->logger); + dispatcher_DestroyListener(forwarder_GetDispatcher(tcp->forwarder), + &tcp->listener); + addressDestroy(&tcp->localAddress); + parcMemory_Deallocate((void **)&tcp); + *listenerPtr = NULL; +} + +// ================================================== + +static void _tcpListener_Listen(int fd, struct sockaddr *sa, int socklen, + void *tcpVoid) { + _TcpListener *tcp = (_TcpListener *)tcpVoid; + + Address *remote; + + switch (sa->sa_family) { + case AF_INET: + remote = addressCreateFromInet((struct sockaddr_in *)sa); + break; + + case AF_INET6: + remote = addressCreateFromInet6((struct sockaddr_in6 *)sa); + break; + + default: + parcTrapIllegalValue(sa, "Expected INET or INET6, got %d", sa->sa_family); + abort(); + } + + AddressPair *pair = addressPair_Create(tcp->localAddress, remote); + + IoOperations *ops = streamConnection_AcceptConnection( + tcp->forwarder, fd, pair, tcp->isLocalAddressLocal); + Connection *conn = connection_Create(ops); + + connectionTable_Add(forwarder_GetConnectionTable(tcp->forwarder), conn); + + if (logger_IsLoggable(tcp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + logger_Log(tcp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "TcpListener %p listen started", (void *)tcp); + } + + addressDestroy(&remote); +} + +static void _tcpListener_OpsDestroy(ListenerOps **listenerOpsPtr) { + ListenerOps *ops = *listenerOpsPtr; + _TcpListener *tcp = (_TcpListener *)ops->context; + _tcpListener_Destroy(&tcp); + parcMemory_Deallocate((void **)&ops); + *listenerOpsPtr = NULL; +} + +static unsigned _tcpListener_OpsGetInterfaceIndex(const ListenerOps *ops) { + _TcpListener *tcp = (_TcpListener *)ops->context; + return tcp->id; +} + +static const Address *_tcpListener_OpsGetListenAddress(const ListenerOps *ops) { + _TcpListener *tcp = (_TcpListener *)ops->context; + return tcp->localAddress; +} + +static EncapType _tcpListener_OpsGetEncapType(const ListenerOps *ops) { + return ENCAP_TCP; +} diff --git a/hicn-light/src/io/tcpListener.h b/hicn-light/src/io/tcpListener.h new file mode 100755 index 000000000..c5d1e33af --- /dev/null +++ b/hicn-light/src/io/tcpListener.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file tcpListener.h + * @brief Listens for in coming TCP connections + * + * This is the "server socket" of hicn-light for TCP connections. The actual + * I/O is handled by {@link StreamConnection}. + * + */ + +#ifndef tcpListener_h +#define tcpListener_h + +#include <netinet/in.h> +#include <src/core/forwarder.h> +#include <src/io/listener.h> +#include <stdlib.h> + +ListenerOps *tcpListener_CreateInet6(Forwarder *forwarder, + struct sockaddr_in6 sin6); +ListenerOps *tcpListener_CreateInet(Forwarder *forwarder, + struct sockaddr_in sin); +#endif // tcpListener_h diff --git a/hicn-light/src/io/tcpTunnel.c b/hicn-light/src/io/tcpTunnel.c new file mode 100755 index 000000000..a2bf2bd30 --- /dev/null +++ b/hicn-light/src/io/tcpTunnel.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <src/config.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> + +#include <parc/assert/parc_Assert.h> +#include <src/io/streamConnection.h> +#include <src/io/tcpListener.h> +#include <src/io/tcpTunnel.h> + +IoOperations *tcpTunnel_Create(Forwarder *forwarder, + const Address *localAddress, + const Address *remoteAddress) { + IoOperations *ops = NULL; + + address_type localType = addressGetType(localAddress); + address_type remoteType = addressGetType(remoteAddress); + + if (localType == remoteType) { + AddressPair *pair = addressPair_Create(localAddress, remoteAddress); + bool isLocal = false; + + ops = streamConnection_OpenConnection(forwarder, pair, isLocal); + } + + return ops; +} diff --git a/hicn-light/src/io/tcpTunnel.h b/hicn-light/src/io/tcpTunnel.h new file mode 100755 index 000000000..4daa7d032 --- /dev/null +++ b/hicn-light/src/io/tcpTunnel.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file tcpTunnel.h + * @brief Establish a tunnel to a remote system + * + */ + +#ifndef tcpTunnel_h +#define tcpTunnel_h + +#include <src/core/forwarder.h> +#include <src/io/ioOperations.h> +#include <src/io/listener.h> +#include <src/utils/address.h> + +/** + */ +// IoOperations *tcpTunnel_CreateOnListener(Forwarder *forwarder, +// ListenerOps *localListener, +// const Address *remoteAddress); + +/** + */ +IoOperations *tcpTunnel_Create(Forwarder *forwarder, + const Address *localAddress, + const Address *remoteAddress); + +#endif // tcpTunnel_h diff --git a/hicn-light/src/io/udpConnection.c b/hicn-light/src/io/udpConnection.c new file mode 100755 index 000000000..2aa6edc51 --- /dev/null +++ b/hicn-light/src/io/udpConnection.c @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Embodies the reader/writer for a UDP connection + * + * NB The Send() function may overflow the output buffer + * + */ + +#include <errno.h> +#include <src/config.h> +#include <stdio.h> +#include <string.h> + +#include <src/core/messageHandler.h> +#include <src/io/udpConnection.h> + +#include <parc/algol/parc_Hash.h> +#include <parc/algol/parc_Memory.h> +#include <parc/assert/parc_Assert.h> +#include <src/core/connection.h> +#include <src/core/forwarder.h> +#include <src/core/message.h> + +typedef struct udp_state { + Forwarder *forwarder; + Logger *logger; + + // the udp listener socket we receive packets on + int udpListenerSocket; + + AddressPair *addressPair; + + // We need to access this all the time, so grab it out + // of the addressPair; + struct sockaddr *peerAddress; + socklen_t peerAddressLength; + + bool isLocal; + bool isUp; + unsigned id; + + unsigned delay; +} _UdpState; + +// Prototypes +static bool _send(IoOperations *ops, const Address *nexthop, Message *message); +static const Address *_getRemoteAddress(const IoOperations *ops); +static const AddressPair *_getAddressPair(const IoOperations *ops); +static unsigned _getConnectionId(const IoOperations *ops); +static bool _isUp(const IoOperations *ops); +static bool _isLocal(const IoOperations *ops); +static void _destroy(IoOperations **opsPtr); +static list_connections_type _getConnectionType(const IoOperations *ops); +static Ticks _sendProbe(IoOperations *ops, unsigned probeType, + uint8_t *message); +/* + * This assigns a unique pointer to the void * which we use + * as a GUID for this class. + */ +static const void *_IoOperationsGuid = __FILE__; + +/* + * Return our GUID + */ +static const void *_streamConnection_Class(const IoOperations *ops) { + return _IoOperationsGuid; +} + +static IoOperations _template = {.closure = NULL, + .send = &_send, + .getRemoteAddress = &_getRemoteAddress, + .getAddressPair = &_getAddressPair, + .getConnectionId = &_getConnectionId, + .isUp = &_isUp, + .isLocal = &_isLocal, + .destroy = &_destroy, + .class = &_streamConnection_Class, + .getConnectionType = &_getConnectionType, + .sendProbe = &_sendProbe}; + +// ================================================================= + +static void _setConnectionState(_UdpState *Udp, bool isUp); +static bool _saveSockaddr(_UdpState *udpConnState, const AddressPair *pair); + +IoOperations *udpConnection_Create(Forwarder *forwarder, int fd, + const AddressPair *pair, bool isLocal) { + IoOperations *io_ops = NULL; + + _UdpState *udpConnState = parcMemory_AllocateAndClear(sizeof(_UdpState)); + parcAssertNotNull(udpConnState, + "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(_UdpState)); + + udpConnState->forwarder = forwarder; + udpConnState->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + + bool saved = _saveSockaddr(udpConnState, pair); + if (saved) { + udpConnState->udpListenerSocket = fd; + udpConnState->id = forwarder_GetNextConnectionId(forwarder); + udpConnState->addressPair = addressPair_Acquire(pair); + udpConnState->isLocal = isLocal; + + // allocate a connection + io_ops = parcMemory_AllocateAndClear(sizeof(IoOperations)); + parcAssertNotNull(io_ops, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(IoOperations)); + memcpy(io_ops, &_template, sizeof(IoOperations)); + io_ops->closure = udpConnState; + + _setConnectionState(udpConnState, true); + + if (logger_IsLoggable(udpConnState->logger, LoggerFacility_IO, + PARCLogLevel_Info)) { + char *str = addressPair_ToString(udpConnState->addressPair); + logger_Log(udpConnState->logger, LoggerFacility_IO, PARCLogLevel_Info, + __func__, + "UdpConnection %p created for address %s (isLocal %d)", + (void *)udpConnState, str, udpConnState->isLocal); + free(str); + } + + messenger_Send( + forwarder_GetMessenger(forwarder), + missive_Create(MissiveType_ConnectionCreate, udpConnState->id)); + messenger_Send(forwarder_GetMessenger(forwarder), + missive_Create(MissiveType_ConnectionUp, udpConnState->id)); + } else { + // _saveSockaddr will already log an error, no need for extra log message + // here + logger_Release(&udpConnState->logger); + parcMemory_Deallocate((void **)&udpConnState); + } + + return io_ops; +} + +// ================================================================= +// I/O Operations implementation + +static void _destroy(IoOperations **opsPtr) { + parcAssertNotNull(opsPtr, "Parameter opsPtr must be non-null double pointer"); + parcAssertNotNull(*opsPtr, + "Parameter opsPtr must dereference to non-null pointer"); + + IoOperations *ops = *opsPtr; + parcAssertNotNull(ioOperations_GetClosure(ops), + "ops->context must not be null"); + + _UdpState *udpConnState = (_UdpState *)ioOperations_GetClosure(ops); + addressPair_Release(&udpConnState->addressPair); + parcMemory_Deallocate((void **)&(udpConnState->peerAddress)); + + messenger_Send( + forwarder_GetMessenger(udpConnState->forwarder), + missive_Create(MissiveType_ConnectionDestroyed, udpConnState->id)); + + if (logger_IsLoggable(udpConnState->logger, LoggerFacility_IO, + PARCLogLevel_Info)) { + logger_Log(udpConnState->logger, LoggerFacility_IO, PARCLogLevel_Info, + __func__, "UdpConnection %p destroyed", (void *)udpConnState); + } + + // do not close udp->udpListenerSocket, the listener will close + // that when its done + + logger_Release(&udpConnState->logger); + parcMemory_Deallocate((void **)&udpConnState); + parcMemory_Deallocate((void **)&ops); + + *opsPtr = NULL; +} + +static bool _isUp(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _UdpState *udpConnState = + (const _UdpState *)ioOperations_GetClosure(ops); + return udpConnState->isUp; +} + +static bool _isLocal(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _UdpState *udpConnState = + (const _UdpState *)ioOperations_GetClosure(ops); + return udpConnState->isLocal; +} + +static const Address *_getRemoteAddress(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _UdpState *udpConnState = + (const _UdpState *)ioOperations_GetClosure(ops); + return addressPair_GetRemote(udpConnState->addressPair); +} + +static const AddressPair *_getAddressPair(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _UdpState *udpConnState = + (const _UdpState *)ioOperations_GetClosure(ops); + return udpConnState->addressPair; +} + +static unsigned _getConnectionId(const IoOperations *ops) { + parcAssertNotNull(ops, "Parameter must be non-null"); + const _UdpState *udpConnState = + (const _UdpState *)ioOperations_GetClosure(ops); + return udpConnState->id; +} + +/** + * @function metisUdpConnection_Send + * @abstract Non-destructive send of the message. + * @discussion + * sends a message to the peer. + * + * @param dummy is ignored. A udp connection has only one peer. + * @return <#return#> + */ +static bool _send(IoOperations *ops, const Address *dummy, Message *message) { + parcAssertNotNull(ops, "Parameter ops must be non-null"); + parcAssertNotNull(message, "Parameter message must be non-null"); + _UdpState *udpConnState = (_UdpState *)ioOperations_GetClosure(ops); + + // NAT for HICN + // in this particular connection we don't need natting beacause we send the + // packet to the next hop using upd connection + +#if 0 + if((hicnConnState->peerAddressLength == sizeof(struct sockaddr_in)) || (hicnConnState->localAddressLength == sizeof(struct sockaddr_in))) + return false; + + if(message_GetType(message) = MessagePacketType_ContentObject){ + //this is a data packet. We need to put the remote address in the destination field + messageHandler_SetDestination_IPv6((uint8_t *) message_FixedHeader(message), + &((struct sockaddr_in6 *) hicnConnState->peerAddress)->sin6_addr); + + } else if (message_GetType(message) == MessagePacketType_Interest) { + //this si an interest packet. We need to put the local address in the source field + messageHandler_SetSource_IPv6((uint8_t *) message_FixedHeader(message), + &((struct sockaddr_in6 *) hicnConnState->localAddress)->sin6_addr); + + //only in this case we may need to set the probeDestAddress + if(hicnConnState->refreshProbeDestAddress){ + _refreshProbeDestAddress(hicnConnState, message_FixedHeader(message)); + } + + } else if (message_GetType(message) == MessagePacketType_WldrNotification) { + //here we don't need to do anything for now + }else{ + //unkown packet + if (logger_IsLoggable(hicnConnState->logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + logger_Log(hicnConnState->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "connid %u can't parse the message", + hicnConnState->id); + } + return false; + } +#endif + + ssize_t writeLength = + sendto(udpConnState->udpListenerSocket, message_FixedHeader(message), + message_Length(message), 0, udpConnState->peerAddress, + udpConnState->peerAddressLength); + + if (writeLength < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return false; + } else { + // this print is for debugging + printf("Incorrect write length %zd, expected %zd: (%d) %s\n", writeLength, + message_Length(message), errno, strerror(errno)); + return false; + } + } + + return true; +} + +static list_connections_type _getConnectionType(const IoOperations *ops) { + return CONN_UDP; +} + +static Ticks _sendProbe(IoOperations *ops, unsigned probeType, + uint8_t *message) { +#if 0 + parcAssertNotNull(ops, "Parameter ops must be non-null"); + _MetisUdpState *udpConnState = (_MetisUdpState *) metisIoOperations_GetClosure(ops); + + + uint8_t *pkt; + size_t pkt_size = 8; + pkt = (uint8_t *) malloc(sizeof(uint8_t) * pkt_size); + for (unsigned i = 0; i < pkt_size; i++) { + pkt[i] = 0; + } + pkt[0] = 1; //type + pkt[1] = probeType; //packet type + pkt[6] = 8; //header len (16bit, network order) + + ssize_t writeLen = sendto(udpConnState->udpListenerSocket, pkt, pkt_size, 0, udpConnState->peerAddress, udpConnState->peerAddressLength); + + if (writeLen < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + free(pkt); + return 0; + } else { + //this print is for debugging + printf("Incorrect write length %zd, expected %zd: (%d) %s\n", writeLen, pkt_size, errno, strerror(errno)); + free(pkt); + return 0; + } + } + + free(pkt); + return metisForwarder_GetTicks(udpConnState->metis); +#endif + return 0; +} + +// ================================================================= +// Internal API + +static bool _saveSockaddr(_UdpState *udpConnState, const AddressPair *pair) { + bool success = false; + const Address *remoteAddress = addressPair_GetRemote(pair); + + switch (addressGetType(remoteAddress)) { + case ADDR_INET: { + size_t bytes = sizeof(struct sockaddr_in); + udpConnState->peerAddress = parcMemory_Allocate(bytes); + parcAssertNotNull(udpConnState->peerAddress, + "parcMemory_Allocate(%zu) returned NULL", bytes); + + addressGetInet(remoteAddress, + (struct sockaddr_in *)udpConnState->peerAddress); + udpConnState->peerAddressLength = (socklen_t)bytes; + + success = true; + break; + } + + case ADDR_INET6: { + size_t bytes = sizeof(struct sockaddr_in6); + udpConnState->peerAddress = parcMemory_Allocate(bytes); + parcAssertNotNull(udpConnState->peerAddress, + "parcMemory_Allocate(%zu) returned NULL", bytes); + + addressGetInet6(remoteAddress, + (struct sockaddr_in6 *)udpConnState->peerAddress); + udpConnState->peerAddressLength = (socklen_t)bytes; + + success = true; + break; + } + + default: + if (logger_IsLoggable(udpConnState->logger, LoggerFacility_IO, + PARCLogLevel_Error)) { + char *str = addressToString(remoteAddress); + logger_Log(udpConnState->logger, LoggerFacility_IO, PARCLogLevel_Error, + __func__, "Remote address is not INET or INET6: %s", str); + parcMemory_Deallocate((void **)&str); + } + break; + } + return success; +} + +static void _setConnectionState(_UdpState *udpConnState, bool isUp) { + parcAssertNotNull(udpConnState, "Parameter Udp must be non-null"); + + Messenger *messenger = forwarder_GetMessenger(udpConnState->forwarder); + + bool oldStateIsUp = udpConnState->isUp; + udpConnState->isUp = isUp; + + if (oldStateIsUp && !isUp) { + // bring connection DOWN + Missive *missive = + missive_Create(MissiveType_ConnectionDown, udpConnState->id); + messenger_Send(messenger, missive); + return; + } + + if (!oldStateIsUp && isUp) { + // bring connection UP + Missive *missive = + missive_Create(MissiveType_ConnectionUp, udpConnState->id); + messenger_Send(messenger, missive); + return; + } +} diff --git a/hicn-light/src/io/udpConnection.h b/hicn-light/src/io/udpConnection.h new file mode 100755 index 000000000..122f332d5 --- /dev/null +++ b/hicn-light/src/io/udpConnection.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file udpConnection.h + * @brief Represents a UDP connection (socket) for the connection table + * + * <#Detailed Description#> + * + */ + +#ifndef udpConnection_h +#define udpConnection_h + +#include <src/core/forwarder.h> +#include <src/io/addressPair.h> +#include <src/io/ioOperations.h> +#include <src/utils/address.h> + +/** + * Creates a UDP connection that can send to the remote address + * + * The address pair must both be same type (i.e. INET or INET6). + * + * @param [in] metis An allocated MetisForwarder (saves reference) + * @param [in] fd The socket to use + * @param [in] pair An allocated address pair for the connection (saves + * reference) + * @param [in] isLocal determines if the remote address is on the current system + * + * @retval non-null An allocated Io operations + * @retval null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +IoOperations *udpConnection_Create(Forwarder *forwarder, int fd, + const AddressPair *pair, bool isLocal); +#endif // udpConnection_h diff --git a/hicn-light/src/io/udpListener.c b/hicn-light/src/io/udpListener.c new file mode 100755 index 000000000..31c0e673b --- /dev/null +++ b/hicn-light/src/io/udpListener.c @@ -0,0 +1,533 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <arpa/inet.h> +#include <errno.h> +#include <fcntl.h> +#include <src/config.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <src/core/messageHandler.h> + +#include <src/io/udpConnection.h> +#include <src/io/udpListener.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/assert/parc_Assert.h> +#include <src/core/connection.h> +#include <src/core/forwarder.h> +#include <src/core/messagePacketType.h> + +#ifdef WITH_MAPME +#include <src/core/mapMe.h> +#endif /* WITH_MAPME */ + +#define IPv4 4 +#define IPv6 6 + +struct udp_listener { + Forwarder *forwarder; + Logger *logger; + + PARCEvent *udp_event; + SocketType udp_socket; + uint16_t port; + + unsigned id; + Address *localAddress; +}; + +static void _destroy(ListenerOps **listenerOpsPtr); +static unsigned _getInterfaceIndex(const ListenerOps *ops); +static const Address *_getListenAddress(const ListenerOps *ops); +static EncapType _getEncapType(const ListenerOps *ops); +static int _getSocket(const ListenerOps *ops); + +static ListenerOps udpTemplate = {.context = NULL, + .destroy = &_destroy, + .getInterfaceIndex = &_getInterfaceIndex, + .getListenAddress = &_getListenAddress, + .getEncapType = &_getEncapType, + .getSocket = &_getSocket}; + +static void _readcb(int fd, PARCEventType what, void *udpVoid); + +ListenerOps *udpListener_CreateInet6(Forwarder *forwarder, + struct sockaddr_in6 sin6) { + ListenerOps *ops = NULL; + + UdpListener *udp = parcMemory_AllocateAndClear(sizeof(UdpListener)); + parcAssertNotNull(udp, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(UdpListener)); + udp->forwarder = forwarder; + udp->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + udp->localAddress = addressCreateFromInet6(&sin6); + udp->id = forwarder_GetNextConnectionId(forwarder); + + udp->udp_socket = socket(AF_INET6, SOCK_DGRAM, 0); + parcAssertFalse(udp->udp_socket < 0, "Error opening UDP socket: (%d) %s", + errno, strerror(errno)); + + // Set non-blocking flag + int flags = fcntl(udp->udp_socket, F_GETFL, NULL); + parcAssertTrue(flags != -1, + "fcntl failed to obtain file descriptor flags (%d)", errno); + int failure = fcntl(udp->udp_socket, F_SETFL, flags | O_NONBLOCK); + parcAssertFalse(failure, "fcntl failed to set file descriptor flags (%d)", + errno); + + int one = 1; + // don't hang onto address after listener has closed + failure = setsockopt(udp->udp_socket, SOL_SOCKET, SO_REUSEADDR, (void *)&one, + (socklen_t)sizeof(one)); + parcAssertFalse(failure, "failed to set REUSEADDR on socket(%d)", errno); + + failure = bind(udp->udp_socket, (struct sockaddr *)&sin6, sizeof(sin6)); + if (failure == 0) { + udp->udp_event = + dispatcher_CreateNetworkEvent(forwarder_GetDispatcher(forwarder), true, + _readcb, (void *)udp, udp->udp_socket); + dispatcher_StartNetworkEvent(forwarder_GetDispatcher(forwarder), + udp->udp_event); + + ops = parcMemory_AllocateAndClear(sizeof(ListenerOps)); + parcAssertNotNull(ops, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ListenerOps)); + memcpy(ops, &udpTemplate, sizeof(ListenerOps)); + ops->context = udp; + + if (logger_IsLoggable(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + char *str = addressToString(udp->localAddress); + logger_Log(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "UdpListener %p created for address %s", (void *)udp, str); + parcMemory_Deallocate((void **)&str); + } + } else { + if (logger_IsLoggable(udp->logger, LoggerFacility_IO, PARCLogLevel_Error)) { + int myerrno = errno; + char *str = addressToString(udp->localAddress); + logger_Log(udp->logger, LoggerFacility_IO, PARCLogLevel_Error, __func__, + "Error binding UDP socket to address %s: (%d) %s", str, + myerrno, strerror(myerrno)); + parcMemory_Deallocate((void **)&str); + } + + close(udp->udp_socket); + addressDestroy(&udp->localAddress); + logger_Release(&udp->logger); + parcMemory_Deallocate((void **)&udp); + } + + return ops; +} + +ListenerOps *udpListener_CreateInet(Forwarder *forwarder, + struct sockaddr_in sin) { + ListenerOps *ops = NULL; + + UdpListener *udp = parcMemory_AllocateAndClear(sizeof(UdpListener)); + parcAssertNotNull(udp, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(UdpListener)); + udp->forwarder = forwarder; + udp->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + udp->localAddress = addressCreateFromInet(&sin); + udp->id = forwarder_GetNextConnectionId(forwarder); + + udp->udp_socket = socket(AF_INET, SOCK_DGRAM, 0); + parcAssertFalse(udp->udp_socket < 0, "Error opening UDP socket: (%d) %s", + errno, strerror(errno)); + + // Set non-blocking flag + int flags = fcntl(udp->udp_socket, F_GETFL, NULL); + parcAssertTrue(flags != -1, + "fcntl failed to obtain file descriptor flags (%d)", errno); + int failure = fcntl(udp->udp_socket, F_SETFL, flags | O_NONBLOCK); + parcAssertFalse(failure, "fcntl failed to set file descriptor flags (%d)", + errno); + + int one = 1; + // don't hang onto address after listener has closed + failure = setsockopt(udp->udp_socket, SOL_SOCKET, SO_REUSEADDR, (void *)&one, + (socklen_t)sizeof(one)); + parcAssertFalse(failure, "failed to set REUSEADDR on socket(%d)", errno); + + failure = bind(udp->udp_socket, (struct sockaddr *)&sin, sizeof(sin)); + if (failure == 0) { + udp->udp_event = + dispatcher_CreateNetworkEvent(forwarder_GetDispatcher(forwarder), true, + _readcb, (void *)udp, udp->udp_socket); + dispatcher_StartNetworkEvent(forwarder_GetDispatcher(forwarder), + udp->udp_event); + + ops = parcMemory_AllocateAndClear(sizeof(ListenerOps)); + parcAssertNotNull(ops, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(ListenerOps)); + memcpy(ops, &udpTemplate, sizeof(ListenerOps)); + ops->context = udp; + + if (logger_IsLoggable(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + char *str = addressToString(udp->localAddress); + logger_Log(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "UdpListener %p created for address %s", (void *)udp, str); + parcMemory_Deallocate((void **)&str); + } + } else { + if (logger_IsLoggable(udp->logger, LoggerFacility_IO, PARCLogLevel_Error)) { + int myerrno = errno; + char *str = addressToString(udp->localAddress); + logger_Log(udp->logger, LoggerFacility_IO, PARCLogLevel_Error, __func__, + "Error binding UDP socket to address %s: (%d) %s", str, + myerrno, strerror(myerrno)); + parcMemory_Deallocate((void **)&str); + } + + close(udp->udp_socket); + addressDestroy(&udp->localAddress); + logger_Release(&udp->logger); + parcMemory_Deallocate((void **)&udp); + } + + return ops; +} + +static void udpListener_Destroy(UdpListener **listenerPtr) { + parcAssertNotNull(listenerPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*listenerPtr, + "Parameter must derefernce to non-null pointer"); + + UdpListener *udp = *listenerPtr; + + if (logger_IsLoggable(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + logger_Log(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "UdpListener %p destroyed", (void *)udp); + } + + close(udp->udp_socket); + addressDestroy(&udp->localAddress); + dispatcher_DestroyNetworkEvent(forwarder_GetDispatcher(udp->forwarder), + &udp->udp_event); + logger_Release(&udp->logger); + parcMemory_Deallocate((void **)&udp); + *listenerPtr = NULL; +} + +static void _destroy(ListenerOps **listenerOpsPtr) { + ListenerOps *ops = *listenerOpsPtr; + UdpListener *udp = (UdpListener *)ops->context; + udpListener_Destroy(&udp); + parcMemory_Deallocate((void **)&ops); + *listenerOpsPtr = NULL; +} + +static unsigned _getInterfaceIndex(const ListenerOps *ops) { + UdpListener *udp = (UdpListener *)ops->context; + return udp->id; +} + +static const Address *_getListenAddress(const ListenerOps *ops) { + UdpListener *udp = (UdpListener *)ops->context; + return udp->localAddress; +} + +static EncapType _getEncapType(const ListenerOps *ops) { return ENCAP_UDP; } + +static int _getSocket(const ListenerOps *ops) { + UdpListener *udp = (UdpListener *)ops->context; + return (int)udp->udp_socket; +} + +// void +// udpListener_SetPacketType(ListenerOps *ops, MessagePacketType type) +//{ +// return; +//} + +// ===================================================================== + +/** + * @function peekMesageLength + * @abstract Peek at the next packet to learn its length by reading the fixed + * header + * @discussion + * <#Discussion#> + * + * @param <#param1#> + * @return <#return#> + */ +static size_t _peekMessageLength(UdpListener *udp, int fd, + struct sockaddr *peerIpAddress, + socklen_t *peerIpAddressLengthPtr) { + // to be fast I try to use just ipv6, this needs to be validated for ipv4 + + size_t packetLength = 0; + + uint8_t fixedHeader[messageHandler_GetIPHeaderLength(IPv6)]; + + // peek at the UDP packet and read in the fixed header. + // Also returns the socket information for the remote peer + + ssize_t res = recvfrom( + fd, fixedHeader, messageHandler_GetIPHeaderLength(IPv6), MSG_PEEK, + (struct sockaddr *)peerIpAddress, peerIpAddressLengthPtr); + + if (res == messageHandler_GetIPHeaderLength(IPv6)) { + packetLength = + messageHandler_GetTotalPacketLength((const uint8_t *)&fixedHeader); + } else { + if (res < 0) { + printf("error while readin packet\n"); + } + } + + return packetLength; +} + +/** + * @function _constructAddressPair + * @abstract Creates the address pair that uniquely identifies the connection + * @discussion + * The peerIpAddress must be of AF_INET or AF_INET6 family. + * + * @param <#param1#> + * @return Allocated MetisAddressPair, must be destroyed + */ +static AddressPair *_constructAddressPair(UdpListener *udp, + struct sockaddr *peerIpAddress, + socklen_t peerIpAddressLength) { + Address *remoteAddress; + + switch (peerIpAddress->sa_family) { + case AF_INET: + remoteAddress = + addressCreateFromInet((struct sockaddr_in *)peerIpAddress); + break; + + case AF_INET6: + remoteAddress = + addressCreateFromInet6((struct sockaddr_in6 *)peerIpAddress); + break; + + default: + parcTrapIllegalValue(peerIpAddress, + "Peer address unrecognized family for IP: %d", + peerIpAddress->sa_family); + } + + AddressPair *pair = addressPair_Create(udp->localAddress, remoteAddress); + addressDestroy(&remoteAddress); + + return pair; +} + +/** + * @function _lookupConnectionId + * @abstract Lookup a connection in the connection table + * @discussion + * Looks up the connection in the connection table and returns the connection + * id if it exists. + * + * @param outputConnectionIdPtr is the output parameter + * @return true if connection found and outputConnectionIdPtr set + */ +static bool _lookupConnectionId(UdpListener *udp, AddressPair *pair, + unsigned *outputConnectionIdPtr) { + ConnectionTable *connTable = forwarder_GetConnectionTable(udp->forwarder); + + const Connection *conn = connectionTable_FindByAddressPair(connTable, pair); + if (conn) { + *outputConnectionIdPtr = connection_GetConnectionId(conn); + return true; + } else { + *outputConnectionIdPtr = 0; + return false; + } +} + +/** + * @function _createNewConnection + * @abstract Creates a new Metis connection for the peer + * @discussion + * PRECONDITION: you know there's not an existing connection with the address + * pair + * + * Creates a new connection and adds it to the connection table. + * + * @param <#param1#> + * @return The connection id for the new connection + */ + +static unsigned _createNewConnection(UdpListener *udp, int fd, + const AddressPair *pair) { + bool isLocal = false; + + // metisUdpConnection_Create takes ownership of the pair + IoOperations *ops = udpConnection_Create(udp->forwarder, fd, pair, isLocal); + Connection *conn = connection_Create(ops); + // connection_AllowWldrAutoStart(conn); + + connectionTable_Add(forwarder_GetConnectionTable(udp->forwarder), conn); + unsigned connid = ioOperations_GetConnectionId(ops); + + return connid; +} + +static void _handleProbeMessage(UdpListener *udp, uint8_t *msgBuffer) { + // TODO + parcMemory_Deallocate((void **)&msgBuffer); +} + +static void _handleWldrNotification(UdpListener *udp, unsigned connId, + uint8_t *msgBuffer) { + const Connection *conn = connectionTable_FindById( + forwarder_GetConnectionTable(udp->forwarder), connId); + if (conn == NULL) { + return; + } + + Message *message = message_CreateFromByteArray( + connId, msgBuffer, MessagePacketType_WldrNotification, + forwarder_GetTicks(udp->forwarder), forwarder_GetLogger(udp->forwarder)); + + connection_HandleWldrNotification((Connection *)conn, message); + + message_Release(&message); +} + +static Message *_readMessage(UdpListener *udp, int fd, size_t packetLength, + AddressPair *pair) { + uint8_t *msgBuffer = parcMemory_AllocateAndClear(packetLength); + + ssize_t readLength = read(fd, msgBuffer, packetLength); + + Message *message = NULL; + + if (readLength < 0) { + printf("read failed %d: (%d) %s\n", fd, errno, strerror(errno)); + return message; + } + + unsigned connid = 0; + bool foundConnection = _lookupConnectionId(udp, pair, &connid); + + if (readLength == packetLength) { + // we need to check if it is a valid packet + if (messageHandler_IsTCP(msgBuffer)) { + MessagePacketType pktType; + + if (messageHandler_IsData(msgBuffer)) { + pktType = MessagePacketType_ContentObject; + if (!foundConnection) { + parcMemory_Deallocate((void **)&msgBuffer); + return message; + } + } else if (messageHandler_IsInterest(msgBuffer)) { + pktType = MessagePacketType_Interest; + if (!foundConnection) { + connid = _createNewConnection(udp, fd, pair); + } + } else { + printf("Got a packet that is not a data nor an interest, drop it!\n"); + parcMemory_Deallocate((void **)&msgBuffer); + return message; + } + + message = message_CreateFromByteArray( + connid, msgBuffer, pktType, forwarder_GetTicks(udp->forwarder), + forwarder_GetLogger(udp->forwarder)); + + if (message == NULL) { + parcMemory_Deallocate((void **)&msgBuffer); + } + } else if (messageHandler_IsWldrNotification(msgBuffer)) { + _handleWldrNotification(udp, connid, msgBuffer); + } else if (messageHandler_IsLoadBalancerProbe(msgBuffer)) { + _handleProbeMessage(udp, msgBuffer); + } +#ifdef WITH_MAPME + else if (mapMe_isMapMe(msgBuffer)) { + forwarder_ProcessMapMe(udp->forwarder, msgBuffer, connid); + } +#endif /* WITH_MAPME */ + } + + return message; +} + +static void _receivePacket(UdpListener *udp, int fd, size_t packetLength, + struct sockaddr_storage *peerIpAddress, + socklen_t peerIpAddressLength) { + AddressPair *pair = _constructAddressPair( + udp, (struct sockaddr *)peerIpAddress, peerIpAddressLength); + + Message *message = _readMessage(udp, fd, packetLength, pair); + addressPair_Release(&pair); + + if (message) { + forwarder_Receive(udp->forwarder, message); + } else { + return; + } +} + +static void _readFrameToDiscard(UdpListener *udp, int fd) { + // we need to discard the frame. Read 1 byte. This will clear it off the + // stack. + uint8_t buffer; + ssize_t nread = read(fd, &buffer, 1); + + if (nread == 1) { + if (logger_IsLoggable(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + logger_Log(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "Discarded frame from fd %d", fd); + } + } else if (nread < 0) { + if (logger_IsLoggable(udp->logger, LoggerFacility_IO, PARCLogLevel_Error)) { + logger_Log(udp->logger, LoggerFacility_IO, PARCLogLevel_Error, __func__, + "Error trying to discard frame from fd %d: (%d) %s", fd, errno, + strerror(errno)); + } + } +} + +static void _readcb(int fd, PARCEventType what, void *udpVoid) { + UdpListener *udp = (UdpListener *)udpVoid; + + if (logger_IsLoggable(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + logger_Log(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "%s socket %d what %s%s%s%s data %p", __func__, fd, + (what & PARCEventType_Timeout) ? " timeout" : "", + (what & PARCEventType_Read) ? " read" : "", + (what & PARCEventType_Write) ? " write" : "", + (what & PARCEventType_Signal) ? " signal" : "", udpVoid); + } + + if (what & PARCEventType_Read) { + struct sockaddr_storage peerIpAddress; + socklen_t peerIpAddressLength = sizeof(peerIpAddress); + + size_t packetLength = _peekMessageLength( + udp, fd, (struct sockaddr *)&peerIpAddress, &peerIpAddressLength); + + if (packetLength > 0) { + _receivePacket(udp, fd, packetLength, &peerIpAddress, + peerIpAddressLength); + } else { + _readFrameToDiscard(udp, fd); + } + } +} diff --git a/hicn-light/src/io/udpListener.h b/hicn-light/src/io/udpListener.h new file mode 100755 index 000000000..1cf3bd887 --- /dev/null +++ b/hicn-light/src/io/udpListener.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef udpListener_h +#define udpListener_h + +#include <netinet/in.h> +#include <src/core/forwarder.h> +#include <src/io/listener.h> +#include <stdlib.h> + +struct udp_listener; +typedef struct udp_listener UdpListener; + +ListenerOps *udpListener_CreateInet6(Forwarder *forwarder, + struct sockaddr_in6 sin6); +ListenerOps *udpListener_CreateInet(Forwarder *forwarder, + struct sockaddr_in sin); +// void udpListener_SetPacketType(ListenerOps *ops, MessagePacketType type); +#endif // udpListener_h diff --git a/hicn-light/src/io/udpTunnel.c b/hicn-light/src/io/udpTunnel.c new file mode 100755 index 000000000..d06a35ce6 --- /dev/null +++ b/hicn-light/src/io/udpTunnel.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <src/config.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> + +#include <parc/assert/parc_Assert.h> +#include <src/io/udpConnection.h> +#include <src/io/udpListener.h> +#include <src/io/udpTunnel.h> + +IoOperations *udpTunnel_CreateOnListener(Forwarder *forwarder, + ListenerOps *localListener, + const Address *remoteAddress) { + parcAssertNotNull(forwarder, "Parameter metis must be non-null"); + parcAssertNotNull(localListener, "Parameter localListener must be non-null"); + parcAssertNotNull(remoteAddress, "Parameter remoteAddress must be non-null"); + + Logger *logger = forwarder_GetLogger(forwarder); + + IoOperations *ops = NULL; + if (localListener->getEncapType(localListener) == ENCAP_UDP) { + const Address *localAddress = + localListener->getListenAddress(localListener); + address_type localType = addressGetType(localAddress); + address_type remoteType = addressGetType(remoteAddress); + + if (localType == remoteType) { + AddressPair *pair = addressPair_Create(localAddress, remoteAddress); + bool isLocal = false; + int fd = localListener->getSocket(localListener); + // udpListener_SetPacketType(localListener, + // MessagePacketType_ContentObject); + ops = udpConnection_Create(forwarder, fd, pair, isLocal); + + addressPair_Release(&pair); + } else { + if (logger_IsLoggable(logger, LoggerFacility_IO, PARCLogLevel_Error)) { + logger_Log(logger, LoggerFacility_IO, PARCLogLevel_Error, __func__, + "Local listener of type %s and remote type %s, cannot " + "establish tunnel", + addressTypeToString(localType), + addressTypeToString(remoteType)); + } + } + } else { + if (logger_IsLoggable(logger, LoggerFacility_IO, PARCLogLevel_Error)) { + logger_Log(logger, LoggerFacility_IO, PARCLogLevel_Error, __func__, + "Local listener %p is not type UDP, cannot establish tunnel", + (void *)localListener); + } + } + + return ops; +} + +IoOperations *udpTunnel_Create(Forwarder *forwarder, + const Address *localAddress, + const Address *remoteAddress) { + ListenerSet *set = forwarder_GetListenerSet(forwarder); + ListenerOps *listener = listenerSet_Find(set, ENCAP_UDP, localAddress); + IoOperations *ops = NULL; + if (listener) { + ops = udpTunnel_CreateOnListener(forwarder, listener, remoteAddress); + } else { + if (logger_IsLoggable(forwarder_GetLogger(forwarder), LoggerFacility_IO, + PARCLogLevel_Error)) { + char *str = addressToString(localAddress); + logger_Log(forwarder_GetLogger(forwarder), LoggerFacility_IO, + PARCLogLevel_Error, __func__, + "Could not find listener to match address %s", str); + parcMemory_Deallocate((void **)&str); + } + } + return ops; +} diff --git a/hicn-light/src/io/udpTunnel.h b/hicn-light/src/io/udpTunnel.h new file mode 100755 index 000000000..a79ca4a4e --- /dev/null +++ b/hicn-light/src/io/udpTunnel.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file udpTunnel.h + * @brief Establish a tunnel to a remote system + * + */ + +#ifndef udpTunnel_h +#define udpTunnel_h + +#include <src/core/forwarder.h> +#include <src/io/ioOperations.h> +#include <src/io/listener.h> +#include <src/utils/address.h> + +/** + */ +IoOperations *udpTunnel_CreateOnListener(Forwarder *forwarder, + ListenerOps *localListener, + const Address *remoteAddress); + +/** + */ +IoOperations *udpTunnel_Create(Forwarder *forwarder, + const Address *localAddress, + const Address *remoteAddress); + +#endif // udpTunnel_h diff --git a/hicn-light/src/messenger/CMakeLists.txt b/hicn-light/src/messenger/CMakeLists.txt new file mode 100755 index 000000000..92bc13b5b --- /dev/null +++ b/hicn-light/src/messenger/CMakeLists.txt @@ -0,0 +1,32 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/missiveDeque.h + ${CMAKE_CURRENT_SOURCE_DIR}/missive.h + ${CMAKE_CURRENT_SOURCE_DIR}/missiveType.h + ${CMAKE_CURRENT_SOURCE_DIR}/messenger.h + ${CMAKE_CURRENT_SOURCE_DIR}/messengerRecipient.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/messenger.c + ${CMAKE_CURRENT_SOURCE_DIR}/messengerRecipient.c + ${CMAKE_CURRENT_SOURCE_DIR}/missive.c + ${CMAKE_CURRENT_SOURCE_DIR}/missiveDeque.c +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE)
\ No newline at end of file diff --git a/hicn-light/src/messenger/messenger.c b/hicn-light/src/messenger/messenger.c new file mode 100755 index 000000000..26c7a85e2 --- /dev/null +++ b/hicn-light/src/messenger/messenger.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * + * The messenger is contructued with a reference to the forwarder's dispatcher + * so it can schedule future events. When someone calls messenger_Send(...), it + * will put the message on a queue. If the queue was empty, it will scheudle + * itself to be run. By running the queue in a future dispatcher slice, it + * guarantees that there will be no re-entrant behavior between callers and + * message listeners. + * + * A recipient will receive a reference counted copy of the missive, so it must + * call + * {@link missive_Release} on it. + * + */ + +#include <parc/algol/parc_ArrayList.h> +#include <parc/algol/parc_Event.h> +#include <parc/algol/parc_EventScheduler.h> +#include <parc/algol/parc_Memory.h> +#include <parc/assert/parc_Assert.h> +#include <src/config.h> +#include <stdio.h> + +#include <src/messenger/messenger.h> +#include <src/messenger/missiveDeque.h> + +struct messenger { + PARCArrayList *callbacklist; + Dispatcher *dispatcher; + MissiveDeque *eventQueue; + + PARCEventTimer *timerEvent; +}; + +static void messenger_Dequeue(int fd, PARCEventType which_event, + void *messengerVoidPtr); + +// ========================================= +// Public API + +Messenger *messenger_Create(Dispatcher *dispatcher) { + Messenger *messenger = parcMemory_AllocateAndClear(sizeof(Messenger)); + parcAssertNotNull(messenger, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Messenger)); + + // NULL destroyer because we're storing structures owned by the caller + messenger->dispatcher = dispatcher; + messenger->callbacklist = parcArrayList_Create(NULL); + messenger->eventQueue = missiveDeque_Create(); + + // creates the timer, but does not start it + messenger->timerEvent = + dispatcher_CreateTimer(dispatcher, false, messenger_Dequeue, messenger); + + return messenger; +} + +void messenger_Destroy(Messenger **messengerPtr) { + parcAssertNotNull(messengerPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*messengerPtr, + "Parameter must dereference to non-null pointer"); + + Messenger *messenger = *messengerPtr; + parcArrayList_Destroy(&messenger->callbacklist); + missiveDeque_Release(&messenger->eventQueue); + dispatcher_DestroyTimerEvent(messenger->dispatcher, &messenger->timerEvent); + parcMemory_Deallocate((void **)&messenger); + *messengerPtr = NULL; +} + +void messenger_Send(Messenger *messenger, Missive *missive) { + parcAssertNotNull(messenger, "Parameter messenger must be non-null"); + parcAssertNotNull(missive, "Parameter event must be non-null"); + + missiveDeque_Append(messenger->eventQueue, missive); + if (missiveDeque_Size(messenger->eventQueue) == 1) { + // We need to scheudle ourself when an event is added to an empty queue + + // precondition: timer should not be running. + struct timeval immediateTimeout = {0, 0}; + dispatcher_StartTimer(messenger->dispatcher, messenger->timerEvent, + &immediateTimeout); + } +} + +static void removeRecipient(Messenger *messenger, + const MessengerRecipient *recipient) { + // don't increment i in the loop + for (size_t i = 0; i < parcArrayList_Size(messenger->callbacklist);) { + const void *p = parcArrayList_Get(messenger->callbacklist, i); + if (p == recipient) { + // removing will compact the list, so next element will also be at i. + parcArrayList_RemoveAndDestroyAtIndex(messenger->callbacklist, i); + } else { + i++; + } + } +} + +/** + * @function eventMessenger_Register + * @abstract Receive all event messages + */ +void messenger_Register(Messenger *messenger, + const MessengerRecipient *recipient) { + parcAssertNotNull(messenger, "Parameter messenger must be non-null"); + parcAssertNotNull(recipient, "Parameter recipient must be non-null"); + + // do not allow duplicates + removeRecipient(messenger, recipient); + + parcArrayList_Add(messenger->callbacklist, recipient); +} + +/** + * @function eventMessenger_Unregister + * @abstract Stop receiving event messages + */ +void messenger_Unregister(Messenger *messenger, + const MessengerRecipient *recipient) { + parcAssertNotNull(messenger, "Parameter messenger must be non-null"); + parcAssertNotNull(recipient, "Parameter recipient must be non-null"); + + removeRecipient(messenger, recipient); +} + +/** + * Called by event scheduler to give us a slice in which to dequeue events + * + * Called inside an event callback, so we now have exclusive access to the + * system. Dequeues all pending events and calls all the listeners for each one. + * + * @param [in] fd unused, required for compliance with function prototype + * @param [in] which_event unused, required for compliance with function + * prototype + * @param [in] messengerVoidPtr A void* to Messenger + */ +static void messenger_Dequeue(int fd, PARCEventType which_event, + void *messengerVoidPtr) { + Messenger *messenger = (Messenger *)messengerVoidPtr; + parcAssertNotNull(messenger, "Called with null messenger pointer"); + + Missive *missive; + while ((missive = missiveDeque_RemoveFirst(messenger->eventQueue)) != NULL) { + for (size_t i = 0; i < parcArrayList_Size(messenger->callbacklist); i++) { + MessengerRecipient *recipient = + parcArrayList_Get(messenger->callbacklist, i); + parcAssertTrue(recipient, "Recipient is null at index %zu", i); + + messengerRecipient_Deliver(recipient, missive_Acquire(missive)); + } + + // now let go of our reference to the missive + missive_Release(&missive); + } +} diff --git a/hicn-light/src/messenger/messenger.h b/hicn-light/src/messenger/messenger.h new file mode 100755 index 000000000..f945e7e72 --- /dev/null +++ b/hicn-light/src/messenger/messenger.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * The EventMessenger is the system that messages events between + * producers and consumers. + * + * Events are delivered in a deferred event cycle to avoid event callbacks + * firing when the event generator is still running. + */ + +#ifndef messenger_h +#define messenger_h + +#include <src/core/dispatcher.h> +#include <src/messenger/messengerRecipient.h> +#include <src/messenger/missive.h> + +struct messenger; +typedef struct messenger Messenger; + +/** + * @function eventmessenger_Create + * @abstract Creates an event notification system + * @discussion + * Typically there's only one of these managed by forwarder. + * + * @param dispatcher is the event dispatcher to use to schedule events. + */ +Messenger *messenger_Create(Dispatcher *dispatcher); + +/** + * @function eventMessenger_Destroy + * @abstract Destroys the messenger system, no notification is sent + */ +void messenger_Destroy(Messenger **messengerPtr); + +/** + * @function eventMessenger_Send + * @abstract Send an event message, takes ownership of the event memory + */ +void messenger_Send(Messenger *messenger, Missive *missive); + +/** + * @function eventMessenger_Register + * @abstract Receive all event messages + */ +void messenger_Register(Messenger *messenger, + const MessengerRecipient *recipient); + +/** + * @function eventMessenger_Unregister + * @abstract Stop receiving event messages + */ +void messenger_Unregister(Messenger *messenger, + const MessengerRecipient *recipient); +#endif // messenger_h diff --git a/hicn-light/src/messenger/messengerRecipient.c b/hicn-light/src/messenger/messengerRecipient.c new file mode 100755 index 000000000..14251f8eb --- /dev/null +++ b/hicn-light/src/messenger/messengerRecipient.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <parc/algol/parc_Memory.h> +#include <parc/assert/parc_Assert.h> +#include <src/config.h> +#include <stdio.h> + +#include <src/messenger/messenger.h> +#include <src/messenger/messengerRecipient.h> + +struct messenger_recipient { + void *context; + MessengerRecipientCallback *notify; +}; + +MessengerRecipient *messengerRecipient_Create( + void *recipientContext, MessengerRecipientCallback *recipientCallback) { + parcAssertNotNull(recipientCallback, + "Parameter recipientCallback must be non-null"); + + MessengerRecipient *recipient = + parcMemory_AllocateAndClear(sizeof(MessengerRecipient)); + parcAssertNotNull(recipient, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(MessengerRecipient)); + recipient->context = recipientContext; + recipient->notify = recipientCallback; + return recipient; +} + +void messengerRecipient_Destroy(MessengerRecipient **recipientPtr) { + parcAssertNotNull(recipientPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*recipientPtr, + "Parameter must dereference to non-null pointer"); + + parcMemory_Deallocate((void **)recipientPtr); + *recipientPtr = NULL; +} + +void *messengerRecipient_GetRecipientContext(MessengerRecipient *recipient) { + parcAssertNotNull(recipient, "Parameter must be non-null"); + + return recipient->context; +} + +void messengerRecipient_Deliver(MessengerRecipient *recipient, + Missive *missive) { + parcAssertNotNull(recipient, "Parameter must be non-null"); + recipient->notify(recipient, missive); +} diff --git a/hicn-light/src/messenger/messengerRecipient.h b/hicn-light/src/messenger/messengerRecipient.h new file mode 100755 index 000000000..66d8f40f5 --- /dev/null +++ b/hicn-light/src/messenger/messengerRecipient.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file messengerRecipient.h + * @brief A recipient represents the entity that will recieve a Missive from the + * Messenger. + * + * A recipient is identified by the pair (contenxt, callback). The context is + * the recipients context, such as it's object pointer. The callback is the + * function the recipient uses to receive a Missive. + * + * If the receiver is going to do a lot of work or potentially send other + * missives, the receiver should queue the received notifications and process + * them in its own slice. + * + * A recipient will receive a reference counted copy of the missive, so it must + * call + * {@link missive_Release} on it. + * + * + */ + +#ifndef messengerRecipient_h +#define messengerRecipient_h + +#include <src/messenger/missive.h> + +struct messenger_recipient; +typedef struct messenger_recipient MessengerRecipient; + +/** + * @typedef MessengerRecipientCallback + * @abstract A recipient implements a callback to receive Missives. + * @constant recipient The recipient to recieve the missive + * @constant missive The missive, recipient must call {@link missive_Release} on + * it + */ +typedef void(MessengerRecipientCallback)(MessengerRecipient *recipient, + Missive *missive); + +/** + * Creates a Recipient, which represents a reciever of missives. + * + * Creates a Recipient that can be registerd with the Messenger using {@link + * messenger_Register}. + * + * @param [in] recipientContext This pointer will be passed back to the + * recipient with each missive, may be NULL + * @param [in] recipientCallback The function that receives the missive, must be + * non-NULL. + * + * @return non-null A recipient object + */ +MessengerRecipient *messengerRecipient_Create( + void *recipientContext, MessengerRecipientCallback *recipientCallback); + +/** + * Destroys a recipient. You should unregister it first. + * + * Destroying a recipient does not unregister it, so be sure to call + * {@link messenger_Unregister} first. + * + * @param [in,out] recipientPtr Double pointer to the recipient to destroy, will + * be NULL'd. + */ +void messengerRecipient_Destroy(MessengerRecipient **recipientPtr); + +/** + * Returns the recipient context passed on Create + * + * @param [in] recipient The recipient object + * + * @return pointer The context pointer used to create the object, maybe NULL + */ +void *messengerRecipient_GetRecipientContext(MessengerRecipient *recipient); + +/** + * Delivers a Missive to the recipient + * + * Passes the missive to the recipients callback. + * + * A recipient will receive a reference counted copy of the missive, so it must + * call + * {@link missive_Release} on it. + * + * @param [in] recipient The receiver + * @param [in] missive The message to send + */ +void messengerRecipient_Deliver(MessengerRecipient *recipient, + Missive *missive); +#endif // messengerRecipient_h diff --git a/hicn-light/src/messenger/missive.c b/hicn-light/src/messenger/missive.c new file mode 100755 index 000000000..a8bcb0282 --- /dev/null +++ b/hicn-light/src/messenger/missive.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> +#include <parc/assert/parc_Assert.h> +#include <src/config.h> +#include <stdio.h> + +#include <src/messenger/missive.h> + +struct missive { + MissiveType missiveType; + unsigned connectionid; +}; + +parcObject_Override(Missive, PARCObject, .isLockable = false); + +Missive *missive_Create(MissiveType missiveType, unsigned connectionid) { + Missive *missive = parcObject_CreateInstance(Missive); + missive->missiveType = missiveType; + missive->connectionid = connectionid; + return missive; +} + +Missive *missive_Acquire(const Missive *missive) { + return parcObject_Acquire(missive); +} + +void missive_Release(Missive **missivePtr) { + parcObject_Release((void **)missivePtr); +} + +MissiveType missive_GetType(const Missive *missive) { + parcAssertNotNull(missive, "Parameter missive must be non-null"); + return missive->missiveType; +} + +unsigned missive_GetConnectionId(const Missive *missive) { + parcAssertNotNull(missive, "Parameter missive must be non-null"); + return missive->connectionid; +} diff --git a/hicn-light/src/messenger/missive.h b/hicn-light/src/messenger/missive.h new file mode 100755 index 000000000..33f3ef8b8 --- /dev/null +++ b/hicn-light/src/messenger/missive.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file missive.h + * @brief A Missive is a status message sent over a broadcast channel inside + * hicn-light + * + * Recipients use {@link messenger_Register} to receive missives. They are + * broadcast to all recipients. + * + */ +#ifndef missive_h +#define missive_h + +#include <src/messenger/missiveType.h> + +struct missive; +typedef struct missive Missive; + +/** + * Creates a Missive and sets the reference count to 1 + * + * A Missive may be sent to listeners of the Messenger to inform them of events + * on a connection id. + * + * @param [in] MissiveType The event type + * @param [in] connectionid The relevant conneciton id + * + * @return non-null A message + * @retrun null An error + */ +Missive *missive_Create(MissiveType missiveType, unsigned connectionid); + +/** + * Acquire a reference counted copy + * + * Increases the reference count by 1 and returns the original object. + * + * @param [in] missive An allocated missive + * + * @return non-null The original missive with increased reference count + */ +Missive *missive_Acquire(const Missive *missive); + +/** + * Releases a reference counted copy. + * + * If it is the last reference, the missive is freed. + * + * @param [in,out] missivePtr Double pointer to a missive, will be nulled. + */ +void missive_Release(Missive **missivePtr); + +/** + * Returns the type of the missive + * + * Returns the type of event the missive represents + * + * @param [in] missive An allocated missive + * + * @return MissiveType The event type + */ +MissiveType missive_GetType(const Missive *missive); + +/** + * Returns the connection ID of the missive + * + * An event is usually associated with a connection id (i.e. the I/O channel + * that originaged the event). + * + * @param [in] missive An allocated missive + * + * @return number The relevant connection id. + */ +unsigned missive_GetConnectionId(const Missive *missive); +#endif // missive_h diff --git a/hicn-light/src/messenger/missiveDeque.c b/hicn-light/src/messenger/missiveDeque.c new file mode 100755 index 000000000..418027d7a --- /dev/null +++ b/hicn-light/src/messenger/missiveDeque.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * A type-safe wrapper for Missives around a {@link PARCDeque}. We only + * implement the subset of functions used. + * + */ + +#include <parc/algol/parc_Deque.h> +#include <parc/algol/parc_Memory.h> +#include <parc/assert/parc_Assert.h> +#include <src/config.h> +#include <stdio.h> + +#include <src/messenger/missive.h> +#include <src/messenger/missiveDeque.h> + +struct missive_deque { + PARCDeque *queue; +}; + +MissiveDeque *missiveDeque_Create(void) { + MissiveDeque *missiveDeque = + parcMemory_AllocateAndClear(sizeof(MissiveDeque)); + parcAssertNotNull(missiveDeque, + "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(MissiveDeque)); + missiveDeque->queue = parcDeque_Create(); + return missiveDeque; +} + +void missiveDeque_Release(MissiveDeque **dequePtr) { + parcAssertNotNull(dequePtr, "Double pointer must be non-null"); + parcAssertNotNull(*dequePtr, "Double pointer must dereference to non-null"); + MissiveDeque *missiveDeque = *dequePtr; + + // flush the queue + while (!parcDeque_IsEmpty(missiveDeque->queue)) { + Missive *missive = missiveDeque_RemoveFirst(missiveDeque); + missive_Release(&missive); + } + + parcDeque_Release(&missiveDeque->queue); + parcMemory_Deallocate((void **)&missiveDeque); + *dequePtr = NULL; +} + +MissiveDeque *missiveDeque_Append(MissiveDeque *deque, Missive *missive) { + parcAssertNotNull(deque, "Parameter deque must be non-null"); + parcAssertNotNull(missive, "Parameter missive must be non-null"); + + parcDeque_Append(deque->queue, missive); + return deque; +} + +Missive *missiveDeque_RemoveFirst(MissiveDeque *deque) { + parcAssertNotNull(deque, "Parameter deque must be non-null"); + return (Missive *)parcDeque_RemoveFirst(deque->queue); +} + +size_t missiveDeque_Size(const MissiveDeque *deque) { + parcAssertNotNull(deque, "Parameter deque must be non-null"); + return parcDeque_Size(deque->queue); +} diff --git a/hicn-light/src/messenger/missiveDeque.h b/hicn-light/src/messenger/missiveDeque.h new file mode 100755 index 000000000..c6f955ce0 --- /dev/null +++ b/hicn-light/src/messenger/missiveDeque.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file missiveDeque + * @brief Double ended queue of Missives + * + * Used to queue Missives. This is a type-safe wrapper around {@link PARCDeque} + * + */ + +#ifndef missiveDeque_h +#define missiveDeque_h + +struct missive_deque; + +typedef struct missive_deque MissiveDeque; + +/** + * Create a `PARCDeque` instance with the default element equals function. + * + * The queue is created with no elements. + * + * The default element equals function is used by the `parcDeque_Equals` + * function and simply compares the values using the `==` operator. Users that + * need more sophisticated comparisons of the elements need to supply their own + * function via the `parcDeque_CreateCustom` function. + * + * @return non-NULL A pointer to a PARCDeque instance. + */ +MissiveDeque *missiveDeque_Create(void); + +void missiveDeque_Release(MissiveDeque **dequePtr); + +/** + * Appends the missive to the queue, taking ownership of the memory + */ +MissiveDeque *missiveDeque_Append(MissiveDeque *deque, Missive *missive); + +Missive *missiveDeque_RemoveFirst(MissiveDeque *deque); + +size_t missiveDeque_Size(const MissiveDeque *deque); +#endif // missiveDeque_h diff --git a/hicn-light/src/messenger/missiveType.h b/hicn-light/src/messenger/missiveType.h new file mode 100755 index 000000000..b0d9c7704 --- /dev/null +++ b/hicn-light/src/messenger/missiveType.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file missiveType + * @brief Defines what a Missive represents + * + * Currently, missives only carry information about the state of a connection + * (created, up, down, closed, destroyed). + * + */ + +#ifndef missiveType_h +#define missiveType_h + +/** + * @typedef Represents the state of a connection + * @abstract CREATE is the initial state. UP & DOWN are recurrent states. + * CLOSED is transient. DESTROYED is the terminal state. + * @constant MissiveType_ConnectionCreate Connection created (new) + * @constant MissiveType_ConnectionUp Connection is active and passing + * data + * @constant MissiveType_ConnectionDown Connection is inactive and cannot + * pass data + * @constant MissiveType_ConnectionClosed Connection closed and will be + * destroyed + * @constant MissiveType_ConnectionDestroyed Connection destroyed + * @discussion State transitions: + * initial -> CREATE + * CREATE -> (UP | DOWN) + * UP -> (DOWN | DESTROYED) + * DOWN -> (UP | CLOSED | DESTROYED) + * CLOSED -> DESTROYED + * DESTROYED -> terminal + */ +typedef enum { + MissiveType_ConnectionCreate, + MissiveType_ConnectionUp, + MissiveType_ConnectionDown, + MissiveType_ConnectionClosed, + MissiveType_ConnectionDestroyed +} MissiveType; +#endif // missiveType_h diff --git a/hicn-light/src/platforms/CMakeLists.txt b/hicn-light/src/platforms/CMakeLists.txt new file mode 100755 index 000000000..fcb4282ba --- /dev/null +++ b/hicn-light/src/platforms/CMakeLists.txt @@ -0,0 +1,31 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +if(${CMAKE_SYSTEM_NAME} STREQUAL "Android") + list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/android/system.c + ) +elseif(APPLE) + list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/darwin/system.c + ) +elseif( ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" ) + list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/linux/system.c + ) +endif() + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE)
\ No newline at end of file diff --git a/hicn-light/src/platforms/README.txt b/hicn-light/src/platforms/README.txt new file mode 100755 index 000000000..a1293944c --- /dev/null +++ b/hicn-light/src/platforms/README.txt @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +Operating system dependent modules. + diff --git a/hicn-light/src/platforms/android/system.c b/hicn-light/src/platforms/android/system.c new file mode 100755 index 000000000..68f99424b --- /dev/null +++ b/hicn-light/src/platforms/android/system.c @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <src/config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <unistd.h> + +//#define __USE_MISC +#include <net/if.h> + +// to get the list of arp types +#include <net/if_arp.h> + +// for the mac address +#include <netpacket/packet.h> + +#include <src/core/forwarder.h> +#include <src/utils/interfaceSet.h> + +#include <parc/assert/parc_Assert.h> + +#include "ifaddrs.h" + +/** + * Returns the MTU for a named interface + * + * On linux, we get the MTU by opening a socket and reading SIOCGIFMTU + * + * @param [in] ifname Interface name (e.g. "eth0") + * + * @retval number The MTU in bytes + * + * Example: + * @code + * <#example#> + * @endcode + */ +static int getMtu(const char *ifname) { + struct ifreq ifr; + int fd; + + fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + + strcpy(ifr.ifr_name, ifname); + ioctl(fd, SIOCGIFMTU, &ifr); + + close(fd); + return ifr.ifr_mtu; +} + +InterfaceSet *system_Interfaces(Forwarder *forwarder) { + InterfaceSet *set = interfaceSetCreate(); + + Logger *logger = forwarder_GetLogger(forwarder); + + // this is the dynamically allocated head of the list + struct ifaddrs *ifaddr; + int failure = getifaddrs(&ifaddr); + parcAssertFalse(failure, "Error getifaddrs: (%d) %s", errno, strerror(errno)); + + struct ifaddrs *next; + for (next = ifaddr; next != NULL; next = next->ifa_next) { + if ((next->ifa_addr == NULL) || ((next->ifa_flags & IFF_UP) == 0)) { + continue; + } + + Interface *iface = interfaceSetGetByName(set, next->ifa_name); + if (iface == NULL) { + unsigned mtu = (unsigned)getMtu(next->ifa_name); + + iface = interfaceCreate( + next->ifa_name, forwarder_GetNextConnectionId(forwarder), + next->ifa_flags & IFF_LOOPBACK, next->ifa_flags & IFF_MULTICAST, mtu); + + interfaceSetAdd(set, iface); + } + + int family = next->ifa_addr->sa_family; + switch (family) { + case AF_INET: { + Address *address = + addressCreateFromInet((struct sockaddr_in *)next->ifa_addr); + interfaceAddAddress(iface, address); + break; + } + + case AF_INET6: { + Address *address = + addressCreateFromInet6((struct sockaddr_in6 *)next->ifa_addr); + interfaceAddAddress(iface, address); + break; + } + + case AF_PACKET: { + struct sockaddr_ll *addr_ll = (struct sockaddr_ll *)next->ifa_addr; + + if (logger_IsLoggable(logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + logger_Log(logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "sockaddr_ll family %d proto %d ifindex %d hatype %d " + "pkttype %d halen %d", + addr_ll->sll_family, addr_ll->sll_protocol, + addr_ll->sll_ifindex, addr_ll->sll_hatype, + addr_ll->sll_pkttype, addr_ll->sll_halen); + } + + switch (addr_ll->sll_hatype) { + // list of the ARP hatypes we can extract a MAC address from + case ARPHRD_ETHER: + // fallthrough + case ARPHRD_IEEE802: { + Address *address = addressCreateFromLink( + (uint8_t *)addr_ll->sll_addr, addr_ll->sll_halen); + interfaceAddAddress(iface, address); + break; + } + default: + break; + } + + break; + } + } + } + + freeifaddrs(ifaddr); + return set; +} + +Address *system_GetMacAddressByName(Forwarder *forwarder, + const char *interfaceName) { + Address *linkAddress = NULL; + + InterfaceSet *interfaceSet = system_Interfaces(forwarder); + Interface *interface = interfaceSetGetByName(interfaceSet, interfaceName); + + if (interface) { + const AddressList *addressList = interfaceGetAddresses(interface); + + size_t length = addressListLength(addressList); + for (size_t i = 0; i < length && !linkAddress; i++) { + const Address *a = addressListGetItem(addressList, i); + if (addressGetType(a) == ADDR_LINK) { + linkAddress = addressCopy(a); + } + } + } + + interfaceSetDestroy(&interfaceSet); + + return linkAddress; +} + +unsigned system_InterfaceMtu(Forwarder *forwarder, const char *interfaceName) { + unsigned mtu = 0; + + InterfaceSet *interfaceSet = system_Interfaces(forwarder); + Interface *interface = interfaceSetGetByName(interfaceSet, interfaceName); + + if (interface) { + mtu = interfaceGetMTU(interface); + } + + interfaceSetDestroy(&interfaceSet); + + return mtu; +} diff --git a/hicn-light/src/platforms/darwin/system.c b/hicn-light/src/platforms/darwin/system.c new file mode 100755 index 000000000..b8ef80c63 --- /dev/null +++ b/hicn-light/src/platforms/darwin/system.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <ifaddrs.h> +#include <src/config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <sys/socket.h> + +#include <parc/assert/parc_Assert.h> + +#include <src/utils/interfaceSet.h> + +#include <src/core/forwarder.h> +#include <src/core/system.h> + +InterfaceSet *system_Interfaces(Forwarder *forwarder) { + InterfaceSet *set = interfaceSetCreate(); + + // this is the dynamically allocated head of the list + struct ifaddrs *ifaddr; + int failure = getifaddrs(&ifaddr); + parcAssertFalse(failure, "Error getifaddrs: (%d) %s", errno, strerror(errno)); + + struct ifaddrs *next; + for (next = ifaddr; next != NULL; next = next->ifa_next) { + if ((next->ifa_addr == NULL) || ((next->ifa_flags & IFF_UP) == 0)) { + continue; + } + + // This assumes the LINK address comes first so we can get the MTU + // when the interface is created. + + Interface *iface = interfaceSetGetByName(set, next->ifa_name); + if (iface == NULL) { + unsigned mtu = 0; + + if (next->ifa_data != NULL) { + struct if_data *ifdata = (struct if_data *)next->ifa_data; + mtu = ifdata->ifi_mtu; + } + + iface = interfaceCreate( + next->ifa_name, forwarder_GetNextConnectionId(forwarder), + next->ifa_flags & IFF_LOOPBACK, next->ifa_flags & IFF_MULTICAST, mtu); + + interfaceSetAdd(set, iface); + } + + int family = next->ifa_addr->sa_family; + switch (family) { + case AF_INET: { + Address *address = + addressCreateFromInet((struct sockaddr_in *)next->ifa_addr); + interfaceAddAddress(iface, address); + break; + } + + case AF_INET6: { + Address *address = + addressCreateFromInet6((struct sockaddr_in6 *)next->ifa_addr); + interfaceAddAddress(iface, address); + break; + } + + case AF_LINK: { + struct sockaddr_dl *addr_dl = (struct sockaddr_dl *)next->ifa_addr; + + // skip links with 0-length address + if (addr_dl->sdl_alen > 0) { + // addr_dl->sdl_data[12] contains the interface name followed by the + // MAC address, so need to offset in to the array past the interface + // name. + Address *address = addressCreateFromLink( + (uint8_t *)&addr_dl->sdl_data[addr_dl->sdl_nlen], + addr_dl->sdl_alen); + interfaceAddAddress(iface, address); + } + break; + } + } + } + + freeifaddrs(ifaddr); + + return set; +} + +Address *system_GetMacAddressByName(Forwarder *forwarder, + const char *interfaceName) { + Address *linkAddress = NULL; + + InterfaceSet *interfaceSet = system_Interfaces(forwarder); + Interface *interface = interfaceSetGetByName(interfaceSet, interfaceName); + + if (interface) { + const AddressList *addressList = interfaceGetAddresses(interface); + + size_t length = addressListLength(addressList); + for (size_t i = 0; i < length && !linkAddress; i++) { + const Address *a = addressListGetItem(addressList, i); + if (addressGetType(a) == ADDR_LINK) { + linkAddress = addressCopy(a); + } + } + } + + interfaceSetDestroy(&interfaceSet); + + return linkAddress; +} + +unsigned system_InterfaceMtu(Forwarder *forwarder, const char *interfaceName) { + unsigned mtu = 0; + + if (interfaceName) { + InterfaceSet *interfaceSet = system_Interfaces(forwarder); + Interface *interface = interfaceSetGetByName(interfaceSet, interfaceName); + + if (interface) { + mtu = interfaceGetMTU(interface); + } + + interfaceSetDestroy(&interfaceSet); + } + return mtu; +} diff --git a/hicn-light/src/platforms/linux/system.c b/hicn-light/src/platforms/linux/system.c new file mode 100755 index 000000000..fcf13becc --- /dev/null +++ b/hicn-light/src/platforms/linux/system.c @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <ifaddrs.h> +#include <src/config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <unistd.h> + +//#define __USE_MISC +#include <net/if.h> + +// to get the list of arp types +#include <net/if_arp.h> + +// for the mac address +#include <netpacket/packet.h> + +#include <src/core/forwarder.h> +#include <src/utils/interfaceSet.h> + +#include <parc/assert/parc_Assert.h> + +#include <src/utils/addressList.h> + +/** + * Returns the MTU for a named interface + * + * On linux, we get the MTU by opening a socket and reading SIOCGIFMTU + * + * @param [in] ifname Interface name (e.g. "eth0") + * + * @retval number The MTU in bytes + * + * Example: + * @code + * <#example#> + * @endcode + */ +static int getMtu(const char *ifname) { + struct ifreq ifr; + int fd; + + fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + + strcpy(ifr.ifr_name, ifname); + ioctl(fd, SIOCGIFMTU, &ifr); + + close(fd); + return ifr.ifr_mtu; +} + +InterfaceSet *system_Interfaces(Forwarder *forwarder) { + InterfaceSet *set = interfaceSetCreate(); + + Logger *logger = forwarder_GetLogger(forwarder); + + // this is the dynamically allocated head of the list + struct ifaddrs *ifaddr; + int failure = getifaddrs(&ifaddr); + parcAssertFalse(failure, "Error getifaddrs: (%d) %s", errno, strerror(errno)); + + struct ifaddrs *next; + for (next = ifaddr; next != NULL; next = next->ifa_next) { + if ((next->ifa_addr == NULL) || ((next->ifa_flags & IFF_UP) == 0)) { + continue; + } + + Interface *iface = interfaceSetGetByName(set, next->ifa_name); + if (iface == NULL) { + unsigned mtu = (unsigned)getMtu(next->ifa_name); + + iface = interfaceCreate( + next->ifa_name, forwarder_GetNextConnectionId(forwarder), + next->ifa_flags & IFF_LOOPBACK, next->ifa_flags & IFF_MULTICAST, mtu); + + interfaceSetAdd(set, iface); + } + + int family = next->ifa_addr->sa_family; + switch (family) { + case AF_INET: { + Address *address = + addressCreateFromInet((struct sockaddr_in *)next->ifa_addr); + interfaceAddAddress(iface, address); + break; + } + + case AF_INET6: { + Address *address = + addressCreateFromInet6((struct sockaddr_in6 *)next->ifa_addr); + interfaceAddAddress(iface, address); + break; + } + + case AF_PACKET: { + struct sockaddr_ll *addr_ll = (struct sockaddr_ll *)next->ifa_addr; + + if (logger_IsLoggable(logger, LoggerFacility_IO, PARCLogLevel_Debug)) { + logger_Log(logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__, + "sockaddr_ll family %d proto %d ifindex %d hatype %d " + "pkttype %d halen %d", + addr_ll->sll_family, addr_ll->sll_protocol, + addr_ll->sll_ifindex, addr_ll->sll_hatype, + addr_ll->sll_pkttype, addr_ll->sll_halen); + } + + switch (addr_ll->sll_hatype) { + // list of the ARP hatypes we can extract a MAC address from + case ARPHRD_ETHER: + // fallthrough + case ARPHRD_IEEE802: { + Address *address = addressCreateFromLink( + (uint8_t *)addr_ll->sll_addr, addr_ll->sll_halen); + interfaceAddAddress(iface, address); + break; + } + default: + break; + } + + break; + } + } + } + + freeifaddrs(ifaddr); + return set; +} + +Address *system_GetMacAddressByName(Forwarder *forwarder, + const char *interfaceName) { + Address *linkAddress = NULL; + + InterfaceSet *interfaceSet = system_Interfaces(forwarder); + Interface *interface = interfaceSetGetByName(interfaceSet, interfaceName); + + if (interface) { + const AddressList *addressList = interfaceGetAddresses(interface); + + size_t length = addressListLength(addressList); + for (size_t i = 0; i < length && !linkAddress; i++) { + const Address *a = addressListGetItem(addressList, i); + if (addressGetType(a) == ADDR_LINK) { + linkAddress = addressCopy(a); + } + } + } + + interfaceSetDestroy(&interfaceSet); + + return linkAddress; +} + +unsigned system_InterfaceMtu(Forwarder *forwarder, const char *interfaceName) { + unsigned mtu = 0; + + InterfaceSet *interfaceSet = system_Interfaces(forwarder); + Interface *interface = interfaceSetGetByName(interfaceSet, interfaceName); + + if (interface) { + mtu = interfaceGetMTU(interface); + } + + interfaceSetDestroy(&interfaceSet); + + return mtu; +} diff --git a/hicn-light/src/processor/CMakeLists.txt b/hicn-light/src/processor/CMakeLists.txt new file mode 100755 index 000000000..b7eeabe3b --- /dev/null +++ b/hicn-light/src/processor/CMakeLists.txt @@ -0,0 +1,40 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/fibEntry.h + ${CMAKE_CURRENT_SOURCE_DIR}/fibEntryList.h + ${CMAKE_CURRENT_SOURCE_DIR}/messageProcessor.h + ${CMAKE_CURRENT_SOURCE_DIR}/hashTableFunction.h + ${CMAKE_CURRENT_SOURCE_DIR}/pit.h + ${CMAKE_CURRENT_SOURCE_DIR}/fib.h + ${CMAKE_CURRENT_SOURCE_DIR}/pitEntry.h + ${CMAKE_CURRENT_SOURCE_DIR}/pitVerdict.h + ${CMAKE_CURRENT_SOURCE_DIR}/pitStandard.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/hashTableFunction.c + ${CMAKE_CURRENT_SOURCE_DIR}/fib.c + ${CMAKE_CURRENT_SOURCE_DIR}/fibEntry.c + ${CMAKE_CURRENT_SOURCE_DIR}/fibEntryList.c + ${CMAKE_CURRENT_SOURCE_DIR}/messageProcessor.c + ${CMAKE_CURRENT_SOURCE_DIR}/pit.c + ${CMAKE_CURRENT_SOURCE_DIR}/pitEntry.c + ${CMAKE_CURRENT_SOURCE_DIR}/pitStandard.c +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) diff --git a/hicn-light/src/processor/fib.c b/hicn-light/src/processor/fib.c new file mode 100755 index 000000000..33d31fd8a --- /dev/null +++ b/hicn-light/src/processor/fib.c @@ -0,0 +1,448 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> +#include <stdio.h> + +#include <src/processor/fib.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Network.h> + +#include <parc/assert/parc_Assert.h> + +#define NULL_POS 128 +#define MSB_POS 127 + +struct node; +typedef struct node FibNode; + +struct node { + FibNode *left; + FibNode *right; + FibEntry *entry; + unsigned pos; +}; + +struct fib { + FibNode *root; + unsigned size; +}; + +// ===================================================== +// Public API + +FibNode *_createNode(FibNode *left, FibNode *right, FibEntry *entry, + unsigned pos) { + FibNode *n = parcMemory_AllocateAndClear(sizeof(FibNode)); + parcAssertNotNull(n, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(FibNode)); + + n->left = left; + n->right = right; + n->entry = entry; + n->pos = pos; + + return n; +} + +FIB *fib_Create() { + FIB *hicnFib = parcMemory_AllocateAndClear(sizeof(FIB)); + parcAssertNotNull(hicnFib, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(FIB)); + + hicnFib->root = + _createNode(NULL, NULL, NULL, + NULL_POS); // the pos will decrease going down in the trie + hicnFib->root->left = hicnFib->root; + hicnFib->root->right = hicnFib->root; + + hicnFib->size = 0; + + return hicnFib; +} + +void _destroyNode(FibNode *n) { + fibEntry_Release(&n->entry); + parcMemory_Deallocate((void **)&n); + n = NULL; +} + +void _destroyFib(FIB *fib) { + // XXX + // to be done + return; +} + +void fib_Destroy(FIB **fibPtr) { + parcAssertNotNull(fibPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*fibPtr, "Parameter must dereference to non-null pointer"); + + FIB *fib = *fibPtr; + + _destroyFib(fib); + parcMemory_Deallocate((void **)&fib); + *fibPtr = NULL; +} + +void fib_Add(FIB *fib, FibEntry *entry) { + parcAssertNotNull(fib, "Parameter must be non-null"); + parcAssertNotNull(entry, "Parameter must be non-null"); + + NameBitvector *name = name_GetContentName(fibEntry_GetPrefix(entry)); + + // search the name + FibNode *prev = fib->root; + FibNode *curr; + + if (nameBitvector_testBit(name, MSB_POS)) { + curr = fib->root->right; + } else { + curr = fib->root->left; + } + + while (prev->pos > curr->pos) { + prev = curr; + if (nameBitvector_testBit(name, curr->pos)) { + curr = curr->right; + } else { + curr = curr->left; + } + } + + if (curr->entry != NULL && + nameBitvector_Equals( + name, name_GetContentName(fibEntry_GetPrefix(curr->entry)))) { + // there is already an entry with this name + // do nothing. Before call ADD we should check + // if the node exists, and, in that case update it + return; + } + + // if the name is not in the FIB search for the first different bit between + // the new name to add and the node found in the trie + uint8_t pos = MSB_POS; + if (curr->entry != NULL) + pos = nameBitvector_firstDiff( + name, name_GetContentName(fibEntry_GetPrefix(curr->entry))); + + // reset pointer and search the insertion point + prev = fib->root; + if (nameBitvector_testBit(name, MSB_POS)) + curr = fib->root->right; + else + curr = fib->root->left; + + while (prev->pos > curr->pos && curr->pos > pos) { + prev = curr; + if (nameBitvector_testBit(name, curr->pos)) { + curr = curr->right; + } else { + curr = curr->left; + } + } + + // insert the node + fib->size++; + FibNode *n = _createNode(NULL, NULL, entry, pos); + + if (nameBitvector_testBit(name, pos)) { + n->left = curr; + n->right = n; + } else { + n->left = n; + n->right = curr; + } + + uint8_t new_pos = prev->pos; + if (new_pos == NULL_POS) new_pos = MSB_POS; + + if (nameBitvector_testBit(name, new_pos)) { + prev->right = n; + } else { + prev->left = n; + } +} + +FibEntry *fib_Contains(const FIB *fib, const Name *prefix) { + parcAssertNotNull(fib, "Parameter must be non-null"); + parcAssertNotNull(prefix, "Parameter must be non-null"); + + NameBitvector *name = name_GetContentName(prefix); + + // this is the same as the first part of the add function + // we cannnot call this function inside the add because + // we need the pointer prev and curr for the insertion + + FibNode *prev = fib->root; + FibNode *curr; + + if (nameBitvector_testBit(name, MSB_POS)) + curr = fib->root->right; + else + curr = fib->root->left; + + while (prev->pos > curr->pos) { + prev = curr; + + if (nameBitvector_testBit(name, curr->pos)) { + curr = curr->right; + } else { + curr = curr->left; + } + } + + if (curr->entry != NULL && + nameBitvector_Equals( + name, name_GetContentName(fibEntry_GetPrefix(curr->entry)))) { + return curr->entry; + } else { + return NULL; + } +} + +void _removeNode(FIB *fib, const Name *prefix) { + parcAssertNotNull(fib, "Parameter must be non-null"); + parcAssertNotNull(prefix, "Parameter must be non-null"); + + FibNode *grand = NULL; // grandparent + FibNode *prev = + fib->root; // parent: it will points to curr of the next hop in the trie + FibNode *curr; // current node: the node to remove + + NameBitvector *name = name_GetContentName(prefix); + + if (nameBitvector_testBit(name, MSB_POS)) { + curr = fib->root->right; + } else { + curr = fib->root->left; + } + + // in the first loop we always search the node to remove + while (prev->pos > curr->pos) { + grand = prev; + prev = curr; + + if (nameBitvector_testBit(name, curr->pos)) { + curr = curr->right; + } else { + curr = curr->left; + } + } + + if (!nameBitvector_Equals( + name, name_GetContentName(fibEntry_GetPrefix(curr->entry)))) { + // the node does not exists + return; + } + + // search for the real parent of curr (*tmpPrev) + // prev points to curr or next node in the trie + // this is because of the loopback links + + FibNode *tmpPrev = fib->root; + FibNode *tmpCurr; + + if (nameBitvector_testBit(name, MSB_POS)) { + tmpCurr = fib->root->right; + } else { + tmpCurr = fib->root->left; + } + + // here we compare pointer so we are sure to stop at the right potion + while (tmpCurr != curr) { + tmpPrev = tmpCurr; + + if (nameBitvector_testBit(name, tmpCurr->pos)) { + tmpCurr = tmpCurr->right; + } else { + tmpCurr = tmpCurr->left; + } + } + + // now curr is the node to remove and tmpPrev is the real parent of curr + + if (curr == prev) { + // this is the case where curr is a leaf node + FibNode *next; // child of curr (the loopback) + + if (nameBitvector_testBit(name, curr->pos)) { + next = curr->left; + } else { + next = curr->right; + } + + if (nameBitvector_testBit(name, tmpPrev->pos)) { + tmpPrev->right = next; + } else { + tmpPrev->left = next; + } + + } else { + // curr is an internal node + FibNode *next; // child of prev (loopback) + + if (nameBitvector_testBit(name, prev->pos)) { + next = prev->left; + } else { + next = prev->right; + } + + if (nameBitvector_testBit(name, grand->pos)) { + grand->right = next; + } else { + grand->left = next; + } + + if (nameBitvector_testBit(name, tmpPrev->pos)) { + tmpPrev->right = prev; + } else { + tmpPrev->left = prev; + } + + prev->left = curr->left; + prev->right = curr->right; + prev->pos = curr->pos; + } + + fib->size--; + _destroyNode(curr); +} + +void fib_Remove(FIB *fib, const Name *name, unsigned connId) { + parcAssertNotNull(fib, "Parameter must be non-null"); + parcAssertNotNull(name, "Parameter must be non-null"); + + FibEntry *entry = fib_Contains(fib, name); + + if (entry == NULL) { + return; + } + + fibEntry_RemoveNexthopByConnectionId(entry, connId); + if (fibEntry_NexthopCount(entry) == 0) { + _removeNode(fib, name); + } +} + +void _removeConnectionId(FibNode *n, unsigned pos, unsigned connectionId, + FibEntryList *list) { + if (n->pos < pos) { + fibEntry_RemoveNexthopByConnectionId(n->entry, connectionId); + if (fibEntry_NexthopCount(n->entry) == 0) { + fibEntryList_Append(list, n->entry); + } + _removeConnectionId(n->left, n->pos, connectionId, list); + _removeConnectionId(n->right, n->pos, connectionId, list); + } +} + +void fib_RemoveConnectionId(FIB *fib, unsigned connectionId) { + parcAssertNotNull(fib, "Parameter must be non-null"); + + // 1 - we vist the tree to remove the connection id + // 2 - during the visit we collect the fib entry with 0 nexthop + // 3 - after the visit we remove this entries + + FibEntryList *list = fibEntryList_Create(); + + _removeConnectionId(fib->root->left, fib->root->pos, connectionId, list); + _removeConnectionId(fib->root->right, fib->root->pos, connectionId, list); + + for (int i = 0; i < fibEntryList_Length(list); i++) { + _removeNode(fib, fibEntry_GetPrefix(fibEntryList_Get(list, i))); + } + + fibEntryList_Destroy(&list); +} + +size_t fib_Length(const FIB *fib) { + parcAssertNotNull(fib, "Parameter must be non-null"); + return fib->size; +} + +FibEntry *fib_Match(const FIB *fib, const Message *interestMessage) { + parcAssertNotNull(fib, "Parameter must be non-null"); + parcAssertNotNull(interestMessage, "Parameter must be non-null"); + + NameBitvector *name = name_GetContentName(message_GetName(interestMessage)); + + FibNode *prev = fib->root; + FibNode *curr; + + FibNode *match = NULL; + unsigned len = 0; + + if (nameBitvector_testBit(name, MSB_POS)) + curr = fib->root->right; + else + curr = fib->root->left; + + while (prev->pos > curr->pos) { + prev = curr; + + if (curr->entry != NULL) { + if (nameBitvector_StartsWith( + name, name_GetContentName(fibEntry_GetPrefix(curr->entry))) && + nameBitvector_GetLength( + name_GetContentName(fibEntry_GetPrefix(curr->entry))) > len) { + match = curr; + len = nameBitvector_GetLength( + name_GetContentName(fibEntry_GetPrefix(curr->entry))); + } + } + + if (nameBitvector_testBit(name, curr->pos)) + curr = curr->right; + else + curr = curr->left; + } + + if (curr->entry != NULL) { + if (nameBitvector_StartsWith( + name, name_GetContentName(fibEntry_GetPrefix(curr->entry))) && + nameBitvector_GetLength( + name_GetContentName(fibEntry_GetPrefix(curr->entry))) > len) { + match = curr; + len = nameBitvector_GetLength( + name_GetContentName(fibEntry_GetPrefix(curr->entry))); + } + } + + if (match != NULL && match->entry != NULL) { + return match->entry; + } else { + return NULL; + } +} + +void _collectFibEntries(FibNode *n, int pos, FibEntryList *list) { + if (n->pos < pos) { + fibEntryList_Append(list, n->entry); + _collectFibEntries(n->left, n->pos, list); + _collectFibEntries(n->right, n->pos, list); + } +} + +FibEntryList *fib_GetEntries(const FIB *fib) { + parcAssertNotNull(fib, "Parameter must be non-null"); + + FibEntryList *list = fibEntryList_Create(); + + _collectFibEntries(fib->root->left, fib->root->pos, list); + _collectFibEntries(fib->root->right, fib->root->pos, list); + + return list; +} diff --git a/hicn-light/src/processor/fib.h b/hicn-light/src/processor/fib.h new file mode 100755 index 000000000..4409419db --- /dev/null +++ b/hicn-light/src/processor/fib.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef fib_h +#define fib_h + +#include <src/core/message.h> +#include <src/core/name.h> +#include <src/processor/fibEntry.h> +#include <src/processor/fibEntryList.h> + +struct fib; +typedef struct fib FIB; + +FIB *fib_Create(); + +void fib_Destroy(FIB **fibPtr); + +void fib_Add(FIB *fib, FibEntry *node); + +FibEntry *fib_Contains(const FIB *fib, const Name *prefix); + +void fib_Remove(FIB *fib, const Name *prefix, unsigned connId); + +void fib_RemoveConnectionId(FIB *fib, unsigned connectionId); + +FibEntry *fib_Match(const FIB *fib, const Message *interestMessage); + +size_t fib_Length(const FIB *fib); + +FibEntryList *fib_GetEntries(const FIB *fib); +#endif // fib_h diff --git a/hicn-light/src/processor/fibEntry.c b/hicn-light/src/processor/fibEntry.c new file mode 100755 index 000000000..bb877030f --- /dev/null +++ b/hicn-light/src/processor/fibEntry.c @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> +#include <stdio.h> + +#include <src/core/numberSet.h> +#include <src/processor/fibEntry.h> + +#include <src/core/nameBitvector.h> + +#include <src/strategies/loadBalancer.h> +#include <src/strategies/loadBalancerWithPD.h> +#include <src/strategies/rnd.h> +#include <src/strategies/rndSegment.h> +#include <src/strategies/strategyImpl.h> +#ifdef WITH_MAPME +#include <parc/algol/parc_HashMap.h> +#include <src/core/ticks.h> +#endif /* WITH_MAPME */ + +#include <parc/algol/parc_Memory.h> +#include <parc/assert/parc_Assert.h> + +#include <src/utils/commands.h> + +struct fib_entry { + Name *name; + unsigned refcount; + StrategyImpl *fwdStrategy; +#ifdef WITH_MAPME + void *userData; + void (*userDataRelease)(void **userData); +#endif /* WITH_MAPME */ +}; + +FibEntry *fibEntry_Create(Name *name, strategy_type fwdStrategy) { + FibEntry *fibEntry = parcMemory_AllocateAndClear(sizeof(FibEntry)); + parcAssertNotNull(fibEntry, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(FibEntry)); + fibEntry->name = name_Acquire(name); + + if (fwdStrategy) { + switch (fwdStrategy) { + case SET_STRATEGY_LOADBALANCER: + fibEntry->fwdStrategy = strategyLoadBalancer_Create(); + break; + + case SET_STRATEGY_RANDOM_PER_DASH_SEGMENT: + fibEntry->fwdStrategy = strategyRndSegment_Create(); + break; + + case SET_STRATEGY_LOADBALANCER_WITH_DELAY: + fibEntry->fwdStrategy = strategyLoadBalancerWithPD_Create(); + break; + + default: + // LB is the defualt strategy + fibEntry->fwdStrategy = strategyLoadBalancer_Create(); + // the LB strategy is the default one + // other strategies can be set using the appropiate function + break; + } + + } else { + fibEntry->fwdStrategy = strategyLoadBalancer_Create(); + } + + fibEntry->refcount = 1; + +#ifdef WITH_MAPME + fibEntry->userData = NULL; + fibEntry->userDataRelease = NULL; +#endif /* WITH_MAPME */ + + return fibEntry; +} + +FibEntry *fibEntry_Acquire(const FibEntry *fibEntry) { + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null"); + FibEntry *copy = (FibEntry *)fibEntry; + copy->refcount++; + return copy; +} + +void fibEntry_Release(FibEntry **fibEntryPtr) { + FibEntry *fibEntry = *fibEntryPtr; + parcAssertTrue(fibEntry->refcount > 0, "Illegal state: refcount is 0"); + fibEntry->refcount--; + if (fibEntry->refcount == 0) { + name_Release(&fibEntry->name); + fibEntry->fwdStrategy->destroy(&(fibEntry->fwdStrategy)); +#ifdef WITH_MAPME + if (fibEntry->userData) { + fibEntry->userDataRelease(&fibEntry->userData); + } +#endif /* WITH_MAPME */ + parcMemory_Deallocate((void **)&fibEntry); + } + *fibEntryPtr = NULL; +} + +void fibEntry_SetStrategy(FibEntry *fibEntry, strategy_type strategy) { + StrategyImpl *fwdStrategyImpl; + + switch (strategy) { + case SET_STRATEGY_LOADBALANCER: + fwdStrategyImpl = strategyLoadBalancer_Create(); + break; + + case SET_STRATEGY_RANDOM_PER_DASH_SEGMENT: + fwdStrategyImpl = strategyRndSegment_Create(); + break; + + case SET_STRATEGY_LOADBALANCER_WITH_DELAY: + fwdStrategyImpl = strategyLoadBalancerWithPD_Create(); + break; + + default: + // LB is the defualt strategy + fwdStrategyImpl = strategyLoadBalancer_Create(); + // the LB strategy is the default one + // other strategies can be set using the appropiate function + break; + } + + const NumberSet *nexthops = fibEntry_GetNexthops(fibEntry); + unsigned size = fibEntry_NexthopCount(fibEntry); + for (unsigned i = 0; i < size; i++) { + fwdStrategyImpl->addNexthop(fwdStrategyImpl, + numberSet_GetItem(nexthops, i)); + } + fibEntry->fwdStrategy->destroy(&(fibEntry->fwdStrategy)); + fibEntry->fwdStrategy = fwdStrategyImpl; +} +void fibEntry_AddNexthop(FibEntry *fibEntry, unsigned connectionId) { + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null"); + fibEntry->fwdStrategy->addNexthop(fibEntry->fwdStrategy, connectionId); +} + +void fibEntry_RemoveNexthopByConnectionId(FibEntry *fibEntry, + unsigned connectionId) { + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null"); + fibEntry->fwdStrategy->removeNexthop(fibEntry->fwdStrategy, connectionId); +} + +size_t fibEntry_NexthopCount(const FibEntry *fibEntry) { + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null"); + return fibEntry->fwdStrategy->countNexthops(fibEntry->fwdStrategy); +} + +const NumberSet *fibEntry_GetNexthops(const FibEntry *fibEntry) { + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null"); + return fibEntry->fwdStrategy->returnNexthops(fibEntry->fwdStrategy); +} + +const NumberSet *fibEntry_GetNexthopsFromForwardingStrategy( + const FibEntry *fibEntry, const Message *interestMessage) { + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null"); + return fibEntry->fwdStrategy->lookupNexthop(fibEntry->fwdStrategy, + interestMessage); +} + +void fibEntry_ReceiveObjectMessage(const FibEntry *fibEntry, + const NumberSet *egressId, + const Message *objectMessage, Ticks rtt) { + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null"); + fibEntry->fwdStrategy->receiveObject(fibEntry->fwdStrategy, egressId, + objectMessage, rtt); +} + +void fibEntry_OnTimeout(const FibEntry *fibEntry, const NumberSet *egressId) { + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null"); + fibEntry->fwdStrategy->onTimeout(fibEntry->fwdStrategy, egressId); +} + +Name *fibEntry_GetPrefix(const FibEntry *fibEntry) { + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null"); + return fibEntry->name; + // return metisName_Acquire(fibEntry->name); +} + +strategy_type fibEntry_GetFwdStrategyType(const FibEntry *fibEntry) { + return fibEntry->fwdStrategy->getStrategy(fibEntry->fwdStrategy); +} + +StrategyImpl *fibEntry_GetFwdStrategy(const FibEntry *fibEntry) { + return fibEntry->fwdStrategy; +} + +#ifdef WITH_MAPME + +void fibEntry_AddNexthopByConnectionId(FibEntry *fibEntry, + unsigned connectionId) { + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null"); + fibEntry->fwdStrategy->addNexthop(fibEntry->fwdStrategy, connectionId); +} + +void *fibEntry_getUserData(const FibEntry *fibEntry) { + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null"); + return fibEntry->userData; +} + +void fibEntry_setUserData(FibEntry *fibEntry, const void *userData, + void (*userDataRelease)(void **)) { + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null"); + fibEntry->userData = (void *)userData; + fibEntry->userDataRelease = userDataRelease; +} + +#endif /* WITH_MAPME */ diff --git a/hicn-light/src/processor/fibEntry.h b/hicn-light/src/processor/fibEntry.h new file mode 100755 index 000000000..3bcac3884 --- /dev/null +++ b/hicn-light/src/processor/fibEntry.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file fibEntry.h + * @brief A forwarding entry in the FIB table + * + * A Forwarding Information Base (FIB) entry (FibEntry) is a + * set of nexthops for a name. It also indicates the forwarding strategy. + * + * Each nexthop contains the ConnectionId assocaited with it. This could be + * something specific like a MAC address or point-to-point tunnel. Or, it + * could be something general like a MAC group address or ip multicast overlay. + * + * See strategy.h for a description of forwarding strategies. + * In short, a strategy is the algorithm used to select one or more nexthops + * from the set of available nexthops. + * + * Each nexthop also contains a void* to a forwarding strategy data container. + * This allows a strategy to keep proprietary information about each nexthop. + * + * + */ + +#ifndef fibEntry_h +#define fibEntry_h + +#include <src/core/name.h> +#include <src/strategies/strategyImpl.h> + +#ifdef WITH_MAPME +#include <parc/algol/parc_EventTimer.h> +#include <parc/algol/parc_Iterator.h> +#endif /* WITH_MAPME */ + +struct fib_entry; +typedef struct fib_entry FibEntry; + +FibEntry *fibEntry_Create(Name *name, strategy_type fwdStrategy); + +/** + * Decrements the reference count by one, and destroys the memory after last + * release + * + */ +void fibEntry_Release(FibEntry **fibEntryPtr); + +/** + * Returns a reference counted copy of the fib entry + * + * The reference count is increased by one. The returned value must be + * released via fibEnty_Release(). + * + * @param [in] fibEntry An allocated FibEntry + * + * @return non-null A reference counted copy of the fibEntry + * + */ +FibEntry *fibEntry_Acquire(const FibEntry *fibEntry); + +void fibEntry_SetStrategy(FibEntry *fibEntry, strategy_type strategy); + +void fibEntry_AddNexthop(FibEntry *fibEntry, unsigned connectionId); + +void fibEntry_RemoveNexthopByConnectionId(FibEntry *fibEntry, + unsigned connectionId); + +size_t fibEntry_NexthopCount(const FibEntry *fibEntry); + +/** + * @function fibEntry_GetNexthops + * @abstract Returns the nexthop set of the FIB entry. You must Acquire if it + * will be saved. + * @discussion + * Returns the next hop set for the FIB entry. + */ +const NumberSet *fibEntry_GetNexthops(const FibEntry *fibEntry); + +const NumberSet *fibEntry_GetNexthopsFromForwardingStrategy( + const FibEntry *fibEntry, const Message *interestMessage); + +void fibEntry_ReceiveObjectMessage(const FibEntry *fibEntry, + const NumberSet *egressId, + const Message *objectMessage, Ticks rtt); + +void fibEntry_OnTimeout(const FibEntry *fibEntry, const NumberSet *egressId); + +strategy_type fibEntry_GetFwdStrategyType(const FibEntry *fibEntry); + +StrategyImpl *fibEntry_GetFwdStrategy(const FibEntry *fibEntry); + +/** + * @function fibEntry_GetPrefix + * @abstract Returns a copy of the prefix. + * @return A reference counted copy that you must destroy + */ +Name *fibEntry_GetPrefix(const FibEntry *fibEntry); + +#ifdef WITH_MAPME + +/** + * @function fibEntry_AddNexthopByConnectionId + * @abstract Adds a next hop directly from the connection id. + * @param [in] fibEntry - Pointer to the FIB entry. + * @return The sequence number stored in the FIB entry. + */ +void fibEntry_AddNexthopByConnectionId(FibEntry *fibEntry, + unsigned connectionId); + +/** + * @function fibEntry_getUserData + * @abstract Returns user data associated to the FIB entry. + * @param [in] fibEntry - Pointer to the FIB entry. + * @return User data as a void pointer + */ +void *fibEntry_getUserData(const FibEntry *fibEntry); + +/** + * @function fibEntry_getUserData + * @abstract Associates user data and release callback to a FIB entry. + * @param [in] fibEntry - Pointer to the FIB entry. + * @param [in] userData - Generic pointer to user data + * @param [in@ userDataRelease - Callback used to release user data upon change + * of FIB entry removal. + */ +void fibEntry_setUserData(FibEntry *fibEntry, const void *userData, + void (*userDataRelease)(void **)); + +#endif /* WITH_MAPME */ + +#endif // fibEntry_h diff --git a/hicn-light/src/processor/fibEntryList.c b/hicn-light/src/processor/fibEntryList.c new file mode 100755 index 000000000..2221fa614 --- /dev/null +++ b/hicn-light/src/processor/fibEntryList.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> +#include <stdio.h> +#include <stdlib.h> + +#include <parc/algol/parc_ArrayList.h> +#include <parc/algol/parc_Memory.h> +#include <parc/assert/parc_Assert.h> +#include <src/processor/fibEntryList.h> + +struct fib_entry_list { + PARCArrayList *listOfFibEntries; +}; + +static void fibEntryList_ListDestroyer(void **voidPtr) { + FibEntry **entryPtr = (FibEntry **)voidPtr; + fibEntry_Release(entryPtr); +} + +FibEntryList *fibEntryList_Create() { + FibEntryList *fibEntryList = + parcMemory_AllocateAndClear(sizeof(FibEntryList)); + parcAssertNotNull(fibEntryList, + "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(FibEntryList)); + fibEntryList->listOfFibEntries = + parcArrayList_Create(fibEntryList_ListDestroyer); + return fibEntryList; +} + +void fibEntryList_Destroy(FibEntryList **listPtr) { + parcAssertNotNull(listPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*listPtr, "Parameter must dereference to non-null pointer"); + + FibEntryList *list = *listPtr; + parcArrayList_Destroy(&list->listOfFibEntries); + parcMemory_Deallocate((void **)&list); + listPtr = NULL; +} + +void fibEntryList_Append(FibEntryList *list, FibEntry *fibEntry) { + parcAssertNotNull(list, "Parameter list must be non-null pointer"); + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null pointer"); + + FibEntry *copy = fibEntry_Acquire(fibEntry); + parcArrayList_Add(list->listOfFibEntries, copy); +} + +size_t fibEntryList_Length(const FibEntryList *list) { + parcAssertNotNull(list, "Parameter list must be non-null pointer"); + return parcArrayList_Size(list->listOfFibEntries); +} + +const FibEntry *fibEntryList_Get(const FibEntryList *list, size_t index) { + parcAssertNotNull(list, "Parameter list must be non-null pointer"); + FibEntry *entry = parcArrayList_Get(list->listOfFibEntries, index); + return entry; +} diff --git a/hicn-light/src/processor/fibEntryList.h b/hicn-light/src/processor/fibEntryList.h new file mode 100755 index 000000000..0f6066435 --- /dev/null +++ b/hicn-light/src/processor/fibEntryList.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file fibEntryList.h + * @brief A typesafe list of FibEntry + * + * <#Detailed Description#> + * + */ + +#ifndef fibEntryList_h +#define fibEntryList_h + +#include <src/processor/fibEntry.h> + +struct fib_entry_list; +typedef struct fib_entry_list FibEntryList; + +/** + * Creates an emtpy FIB entry list + * + * Must be destroyed with fibEntryList_Destroy. + * + * @retval non-null An allocated FibEntryList + * @retval null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +FibEntryList *fibEntryList_Create(void); + +/** + * @function FibEntryList_Detroy + * @abstract Destroys the list and all entries. + * @discussion + * <#Discussion#> + * + * @param <#param1#> + */ +void fibEntryList_Destroy(FibEntryList **listPtr); + +/** + * @function fibEntryList_Append + * @abstract Will store a reference counted copy of the entry. + * @discussion + * Will create and store a reference counted copy. You keep ownership + * of the parameter <code>fibEntry</code>. + * + * @param <#param1#> + * @return <#return#> + */ +void fibEntryList_Append(FibEntryList *list, FibEntry *fibEntry); + +/** + * Returns the number of entries in the list + * + * <#Paragraphs Of Explanation#> + * + * @param [in] list An allocated FibEntryList + * + * @retval number The number of entries in the list + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t fibEntryList_Length(const FibEntryList *list); + +/** + * @function fibEntryList_Get + * @abstract Gets an element. This is the internal reference, do not destroy. + * @discussion + * Returns an internal reference from the list. You must not destroy it. + * Will assert if you go off the end of the list. + * + * @param <#param1#> + * @return <#return#> + */ +const FibEntry *fibEntryList_Get(const FibEntryList *list, size_t index); +#endif // fibEntryList_h diff --git a/hicn-light/src/processor/hashTableFunction.c b/hicn-light/src/processor/hashTableFunction.c new file mode 100755 index 000000000..6e70ef91a --- /dev/null +++ b/hicn-light/src/processor/hashTableFunction.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> +#include <stdio.h> + +#include <parc/algol/parc_Hash.h> +#include <parc/algol/parc_Memory.h> + +#include <src/core/message.h> +#include <src/processor/hashTableFunction.h> + +#include <parc/assert/parc_Assert.h> + +// ====================================================================== +// Hash table key functions +// We use a Message as the key data type + +bool hashTableFunction_MessageNameEquals(const void *messageA, + const void *messageB) { + const Message *a = (const Message *)messageA; + const Message *b = (const Message *)messageB; + + return name_Equals(message_GetName(a), message_GetName(b)); +} + +HashCodeType hashTableFunction_MessageNameHashCode(const void *messageA) { + const Message *message = (const Message *)messageA; + Name *name = message_GetName(message); + + // we want the cumulative hash for the whole name + uint32_t hash = name_HashCode(name); + + return hash; +}
\ No newline at end of file diff --git a/hicn-light/src/processor/hashTableFunction.h b/hicn-light/src/processor/hashTableFunction.h new file mode 100755 index 000000000..eb9989086 --- /dev/null +++ b/hicn-light/src/processor/hashTableFunction.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file hashTableFunction.h + * @brief These functions are used in PARCHashCodeTables by the + * MatchingRulesTable and ContentStore and PIT. They perform the equality + * and has generation needed by the PARCHashCodeTable. + * + */ +#ifndef hashTableFunction_h +#define hashTableFunction_h + +#include <parc/algol/parc_HashCodeTable.h> + +// ========================================================== +// These functions operate on a message as the key in the HashTable. +// The functions use void * rather than message instances in the function +// signature because it is using generic has code tables from PARC Library + +/** + * Determine if the Names of two `message` instances are equal. + * + * The following equivalence relations on non-null `message` instances are + * maintained: + * + * * It is reflexive: for any non-null reference value x, + * `hashTableFunction_MessageNameEquals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `message_Equals(x, y)` must return true if and only if + * `hashTableFunction_MessageNameEquals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `hashTableFunction_MessageNameEquals(x, y)` returns true and + * `hashTableFunction_MessageNameEquals(y, z)` returns true, + * then `hashTableFunction_MessageNameEquals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `hashTableFunction_MessageNameEquals(x, y)` consistently + * return true or consistently return false. + * + * * For any non-null reference value x, + * `hashTableFunction_MessageNameEquals(x, NULL)` must return false. + * + * @param a A pointer to a `message` instance. + * @param b A pointer to a `message` instance. + * @return true if the names of the two `message` instances are equal. + */ +bool hashTableFunction_MessageNameEquals(const void *messageA, + const void *messageB); + +/** + * @function hashTableFunction_NameHashCode + * @abstract Computes the hash of the entire name in a message + * + * @param messageA is a message + * @return A non-cryptographic hash of Name + */ +HashCodeType hashTableFunction_MessageNameHashCode(const void *messageA); +#endif // hashTableFunction_h
\ No newline at end of file diff --git a/hicn-light/src/processor/matchingRulesTable.c b/hicn-light/src/processor/matchingRulesTable.c new file mode 100755 index 000000000..56e59c29e --- /dev/null +++ b/hicn-light/src/processor/matchingRulesTable.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> +#include <stdio.h> + +#include <parc/algol/parc_Hash.h> +#include <parc/algol/parc_Memory.h> + +#include <parc/assert/parc_Assert.h> +#include <src/processor/hashTableFunction.h> +#include <src/processor/matchingRulesTable.h> + +struct matching_rules_table { + // using this wrapper we can manatain multiple hash tables indexed in + // different ways + // for now we use only a table indexed by name + + PARCHashCodeTable *tableByName; + PARCHashCodeTable_Destroyer dataDestroyer; +}; + +static PARCHashCodeTable *matchingRulesTable_GetTableForMessage( + const MatchingRulesTable *pit, const Message *interestMessage); + +// ====================================================================== + +MatchingRulesTable *matchingRulesTable_Create( + PARCHashCodeTable_Destroyer dataDestroyer) { + size_t initialSize = 65535; + + MatchingRulesTable *table = + parcMemory_AllocateAndClear(sizeof(MatchingRulesTable)); + parcAssertNotNull(table, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(MatchingRulesTable)); + table->dataDestroyer = dataDestroyer; + + table->tableByName = parcHashCodeTable_Create_Size( + hashTableFunction_MessageNameEquals, + hashTableFunction_MessageNameHashCode, NULL, dataDestroyer, initialSize); + + return table; +} + +void matchingRulesTable_Destroy(MatchingRulesTable **tablePtr) { + parcAssertNotNull(tablePtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*tablePtr, + "Parameter must dereference to non-null pointer"); + + MatchingRulesTable *table = *tablePtr; + + parcHashCodeTable_Destroy(&table->tableByName); + + parcMemory_Deallocate((void **)&table); + *tablePtr = NULL; +} + +void *matchingRulesTable_Get(const MatchingRulesTable *rulesTable, + const Message *message) { + parcAssertNotNull(rulesTable, "Parameter rulesTable must be non-null"); + parcAssertNotNull(message, "Parameter message must be non-null"); + + PARCHashCodeTable *hashTable = + matchingRulesTable_GetTableForMessage(rulesTable, message); + return parcHashCodeTable_Get(hashTable, message); +} + +PARCArrayList *matchingRulesTable_GetUnion(const MatchingRulesTable *table, + const Message *message) { + PARCArrayList *list = parcArrayList_Create_Capacity(NULL, NULL, 3); + + void *dataByName = parcHashCodeTable_Get(table->tableByName, message); + if (dataByName) { + parcArrayList_Add(list, dataByName); + } + + return list; +} + +void matchingRulesTable_RemoveFromBest(MatchingRulesTable *rulesTable, + const Message *message) { + parcAssertNotNull(rulesTable, "Parameter rulesTable must be non-null"); + parcAssertNotNull(message, "Parameter message must be non-null"); + + PARCHashCodeTable *hashTable = + matchingRulesTable_GetTableForMessage(rulesTable, message); + parcHashCodeTable_Del(hashTable, message); +} + +void matchingRulesTable_RemoveFromAll(MatchingRulesTable *rulesTable, + const Message *message) { + parcAssertNotNull(rulesTable, "Parameter rulesTable must be non-null"); + parcAssertNotNull(message, "Parameter message must be non-null"); + + parcHashCodeTable_Del(rulesTable->tableByName, message); +} + +bool matchingRulesTable_AddToBestTable(MatchingRulesTable *rulesTable, + Message *key, void *data) { + parcAssertNotNull(rulesTable, "Parameter rulesTable must be non-null"); + parcAssertNotNull(key, "Parameter key must be non-null"); + parcAssertNotNull(data, "Parameter data must be non-null"); + + PARCHashCodeTable *hashTable = + matchingRulesTable_GetTableForMessage(rulesTable, key); + + bool success = parcHashCodeTable_Add(hashTable, key, data); + + return success; +} + +// ======================================================================================== + +static PARCHashCodeTable *matchingRulesTable_GetTableForMessage( + const MatchingRulesTable *pit, const Message *interestMessage) { + PARCHashCodeTable *table; + table = pit->tableByName; + + return table; +} diff --git a/hicn-light/src/processor/matchingRulesTable.h b/hicn-light/src/processor/matchingRulesTable.h new file mode 100755 index 000000000..96d099430 --- /dev/null +++ b/hicn-light/src/processor/matchingRulesTable.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @header matchingRulesTable + * @abstract A generic table (void *) that matches a Message + * @discussion + * Matching is done based on Name + * + * When used in the PIT, one calls + * <code>matchingRulesTable_AddToBestTable()</code> to add an interest to the + * "best" (i.e. most restrictive match) table, then calls + * <code>matchingRulesTable_GetUnion()</code> on a content object to match + * against all of them. + * + * When used in a ContentStore, one calls + * <code>matchingRulesTable_AddToAllTables()</code> to index a Content Object in + * all the tables. one then calls <code>matchingRulesTable_Get()</code> with an + * Interest to do the "best" matching (i.e by hash first, then keyid, then just + * by name). + * + */ + +#ifndef matchingRulesTable_h +#define matchingRulesTable_h + +#include <parc/algol/parc_ArrayList.h> +#include <parc/algol/parc_HashCodeTable.h> +#include <src/core/message.h> + +struct matching_rules_table; +typedef struct matching_rules_table MatchingRulesTable; + +/** + * Creates a MatchigRulesTable and specifies the function to call to de-allocate + * an entry + * + * The datadestroyer will be called when an entry is removed from a table. It + * may be NULL. + */ +MatchingRulesTable *matchingRulesTable_Create( + PARCHashCodeTable_Destroyer dataDestroyer); + +/** + * Destroys the table and removes all stored elements. + * + */ +void matchingRulesTable_Destroy(MatchingRulesTable **tablePtr); + +/** + * @function matchingRulesTable_Get + * @abstract Returns the data item that best matches the message. + * @discussion + * Indexed by NameAndContentObjectHash, NameAndKeyId, and Name, in that order. + * + * @return NULL if nothing matches, otherwise the stored value + */ +void *matchingRulesTable_Get(const MatchingRulesTable *table, + const Message *message); + +/** + * @function matchingRulesTable_GetUnion + * @abstract Returns matching data items from all index tables. + * @discussion + * The PARCArrayList does not have an item destructor, so destroying it will + * not affect the underlying data. + * + * @return Will not be NULL, but may be empty + */ +PARCArrayList *matchingRulesTable_GetUnion(const MatchingRulesTable *table, + const Message *message); + +/** + * @function matchingRulesTable_Add + * @abstract Adds the data to the best table + * @discussion + * The key must be derived from the data and destroyed when the data is + * destroyed. Only the data destroyer is called. + * + * No duplicates are allowed, will return false if not added. + * + * @return true if unique key and added, false if duplicate and no action taken. + */ +bool matchingRulesTable_AddToBestTable(MatchingRulesTable *rulesTable, + Message *key, void *data); + +/** + * @function matchingRulesTable_Remove + * @abstract Removes the matching entry from the best match table, calling the + * destroyer on the data. + */ +void matchingRulesTable_RemoveFromBest(MatchingRulesTable *rulesTable, + const Message *message); + +/** + * @function matchingRulesTable_RemoveFromAll + * @abstract Removes the message from all tables + */ +void matchingRulesTable_RemoveFromAll(MatchingRulesTable *rulesTable, + const Message *message); +#endif // matchingRulesTable_h diff --git a/hicn-light/src/processor/messageProcessor.c b/hicn-light/src/processor/messageProcessor.c new file mode 100755 index 000000000..8c03ee739 --- /dev/null +++ b/hicn-light/src/processor/messageProcessor.c @@ -0,0 +1,742 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> +#include <stdio.h> +#include <string.h> + +#include <parc/algol/parc_ArrayList.h> +#include <parc/algol/parc_Memory.h> +#include <src/processor/messageProcessor.h> + +#include <src/processor/fib.h> +#include <src/processor/pitStandard.h> + +#include <src/content_store/contentStoreInterface.h> +#include <src/content_store/contentStoreLRU.h> + +#include <src/strategies/loadBalancer.h> +#include <src/strategies/loadBalancerWithPD.h> +#include <src/strategies/rnd.h> +#include <src/strategies/rndSegment.h> +#include <src/strategies/strategyImpl.h> + +#include <src/io/streamConnection.h> +#include <src/io/udpListener.h> + +#include <parc/assert/parc_Assert.h> + +#include <src/utils/commands.h> +#include <src/utils/utils.h> + +#include <src/utils/address.h> + +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +typedef struct processor_stats { + uint32_t countReceived; + uint32_t countInterestsReceived; + uint32_t countObjectsReceived; + + uint32_t countInterestsAggregated; + + uint32_t countDropped; + uint32_t countInterestsDropped; + uint32_t countDroppedNoRoute; + uint32_t countDroppedNoReversePath; + + uint32_t countDroppedConnectionNotFound; + uint32_t countObjectsDropped; + + uint32_t countSendFailures; + uint32_t countInterestForwarded; + uint32_t countObjectsForwarded; + uint32_t countInterestsSatisfiedFromStore; + + uint32_t countDroppedNoHopLimit; + uint32_t countDroppedZeroHopLimitFromRemote; + uint32_t countDroppedZeroHopLimitToRemote; +} _ProcessorStats; + +struct message_processor { + Forwarder *forwarder; + Logger *logger; + + PIT *pit; + ContentStoreInterface *contentStore; + FIB *fib; + + bool store_in_cache; + bool serve_from_cache; + + _ProcessorStats stats; +}; + +static void messageProcessor_Drop(MessageProcessor *processor, + Message *message); +static void messageProcessor_ReceiveInterest(MessageProcessor *processor, + Message *interestMessage); +static void messageProcessor_ReceiveContentObject(MessageProcessor *processor, + Message *objectMessage); +static unsigned messageProcessor_ForwardToNexthops(MessageProcessor *processor, + Message *message, + const NumberSet *nexthops); + +static void messageProcessor_ForwardToInterfaceId(MessageProcessor *processor, + Message *message, + unsigned interfaceId); + +// ============================================================ +// Public API + +MessageProcessor *messageProcessor_Create(Forwarder *forwarder) { + size_t objectStoreSize = + configuration_GetObjectStoreSize(forwarder_GetConfiguration(forwarder)); + + MessageProcessor *processor = + parcMemory_AllocateAndClear(sizeof(MessageProcessor)); + parcAssertNotNull(processor, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(MessageProcessor)); + memset(processor, 0, sizeof(MessageProcessor)); + + processor->forwarder = forwarder; + processor->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + processor->pit = pitStandard_Create(forwarder); + + processor->fib = fib_Create(); + + if (logger_IsLoggable(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(processor->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, "MessageProcessor %p created", (void *)processor); + } + + ContentStoreConfig contentStoreConfig = { + .objectCapacity = objectStoreSize, + }; + + processor->contentStore = + contentStoreLRU_Create(&contentStoreConfig, processor->logger); + + // the two flags for the cache are set to true by default. If the cache + // is active it always work as expected unless the use modifies this + // values using controller + processor->store_in_cache = true; + processor->serve_from_cache = true; + + return processor; +} + +void messageProcessor_SetContentObjectStoreSize( + MessageProcessor *processor, size_t maximumContentStoreSize) { + parcAssertNotNull(processor, "Parameter processor must be non-null"); + contentStoreInterface_Release(&processor->contentStore); + + ContentStoreConfig contentStoreConfig = {.objectCapacity = + maximumContentStoreSize}; + + processor->contentStore = + contentStoreLRU_Create(&contentStoreConfig, processor->logger); +} + +void messageProcessor_ClearCache(MessageProcessor *processor) { + parcAssertNotNull(processor, "Parameter processor must be non-null"); + size_t objectStoreSize = configuration_GetObjectStoreSize( + forwarder_GetConfiguration(processor->forwarder)); + + contentStoreInterface_Release(&processor->contentStore); + + ContentStoreConfig contentStoreConfig = { + .objectCapacity = objectStoreSize, + }; + + processor->contentStore = + contentStoreLRU_Create(&contentStoreConfig, processor->logger); +} + +ContentStoreInterface *messageProcessor_GetContentObjectStore( + const MessageProcessor *processor) { + parcAssertNotNull(processor, "Parameter processor must be non-null"); + return processor->contentStore; +} + +void messageProcessor_Destroy(MessageProcessor **processorPtr) { + parcAssertNotNull(processorPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*processorPtr, "Parameter dereference to non-null pointer"); + + MessageProcessor *processor = *processorPtr; + + if (logger_IsLoggable(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(processor->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, "MessageProcessor %p destroyed", (void *)processor); + } + + logger_Release(&processor->logger); + fib_Destroy(&processor->fib); + contentStoreInterface_Release(&processor->contentStore); + pit_Release(&processor->pit); + + parcMemory_Deallocate((void **)&processor); + *processorPtr = NULL; +} + +void messageProcessor_Receive(MessageProcessor *processor, Message *message) { + parcAssertNotNull(processor, "Parameter processor must be non-null"); + parcAssertNotNull(message, "Parameter message must be non-null"); + + processor->stats.countReceived++; + + if (logger_IsLoggable(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + char *nameString = name_ToString(message_GetName(message)); + logger_Log(processor->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, "Message %p ingress %3u length %5u received name %s", + (void *)message, message_GetIngressConnectionId(message), + message_Length(message), nameString); + parcMemory_Deallocate((void **)&nameString); + } + + switch (message_GetType(message)) { + case MessagePacketType_Interest: + messageProcessor_ReceiveInterest(processor, message); + break; + + case MessagePacketType_ContentObject: + messageProcessor_ReceiveContentObject(processor, message); + break; + + default: + messageProcessor_Drop(processor, message); + break; + } + + // if someone wanted to save it, they made a copy + message_Release(&message); +} + +bool messageProcessor_AddOrUpdateRoute(MessageProcessor *processor, + add_route_command *control, + unsigned ifidx) { + Configuration *config = forwarder_GetConfiguration(processor->forwarder); + + const char *prefixStr = utils_PrefixLenToString( + control->addressType, &control->address, &control->len); + strategy_type fwdStrategy = + configuration_GetForwardingStrategy(config, prefixStr); + if (fwdStrategy == LAST_STRATEGY_VALUE) { + fwdStrategy = SET_STRATEGY_LOADBALANCER; + } + + Name *prefix = name_CreateFromAddress(control->addressType, control->address, + control->len); + FibEntry *entry = fib_Contains(processor->fib, prefix); + bool newEntry = false; + if (entry != NULL) { + fibEntry_AddNexthop(entry, ifidx); + } else { + newEntry = true; + entry = fibEntry_Create(prefix, fwdStrategy); + fibEntry_AddNexthop(entry, ifidx); + fib_Add(processor->fib, entry); + } + + name_Release(&prefix); + if (newEntry && (fwdStrategy == SET_STRATEGY_LOADBALANCER_WITH_DELAY)) { + strategyLoadBalancerWithPD_SetConnectionTable( + fibEntry_GetFwdStrategy(entry), + forwarder_GetConnectionTable(processor->forwarder)); + } + + return true; +} + +bool messageProcessor_RemoveRoute(MessageProcessor *processor, + remove_route_command *control, + unsigned ifidx) { + Name *name = name_CreateFromAddress(control->addressType, control->address, + control->len); + fib_Remove(processor->fib, name, ifidx); + name_Release(&name); + + return true; +} + +void messageProcessor_RemoveConnectionIdFromRoutes(MessageProcessor *processor, + unsigned connectionId) { + fib_RemoveConnectionId(processor->fib, connectionId); +} + +void processor_SetStrategy(MessageProcessor *processor, Name *prefix, + strategy_type strategy) { + FibEntry *entry = fib_Contains(processor->fib, prefix); + if (entry != NULL) { + fibEntry_SetStrategy(entry, strategy); + if (strategy == SET_STRATEGY_LOADBALANCER_WITH_DELAY) { + strategyLoadBalancerWithPD_SetConnectionTable( + fibEntry_GetFwdStrategy(entry), + forwarder_GetConnectionTable(processor->forwarder)); + } + } +} + +FibEntryList *messageProcessor_GetFibEntries(MessageProcessor *processor) { + parcAssertNotNull(processor, "Parameter processor must be non-null"); + return fib_GetEntries(processor->fib); +} + +// ============================================================ +// Internal API + +/** + * @function messageProcessor_Drop + * @abstract Whenever we "drop" a message, increment countes + * @discussion + * This is a bookkeeping function. It increments the appropriate counters. + * + * The default action for a message is to destroy it in + * <code>messageProcessor_Receive()</code>, so this function does not need to do + * that. + * + */ +static void messageProcessor_Drop(MessageProcessor *processor, + Message *message) { + processor->stats.countDropped++; + + switch (message_GetType(message)) { + case MessagePacketType_Interest: + processor->stats.countInterestsDropped++; + break; + + case MessagePacketType_ContentObject: + processor->stats.countObjectsDropped++; + break; + + default: + break; + } + + // dont destroy message here, its done at end of receive +} + +/** + * @function messageProcessor_AggregateInterestInPit + * @abstract Try to aggregate the interest in the PIT + * @discussion + * Tries to aggregate the interest with another interest. + * + * @return true if interest aggregagted (no more forwarding needed), false if + * need to keep processing it. + */ +static bool messageProcessor_AggregateInterestInPit(MessageProcessor *processor, + Message *interestMessage) { + PITVerdict verdict = pit_ReceiveInterest(processor->pit, interestMessage); + + if (verdict == PITVerdict_Aggregate) { + // PIT has it, we're done + processor->stats.countInterestsAggregated++; + + if (logger_IsLoggable(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log( + processor->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, "Message %p aggregated in PIT (aggregated count %u)", + (void *)interestMessage, processor->stats.countInterestsAggregated); + } + + return true; + } + + if (logger_IsLoggable(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log( + processor->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, "Message %p not aggregated in PIT (aggregated count %u)", + (void *)interestMessage, processor->stats.countInterestsAggregated); + } + + return false; +} + +static bool _satisfyFromContentStore(MessageProcessor *processor, + Message *interestMessage) { + bool result = false; + + if (message_GetInterestLifetimeTicks(interestMessage) == 0) { + return false; + } + + if (!processor->serve_from_cache) { + return result; + } + + // See if there's a match in the store. + Message *objectMessage = contentStoreInterface_MatchInterest( + processor->contentStore, interestMessage, + forwarder_GetTicks(processor->forwarder)); + + if (objectMessage != NULL) { + // Remove it from the PIT. nexthops is allocated, so need to destroy + NumberSet *nexthops = pit_SatisfyInterest(processor->pit, objectMessage); + parcAssertNotNull( + nexthops, + "Illegal state: got a null nexthops for an interest we just inserted."); + + // send message in reply, then done + processor->stats.countInterestsSatisfiedFromStore++; + + if (logger_IsLoggable(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug, __func__, + "Message %p satisfied from content store (satisfied count %u)", + (void *)interestMessage, + processor->stats.countInterestsSatisfiedFromStore); + } + + message_ResetPathLabel(objectMessage); + + messageProcessor_ForwardToNexthops(processor, objectMessage, nexthops); + numberSet_Release(&nexthops); + + result = true; + } + + return result; +} + +/** + * @function messageProcessor_ForwardViaFib + * @abstract Try to forward the interest via the FIB + * @discussion + * This calls <code>messageProcessor_ForwardToNexthops()</code>, so if we find + * any nexthops, the interest will be sent on its way. Depending on the + * IoOperations of each nexthop, it may be a deferred write and bump up the + * <code>interestMessage</code> refernce count, or it may copy the data out. + * + * A TRUE return means we did our best to forward it via the routes. If those + * routes are actually down or have errors, we still return TRUE. A FALSE + * return means there were no routes to try. + * + * @return true if we found a route and tried to forward it, false if no route + */ +static bool messageProcessor_ForwardViaFib(MessageProcessor *processor, + Message *interestMessage) { + FibEntry *fibEntry = fib_Match(processor->fib, interestMessage); + if (fibEntry == NULL) { + return false; + } + + PitEntry *pitEntry = pit_GetPitEntry(processor->pit, interestMessage); + if (pitEntry == NULL) { + return false; + } + + pitEntry_AddFibEntry(pitEntry, fibEntry); + + NumberSet *nexthops = (NumberSet *)fibEntry_GetNexthopsFromForwardingStrategy( + fibEntry, interestMessage); + // this requires some additional checks. It may happen that some of the output + // faces selected by the forwarding strategy are not usable. So far all the + // forwarding strategy return only valid faces (or an empty list) + for (unsigned i = 0; i < numberSet_Length(nexthops); i++) { + pitEntry_AddEgressId(pitEntry, numberSet_GetItem(nexthops, i)); + } + + // The function GetPitEntry encreases the ref counter in the pit entry + // we need to decrease it + pitEntry_Release(&pitEntry); + + if (messageProcessor_ForwardToNexthops(processor, interestMessage, nexthops) > + 0) { + numberSet_Release(&nexthops); + return true; + } else { + if (logger_IsLoggable(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug, __func__, + "Message %p returned an emtpy next hop set", + (void *)interestMessage); + } + } + + return false; +} + +/** + * @function messageProcessor_ReceiveInterest + * @abstract Receive an interest from the network + * @discussion + * (1) if interest in the PIT, aggregate in PIT + * (2) if interest in the ContentStore, reply + * (3) if in the FIB, forward + * (4) drop + * + */ +static void messageProcessor_ReceiveInterest(MessageProcessor *processor, + Message *interestMessage) { + processor->stats.countInterestsReceived++; + + // (1) Try to aggregate in PIT + if (messageProcessor_AggregateInterestInPit(processor, interestMessage)) { + // done + return; + } + + // At this point, we just created a PIT entry. If we don't forward the + // interest, we need to remove the PIT entry. + + // (2) Try to satisfy from content store + if (_satisfyFromContentStore(processor, interestMessage)) { + // done + // If we found a content object in the CS, + // messageProcess_SatisfyFromContentStore already cleared the PIT state + return; + } + + // (3) Try to forward it + if (messageProcessor_ForwardViaFib(processor, interestMessage)) { + // done + return; + } + + // Remove the PIT entry? + processor->stats.countDroppedNoRoute++; + + if (logger_IsLoggable(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(processor->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, "Message %p did not match FIB, no route (count %u)", + (void *)interestMessage, processor->stats.countDroppedNoRoute); + } + + messageProcessor_Drop(processor, interestMessage); +} + +/** + * @function messageProcessor_ReceiveContentObject + * @abstract Process an in-bound content object + * @discussion + * (1) If it does not match anything in the PIT, drop it + * (2) Add to Content Store + * (3) Reverse path forward via PIT entries + * + * @param <#param1#> + */ +static void messageProcessor_ReceiveContentObject(MessageProcessor *processor, + Message *message) { + processor->stats.countObjectsReceived++; + + NumberSet *ingressSetUnion = pit_SatisfyInterest(processor->pit, message); + + if (numberSet_Length(ingressSetUnion) == 0) { + // (1) If it does not match anything in the PIT, drop it + processor->stats.countDroppedNoReversePath++; + + if (logger_IsLoggable(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug, __func__, + "Message %p did not match PIT, no reverse path (count %u)", + (void *)message, processor->stats.countDroppedNoReversePath); + } + + // we store the packets in the content store enven in the case where there + // is no match in the PIT table in this way the applications can push the + // content in the CS of the forwarder. We allow this only for local faces + bool isLocal = connection_IsLocal(connectionTable_FindById( + forwarder_GetConnectionTable(processor->forwarder), + message_GetIngressConnectionId((const Message *)message))); + if (processor->store_in_cache && isLocal) { + uint64_t currentTimeTicks = forwarder_GetTicks(processor->forwarder); + contentStoreInterface_PutContent(processor->contentStore, message, + currentTimeTicks); + if (logger_IsLoggable(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug, __func__, + "Message %p sotred in the CS anyway", (void *)message); + } + } + + messageProcessor_Drop(processor, message); + } else { + // (2) Add to Content Store. Store may remove expired content, if necessary, + // depending on store policy. + if (processor->store_in_cache) { + uint64_t currentTimeTicks = forwarder_GetTicks(processor->forwarder); + contentStoreInterface_PutContent(processor->contentStore, message, + currentTimeTicks); + } + // (3) Reverse path forward via PIT entries + messageProcessor_ForwardToNexthops(processor, message, ingressSetUnion); + } + + numberSet_Release(&ingressSetUnion); +} + +/** + * @function messageProcessor_ForwardToNexthops + * @abstract Try to forward to each nexthop listed in the NumberSet + * @discussion + * Will not forward to the ingress connection. + * + * @return The number of nexthops tried + */ +static unsigned messageProcessor_ForwardToNexthops(MessageProcessor *processor, + Message *message, + const NumberSet *nexthops) { + unsigned forwardedCopies = 0; + + size_t length = numberSet_Length(nexthops); + + unsigned ingressId = message_GetIngressConnectionId(message); + uint32_t old_path_label = 0; + + if (message_GetType(message) == MessagePacketType_ContentObject) { + old_path_label = message_GetPathLabel(message); + } + + for (size_t i = 0; i < length; i++) { + unsigned egressId = numberSet_GetItem(nexthops, i); + if (egressId != ingressId) { + forwardedCopies++; + messageProcessor_ForwardToInterfaceId(processor, message, egressId); + + if (message_GetType(message) == MessagePacketType_ContentObject) { + // everytime we send out a message we need to restore the original path + // label of the message this is important because we keep a single copy + // of the message (single pointer) and we modify the path label at each + // send. + message_SetPathLabel(message, old_path_label); + } + } + } + return forwardedCopies; +} + +/** + * caller has checked that the hop limit is ok. Try to send out the connection. + */ +static void messageProcessor_SendWithGoodHopLimit(MessageProcessor *processor, + Message *message, + unsigned interfaceId, + const Connection *conn) { + bool success = connection_Send(conn, message); + if (success) { + switch (message_GetType(message)) { + case MessagePacketType_Interest: + processor->stats.countInterestForwarded++; + break; + + case MessagePacketType_ContentObject: + processor->stats.countObjectsForwarded++; + break; + + default: + break; + } + + if (logger_IsLoggable(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log( + processor->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, "forward message %p to interface %u (int %u, obj %u)", + (void *)message, interfaceId, processor->stats.countInterestForwarded, + processor->stats.countObjectsForwarded); + } + } else { + processor->stats.countSendFailures++; + + if (logger_IsLoggable(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug, __func__, + "forward message %p to interface %u send failure (count %u)", + (void *)message, interfaceId, + processor->stats.countSendFailures); + } + messageProcessor_Drop(processor, message); + } +} + +/* + * If the hoplimit is equal to 0, then we may only forward it to local + * applications. Otherwise, we may forward it off the system. + * + */ +static void messageProcessor_ForwardToInterfaceId(MessageProcessor *processor, + Message *message, + unsigned interfaceId) { + ConnectionTable *connectionTable = + forwarder_GetConnectionTable(processor->forwarder); + const Connection *conn = + connectionTable_FindById(connectionTable, interfaceId); + + if (conn != NULL) { + messageProcessor_SendWithGoodHopLimit(processor, message, interfaceId, + conn); + } else { + processor->stats.countDroppedConnectionNotFound++; + + if (logger_IsLoggable(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(processor->logger, LoggerFacility_Processor, + PARCLogLevel_Debug, __func__, + "forward message %p to interface %u not found (count %u)", + (void *)message, interfaceId, + processor->stats.countDroppedConnectionNotFound); + } + + messageProcessor_Drop(processor, message); + } +} + +void messageProcessor_SetCacheStoreFlag(MessageProcessor *processor, bool val) { + processor->store_in_cache = val; +} + +bool messageProcessor_GetCacheStoreFlag(MessageProcessor *processor) { + return processor->store_in_cache; +} + +void messageProcessor_SetCacheServeFlag(MessageProcessor *processor, bool val) { + processor->serve_from_cache = val; +} + +bool messageProcessor_GetCacheServeFlag(MessageProcessor *processor) { + return processor->serve_from_cache; +} + +#ifdef WITH_MAPME + +FIB *messageProcessor_getFib(MessageProcessor *processor) { + return processor->fib; +} + +#endif /* WITH_MAPME */ diff --git a/hicn-light/src/processor/messageProcessor.h b/hicn-light/src/processor/messageProcessor.h new file mode 100755 index 000000000..ce3049938 --- /dev/null +++ b/hicn-light/src/processor/messageProcessor.h @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file messageProcessor.h + * @brief Executes the set of rules dictated by the PacketType + * + * This is a "run-to-completion" handling of a message based on the PacketType. + * + * The MessageProcessor also owns the PIT and FIB tables. + * + */ + +#ifndef messageProcessor_h +#define messageProcessor_h + +#include <src/content_store/contentStoreInterface.h> +#include <src/core/forwarder.h> +#include <src/core/message.h> + +#include <src/utils/commands.h> + +struct message_processor; +typedef struct message_processor MessageProcessor; + +/** + * Allocates a MessageProcessor along with PIT, FIB and ContentStore tables + * + * The hicn-light pointer is primarily used for logging (forwarder_Log), getting + * the configuration, and accessing the connection table. + * + * @param [in] Pointer to owning hicn-light process + * + * @retval non-null An allocated message processor + * @retval null An error + * + */ +MessageProcessor *messageProcessor_Create(Forwarder *forwarder); + +/** + * Deallocates a message processor an all internal tables + * + * @param [in,out] processorPtr Pointer to message processor to de-allocate, + * will be NULL'd. + */ +void messageProcessor_Destroy(MessageProcessor **processorPtr); + +/** + * @function messageProcessor_Receive + * @abstract Process the message, takes ownership of the memory. + * @discussion + * Will call destroy on the memory when done with it, so if the caller wants + * to keep it, make a reference counted copy. + * + * Receive may modify some fields in the message, such as the HopLimit field. + */ +void messageProcessor_Receive(MessageProcessor *procesor, Message *message); + +/** + * Adds or updates a route in the FIB + * + * If the route already exists, it is replaced + * + * @param [in] procesor An allocated message processor + * @param [in] route The route to update + * + * @retval true added or updated + * @retval false An error + */ +bool messageProcessor_AddOrUpdateRoute(MessageProcessor *processor, + add_route_command *control, + unsigned ifidx); + +/** + * Removes a route from the FIB + * + * Removes a specific nexthop for a route. If there are no nexthops left after + * the removal, the entire route is deleted from the FIB. + * + * @param [in] procesor An allocated message processor + * @param [in] route The route to remove + * + * @retval true Route completely removed + * @retval false There is still a nexthop for the route + */ + +bool messageProcessor_RemoveRoute(MessageProcessor *processor, + remove_route_command *control, + unsigned ifidx); + +/** + * Removes a given connection id from all FIB entries + * + * Iterates the FIB and removes the given connection ID from every route. + */ +void messageProcessor_RemoveConnectionIdFromRoutes(MessageProcessor *processor, + unsigned connectionId); + +/** + * Returns a list of all FIB entries + * + * You must destroy the list. + * + * @retval non-null The list of FIB entries + * @retval null An error + */ +FibEntryList *messageProcessor_GetFibEntries(MessageProcessor *processor); + +/** + * Adjusts the ContentStore to the given size. + * + * This will destroy and re-create the content store, so any cached objects will + * be lost. + * + */ +void messageProcessor_SetContentObjectStoreSize(MessageProcessor *processor, + size_t maximumContentStoreSize); + +/** + * Return the interface to the currently instantiated ContentStore, if any. + * + * @param [in] processor the `MessageProcessor` from which to return the + * ContentStoreInterface. + * + */ +ContentStoreInterface *messageProcessor_GetContentObjectStore( + const MessageProcessor *processor); + +void messageProcessor_SetCacheStoreFlag(MessageProcessor *processor, bool val); + +bool messageProcessor_GetCacheStoreFlag(MessageProcessor *processor); + +void messageProcessor_SetCacheServeFlag(MessageProcessor *processor, bool val); + +bool messageProcessor_GetCacheServeFlag(MessageProcessor *processor); + +void messageProcessor_ClearCache(MessageProcessor *processor); + +void processor_SetStrategy(MessageProcessor *processor, Name *prefix, + strategy_type strategy); + +#ifdef WITH_MAPME + +/** + * @function messageProcessor_getFib + * @abstract Returns the hICN processor's FIB. + * @param [in] forwarder - Pointer to the hICN processor. + * @returns Pointer to the hICN FIB. + */ +FIB *messageProcessor_getFib(MessageProcessor *processor); + +#endif /* WITH_MAPME */ + +#endif // messageProcessor_h diff --git a/hicn-light/src/processor/pit.c b/hicn-light/src/processor/pit.c new file mode 100755 index 000000000..9cae4062e --- /dev/null +++ b/hicn-light/src/processor/pit.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Generic interface to PIT table + * + */ + +#include <parc/assert/parc_Assert.h> +#include <src/config.h> +#include <stdio.h> + +#include <src/processor/pit.h> + +void *pit_Closure(const PIT *pit) { return pit->closure; } + +void pit_Release(PIT **pitPtr) { (*pitPtr)->release(pitPtr); } + +PITVerdict pit_ReceiveInterest(PIT *pit, Message *interestMessage) { + return pit->receiveInterest(pit, interestMessage); +} + +NumberSet *pit_SatisfyInterest(PIT *pit, const Message *objectMessage) { + return pit->satisfyInterest(pit, objectMessage); +} + +void pit_RemoveInterest(PIT *pit, const Message *interestMessage) { + pit->removeInterest(pit, interestMessage); +} + +PitEntry *pit_GetPitEntry(const PIT *pit, const Message *interestMessage) { + return pit->getPitEntry(pit, interestMessage); +} diff --git a/hicn-light/src/processor/pit.h b/hicn-light/src/processor/pit.h new file mode 100755 index 000000000..1f909be3e --- /dev/null +++ b/hicn-light/src/processor/pit.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file pit.h + * @brief The Pending Interest Table interface + * + * Interface for implementing a PIT table + * + */ + +#ifndef pit_h +#define pit_h + +#include <src/core/forwarder.h> +#include <src/core/message.h> +#include <src/core/numberSet.h> +#include <src/processor/pitEntry.h> +#include <src/processor/pitVerdict.h> + +struct pit; +typedef struct pit PIT; + +struct pit { + void (*release)(PIT **pitPtr); + PITVerdict (*receiveInterest)(PIT *pit, Message *interestMessage); + NumberSet *(*satisfyInterest)(PIT *pit, const Message *objectMessage); + void (*removeInterest)(PIT *pit, const Message *interestMessage); + PitEntry *(*getPitEntry)(const PIT *pit, const Message *interestMessage); + void *closure; +}; + +void *pit_Closure(const PIT *pit); + +/** + * Destroys the PIT table and all entries contained in it. + * + * PIT entries are reference counted, so if the user has stored one outside the + * PIT table it will still be valid. + * + * @param [in,out] pitPtr Double pointer to PIT table, will be NULLed + */ +void pit_Release(PIT **pitPtr); + +/** + * @function pit_ReceiveInterest + * @abstract Receives an interest and adds to PIT table + * @discussion + * If not present, adds entry to the PIT table and returns + * PIT_VERDICT_NEW_ENTRY. If present and aggregated, returns + * PIT_VERDICT_EXISTING_ENTRY. + * + * Some aggregated interests may return PIT_VERDICT_NEW_ENTRY if the interest + * needs to be forwarded again (e.g. the lifetime is extended). + * + * If the PIT stores the message in its table, it will store a reference + * counted copy. + * + * @return Verdict of receiving the interest + */ +PITVerdict pit_ReceiveInterest(PIT *pit, Message *interestMessage); + +/** + * @function pit_SatisfyInterest + * @abstract Tries to satisfy PIT entries based on the message, returning where + * to send message + * @discussion + * If matching interests are in the PIT, will return the set of reverse + * paths to use to forward the content object. + * + * The return value is allocated and must be destroyed. + * + * @return Set of ConnectionTable id's to forward the message, may be empty or + * NULL. Must be destroyed. + */ +NumberSet *pit_SatisfyInterest(PIT *pit, const Message *objectMessage); + +/** + * @function pit_RemoveInterest + * @abstract Unconditionally remove the interest from the PIT + * @discussion + * The PIT may store a specific name in several tables. This function will + * remove the interest from the specific table it lives it. It will not + * remove PIT entries in different tables with the same name. + * + * The different tables index interests based on their matching criteria, + * such as by name, by name and keyid, etc. + * + */ +void pit_RemoveInterest(PIT *pit, const Message *interestMessage); + +/** + * @function pit_GetPitEntry + * @abstract Retrieve the best matching PIT entry for the message. + * @discussion + * Returns a reference counted copy of the entry, must call + * <code>pitEntry_Destory()</code> on it. + * + * @return NULL if not in table, otherwise a reference counted copy of the entry + */ +PitEntry *pit_GetPitEntry(const PIT *pit, const Message *interestMessage); +#endif // pit_h diff --git a/hicn-light/src/processor/pitEntry.c b/hicn-light/src/processor/pitEntry.c new file mode 100755 index 000000000..38103cb8e --- /dev/null +++ b/hicn-light/src/processor/pitEntry.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> +#include <stdio.h> + +#include <parc/algol/parc_Memory.h> +#include <src/core/numberSet.h> +#include <src/processor/pitEntry.h> + +#include <parc/assert/parc_Assert.h> + +struct pit_entry { + Message *message; + NumberSet *ingressIdSet; + NumberSet *egressIdSet; + + FibEntry *fibEntry; + + Ticks creationTime; + Ticks expiryTime; + + unsigned refcount; +}; + +PitEntry *pitEntry_Create(Message *message, Ticks expiryTime, + Ticks creationTime) { + PitEntry *pitEntry = parcMemory_AllocateAndClear(sizeof(PitEntry)); + parcAssertNotNull(pitEntry, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(PitEntry)); + pitEntry->message = message; + pitEntry->ingressIdSet = numberSet_Create(); + pitEntry->egressIdSet = numberSet_Create(); + pitEntry->refcount = 1; + + // add the message to the reverse path set + numberSet_Add(pitEntry->ingressIdSet, + message_GetIngressConnectionId(message)); + + // hack in a 4-second timeout + pitEntry->expiryTime = expiryTime; + pitEntry->fibEntry = NULL; + + pitEntry->creationTime = creationTime; + return pitEntry; +} + +void pitEntry_Release(PitEntry **pitEntryPtr) { + parcAssertNotNull(pitEntryPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*pitEntryPtr, + "Parameter must dereference to non-null pointer"); + + PitEntry *pitEntry = *pitEntryPtr; + parcTrapIllegalValueIf(pitEntry->refcount == 0, + "Illegal state: has refcount of 0"); + + pitEntry->refcount--; + if (pitEntry->refcount == 0) { + if (pitEntry->fibEntry != NULL) { + fibEntry_Release(&pitEntry->fibEntry); + } + numberSet_Release(&pitEntry->ingressIdSet); + numberSet_Release(&pitEntry->egressIdSet); + message_Release(&pitEntry->message); + parcMemory_Deallocate((void **)&pitEntry); + } + *pitEntryPtr = NULL; +} + +PitEntry *pitEntry_Acquire(PitEntry *original) { + parcAssertNotNull(original, "Parameter original must be non-null"); + original->refcount++; + return original; +} + +void pitEntry_AddIngressId(PitEntry *pitEntry, unsigned ingressId) { + parcAssertNotNull(pitEntry, "Parameter pitEntry must be non-null"); + numberSet_Add(pitEntry->ingressIdSet, ingressId); +} + +void pitEntry_AddEgressId(PitEntry *pitEntry, unsigned egressId) { + parcAssertNotNull(pitEntry, "Parameter pitEntry must be non-null"); + numberSet_Add(pitEntry->egressIdSet, egressId); +} + +void pitEntry_AddFibEntry(PitEntry *pitEntry, FibEntry *fibEntry) { + parcAssertNotNull(pitEntry, "Parameter pitEntry must be non-null"); + parcAssertNotNull(fibEntry, "Parameter fibEntry must be non-null"); + // the fibEntry should be always the same for all the interests in the same + // pitEntry + if (pitEntry->fibEntry == NULL) { + fibEntry_Acquire(fibEntry); + pitEntry->fibEntry = fibEntry; + } +} + +FibEntry *pitEntry_GetFibEntry(PitEntry *pitEntry) { + parcAssertNotNull(pitEntry, "Parameter pitEntry must be non-null"); + return pitEntry->fibEntry; +} + +Ticks pitEntry_GetExpiryTime(const PitEntry *pitEntry) { + parcAssertNotNull(pitEntry, "Parameter pitEntry must be non-null"); + return pitEntry->expiryTime; +} + +Ticks pitEntry_GetCreationTime(const PitEntry *pitEntry) { + parcAssertNotNull(pitEntry, "Parameter pitEntry must be non-null"); + return pitEntry->creationTime; +} + +void pitEntry_SetExpiryTime(PitEntry *pitEntry, Ticks expiryTime) { + parcAssertNotNull(pitEntry, "Parameter pitEntry must be non-null"); + pitEntry->expiryTime = expiryTime; +} + +const NumberSet *pitEntry_GetIngressSet(const PitEntry *pitEntry) { + parcAssertNotNull(pitEntry, "Parameter pitEntry must be non-null"); + return pitEntry->ingressIdSet; +} + +const NumberSet *pitEntry_GetEgressSet(const PitEntry *pitEntry) { + parcAssertNotNull(pitEntry, "Parameter pitEntry must be non-null"); + return pitEntry->egressIdSet; +} + +Message *pitEntry_GetMessage(const PitEntry *pitEntry) { + parcAssertNotNull(pitEntry, "Parameter pitEntry must be non-null"); + return message_Acquire(pitEntry->message); +} diff --git a/hicn-light/src/processor/pitEntry.h b/hicn-light/src/processor/pitEntry.h new file mode 100755 index 000000000..b7d45e6a4 --- /dev/null +++ b/hicn-light/src/processor/pitEntry.h @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file pitEntry.h + * @brief The embodiment of a PIT entry + * + * Embodies a PIT entry + * + */ + +#ifndef pitEntry_h +#define pitEntry_h + +#include <src/core/message.h> +#include <src/core/numberSet.h> +#include <src/core/ticks.h> +#include <src/processor/fibEntry.h> + +struct pit_entry; +typedef struct pit_entry PitEntry; + +/** + * @function pitEntry_Create + * @abstract Takes ownership of the message inside the PitEntry + * @discussion + * When the PIT entry is destroyed, will call <code>message_Release()</code> + * on the message. + * + */ +PitEntry *pitEntry_Create(Message *message, Ticks expiryTime, + Ticks CreationTime); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] pitEntryPtr A pointer to a PitEntry instance pointer, which + * will be set to zero on return. + * + */ +void pitEntry_Release(PitEntry **pitEntryPtr); + +/** + * @function pitEntry_Acquire + * @abstract Returns a reference counted copy + * @discussion + * A reference counted copy that shares the same state as the original. + * Caller must use <code>pitEntry_Release()</code> on it when done. + * + * @return A reference counted copy, use Destroy on it. + */ +PitEntry *pitEntry_Acquire(PitEntry *original); + +/** + * @function pitEntry_AddIngressId + * @abstract Add an ingress connection id to the list of reverse paths + * @discussion + * A PitEntry has two NumberSets. The first is the set of ingress ports, + * which make up the reverse path. The second is the set of egress ports, which + * make up its forward path. + * + * This function tracks which reverse paths have sent us the interest. + * + * @param ingressId the reverse path + */ +void pitEntry_AddIngressId(PitEntry *pitEntry, unsigned ingressId); + +/** + * @function pitEntry_AddEgressId + * @abstract Add an egress connection id to the list of attempted paths + * @discussion + * A PitEntry has two NumberSets. The first is the set of ingress ports, + * which make up the reverse path. The second is the set of egress ports, which + * make up its forward path. + * + * This function tracks which forward paths we've tried for the interest. + * + * @param egressId the forwarded path + */ +void pitEntry_AddEgressId(PitEntry *pitEntry, unsigned egressId); + +void pitEntry_AddFibEntry(PitEntry *pitEntry, FibEntry *fibEntry); + +FibEntry *pitEntry_GetFibEntry(PitEntry *pitEntry); + +/** + * @function pitEntry_GetIngressSet + * @abstract The Ingress connection id set + * @discussion + * You must acquire a copy of the number set if you will store the result. + * This is the internal reference. + * + * @return May be empty, will not be null. Must be destroyed. + */ +const NumberSet *pitEntry_GetIngressSet(const PitEntry *pitEntry); + +/** + * @function pitEntry_GetEgressSet + * @abstract The Egress connection id set + * @discussion + * You must acquire a copy of the number set if you will store the result. + * This is the internal reference. + * + * @param <#param1#> + * @return May be empty, will not be null. Must be destroyed. + */ +const NumberSet *pitEntry_GetEgressSet(const PitEntry *pitEntry); + +/** + * @function pitEntry_GetMessage + * @abstract Gets the interest underpinning the PIT entry + * @discussion + * A reference counted copy, call <code>Message_Release()</code> on it. + * + * @return A reference counted copy, call <code>Message_Release()</code> on it. + */ +Message *pitEntry_GetMessage(const PitEntry *pitEntry); + +/** + * Returns the time (in ticks) at which the PIT entry is no longer valid + * + * The ExpiryTime is computed when the PIT entry is added (or via + * pitEntry_SetExpiryTime). It is the aboslute time (in Ticks) at which the Pit + * entry is no longer valid. + * + * @param [in] PitEntry An allocated PIT entry + * + * @retval number The abosolute time (in Ticks) of the Expiry + */ +Ticks pitEntry_GetExpiryTime(const PitEntry *pitEntry); + +Ticks pitEntry_GetCreationTime(const PitEntry *pitEntry); +/** + * Sets the ExpriyTime of the PIT entry to the given value + * + * It is probalby an error to set the expiryTime to a smaller value than + * currently set to, but this is not enforced. PIT entries use lazy delete. + * + * @param [in] pitEntry The allocated PIT entry to modify + * @param [in] expiryTime The new expiryTime (UTC in forwarder Ticks) + * + */ +void pitEntry_SetExpiryTime(PitEntry *pitEntry, Ticks expiryTime); + +#endif // pitEntry_h diff --git a/hicn-light/src/processor/pitStandard.c b/hicn-light/src/processor/pitStandard.c new file mode 100755 index 000000000..8d507626a --- /dev/null +++ b/hicn-light/src/processor/pitStandard.c @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * The pending interest table. + * + * Interest aggregation strategy: + * - The first Interest for a name is forwarded + * - A second Interest for a name from a different reverse path may be + * aggregated + * - A second Interest for a name from an existing Interest is forwarded + * - The Interest Lifetime is like a subscription time. A reverse path entry is + * removed once the lifetime is exceeded. + * - Whan an Interest arrives or is aggregated, the Lifetime for that reverse + * hop is extended. As a simplification, we only keep a single lifetime not per + * reverse hop. + * + */ + +#include <src/config.h> +#include <stdio.h> + +#define __STDC_FORMAT_MACROS +#include <inttypes.h> + +#include <src/processor/hashTableFunction.h> +#include <src/processor/pit.h> + +#include <src/core/ticks.h> + +#include <parc/algol/parc_Hash.h> +#include <parc/algol/parc_HashCodeTable.h> +#include <parc/algol/parc_Memory.h> + +#include <src/core/forwarder.h> + +#include <parc/assert/parc_Assert.h> + +struct standard_pit; +typedef struct standard_pit StandardPIT; + +struct standard_pit { + Forwarder *forwarder; + Logger *logger; + PARCHashCodeTable *table; // PIT indexed by name +}; + +static void _pit_StoreInTable(StandardPIT *pit, Message *interestMessage); + +static void _pit_PitEntryDestroyer(void **dataPtr) { + pitEntry_Release((PitEntry **)dataPtr); +} + +static bool _pit_IngressSetContains(PitEntry *pitEntry, unsigned connectionId) { + const NumberSet *set = pitEntry_GetIngressSet(pitEntry); + bool numberInSet = numberSet_Contains(set, connectionId); + return numberInSet; +} + +static Ticks _pit_CalculateLifetime(StandardPIT *pit, + Message *interestMessage) { + uint64_t interestLifetimeTicks = + message_GetInterestLifetimeTicks(interestMessage); + if (interestLifetimeTicks == 0) { + interestLifetimeTicks = forwarder_NanosToTicks(4000000000ULL); + } + + Ticks expiryTime = forwarder_GetTicks(pit->forwarder) + interestLifetimeTicks; + return expiryTime; +} + +static void _pit_StoreInTable(StandardPIT *pit, Message *interestMessage) { + Message *key = message_Acquire(interestMessage); + + Ticks expiryTime = _pit_CalculateLifetime(pit, interestMessage); + + PitEntry *pitEntry = + pitEntry_Create(key, expiryTime, forwarder_GetTicks(pit->forwarder)); + + parcHashCodeTable_Add(pit->table, key, pitEntry); + + if (logger_IsLoggable(pit->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(pit->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, + "Message %p added to PIT (expiry %" PRIu64 ") ingress %u", + (void *)interestMessage, pitEntry_GetExpiryTime(pitEntry), + message_GetIngressConnectionId(interestMessage)); + } +} + +static void _pit_ExtendLifetime(StandardPIT *pit, PitEntry *pitEntry, + Message *interestMessage) { + Ticks expiryTime = _pit_CalculateLifetime(pit, interestMessage); + + if (expiryTime > pitEntry_GetExpiryTime(pitEntry)) + pitEntry_SetExpiryTime(pitEntry, expiryTime); +} + +// ====================================================================== +// Interface API + +static void _pitStandard_Destroy(PIT **pitPtr) { + parcAssertNotNull(pitPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*pitPtr, "Parameter must dereference to non-null pointer"); + + StandardPIT *pit = pit_Closure(*pitPtr); + + if (logger_IsLoggable(pit->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(pit->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, "PIT %p destroyed", (void *)pit); + } + + parcHashCodeTable_Destroy(&pit->table); + logger_Release(&pit->logger); + parcMemory_Deallocate(pitPtr); +} + +static PITVerdict _pitStandard_ReceiveInterest(PIT *generic, + Message *interestMessage) { + parcAssertNotNull(generic, "Parameter pit must be non-null"); + parcAssertNotNull(interestMessage, + "Parameter interestMessage must be non-null"); + + StandardPIT *pit = pit_Closure(generic); + + PitEntry *pitEntry = parcHashCodeTable_Get(pit->table, interestMessage); + + if (pitEntry) { + // has it expired? + Ticks now = forwarder_GetTicks(pit->forwarder); + if (now < pitEntry_GetExpiryTime(pitEntry)) { + _pit_ExtendLifetime(pit, pitEntry, interestMessage); + + // Is the reverse path already in the PIT entry? + if (_pit_IngressSetContains( + pitEntry, message_GetIngressConnectionId(interestMessage))) { + // It is already in the PIT entry, so this is a retransmission, so + // forward it. + + if (logger_IsLoggable(pit->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(pit->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, + "Message %p existing entry (expiry %" PRIu64 + ") and reverse path, forwarding", + (void *)interestMessage, pitEntry_GetExpiryTime(pitEntry)); + } + + return PITVerdict_Forward; + } + + // It is in the PIT but this is the first interest for the reverse path + pitEntry_AddIngressId(pitEntry, + message_GetIngressConnectionId(interestMessage)); + + if (logger_IsLoggable(pit->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(pit->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, + "Message %p existing entry (expiry %" PRIu64 + ") and reverse path is new, aggregate", + (void *)interestMessage, pitEntry_GetExpiryTime(pitEntry)); + } + + return PITVerdict_Aggregate; + } + // this is a timeout.... + FibEntry *fibEntry = pitEntry_GetFibEntry(pitEntry); + if (fibEntry != NULL) { + fibEntry_OnTimeout(fibEntry, pitEntry_GetEgressSet(pitEntry)); + } + + // it's an old entry, remove it + parcHashCodeTable_Del(pit->table, interestMessage); + } + + _pit_StoreInTable(pit, interestMessage); + + return PITVerdict_Forward; +} + +static NumberSet *_pitStandard_SatisfyInterest(PIT *generic, + const Message *objectMessage) { + parcAssertNotNull(generic, "Parameter pit must be non-null"); + parcAssertNotNull(objectMessage, "Parameter objectMessage must be non-null"); + + StandardPIT *pit = pit_Closure(generic); + + NumberSet *ingressSet = numberSet_Create(); + + PitEntry *pitEntry = parcHashCodeTable_Get(pit->table, objectMessage); + if (pitEntry) { + // here we need to check if the PIT entry is expired + // if so, remove the PIT entry. + Ticks now = forwarder_GetTicks(pit->forwarder); + if (now < pitEntry_GetExpiryTime(pitEntry)) { + // PIT entry is not expired, use it + FibEntry *fibEntry = pitEntry_GetFibEntry(pitEntry); + if (fibEntry != NULL) { + // this is a rough estimation of the residual RTT + Ticks rtt = forwarder_GetTicks(pit->forwarder) - + pitEntry_GetCreationTime(pitEntry); + fibEntry_ReceiveObjectMessage(fibEntry, pitEntry_GetEgressSet(pitEntry), + objectMessage, + rtt); // need to implement RTT + } + const NumberSet *is = pitEntry_GetIngressSet(pitEntry); + numberSet_AddSet(ingressSet, is); // with this we do a copy so we can + // remove the entry from the PIT + } + // remove the entry from the PIT. Key is a reference counted copy of the + // pit entry message + Message *key = pitEntry_GetMessage(pitEntry); + parcHashCodeTable_Del(pit->table, key); + message_Release(&key); + } + + return ingressSet; +} + +static void _pitStandard_RemoveInterest(PIT *generic, + const Message *interestMessage) { + parcAssertNotNull(generic, "Parameter pit must be non-null"); + parcAssertNotNull(interestMessage, + "Parameter interestMessage must be non-null"); + + StandardPIT *pit = pit_Closure(generic); + + if (logger_IsLoggable(pit->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(pit->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, "Message %p removed from PIT", + (void *)interestMessage); + } + + parcHashCodeTable_Del(pit->table, interestMessage); +} + +static PitEntry *_pitStandard_GetPitEntry(const PIT *generic, + const Message *interestMessage) { + parcAssertNotNull(generic, "Parameter pit must be non-null"); + parcAssertNotNull(interestMessage, + "Parameter interestMessage must be non-null"); + + StandardPIT *pit = pit_Closure(generic); + + PitEntry *entry = parcHashCodeTable_Get(pit->table, interestMessage); + if (entry) { + return pitEntry_Acquire(entry); + } + return NULL; +} + +// ====================================================================== +// Public API + +PIT *pitStandard_Create(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + + size_t allocation = sizeof(PIT) + sizeof(StandardPIT); + + PIT *generic = parcMemory_AllocateAndClear(allocation); + parcAssertNotNull(generic, "parcMemory_AllocateAndClear(%zu) returned NULL", + allocation); + generic->closure = (uint8_t *)generic + sizeof(PIT); + + StandardPIT *pit = pit_Closure(generic); + pit->forwarder = forwarder; + pit->logger = logger_Acquire(forwarder_GetLogger(forwarder)); + + size_t initialSize = 65535; + pit->table = + parcHashCodeTable_Create_Size(hashTableFunction_MessageNameEquals, + hashTableFunction_MessageNameHashCode, NULL, + _pit_PitEntryDestroyer, initialSize); + + if (logger_IsLoggable(pit->logger, LoggerFacility_Processor, + PARCLogLevel_Debug)) { + logger_Log(pit->logger, LoggerFacility_Processor, PARCLogLevel_Debug, + __func__, "PIT %p created", (void *)pit); + } + + generic->getPitEntry = _pitStandard_GetPitEntry; + generic->receiveInterest = _pitStandard_ReceiveInterest; + generic->release = _pitStandard_Destroy; + generic->removeInterest = _pitStandard_RemoveInterest; + generic->satisfyInterest = _pitStandard_SatisfyInterest; + + return generic; +} diff --git a/hicn-light/src/processor/pitStandard.h b/hicn-light/src/processor/pitStandard.h new file mode 100755 index 000000000..b9ba026c8 --- /dev/null +++ b/hicn-light/src/processor/pitStandard.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file pitStandard.h + * @brief The Pending Interest Table + * + * Implements the standard Pending Interest Table. + * + */ + +#ifndef pitStandard_h +#define pitStandard_h + +#include <src/processor/pit.h> + +/** + * Creates a PIT table + * + * Creates and allocates an emtpy PIT table. The Forwarder reference is + * used for logging and for time functions. + * + * @param [in] hicn-light The releated Forwarder + * + * @return non-null a PIT table + * @return null An error + */ +PIT *pitStandard_Create(Forwarder *forwarder); +#endif // pit_h diff --git a/hicn-light/src/processor/pitVerdict.h b/hicn-light/src/processor/pitVerdict.h new file mode 100755 index 000000000..16631fa51 --- /dev/null +++ b/hicn-light/src/processor/pitVerdict.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file pitVerdict.h + * @brief Adding an entry to the PIT will return NEW or EXISTING + * + * Adding an entry to the PIT will return NEW or EXISTING + * + */ + +#ifndef pitVerdict_h +#define pitVerdict_h + +/** + * @typedef PitVerdict + * @abstract The verdit of the PIT for receiving a message + * @constant PITVerdict_Forward The message made a new PIT entry, the interest + * should be forwarded + * @constant PITVerdict_Aggregate The Interest was aggregated in the PIT, does + * not need to be forwarded + */ +typedef enum { PITVerdict_Forward, PITVerdict_Aggregate } PITVerdict; +#endif // pitVerdict_h diff --git a/hicn-light/src/socket/CMakeLists.txt b/hicn-light/src/socket/CMakeLists.txt new file mode 100755 index 000000000..6ea94dcfa --- /dev/null +++ b/hicn-light/src/socket/CMakeLists.txt @@ -0,0 +1,31 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +if (UNIX AND NOT APPLE AND NOT ANDROID_API) + list(APPEND HEADER_FILES + socket/api.h + socket/error.h + socket/ops.h + ) + + list(APPEND SOURCE_FILES + socket/api.c + socket/error.c + socket/ops_linux.c + ) +endif() + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE)
\ No newline at end of file diff --git a/hicn-light/src/socket/api.c b/hicn-light/src/socket/api.c new file mode 100755 index 000000000..aede01efe --- /dev/null +++ b/hicn-light/src/socket/api.c @@ -0,0 +1,604 @@ +#include <arpa/inet.h> // inet_ntop +#include <netdb.h> // '' +#include <search.h> // tfind(), tdestroy(), twalk(), preorder... +#include <stdbool.h> +#include <stdio.h> // perror +#include <stdlib.h> // calloc +#include <string.h> // memcpy +#include <sys/socket.h> // '' +#include <sys/types.h> // getaddrinfo +#include <unistd.h> // close + +#include "api.h" +#include "error.h" +#include "ops.h" + +#define INET_MAX_ADDRSTRLEN INET6_ADDRSTRLEN + +#define IF_NAMESIZE 16 +#define MAX_TABLES 256 + +#define DEFAULT_INTERVAL 1000 +#define DEFAULT_IDENTIFIER "hicn" +#define DEFAULT_SOCKET_IDENTIFIER "main" +#define LOCAL_IPV6_PREFIX "fe80" + +#define LOCAL_PRIORITY 32000 + +extern hicn_socket_ops_t ops; + +/* Configuration stored as a global variable to allow access from signal + * handlers for instance */ + +static hicn_conf_t hicn_default_conf = { + .identifier = DEFAULT_IDENTIFIER, + //.format = HF_INET6_TCP +}; + +/* Global state */ +// FIXME move into helper state ? + +struct ip_rule_state_ { + char tun_name[IF_NAMESIZE]; + ip_address_t ip_address; + uint32_t table_id; + uint8_t priority; + uint8_t address_family; +}; + +struct ip_route_state_ { + char remote_ip_address[128]; // this is to big, but it is fine for now + uint8_t address_family; + uint32_t table_id; +}; + +typedef struct ip_rule_state_ ip_rule_state; +typedef struct ip_route_state_ ip_route_state; + +int punting_table_id; +uint16_t rules_counter; +uint16_t routes_counter; +static ip_rule_state rules_to_remove[MAX_TABLES]; +static ip_route_state routes_to_remove[MAX_TABLES]; + +// END FIXME + +hicn_socket_helper_t *hicn_create() { + int rc; + + punting_table_id = -1; + rules_counter = 0; + + hicn_socket_helper_t *hicn = malloc(sizeof(hicn_socket_helper_t)); + if (!hicn) { + goto ERR_MALLOC; + } + + hicn->conf = malloc(sizeof(hicn_conf_t)); + if (hicn->conf < 0) goto ERR_CONF; + memcpy(hicn->conf, &hicn_default_conf, sizeof(hicn_conf_t)); + + /* Initialize socket tree to empty */ + hicn->socket_root = NULL; + + // enable forwarding globally. Per-interface forwarding will be enabled when + // interfaces are created (TODO) + rc = ops.enable_v6_forwarding(NULL); + if (rc < 0) { + goto ERR_FW; + } + + rc = ops.enable_v4_forwarding(); + if (rc < 0) { + goto ERR_FW; + } + + // modify priority of table local + /* ip -6 rule del from all prio 0 table local */ + /* ip -6 rule add from all prio 32000 table local */ + + rc = ops.del_lo_prio_rule(NULL, AF_INET6, 0); + if (rc < 0) { + goto ERR_FW; + } + + rc = ops.del_lo_prio_rule(NULL, AF_INET, 0); + if (rc < 0) { + goto ERR_FW; + } + + rc = ops.add_lo_prio_rule(NULL, AF_INET6, LOCAL_PRIORITY); + if (rc < 0) { + goto ERR_FW; + } + + rc = ops.add_lo_prio_rule(NULL, AF_INET, LOCAL_PRIORITY); + if (rc < 0) { + goto ERR_FW; + } + + return hicn; + +ERR_FW: + free(hicn->conf); +ERR_CONF: + free(hicn); +ERR_MALLOC: + return NULL; +} + +void hicn_destroy() { + int rc; + uint16_t i; + + /* Restore default rules */ + printf("Restoring default configuration.\n"); + rc = ops.del_lo_prio_rule(NULL, AF_INET6, LOCAL_PRIORITY); + if (rc < 0) { + goto ERR; + } + + rc = ops.del_lo_prio_rule(NULL, AF_INET, LOCAL_PRIORITY); + if (rc < 0) { + goto ERR; + } + + rc = ops.add_lo_prio_rule(NULL, AF_INET6, 0); + if (rc < 0) { + goto ERR; + } + + rc = ops.add_lo_prio_rule(NULL, AF_INET, 0); + if (rc < 0) { + goto ERR; + } + + for (i = 0; i < rules_counter; i++) { + if (strcmp(rules_to_remove[i].tun_name, "NONE") != 0) { + rc = ops.del_rule(rules_to_remove[i].tun_name, + rules_to_remove[i].address_family, + rules_to_remove[i].table_id); + if (rc < 0) { + goto ERR; + } + } else { + rc = ops.del_prio_rule( + &rules_to_remove[i].ip_address, rules_to_remove[i].address_family, + rules_to_remove[i].priority, rules_to_remove[i].table_id); + if (rc < 0) { + goto ERR; + } + } + } + + for (i = 0; i < routes_counter; i++) { + rc = ops.del_out_route(routes_to_remove[i].remote_ip_address, + routes_to_remove[i].address_family, + routes_to_remove[i].table_id); + if (rc < 0) { + goto ERR; + } + } + +ERR: + if (rc < 0) printf("Unexpected exit. Some state may not be deleted.\n"); + return; +} + +void hicn_free(hicn_socket_helper_t *hicn) { + // close tun ? + free(hicn); +} + +hicn_socket_t *hicn_socket_create() { + hicn_socket_t *socket = calloc(1, sizeof(hicn_socket_t)); + if (!socket) { + goto ERR_SOCKET; + } + socket->type = HS_UNSPEC; + + return socket; + +ERR_SOCKET: + return NULL; +} + +int hicn_socket_cmp(hicn_socket_t *a, hicn_socket_t *b) { + return b->fd - a->fd; +} + +ip_address_t *hicn_socket_get_src_ip(hicn_socket_t *socket) { + if (socket->type != HS_CONNECTION) { + return NULL; + } + return &socket->connection.tun_ip_address; +} + +typedef int (*cmp_t)(const void *, const void *); + +int hicn_socket_add(hicn_socket_helper_t *hicn, hicn_socket_t *socket) { + if (!(tsearch(socket, &hicn->socket_root, (cmp_t)hicn_socket_cmp))) { + // ERROR("Could not insert field id into index"); + return -1; + } + return 0; +} + +hicn_socket_t *hicn_socket_find(hicn_socket_helper_t *hicn, int fd) { + hicn_socket_t search = { + .fd = fd, + }; + hicn_socket_t **socket = + tfind(&search, &hicn->socket_root, (cmp_t)hicn_socket_cmp); + return socket ? *socket : NULL; +} + +/******************************************************************************* + * New API + *******************************************************************************/ + +int hicn_set_local_endpoint(hicn_socket_t *socket, const char *local_ip_address, + bool allow_null) { + int rc = HICN_SOCKET_ERROR_NONE; + + if (!local_ip_address) { + if (!allow_null) { + rc = HICN_SOCKET_ERROR_SOCKET_LOCAL_NULL_ADDRESS; + } + goto end; + } + + /* local_ip_address should be a prefix with global scope in which to pick + * the locator address to use as the source. + * If we expect to pick another IP for the tun, then it needs to be of size + * less than 128. + */ + + /* Copy the local IP address inside the connection */ + rc = hicn_ip_pton(local_ip_address, &socket->connection.tun_ip_address); + if (rc < 0) { + rc = HICN_SOCKET_ERROR_SOCKET_LOCAL_REPR; + goto end; + } + +end: + return rc; +} + +// XXX This could be used by hicn_set_remote_endpoint +// XXX This has been introduced for mapme +int hicn_get_local_address(const ip_address_t *remote_address, + ip_address_t *local_address) { + int rc = 0; + uint32_t interface_id; + char remote_address_str[INET_MAX_ADDRSTRLEN]; + + rc = hicn_ip_ntop(remote_address, remote_address_str, + sizeof(remote_address_str)); + if (rc < 0) { + rc = HICN_SOCKET_ERROR_BIND_REMOTE_REPR; + goto ERR; + } + + rc = ops.get_output_ifid(remote_address_str, remote_address->family, + &interface_id); + if (rc < 0 || interface_id == 0) { + rc = HICN_SOCKET_ERROR_BIND_REMOTE_INTERFACE; + goto ERR; + } + + /* Local ip */ + rc = ops.get_ip_addr(interface_id, remote_address->family, local_address); + if (rc < 0) { + rc = HICN_SOCKET_ERROR_BIND_REMOTE_NETMASK; + goto ERR; + } + +ERR: + return rc; +} + +/** + * + * sets socket->interface_id + */ +int hicn_set_remote_endpoint(hicn_socket_t *socket, + const char *remote_ip_address) { + int af, rc = HICN_SOCKET_ERROR_NONE; + ip_address_t addr; + + af = get_addr_family(remote_ip_address); + if ((af != AF_INET6) && (af != AF_INET)) { + return HICN_SOCKET_ERROR_INVALID_IP_ADDRESS; + } + + /* Bind local endpoint if not done yet */ + if (ip_address_empty(&socket->connection.tun_ip_address)) { + char local_ip_address[INET_MAX_ADDRSTRLEN]; + + /* Local interface id */ + // INFO("Getting interface_id from gateway IP address %s", + // remote_ip_address); + ///// + int addr_family = get_addr_family(remote_ip_address); + if (addr_family < 0) { + rc = addr_family; + goto ERR; + } + + rc = ops.get_output_ifid(remote_ip_address, (uint8_t)addr_family, + &socket->connection.interface_id); + if (rc < 0 || socket->connection.interface_id == 0) { + rc = HICN_SOCKET_ERROR_BIND_REMOTE_INTERFACE; + goto ERR; + } + + /* Local ip */ + rc = ops.get_ip_addr(socket->connection.interface_id, (uint8_t)addr_family, + &addr); + if (rc < 0) { + rc = HICN_SOCKET_ERROR_BIND_REMOTE_NETMASK; + goto ERR; + } + ///// + + /* Convert to representation format */ + rc = hicn_ip_ntop(&addr, local_ip_address, sizeof(local_ip_address)); + if (rc < 0) { + rc = HICN_SOCKET_ERROR_BIND_REMOTE_REPR; + goto ERR; + } + + rc = hicn_set_local_endpoint(socket, local_ip_address, true); + if (rc < 0) { + switch (rc) { + case HICN_SOCKET_ERROR_SOCKET_LOCAL_NULL_ADDRESS: + rc = HICN_SOCKET_ERROR_BIND_REMOTE_LOCAL_NULL_ADDR; + break; + case HICN_SOCKET_ERROR_SOCKET_LOCAL_REPR: + rc = HICN_SOCKET_ERROR_BIND_REMOTE_LOCAL_REPR; + break; + case HICN_SOCKET_ERROR_SOCKET_LOCAL_HEURISTIC: + rc = HICN_SOCKET_ERROR_BIND_REMOTE_LOCAL_HEURISTIC; + break; + case HICN_SOCKET_ERROR_SOCKET_LOCAL_SET_TUN_IP: + rc = HICN_SOCKET_ERROR_BIND_REMOTE_LOCAL_SET_TUN_IP; + break; + } + goto ERR; + } + } + return HICN_SOCKET_ERROR_NONE; + +ERR: + return rc; +} + +/** + * + * We need at least an identifier. + */ +int hicn_socket(hicn_socket_helper_t *hicn, const char *identifier, + const char *local_ip_address) { + int rc; + + hicn_socket_t *socket = hicn_socket_create(); + if (!socket) { + rc = -5; + goto ERR_SOCKET; + } + + ops.get_tun_name(hicn->conf->identifier, identifier, socket->tun_name); + + // register the hicn face on which to bind prefixes, create the in/out TUN + // device + socket->fd = ops.tun_create(socket->tun_name); + if (socket->fd <= 0) { + rc = -2; + goto ERR_TUN; + } + + // INFO("Successfully created listener on TUN device %s", socket->tun_name); + + /* Retrieve interface id */ + socket->tun_id = ops.get_ifid(socket->tun_name); + if (socket->tun_id < 0) { + rc = -3; + goto ERR_TUNIFID; + } + // INFO("Interface id=%d", socket->tun_id); + + // WARN("Need to set offload"); + + // INFO("Setting interface up"); + rc = ops.up_if(socket->tun_id); + if (rc < 0) { + rc = -4; + goto ERR_UP; + } + + /* Update state */ + rc = hicn_socket_add(hicn, socket); + if (rc < 0) { + rc = -5; + goto ERR_ADD; + } + + rc = hicn_set_local_endpoint(socket, local_ip_address, true); + if (rc < 0) { + rc = -6; + goto ERR_ADJACENCY; + } + + return socket->fd; + +ERR_ADJACENCY: +ERR_ADD: +ERR_UP: +ERR_TUNIFID: +ERR_TUN: + free(socket); +ERR_SOCKET: + // ERR_PARAMS: + return rc; +} + +int hicn_listen(hicn_socket_helper_t *hicn, int fd, const char *prefix) { + int rc; + hicn_socket_t *socket = hicn_socket_find(hicn, fd); + if (!socket) { + return -1; + } + + /* Check socket is not a connection */ + if (socket->type == HS_CONNECTION) { + return -1; + } + + rc = ops.add_in_route_s(prefix, socket->tun_id); + if (rc < 0) { + return rc; + } + + ip_address_t ip_address; + rc = hicn_ip_pton(prefix, &ip_address); + if (rc < 0) { + return rc; + } + + // ip -6 rule add from b001::/16 prio 0 table 100 + socket->connection.table_id = + socket->tun_id % MAX_TABLES; // this table should be unused + + if (punting_table_id == -1) punting_table_id = socket->connection.table_id; + + rc = ops.add_prio_rule(&ip_address, ip_address.family, 0, + socket->connection.table_id); + if (rc < 0) { + return rc; + } + + strcpy(rules_to_remove[rules_counter].tun_name, "NONE"); + + rules_to_remove[rules_counter].ip_address = ip_address; + rules_to_remove[rules_counter].address_family = ip_address.family; + rules_to_remove[rules_counter].table_id = socket->connection.table_id; + rules_to_remove[rules_counter].priority = 0; + ++rules_counter; + + /* Update socket upon success */ + socket->type = HS_LISTENER; + + return 0; +} + +/** + * + * We can pass all adjacency parameters but identifier + */ +int hicn_bind(hicn_socket_helper_t *hicn, int fd, + const char *remote_ip_address) { + // uint32_t interface_id; + int rc = HICN_SOCKET_ERROR_NONE; + + hicn_socket_t *socket = hicn_socket_find(hicn, fd); + if (!socket) { + rc = HICN_SOCKET_ERROR_BIND_SOCKET_NOT_FOUND; + goto ERR; + } + + /* We allow reuse */ + if (socket->type == HS_CONNECTION) return rc; + + /* Check socket is not a connection */ + if (socket->type != HS_UNSPEC) { + rc = HICN_SOCKET_ERROR_BIND_SOCKET_ALREADY_BOUND; + goto ERR; + } + socket->type = HS_CONNECTION; + + // each connection is associated a table id, let's take it equal to the + // tun ID by default (% MAX_TABLES, assuming TUN IDs do not overlap modulo + // 256...). + // XXX we need to make sure the corresponding table is flushed. + socket->connection.table_id = + socket->tun_id % MAX_TABLES; // interface_id; // ops.get_free_table_id(); + + // XXX use IP address + rc = hicn_set_remote_endpoint(socket, remote_ip_address); + if (rc < 0) { + goto ERR; + } + + // rule + // ip -6 rule from all iif eth0 lookup 200 + // INFO("Adding output rule for %s in table %d", socket->tun_name, + // socket->connection.table_id); + int addr_family = get_addr_family(remote_ip_address); + if (addr_family < 0) { + rc = addr_family; + goto ERR; + } + + rc = ops.add_rule(socket->tun_name, (uint8_t)addr_family, + socket->connection.table_id); + if (rc < 0) { + rc = HICN_SOCKET_ERROR_BIND_RULE; + goto ERR; + } + + strcpy(rules_to_remove[rules_counter].tun_name, socket->tun_name); + rules_to_remove[rules_counter].address_family = addr_family; + rules_to_remove[rules_counter].table_id = socket->connection.table_id; + ++rules_counter; + + // route + // ip -6 route add default via 2002::2 table 28 + // INFO("Adding output route in table %d via gateway %s", + // socket->connection.table_id, + // remote_ip_address); + + // if the address is an IPv6 and start with fe80 we need to specify the device + // in the route + u32 default_interface = ~0; + if (addr_family == AF_INET6 && strncmp(LOCAL_IPV6_PREFIX, remote_ip_address, + strlen(LOCAL_IPV6_PREFIX)) == 0) { + rc = ops.get_output_ifid(remote_ip_address, (uint8_t)addr_family, + &default_interface); + if (rc < 0) { + goto ERR; + } + } + + rc = ops.add_out_route(remote_ip_address, (uint8_t)addr_family, + socket->connection.table_id, default_interface); + if (rc < 0) { + rc = HICN_SOCKET_ERROR_BIND_ROUTE; + goto ERR; + } + + strcpy(routes_to_remove[routes_counter].remote_ip_address, remote_ip_address); + routes_to_remove[routes_counter].table_id = socket->connection.table_id; + routes_to_remove[routes_counter].address_family = (uint8_t)addr_family; + ++routes_counter; + + // add route for data + // ip -6 route add 0:1::/64 dev hicn-if0 table 100 + // this routes are deleted by removing the tun interfaces + + if (punting_table_id == -1) { + // the punting_table_id was not initialized beacause no main-tun was created + // we use as an id (socket->tun_id - 1) % MAX_TABLES, so that we will hava a + // collision only after 255 new interfaces + punting_table_id = (socket->tun_id - 1) % MAX_TABLES; + } + rc = ops.add_in_route_table(&socket->connection.tun_ip_address, + socket->tun_id, punting_table_id); + if (rc < 0) { + rc = HICN_SOCKET_ERROR_BIND_ROUTE; + goto ERR; + } + +ERR: + return rc; +} diff --git a/hicn-light/src/socket/api.h b/hicn-light/src/socket/api.h new file mode 100755 index 000000000..e1516ebe1 --- /dev/null +++ b/hicn-light/src/socket/api.h @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file hicn_face.h + * @brief hICN socket library + * + * This module provides an interface to managing so-called hICN sockets, + * realizing punting of interest and data packets using a TUN device. + */ + +#ifndef HICN_SOCKET_API_H +#define HICN_SOCKET_API_H + +#include <stdint.h> // uint*_t +#include <stdlib.h> + +#include <hicn/hicn.h> +#include "error.h" + +#define BUFSIZE 4096 +#define MAX_CONNECTIONS \ + 255 // We currently limit the number of connections we can establish +#define IF_NAMESIZE 16 + +/* hICN socket helper */ + +/** hICN configuration options */ +typedef struct { + // uint32_t interval; + + /* Identifier used to name hICN TUN interfaces (should be unique) */ + char *identifier; + // hicn_format_t format; + +} hicn_conf_t; + +/** + * hICN adjacency + */ +typedef struct { + char *local_ip_address; + char *gateway_ip_address; +} hicn_adjacency_t; + +#define EMPTY_HICN_ADJACENCY \ + (hicn_adjacency_t) { 0, 0 } + +/* hICN socket operations */ + +typedef struct { + uint8_t pkbuf[BUFSIZE]; + uint32_t rb_pkbuf_r; + uint32_t rb_pkbuf_w; +} hicn_buffer_t; + +typedef enum { HS_UNSPEC, HS_LISTENER, HS_CONNECTION } hicn_socket_type_t; + +typedef struct hicn_socket_s { + hicn_socket_type_t type; + int fd; + + /* Implementation specific state follows */ + char tun_name[IF_NAMESIZE]; + uint32_t tun_id; + + hicn_buffer_t buffer; + void (*cb)(struct hicn_socket_s *, void *, uint8_t *, size_t); + void *cb_data; + + union { + struct { + ip_address_t tun_ip_address; + uint32_t interface_id; + + /* ID of the corresponding table : avoid default values of 0, 32766 and + * 32767 */ + uint8_t table_id; + } connection; + }; +} hicn_socket_t; + +/** + * hICN global state + */ +typedef struct { + /* Configuration data */ + hicn_conf_t *conf; + + // We need state associate to each FD, to know what type of socket it is and + // its state. + void *socket_root; /**< A tree of socket indexed by their fd */ + +} hicn_socket_helper_t; + +/** + * Create an hICN instance. + * + * This is used to configure the state of an hICN router consistently between + * a listener and the different connections. It also regroups all the state + * related to hICN functionalities. + * + * @return A pointer to an hICN instance. + */ +hicn_socket_helper_t *hicn_create(); + +void hicn_destroy(); + +/** + * Retrieve hICN configuration. + * + * Gets the current configuration of an hICN instance for information purposes, + * or later update it. + * + * TODO + * - We might want to prevent configuration updates while the hICN instance is + * running. Define running... + * + * @param [in] hicn Pointer to hICN instance. + * @return Pointer to an hICN configuration data structure. + * + * @see hicn_set_conf + */ +hicn_conf_t *hicn_get_conf(hicn_socket_helper_t *hicn); + +/** + * Update hICN configuration. + * + * @param [in] hicn Pointer to an hICN instance. + * @param [in] hicn_conf Pointer to an hICN configuration data structure. + * @return 0 in case of success, -1 otherwise. + * + * @see hicn_get_conf + */ +int hicn_set_conf(hicn_socket_helper_t *hicn, hicn_conf_t *hicn_conf); + +/** + * Release hICN state. + * + * @param [in] hicn Pointer to an hICN instance. + */ +void hicn_free(hicn_socket_helper_t *hicn); + +// FIXME doc +int hicn_get_local_address(const ip_address_t *remote_address, + ip_address_t *local_address); + +/* hICN socket */ + +/** + * Create an hICN socket. + * + * An hICN socket abstracts the underlying implementation and allows hICN + * packets to be sent and received independently of the underlying + * implementation. + * + * It is possible to further specialize the socket in a listener socket, and a + * connection socket. + * + * @param [in] hicn Pointer to an hICN instance. + * @param [in] identifier Unique identifier for this socket, used to named the + * TUN device + * @param [in] local_ip_address IP address used locally by the socket (or NULL + * for letting the library decide automatically). + * @return File descriptor (>0) in case of success, -1 otherwise. + * + * @see hicn_listen + * @see hicn_bind + */ +int hicn_socket(hicn_socket_helper_t *hicn, const char *identifier, + const char *local_ip_address); + +/** + * Packet punting. + * + * Note that we cannot listen on a socket that is already bound. + * + * @param [in] hicn Pointer to an hICN instance. + * @param [in] fd File descriptor identifying the hICN socket. + * @param [in] prefix Prefix (IPv4 or IPv6) to be bound to hICN in + * RFC-compliant presentation format. + * @return 0 in case of success, -1 otherwise. + * + * @see hicn_socket + */ +int hicn_listen(hicn_socket_helper_t *hicn, int fd, const char *prefix); + +/** + * Packet forwarding + * @param [in] hicn Pointer to an hICN instance. + * @param [in] fd File descriptor identifying the hICN socket. + * @param [in] prefix Prefix (IPv4 or IPv6) to be bound to hICN in + * RFC-compliant presentation format. + * @return 0 in case of success, -1 otherwise. + * + * XXX adjacency does not perform any copy heresofar + * + * @see hicn_socket + */ +int hicn_bind(hicn_socket_helper_t *hicn, int fd, + const char *remote_ip_address); + +#endif /* HICN_SOCKET_API_H */ diff --git a/hicn-light/src/socket/error.c b/hicn-light/src/socket/error.c new file mode 100755 index 000000000..3dafec8cf --- /dev/null +++ b/hicn-light/src/socket/error.c @@ -0,0 +1,7 @@ +#include "error.h" + +const char* HICN_SOCKET_ERROR_STRING[] = { +#define _(a, b, c) [b] = c, + foreach_hicn_socket_error +#undef _ +}; diff --git a/hicn-light/src/socket/error.h b/hicn-light/src/socket/error.h new file mode 100755 index 000000000..8195efd84 --- /dev/null +++ b/hicn-light/src/socket/error.h @@ -0,0 +1,46 @@ +#ifndef HICN_SOCKET_ERROR_H +#define HICN_SOCKET_ERROR_H + +// FIXME remove unused errors +#define foreach_hicn_socket_error \ + _(NONE, 0, "OK") \ + _(UNSPEC, 1, "unspecified error") \ + _(NOT_HICN, 2, "not a hICN paclet") \ + _(UNKNOWN_ADDRESS, 10, "unknown address") \ + _(INVALID_PARAMETER, 20, "invalid parameter") \ + _(INVALID_IP_ADDRESS, 21, "invalid IP address") \ + _(CORRUPTED_PACKET, 22, "corrupted packet") \ + _(UNEXPECTED, 98, "unexpected error") \ + _(NOT_IMPLEMENTED, 99, "not implemented") \ + _(SOCKET_LOCAL_NULL_ADDRESS, 101, "empty local address") \ + _(SOCKET_LOCAL_REPR, 102, "cannot represent local address") \ + _(SOCKET_LOCAL_HEURISTIC, 103, "error finding local address") \ + _(SOCKET_LOCAL_SET_TUN_IP, 104, "cannot set local IP to TUN") \ + _(BIND_SOCKET_NOT_FOUND, 301, "bind: socket not found") \ + _(BIND_SOCKET_ALREADY_BOUND, 302, "bind: socket already bound") \ + _(BIND_REMOTE_INTERFACE, 303, "bind: no interface towards gateway") \ + _(BIND_REMOTE_NETMASK, 304, "bind: no local IP with netmask < 128") \ + _(BIND_REMOTE_REPR, 305, "bind: error representing local IP") \ + _(BIND_REMOTE_LOCAL_NULL_ADDR, 306, "bind: could not set local endpoint") \ + _(BIND_REMOTE_LOCAL_REPR, 307, "bind: error representing remote IP") \ + _(BIND_REMOTE_LOCAL_HEURISTIC, 308, "bind: could not apply heuristic") \ + _(BIND_REMOTE_LOCAL_SET_TUN_IP, 309, "bind: error setting local IP to TUN") \ + _(BIND_NDP, 310, "bind: could not enable NDP proxy") \ + _(BIND_NEIGH_PROXY, 311, "bind: could not neighbour") \ + _(BIND_REPR, 312, "bind: error represeting IP") \ + _(BIND_LO, 313, "bind: could not remove local route") \ + _(BIND_RULE, 314, "bind: could not add rule") \ + _(BIND_ROUTE, 315, "bind: could not add output route") + +typedef enum { +#define _(a, b, c) HICN_SOCKET_ERROR_##a = (-b), + foreach_hicn_socket_error +#undef _ + HICN_SOCKET_N_ERROR, +} hicn_socket_error_t; + +extern const char *HICN_SOCKET_ERROR_STRING[]; + +#define hicn_socket_strerror(errno) (char *)(HICN_SOCKET_ERROR_STRING[-errno]) + +#endif /* HICN_SOCKET_ERROR_H */ diff --git a/hicn-light/src/socket/ops.h b/hicn-light/src/socket/ops.h new file mode 100755 index 000000000..249caf87a --- /dev/null +++ b/hicn-light/src/socket/ops.h @@ -0,0 +1,54 @@ +#ifndef HICN_SOCKET_OPS_H +#define HICN_SOCKET_OPS_H + +#include <hicn/hicn.h> +#include <stdint.h> + +typedef struct { + char *arch; + int (*tun_create)(char *name); + int (*get_tun_name)(const char *prefix, const char *identifier, + char *tun_name); + int (*enable_v6_forwarding)(char *interface_name); + int (*enable_v4_forwarding)(); + int (*enable_ndp_proxy)(); + + uint32_t (*get_ifid)(const char *ifname); + int (*get_output_ifid)(const char *ip_address, uint8_t address_family, + uint32_t *interface_id); + int (*get_ip_addr)(uint32_t interface_id, uint8_t address_family, + ip_address_t *ip_address); + int (*set_ip_addr)(uint32_t interface_id, ip_address_t *ip_address); + int (*up_if)(uint32_t interface_id); + int (*add_in_route_table)(const ip_address_t *prefix, + const uint32_t interface_id, + const uint8_t table_id); + int (*add_in_route_table_s)(const char *prefix, const uint32_t interface_id, + const uint8_t table_id); + int (*add_in_route_s)(const char *prefix, const uint32_t interface_id); + int (*add_out_route)(const char *gateway, const uint8_t address_family, + const uint8_t table_id, int default_route); + int (*del_out_route)(const char *gateway, const uint8_t address_family, + const uint8_t table_id); + int (*del_lo_route)(const ip_address_t *ip_address); + int (*add_rule)(const char *interface_name, const uint8_t address_family, + const uint8_t table_id); + int (*del_rule)(const char *interface_name, const uint8_t address_family, + const uint8_t table_id); + int (*add_neigh_proxy)(const ip_address_t *ip_address, + const uint32_t interface_id); + int (*add_prio_rule)(const ip_address_t *ip_address, + const uint8_t address_family, const uint32_t priority, + const uint8_t table_id); + int (*add_lo_prio_rule)(const ip_address_t *ip_address, + const uint8_t address_family, + const uint32_t priority); + int (*del_prio_rule)(const ip_address_t *ip_address, + const uint8_t address_family, const uint32_t priority, + const uint8_t table_id); + int (*del_lo_prio_rule)(const ip_address_t *ip_address, + const uint8_t address_family, + const uint32_t priority); +} hicn_socket_ops_t; + +#endif /* HICN_SOCKET_OPS_H */ diff --git a/hicn-light/src/socket/ops_linux.c b/hicn-light/src/socket/ops_linux.c new file mode 100755 index 000000000..d085f0d3d --- /dev/null +++ b/hicn-light/src/socket/ops_linux.c @@ -0,0 +1,1723 @@ +#include <sys/ioctl.h> // ioctl +#include <sys/socket.h> // needed by linux/if.h +//#include <linux/if.h> +#include <errno.h> +#include <fcntl.h> // '' +#include <linux/if_tun.h> +#include <linux/limits.h> // PATH_MAX +#include <stdio.h> // fprintf +#include <string.h> // memset +#include <sys/stat.h> // open +#include <sys/uio.h> // writev +#include <unistd.h> // close + +#include "error.h" +#include "ops.h" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) + +/****************************************************************************** + * netlink.h + ******************************************************************************/ + +#ifndef HICN_NETLINK_H +#define HICN_NETLINK_H + +#include <stdint.h> +#include <stdlib.h> + +// DEPRECATED|/* Socket */ +// DEPRECATED|int _nl_get_socket(); +// DEPRECATED|int _nl_send(int s, uint8_t * buffer, size_t len); +// DEPRECATED|size_t _nl_receive(uint8_t * buffer, size_t len); +// DEPRECATED| +// DEPRECATED|/* Netlink packet format */ +// DEPRECATED|int _nl_header(int request, uint8_t * buffer, size_t len, uint32_t +// flags); DEPRECATED|int _nl_payload_rule(uint8_t table_id, uint8_t * buffer, +// size_t len); DEPRECATED|int _nl_payload_link(uint32_t ifindex, uint8_t * +// buffer, size_t len); DEPRECATED|int _nl_payload_route(uint8_t table_id, +// uint8_t dst_len, uint8_t * buffer, size_t len); DEPRECATED| DEPRECATED|int +// _nl_parse(uint8_t * buffer, size_t len); DEPRECATED|int _nl_parse_ret(uint8_t +// * buffer, size_t len); DEPRECATED|int _nl_parse_link_ifid(uint8_t * buffer, +// size_t len, uint32_t * interface_id); DEPRECATED|int +// _nl_parse_link_ip_addr(uint8_t * buffer, size_t len, struct in6_addr * addr); + +/* Public interface */ + +/** + * Get the interface ID of an interface by its name + * + * @return 32-bit interface identifier in case of success, or 0. + * + * @see if_nametoindex + * + */ +uint32_t _nl_get_ifid(const char *ifname); + +/** + * Retrieve the output interface corresponding to the specified IP address. + * + * @param [in] addr IP(v6) address in presentation form. + * @param [out] Identifier of the corresponding output interface. + * @return int 0 in case of success, -1 otherwise + */ +int _nl_get_output_ifid(const char *ip_address, uint8_t address_family, + uint32_t *interface_id); + +/** + * Retrieve the first IP address of an interface (identified by its id) which + * has a netmask < 128. + * + * @param [in] s File descriptor of the netlink socket (deprecated). + * @param [in] interface_id Identifier of the interface for which to retrieve + * the IP address. + * @param [out] addr IP(v6) address in binary form. + * @return int 0 in case of success, -1 otherwise + * + * @see getifaddrs + */ +int _nl_get_ip_addr(uint32_t interface_id, uint8_t address_family, + ip_address_t *ip_address); + +int _nl_set_ip_addr(uint32_t interface_id, ip_address_t *ip_address); + +int _nl_up_if(uint32_t interface_id); + +int _nl_add_in_route_table(const ip_address_t *prefix, + const uint32_t interface_id, const uint8_t table_id); +int _nl_add_in_route_table_s(const char *prefix, const uint32_t interface_id, + const uint8_t table_id); +int _nl_add_in_route_s(const char *prefix, const uint32_t interface_id); + +int _nl_add_out_route(const char *gateway, const uint8_t address_family, + const uint8_t table_idi, int default_route); +int _nl_del_out_route(const char *gateway, const uint8_t address_family, + const uint8_t table_id); + +int _nl_del_lo_route(const ip_address_t *ip_address); + +int _nl_add_rule(const char *interface_name, const uint8_t address_family, + const uint8_t table_id); +int _nl_del_rule(const char *interface_name, const uint8_t address_family, + const uint8_t table_id); + +int _nl_add_neigh_proxy(const ip_address_t *ip_address, + const uint32_t interface_id); + +int _nl_add_prio_rule(const ip_address_t *ip_address, + const uint8_t address_family, const uint32_t priority, + const uint8_t table_id); +int _nl_add_lo_prio_rule(const ip_address_t *ip_address, + const uint8_t address_family, const uint32_t priority); +int _nl_del_prio_rule(const ip_address_t *ip_address, + const uint8_t address_family, const uint32_t priority, + const uint8_t table_id); +int _nl_del_lo_prio_rule(const ip_address_t *ip_address, + const uint8_t address_family, const uint32_t priority); + +#endif /* HICN_NETLINK_H */ + +/****************************************************************************** + * netlink.c + ******************************************************************************/ + +/* + * This module offers an interface to the Netlink API appropriate for + * implementing punting as required by hICN (1). + * + * More specifically, it consists of the following functionalities: + * - LINK + . map interface name to ID + . set and interface up + * - ADDR + . get and set ip addresses on a given interface ID + * - ROUTE + . get output interface id towards IP (ip route get IP > interface_id) + . add input route (ip route add PREFIX dev INTERFACE) for punting + interests . add output route (ip route add default GATEWAY table TABLE) for + routing interests (2, 3) . delete local route towards IP (ip route del IP table + local) for ??? + /!\ could this be avoided by removing the local attribute in the + netlink call ? + * - RULE + * . add output rule (ip rule add iif interface table TABLE) for routing + interests (2, 3) + * - ND PROXY + * . enable NDP proxy functionality for IP on interface ID (ip -6 neigh add + proxy IP dev INTERFACE) + * for allowing the TUN to be reachable on the reverse data path + * + * Implementation notes: + * (1) We have not been using the libnl library because it requires + * manipulating too many function and data structures for a simple purpose. + * Currently, many parts of the code are somehow repetitive, but this might + * be improved by a proper API in a future release. + * (2) allows load balancing over different interfaces = multihoming. Please + * note that it is not possible to have load balancing over two faces using + * the same output interface as we are using the underlying IP network ! + * This might be mitigated with the use of SR however. + * (3) The implementation of punting heavily uses the policy routing + * functionalities, as we need to hook through a TUN into user space a + * whole prefix used as a destination (for interests) or source (for data + * packets). We thus combine the use of rules to assign routing table IDs, + * and routes inside those tables. As there is no easy way to allocate + * which routing tables we use, we made the choice to index them by the ID + * of the interface, assuming there is no external conflict. This might be + * improved in the future. + * + * This hICN implementation uses TUNs in two different ways: + * - a main TUN interface, which receives all punted interests, + * demultiplex them before assigning them an input face (eventually + * dynamically creating it); + * - a set of output TUN interfaces, aka faces, used for routing of + * interests, and for receiving the corresponding data packets on the way + * back. Punting of data packets if based of their destination IP, which + * is the IP of the physical output interface used for the interest, which + * is unique (cf (2)). + * + * The corresponding routing tables IDs are : + * MAIN_TUN_ID -> used for punting of data packets + * OUTPUT_TUN_ID_i -> used for routing of interests towards next hop + * (bypassing local IP routing table) + * + * Note that punting of interests is done just through a route, and routing + * of data packets is done just through the regular IP routing table on the + * note after the address translation done in the forwarder. + * + * - Forging netlink packets + * + * A previous implementation used function calls with pointers to populate + * the various header parts in a buffer in order to build a netlink packet. + * A newer implementation uses nested structs and iovecs to build the whole + * packet in a single write call. This should allow a simpler evolution + * towards a cleaner API. + */ + +#include <arpa/inet.h> // inet_pton +#include <errno.h> // errno +#include <linux/fib_rules.h> // fib_rule_hdr, FRA_* +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <net/if.h> // IFF_UP +#include <netinet/in.h> // in6addr +#include <stdio.h> // perror +#include <string.h> +#include <sys/socket.h> // '' +#include <sys/types.h> // socket +#include <unistd.h> // read + +#include <sys/socket.h> // '' +#include <sys/types.h> // send, recv + +//#include "../../hicn.h" +//#include "../../hicn_util.h" // ARRAY_SIZE, hicn_packet_dump_iov + +#define BUFSIZE 4096 +#define FLAGS_CREATE NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK +// ?? +#define FLAGS_CREATE_MATCH \ + NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK | NLM_F_MATCH + +// XXX putting ACK poses a prolem for the value received by get_if_id. +#define FLAGS_GET NLM_F_REQUEST +#define FLAGS_GET_ROOT (NLM_F_REQUEST | NLM_F_ROOT) + +#define FLAGS_LIST NLM_F_REQUEST | NLM_F_DUMP + +#define IF_NAMESIZE 16 +#define FR_ACT_TO_TBL 1 +#define NLMSG_BOTTOM(nlmsg) \ + ((struct rtattr *)(((void *)(nlmsg)) + NLMSG_ALIGN((nlmsg)->nlmsg_len))) + +int seq = 1; + +static inline size_t iov_length(const struct iovec *iov, + unsigned long nr_segs) { + unsigned long seg; + size_t ret = 0; + + for (seg = 0; seg < nr_segs; seg++) ret += iov[seg].iov_len; + return ret; +} + +typedef struct { + struct nlmsghdr hdr; + struct nlmsgerr payload; +} nl_err_hdr_t; + +/* Low level : nl header */ + +int _nl_get_socket() { return socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); } + +int _nl_header(int request, uint8_t *buffer, size_t len, uint32_t flags) { + struct nlmsghdr *nl = (struct nlmsghdr *)buffer; + + nl->nlmsg_len = 0; // NLMSG_LENGTH(sizeof(struct ifinfomsg)); + nl->nlmsg_type = request; + nl->nlmsg_flags = flags; + nl->nlmsg_seq = seq++; // + nl->nlmsg_pid = 0; // getpid(); + + return 0; +} + +/* Low level : nl protocols */ + +/* Low level : attributes */ + +int addAttr(struct nlmsghdr *nl, int maxlen, int type, void *data, + int attr_len) { + struct rtattr *rta; + int len = RTA_LENGTH(attr_len); + + if (NLMSG_ALIGN(nl->nlmsg_len) + len > maxlen) { + exit(EXIT_FAILURE); + } + + rta = (struct rtattr *)((char *)nl + NLMSG_ALIGN(nl->nlmsg_len)); + rta->rta_type = type; + rta->rta_len = len; + memcpy(RTA_DATA(rta), data, attr_len); + nl->nlmsg_len = NLMSG_ALIGN(nl->nlmsg_len) + len; + return 0; +} + +int _nl_payload_rule(uint8_t table_id, uint8_t address_family, uint8_t *buffer, + size_t len) { + struct nlmsghdr *nl = (struct nlmsghdr *)buffer; + struct fib_rule_hdr *frh = (struct fib_rule_hdr *)(NLMSG_DATA(buffer)); + + memset(frh, 0, sizeof(struct fib_rule_hdr)); + frh->family = address_family; + frh->table = table_id; + frh->action = FR_ACT_TO_TBL, + frh->flags = NLM_F_REPLACE; // 0 + frh->tos = 0; + + nl->nlmsg_len += NLMSG_LENGTH(sizeof(struct fib_rule_hdr)); + + return 0; +} + +int _nl_payload_link(uint32_t ifindex, uint8_t *buffer, size_t len) { + struct nlmsghdr *nl = (struct nlmsghdr *)buffer; + struct ifinfomsg *ifi = (struct ifinfomsg *)(NLMSG_DATA(buffer)); + + memset(ifi, 0, sizeof(struct ifinfomsg)); + ifi->ifi_family = AF_UNSPEC; + // ifi->ifi_type = 0; + ifi->ifi_index = + ifindex; // new interface, could be specified since linux 3.7 + ifi->ifi_flags = 0; + // ifi->ifi_change = 0xffffffff; + + nl->nlmsg_len += NLMSG_LENGTH(sizeof(struct ifinfomsg)); + + return 0; +} + +int _nl_payload_addr(uint32_t ifindex, uint8_t *buffer, size_t len) { + struct nlmsghdr *nl = (struct nlmsghdr *)buffer; + struct ifaddrmsg *addr = (struct ifaddrmsg *)(NLMSG_DATA(buffer)); + + memset(addr, 0, sizeof(struct ifaddrmsg)); + addr->ifa_family = AF_UNSPEC; // INET6; + /* + addr->ifa_prefixlen = 128; + addr->ifa_flags = 0; + addr->ifa_scope = RT_SCOPE_LINK; //IFA_ADDRESS; + addr->ifa_index = ifindex; + */ + + nl->nlmsg_len += NLMSG_LENGTH(sizeof(struct ifaddrmsg)) - 4; + + return 0; +} + +int _nl_payload_route(uint8_t table_id, uint8_t addr_family, uint8_t dst_len, + uint8_t *buffer, size_t len) { + struct nlmsghdr *nl = (struct nlmsghdr *)buffer; + struct rtmsg *raddr = (struct rtmsg *)(NLMSG_DATA(buffer)); + + raddr->rtm_family = addr_family; + raddr->rtm_dst_len = dst_len; + raddr->rtm_src_len = 0; + raddr->rtm_tos = 0; + + raddr->rtm_table = table_id; + raddr->rtm_protocol = RTPROT_BOOT; + raddr->rtm_scope = RT_SCOPE_UNIVERSE; + raddr->rtm_type = RTN_UNICAST; + + raddr->rtm_flags = 0; + + nl->nlmsg_len += NLMSG_LENGTH(sizeof(struct rtmsg)); + + return 0; +} + +uint32_t _nl_get_ifid(const char *interface_name) { + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + size_t n; + int fd; + size_t len = interface_name ? strlen(interface_name) + 1 : 0; + uint8_t padding[RTA_ALIGNTO] = {0, 0, 0, 0}; + + if (len == 0) { + goto ERR_IF; + } + + struct { + struct nlmsghdr hdr; + struct ifinfomsg payload; + } msg = {//.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .hdr.nlmsg_type = RTM_GETLINK, + .hdr.nlmsg_flags = FLAGS_GET, + .payload.ifi_family = AF_UNSPEC, + .payload.ifi_index = 0}; + struct rtattr a_ifname = {RTA_LENGTH(strlen(interface_name) + 1), + IFLA_IFNAME}; + + struct iovec iov[] = {{&msg, sizeof(msg)}, + {&a_ifname, sizeof(a_ifname)}, + {(char *)interface_name, len}, + {padding, RTA_SPACE(len) - RTA_LENGTH(len)}}; + msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR_SOCKET; + } + n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); + if (n == -1) { + goto ERR_SEND; + } + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR_RECV; + } + + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR_NL; + } + return 0; /* Unexpected */ + } + + for (; NLMSG_OK(hdr, n); hdr = NLMSG_NEXT(hdr, n)) { + struct ifinfomsg *payload = (struct ifinfomsg *)NLMSG_DATA(hdr); + return payload->ifi_index; + } + return 0; + +ERR_NL: +ERR_RECV: +ERR_SEND: +ERR_SOCKET: +ERR_IF: + return 0; +} + +int _nl_get_output_ifid(const char *ip_address, uint8_t family_address, + uint32_t *interface_id) { + int rc; + + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + size_t n; + int fd; + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR; + } + + if (family_address == AF_INET6) { + struct in6_addr addr; // V6SPECIFIC + + struct { + struct nlmsghdr hdr; + struct rtmsg payload; + } msg = { + .hdr.nlmsg_type = RTM_GETROUTE, + .hdr.nlmsg_flags = NLM_F_REQUEST, + .hdr.nlmsg_seq = seq++, + .payload.rtm_family = AF_INET6, + .payload.rtm_dst_len = IPV6_ADDR_LEN_BITS, + .payload.rtm_src_len = 0, + .payload.rtm_tos = 0, + .payload.rtm_table = RT_TABLE_UNSPEC, + .payload.rtm_protocol = RTPROT_UNSPEC, + .payload.rtm_scope = RT_SCOPE_UNIVERSE, + .payload.rtm_type = RTN_UNSPEC, + .payload.rtm_flags = 0 // RTM_F_NOTIFY in 'ip route get' + }; + + /* Convert the IP address to binary form */ + rc = inet_pton(AF_INET6, ip_address, &addr); + if (rc <= 0) { + goto ERR; + } + + /* Set attribute = length/type/value */ + struct rtattr a_dst = {RTA_LENGTH(16), RTA_DST}; + struct iovec iov[] = { + {&msg, sizeof(msg)}, + {&a_dst, sizeof(a_dst)}, // attribute + {&addr, sizeof(addr)} // value + }; + msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); + + n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); + if (n == -1) { + goto ERR; + } + } else if (family_address == AF_INET) { + struct in_addr addr; + + struct { + struct nlmsghdr hdr; + struct rtmsg payload; + } msg = { + .hdr.nlmsg_type = RTM_GETROUTE, + .hdr.nlmsg_flags = NLM_F_REQUEST, + .hdr.nlmsg_seq = seq++, + .payload.rtm_family = AF_INET, + .payload.rtm_dst_len = IPV4_ADDR_LEN_BITS, + .payload.rtm_src_len = 0, + .payload.rtm_tos = 0, + .payload.rtm_table = RT_TABLE_UNSPEC, + .payload.rtm_protocol = RTPROT_UNSPEC, + .payload.rtm_scope = RT_SCOPE_UNIVERSE, + .payload.rtm_type = RTN_UNSPEC, + .payload.rtm_flags = 0 // RTM_F_NOTIFY in 'ip route get' + }; + + /* Convert the IP address to binary form */ + rc = inet_pton(AF_INET, ip_address, &addr); + if (rc <= 0) { + goto ERR; + } + + /* Set attribute = length/type/value */ + struct rtattr a_dst = {RTA_LENGTH(4), RTA_DST}; + struct iovec iov[] = { + {&msg, sizeof(msg)}, + {&a_dst, sizeof(a_dst)}, // attribute + {&addr, sizeof(addr)} // value + }; + msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); + + n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); + if (n == -1) { + goto ERR; + } + } else { + goto ERR; + } + + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR; + } + + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR; + } + return HICN_SOCKET_ERROR_UNEXPECTED; /* Unexpected */ + } + + for (; NLMSG_OK(hdr, n); hdr = NLMSG_NEXT(hdr, n)) { + struct rtmsg *rtm = (struct rtmsg *)NLMSG_DATA(hdr); + int attrlen = RTM_PAYLOAD(hdr); + struct rtattr *rta; + for (rta = RTM_RTA(rtm); RTA_OK(rta, attrlen); + rta = RTA_NEXT(rta, attrlen)) { + if (rta->rta_type == RTA_OIF) { + *interface_id = *(uint32_t *)RTA_DATA(rta); + return HICN_SOCKET_ERROR_NONE; + } + } + } + + return HICN_SOCKET_ERROR_NONE; + +ERR: + return HICN_SOCKET_ERROR_UNSPEC; +} + +int _nl_get_ip_addr(uint32_t interface_id, uint8_t address_family, + ip_address_t *ip_address) { + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + size_t n; + int fd; + + struct { + struct nlmsghdr hdr; + struct ifaddrmsg payload; + } msg = {.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)), + .hdr.nlmsg_type = RTM_GETADDR, + .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT, // | NLM_F_MATCH, + .payload.ifa_family = address_family, + .payload.ifa_index = 0}; + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR_SOCKET; + } + + n = send(fd, &msg, sizeof(msg), 0); + if (n == -1) { + goto ERR_SEND; + } + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR_RECV; + } + + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR_NL; + } + return -99; /* Unexpected */ + } + + for (; NLMSG_OK(hdr, n); hdr = NLMSG_NEXT(hdr, n)) { + struct ifaddrmsg *payload = (struct ifaddrmsg *)NLMSG_DATA(hdr); + + if (address_family == AF_INET6) { + if ((payload->ifa_index == interface_id) && + (payload->ifa_prefixlen < IPV6_ADDR_LEN * 8)) { + printf("got ip address\n"); + memcpy(ip_address->buffer, RTA_DATA(payload + 1), IPV6_ADDR_LEN); + ip_address->family = AF_INET6; + ip_address->prefix_len = IPV6_ADDR_LEN_BITS; + printf("returning %d\n", HICN_SOCKET_ERROR_NONE); + return HICN_SOCKET_ERROR_NONE; + } + } else if (address_family == AF_INET) { + if ((payload->ifa_index == interface_id) && + (payload->ifa_prefixlen < IPV4_ADDR_LEN * 8)) { + printf("got ip address\n"); + memcpy(ip_address->buffer, RTA_DATA(payload + 1), IPV4_ADDR_LEN); + ip_address->family = AF_INET; + ip_address->prefix_len = IPV4_ADDR_LEN_BITS; + printf("returning %d\n", HICN_SOCKET_ERROR_NONE); + return HICN_SOCKET_ERROR_NONE; + } + } else { + return -99; + } + } + +ERR_NL: +ERR_RECV: +ERR_SEND: +ERR_SOCKET: + printf("error getting ip address\n"); + return HICN_SOCKET_ERROR_UNSPEC; +} + +int _nl_set_ip_addr(uint32_t interface_id, ip_address_t *ip_address) { + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + size_t n; + int fd; + + struct { + struct nlmsghdr hdr; + struct ifaddrmsg payload; + } msg = { + .hdr.nlmsg_type = RTM_NEWADDR, + .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_MATCH | NLM_F_ATOMIC, + .hdr.nlmsg_seq = seq++, + .payload.ifa_family = ip_address->family, + .payload.ifa_prefixlen = ip_address->prefix_len, + .payload.ifa_flags = 0, + .payload.ifa_scope = RT_SCOPE_UNIVERSE, + .payload.ifa_index = interface_id}; + + /* Set attributes = length/type/value */ + struct rtattr ifa_address = {RTA_LENGTH(ip_address_len(ip_address)), + IFA_ADDRESS}; + // XXX maybe the reason why we have a local route ? + // struct rtattr ifa_local = { RTA_LENGTH(ip_address_len(ip_address)), + // IFA_LOCAL }; + struct iovec iov[] = { + {&msg, sizeof(msg)}, + {&ifa_address, sizeof(ifa_address)}, + {(void *)&ip_address->buffer, sizeof(ip_address->buffer)}, + // { &ifa_local, sizeof(ifa_local) }, + // { (void*)&ip_address->buffer, sizeof(ip_address->buffer) }, + }; + msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR_SOCKET; + } + + // hicn_packet_dump_iov(iov, ARRAY_SIZE(iov)); + + n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); + if (n == -1) { + goto ERR_SEND; + } + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR_RECV; + } + + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR_NL; + } + } + + return 0; + +ERR_NL: +ERR_RECV: +ERR_SEND: +ERR_SOCKET: + return -1; +} + +int _nl_up_if(uint32_t interface_id) { + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + size_t n; + int fd; + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR_SOCKET; + } + + struct { + struct nlmsghdr hdr; + struct ifinfomsg payload; + } msg = { + .hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .hdr.nlmsg_type = RTM_NEWLINK, + .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, + .payload.ifi_family = AF_UNSPEC, + .payload.ifi_index = interface_id, + .payload.ifi_flags = IFF_UP, + .payload.ifi_change = IFF_UP // 0xffffffff + }; + + n = send(fd, &msg, sizeof(msg), 0); + if (n == -1) { + goto ERR_SEND; + } + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR_RECV; + } + + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR_NL; + } + return 0; + } + +ERR_NL: +ERR_RECV: +ERR_SEND: +ERR_SOCKET: + return -1; +} + +struct route_info { + char *dst_addr; + char *src_addr; + char *gateway; + char ifName[IF_NAMESIZE]; +}; + +/* + * ip -6 route add PREFIX dev INTERFACE_NAME + */ +#if 0 +int _nl_add_in_route(const char * prefix, const uint32_t interface_id) +{ + char buffer[BUFSIZE]; + struct nlmsghdr * hdr = (struct nlmsghdr *)buffer; + size_t n; + int fd; + + int pton_fd; + unsigned char dst[sizeof(struct in6_addr)]; + char * p; + char * eptr; + char addr[strlen(prefix)]; + uint32_t dst_len; + + strncpy(addr, prefix, strlen(prefix)); + + p = strchr(addr, '/'); + if (!p) { + dst_len = IPV6_ADDR_LEN; + } else { + dst_len = strtoul(p + 1, &eptr, 10); + if (dst_len > IPV6_ADDR_LEN * 8) { + printf("E: Netmask > IPV6_ADDR_LEN"); + return -1; + } + *p = 0; + } + + pton_fd = inet_pton(AF_INET6, addr, dst); + if (pton_fd <= 0) { + if (pton_fd == 0) + ;//ERROR("Not in presentation format"); + else + perror("inet_pton"); + return -2; + } + + _nl_header(RTM_NEWROUTE, (uint8_t *)buffer, BUFSIZE, FLAGS_CREATE_MATCH); + _nl_payload_route(RT_TABLE_MAIN, dst_len, (uint8_t *)buffer, BUFSIZE); + + addAttr(hdr, BUFSIZE, RTA_DST, dst, IPV6_ADDR_LEN); + addAttr(hdr, BUFSIZE, RTA_OIF, (void*)&interface_id, sizeof(uint32_t)); + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR_SOCKET; + } + + n = send(fd, buffer, hdr->nlmsg_len, 0); + if (n == -1) { + goto ERR_SEND; + } + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR_RECV; + } + + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr * err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR_NL; + } + return 0; + } + +ERR_NL: +ERR_RECV: +ERR_SEND: +ERR_SOCKET: + return -1; + +} +#endif + +/* + * ip -6 route add local default via GATEWAY_IP table TABLE_ID + */ +int _nl_add_out_route(const char *gateway, uint8_t address_family, + const uint8_t table_id, int default_route) { + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + size_t n; + int fd; + + int pton_fd; + + if (address_family == AF_INET) { + struct in_addr gw; + + pton_fd = inet_pton(AF_INET, gateway, (struct in_addr *)&gw); + if (pton_fd < 0) { + return -1; + } + + _nl_header(RTM_NEWROUTE, (uint8_t *)buffer, BUFSIZE, + NLM_F_REQUEST | NLM_F_ACK | NLM_F_MATCH | NLM_F_ATOMIC); + _nl_payload_route(table_id, address_family, 0, (uint8_t *)buffer, BUFSIZE); + + /* gw */ + addAttr(hdr, BUFSIZE, RTA_GATEWAY, &gw, sizeof(gw)); + + } else if (address_family == AF_INET6) { + struct in6_addr gw; + + pton_fd = inet_pton(AF_INET6, gateway, (struct in6_addr *)&gw); + if (pton_fd < 0) { + return -1; + } + + _nl_header(RTM_NEWROUTE, (uint8_t *)buffer, BUFSIZE, + NLM_F_REQUEST | NLM_F_ACK | NLM_F_MATCH | NLM_F_ATOMIC); + _nl_payload_route(table_id, address_family, 0, (uint8_t *)buffer, BUFSIZE); + + /* gw */ + addAttr(hdr, BUFSIZE, RTA_GATEWAY, &gw, sizeof(gw)); + if (default_route != -1) { + addAttr(hdr, BUFSIZE, RTA_OIF, &default_route, sizeof(default_route)); + } + + } else { + return -1; + } + + // For more than 255 tables + // addAttr(msg, BUFSIZE, RTA_TABLE, &table_id, sizeof(uint32_t)); + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR_SOCKET; + } + + n = send(fd, buffer, hdr->nlmsg_len, 0); + if (n == -1) { + goto ERR_SEND; + } + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR_RECV; + } + + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR_NL; + } + return 0; + } + +ERR_NL: +ERR_RECV: +ERR_SEND: +ERR_SOCKET: + return -1; +} + +/* + * ip -6 route del local default via GATEWAY_IP table TABLE_ID + */ +int _nl_del_out_route(const char *gateway, const uint8_t address_family, + const uint8_t table_id) { + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + size_t n; + int fd; + + int pton_fd; + + if (address_family == AF_INET) { + struct in_addr gw; + + pton_fd = inet_pton(AF_INET, gateway, (struct in_addr *)&gw); + if (pton_fd < 0) { + return -1; + } + + _nl_header(RTM_DELROUTE, (uint8_t *)buffer, BUFSIZE, + NLM_F_REQUEST | NLM_F_ACK | NLM_F_MATCH | NLM_F_ATOMIC); + _nl_payload_route(table_id, address_family, 0, (uint8_t *)buffer, BUFSIZE); + + /* gw */ + addAttr(hdr, BUFSIZE, RTA_GATEWAY, &gw, sizeof(gw)); + + } else if (address_family == AF_INET6) { + struct in6_addr gw; + + pton_fd = inet_pton(AF_INET6, gateway, (struct in6_addr *)&gw); + if (pton_fd < 0) { + return -1; + } + + _nl_header(RTM_DELROUTE, (uint8_t *)buffer, BUFSIZE, + NLM_F_REQUEST | NLM_F_ACK | NLM_F_MATCH | NLM_F_ATOMIC); + _nl_payload_route(table_id, address_family, 0, (uint8_t *)buffer, BUFSIZE); + + /* gw */ + addAttr(hdr, BUFSIZE, RTA_GATEWAY, &gw, sizeof(gw)); + + } else { + return -1; + } + + // For more than 255 tables + // addAttr(msg, BUFSIZE, RTA_TABLE, &table_id, sizeof(uint32_t)); + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR_SOCKET; + } + + n = send(fd, buffer, hdr->nlmsg_len, 0); + if (n == -1) { + goto ERR_SEND; + } + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR_RECV; + } + + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR_NL; + } + return 0; + } + +ERR_NL: +ERR_RECV: +ERR_SEND: +ERR_SOCKET: + return -1; +} + +/* + * ip route del 1:2::2 dev lo table local + * + */ +int _nl_del_lo_route(const ip_address_t *ip_address) { + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + size_t n; + int fd; + + struct { + struct nlmsghdr hdr; + struct rtmsg payload; + } msg = { + .hdr.nlmsg_type = RTM_DELROUTE, + .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, + .hdr.nlmsg_seq = seq++, + .payload.rtm_family = ip_address->family, + .payload.rtm_dst_len = ip_address->prefix_len, + .payload.rtm_src_len = 0, + .payload.rtm_tos = 0, + .payload.rtm_table = RT_TABLE_LOCAL, + .payload.rtm_protocol = RTPROT_UNSPEC, + .payload.rtm_scope = RT_SCOPE_UNIVERSE, + .payload.rtm_type = RTN_UNSPEC, + .payload.rtm_flags = 0 // RTM_F_NOTIFY in 'ip route get' + }; + + /* Set attribute = length/type/value */ + uint32_t one = 1; + struct rtattr a_dst = {RTA_LENGTH(ip_address_len(ip_address)), RTA_DST}; + struct rtattr a_ifid_lo = {RTA_LENGTH(sizeof(uint32_t)), RTA_OIF}; + struct iovec iov[] = { + {&msg, sizeof(msg)}, + /* Ip address */ + {&a_dst, sizeof(a_dst)}, + {(void *)&ip_address->buffer, ip_address_len(ip_address)}, + /* Interface id */ + {&a_ifid_lo, sizeof(a_ifid_lo)}, + {&one, sizeof(one)}}; + msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR; + } + + n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); + if (n == -1) { + goto ERR; + } + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR; + } + + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR; + } + return 0; + } + + return HICN_SOCKET_ERROR_NONE; +ERR: + return HICN_SOCKET_ERROR_UNSPEC; +} + +/* + * ip -6 rule add iif INTERFACE_NAME lookup TABLE_ID + */ +int _nl_add_rule(const char *interface_name, uint8_t address_family, + const uint8_t table_id) { + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + size_t n; + int fd; + + _nl_header(RTM_NEWRULE, (uint8_t *)buffer, BUFSIZE, FLAGS_CREATE); + _nl_payload_rule(table_id, address_family, (uint8_t *)buffer, BUFSIZE); + + /* XXX iif */ + addAttr(hdr, BUFSIZE, FRA_IIFNAME, (void *)interface_name, + strlen(interface_name)); + // attr1 = addNestedAttr(hdr, IFLA_LINKINFO); + // endNestedAttr(hdr, attr1); + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR_SOCKET; + } + + n = send(fd, buffer, hdr->nlmsg_len, 0); + if (n == -1) { + goto ERR_SEND; + } + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR_RECV; + } + + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR_NL; + } + return 0; + } + +ERR_NL: +ERR_RECV: +ERR_SEND: +ERR_SOCKET: + return -1; +} + +/* + * ip -6 rule del iif INTERFACE_NAME //lookup TABLE_ID + */ +int _nl_del_rule(const char *interface_name, uint8_t address_family, + const uint8_t table_id) { + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + size_t n; + int fd; + + _nl_header(RTM_DELRULE, (uint8_t *)buffer, BUFSIZE, FLAGS_CREATE); + _nl_payload_rule(table_id, address_family, (uint8_t *)buffer, BUFSIZE); + + /* XXX iif */ + addAttr(hdr, BUFSIZE, FRA_IIFNAME, (void *)interface_name, + strlen(interface_name)); + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR_SOCKET; + } + + n = send(fd, buffer, hdr->nlmsg_len, 0); + if (n == -1) { + goto ERR_SEND; + } + + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR_RECV; + } + + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR_NL; + } + return 0; + } + +ERR_NL: +ERR_RECV: +ERR_SEND: +ERR_SOCKET: + return -1; +} + +/* + * ip -6 neigh add proxy 1:2::2 dev hicnc-cons-eth0 2>&1 | grep nei + * + */ +int _nl_add_neigh_proxy(const ip_address_t *ip_address, + const uint32_t interface_id) { + /* Buffer for holding the response, with appropriate casting on the header */ + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + + /* Used for send and receive operations on netlink socket */ + int fd; + size_t n; + + /* Packet header */ + struct { + struct nlmsghdr hdr; + struct ndmsg payload; + } msg = { + .hdr.nlmsg_type = RTM_NEWNEIGH, + .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK | NLM_F_EXCL, + .hdr.nlmsg_seq = seq++, + .payload.ndm_family = ip_address->family, + .payload.ndm_ifindex = interface_id, + .payload.ndm_state = NUD_PERMANENT, + .payload.ndm_flags = NTF_PROXY, + }; + + /* Message attributes = length/type/value */ + struct rtattr a_dst = {RTA_LENGTH(ip_address_len(ip_address)), NDA_DST}; + + /* Iovec describing the packets */ + struct iovec iov[] = { + {&msg, sizeof(msg)}, + /* Ip address */ + {&a_dst, sizeof(a_dst)}, + {(void *)&ip_address->buffer, sizeof(ip_address->buffer)}, + }; + msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); + + /* Open netlink socket */ + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR; + } + + /* Send packet */ + n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); + if (n == -1) { + goto ERR; + } + + /* Receive answer */ + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR; + } + + /* Parse answer */ + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR; + } + } + + return HICN_SOCKET_ERROR_NONE; +ERR: + return HICN_SOCKET_ERROR_UNSPEC; +} + +/* ip -6 route add 0:1::/64 dev hicn-if0 table 100 */ +/* ip -6 route add 0:2::/64 dev hicn-if1 table 100 */ +int _nl_add_in_route_table(const ip_address_t *prefix, + const uint32_t interface_id, + const uint8_t table_id) { + /* Buffer for holding the response, with appropriate casting on the header */ + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + + /* Used for send and receive operations on netlink socket */ + int fd; + size_t n; + + /* Packet header */ + struct { + struct nlmsghdr hdr; + struct rtmsg payload; + } msg = { + .hdr.nlmsg_type = RTM_NEWROUTE, + .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK | NLM_F_EXCL, + .hdr.nlmsg_seq = seq++, + .payload.rtm_family = prefix->family, + .payload.rtm_dst_len = prefix->prefix_len, // XXX ? XXX dst_len, + .payload.rtm_src_len = 0, + .payload.rtm_tos = 0, + .payload.rtm_table = table_id, /* RT_TABLE_MAIN, etc. */ + .payload.rtm_protocol = RTPROT_BOOT, + .payload.rtm_scope = + prefix->family == AF_INET6 ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK, + .payload.rtm_type = RTN_UNICAST, + .payload.rtm_flags = 0, + }; + + /* Message attributes = length/type/value */ + // XXX This could be put directly inside the iovec maybe ? XXX + struct rtattr a_dst = {RTA_LENGTH(ip_address_len(prefix)), RTA_DST}; + struct rtattr a_oif = {RTA_LENGTH(sizeof(uint32_t)), RTA_OIF}; + + /* Iovec describing the packets */ + struct iovec iov[] = { + {&msg, sizeof(msg)}, + /* Destination prefix / ip address */ + {&a_dst, sizeof(a_dst)}, + {(void *)&prefix->buffer, ip_address_len(prefix)}, + /* Output interface */ + {&a_oif, sizeof(a_oif)}, + {(void *)&interface_id, sizeof(uint32_t)}, + }; + msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); + + /* Open netlink socket */ + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR; + } + + /* Send packet */ + n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); + if (n == -1) { + goto ERR; + } + + /* Receive answer */ + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR; + } + + /* Parse answer */ + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR; + } + } + + return HICN_SOCKET_ERROR_NONE; +ERR: + return HICN_SOCKET_ERROR_UNSPEC; +} + +/* Additional helper functions */ + +int _nl_add_in_route_table_s(const char *prefix, const uint32_t interface_id, + const uint8_t table_id) { + int rc; + ip_address_t ip_address; + + rc = hicn_ip_pton(prefix, &ip_address); + if (rc < 0) { + return rc; + } + + return _nl_add_in_route_table(&ip_address, interface_id, table_id); +} + +int _nl_add_in_route_s(const char *prefix, const uint32_t interface_id) { + return _nl_add_in_route_table_s(prefix, interface_id, RT_TABLE_MAIN); +} + +////////* ip -6 rule add from all prio 10 table local */ +/* ip -6 rule add from b001::/16 prio 0 table 100 */ +int _nl_add_prio_rule(const ip_address_t *ip_address, uint8_t address_family, + const uint32_t priority, const uint8_t table_id) { + /* Buffer for holding the response, with appropriate casting on the header */ + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + + /* Used for send and receive operations on netlink socket */ + int fd; + size_t n; + + /* Packet header */ + struct { + struct nlmsghdr hdr; + struct fib_rule_hdr payload; + } msg = { + .hdr.nlmsg_type = RTM_NEWRULE, + .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK | NLM_F_EXCL, + .hdr.nlmsg_seq = seq++, + .payload.family = address_family, + //.payload.dst_len = , + .payload.src_len = ip_address ? ip_address->prefix_len : 0, + .payload.tos = 0, + .payload.table = table_id, + .payload.action = FR_ACT_TO_TBL, + .payload.flags = NLM_F_REPLACE, // 0 + }; + + /* Open netlink socket */ + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR; + } + + if (ip_address) { + /* Message attributes = length/type/value */ + struct rtattr a_src = {RTA_LENGTH(ip_address_len(ip_address)), FRA_SRC}; + struct rtattr a_prio = {RTA_LENGTH(sizeof(uint32_t)), FRA_PRIORITY}; + + /* Iovec describing the packets */ + struct iovec iov[] = { + {&msg, sizeof(msg)}, + /* Source prefix / ip_address */ + {&a_src, sizeof(a_src)}, + {(void *)&ip_address->buffer, ip_address_len(ip_address)}, + /* Priority */ + {&a_prio, sizeof(a_prio)}, + {(void *)&priority, sizeof(uint32_t)}, + }; + msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); + + /* Send packet */ + n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); + if (n == -1) { + goto ERR; + } + } else { + struct rtattr a_prio = {RTA_LENGTH(sizeof(uint32_t)), FRA_PRIORITY}; + + /* Iovec describing the packets */ + struct iovec iov[] = { + {&msg, sizeof(msg)}, + /* Priority */ + {&a_prio, sizeof(a_prio)}, + {(void *)&priority, sizeof(uint32_t)}, + }; + msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); + + /* Send packet */ + n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); + if (n == -1) { + goto ERR; + } + } + + /* Receive answer */ + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR; + } + + /* Parse answer */ + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0) { + errno = -err->error; + goto ERR; + } + } + + return HICN_SOCKET_ERROR_NONE; +ERR: + return HICN_SOCKET_ERROR_UNSPEC; +} + +int _nl_add_lo_prio_rule(const ip_address_t *ip_address, uint8_t address_family, + const uint32_t priority) { + return _nl_add_prio_rule(ip_address, address_family, priority, + RT_TABLE_LOCAL); +} + +/* ip -6 rule del from all prio 0 table local */ +int _nl_del_prio_rule(const ip_address_t *ip_address, uint8_t address_family, + const uint32_t priority, const uint8_t table_id) { + /* Buffer for holding the response, with appropriate casting on the header */ + char buffer[BUFSIZE]; + struct nlmsghdr *hdr = (struct nlmsghdr *)buffer; + + /* Used for send and receive operations on netlink socket */ + int fd; + size_t n; + + /* Packet header */ + struct { + struct nlmsghdr hdr; + struct fib_rule_hdr payload; + } msg = { + .hdr.nlmsg_type = RTM_DELRULE, + .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK | NLM_F_EXCL, + .hdr.nlmsg_seq = seq++, + .payload.family = address_family, + //.payload.dst_len = , + .payload.src_len = ip_address ? ip_address->prefix_len : 0, + .payload.tos = 0, + .payload.table = table_id, + .payload.action = FR_ACT_TO_TBL, + .payload.flags = NLM_F_REPLACE, // 0 + }; + + /* Open netlink socket */ + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + goto ERR; + } + + /* Message attributes = length/type/value */ + if (ip_address) { + struct rtattr a_src = {RTA_LENGTH(ip_address_len(ip_address)), FRA_SRC}; + struct rtattr a_prio = {RTA_LENGTH(sizeof(uint32_t)), FRA_PRIORITY}; + + /* Iovec describing the packets */ + struct iovec iov[] = { + {&msg, sizeof(msg)}, + /* Source prefix / ip_address */ + {&a_src, sizeof(a_src)}, + {(void *)&ip_address->buffer, ip_address_len(ip_address)}, + /* Priority */ + {&a_prio, sizeof(a_prio)}, + {(void *)&priority, sizeof(uint32_t)}, + }; + msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); + + /* Send packet */ + n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); + if (n == -1) { + goto ERR; + } + + } else { + struct rtattr a_prio = {RTA_LENGTH(sizeof(uint32_t)), FRA_PRIORITY}; + + /* Iovec describing the packets */ + struct iovec iov[] = { + {&msg, sizeof(msg)}, + /* Priority */ + {&a_prio, sizeof(a_prio)}, + {(void *)&priority, sizeof(uint32_t)}, + }; + msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov)); + + /* Send packet */ + n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov)); + if (n == -1) { + goto ERR; + } + } + + /* Receive answer */ + n = recv(fd, buffer, BUFSIZE, 0); + if (n == -1) { + goto ERR; + } + + /* Parse answer */ + if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr); + if (err->error < 0 && + err->error != -2) { //-2 is not such file or directory + errno = -err->error; + goto ERR; + } + } + + return HICN_SOCKET_ERROR_NONE; +ERR: + return HICN_SOCKET_ERROR_UNSPEC; +} + +int _nl_del_lo_prio_rule(const ip_address_t *ip_address, uint8_t address_family, + const uint32_t priority) { + return _nl_del_prio_rule(ip_address, address_family, priority, + RT_TABLE_LOCAL); +} + +/******************************************************************************/ + +// #include <net/if.h> +// duplicate declarations, in the meantime +#define IF_NAMESIZE 16 + +//#define WITH_TUN_PI 1 + +#ifdef WITH_TUN_PI +#define TUN_FLAGS IFF_TUN +#else +#define TUN_FLAGS IFF_TUN | IFF_NO_PI +#endif + +/* + * Taken from Kernel Documentation/networking/tuntap.txt + */ + +int tun_alloc(char *dev, int flags) { + struct ifreq ifr; + int fd, err; + char *clonedev = "/dev/net/tun"; + + /* Arguments taken by the function: + * + * char *dev: the name of an interface (or '\0'). MUST have enough + * space to hold the interface name if '\0' is passed + * int flags: interface flags (eg, IFF_TUN etc.) + */ + + /* open the clone device */ + if ((fd = open(clonedev, O_RDWR)) < 0) { + return fd; + } + + /* preparation of the struct ifr, of type "struct ifreq" */ + memset(&ifr, 0, sizeof(ifr)); + + ifr.ifr_flags = flags; + + if (*dev) { + /* if a device name was specified, put it in the structure; otherwise, + * the kernel will try to allocate the "next" device of the + * specified type */ + strncpy(ifr.ifr_name, dev, IF_NAMESIZE - 1); + } + + /* try to create the device */ + if ((err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0) { + close(fd); + return err; + } + + /* if the operation was successful, write back the name of the + * interface to the variable "dev", so the caller can know + * it. Note that the caller MUST reserve space in *dev (see calling + * code below) */ + strcpy(dev, ifr.ifr_name); + + /* this is the special file descriptor that the caller will use to talk + * with the virtual interface */ + return fd; +} + +int linux_get_tun_name(const char *prefix, const char *identifier, + char *tun_name) { + snprintf(tun_name, IF_NAMESIZE, "%s-%s", prefix, + identifier ? identifier : "main"); + return 0; +} + +int linux_tun_enable_offload(int fd) { + unsigned int offload = 0, tso4 = 1, tso6 = 1, ecn = 1, ufo = 1, csum = 1; + + /* Check if our kernel supports TUNSETOFFLOAD */ + if (ioctl(fd, TUNSETOFFLOAD, 0) != 0 && errno == EINVAL) { + goto ERR_TUN; + } + + if (csum) { + offload |= TUN_F_CSUM; + if (tso4) offload |= TUN_F_TSO4; + if (tso6) offload |= TUN_F_TSO6; + if ((tso4 || tso6) && ecn) offload |= TUN_F_TSO_ECN; + if (ufo) offload |= TUN_F_UFO; + } + + if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) { + offload &= ~TUN_F_UFO; + if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) { + fprintf(stderr, "TUNSETOFFLOAD ioctl() failed: %s\n", strerror(errno)); + } + } + + return 0; + +ERR_TUN: + return -1; +} + +int linux_tun_create(char *name) { + int fd, rc; + + fd = tun_alloc(name, TUN_FLAGS); + if (fd < 0) { + // ERROR("Error connecting to tun/tap interface %s!", name); + errno = -2; + goto ERR_TUN; + } + + rc = linux_tun_enable_offload(fd); + if (rc < 0) { + // WARN("Could not enable hardware offload on TUN device"); + } else { + // INFO("Enabled hardware offload on TUN device"); + } + + return fd; + +ERR_TUN: + return -1; +} + +/* + * + * interface name can be NULL for all interfaces + */ +int linux_enable_proc(char *path) { + int ret = 0; + int fd; + + fd = open(path, O_WRONLY); + if (fd < 0) { + return -1; + } + + if (write(fd, "1", 1) != 1) { + ret = -2; + } + + close(fd); + return ret; +} + +int linux_enable_v4_forwarding() { + return linux_enable_proc("/proc/sys/net/ipv4/ip_forward"); +} + +int linux_enable_v6_forwarding(char *interface_name) { + char path[PATH_MAX]; + snprintf(path, PATH_MAX, "/proc/sys/net/ipv6/conf/%s/forwarding", + (interface_name) ? interface_name : "all"); + + return linux_enable_proc(path); +} + +int linux_enable_ndp_proxy() { + return linux_enable_proc("/proc/sys/net/ipv6/conf/all/proxy_ndp"); +} + +const hicn_socket_ops_t ops = { + .arch = "linux", + .get_tun_name = linux_get_tun_name, + .tun_create = linux_tun_create, + .enable_v4_forwarding = linux_enable_v4_forwarding, + .enable_v6_forwarding = linux_enable_v6_forwarding, + .enable_ndp_proxy = linux_enable_ndp_proxy, + .get_ifid = _nl_get_ifid, + .get_output_ifid = _nl_get_output_ifid, + .get_ip_addr = _nl_get_ip_addr, + .set_ip_addr = _nl_set_ip_addr, + .up_if = _nl_up_if, + .add_in_route_table = _nl_add_in_route_table, + .add_in_route_table_s = _nl_add_in_route_table_s, + .add_in_route_s = _nl_add_in_route_s, + .add_out_route = _nl_add_out_route, + .del_out_route = _nl_del_out_route, + .del_lo_route = _nl_del_lo_route, + .add_rule = _nl_add_rule, + .del_rule = _nl_del_rule, + .add_neigh_proxy = _nl_add_neigh_proxy, + .add_prio_rule = _nl_add_prio_rule, + .add_lo_prio_rule = _nl_add_lo_prio_rule, + .del_prio_rule = _nl_del_prio_rule, + .del_lo_prio_rule = _nl_del_lo_prio_rule, +}; diff --git a/hicn-light/src/strategies/CMakeLists.txt b/hicn-light/src/strategies/CMakeLists.txt new file mode 100755 index 000000000..7f0730b2f --- /dev/null +++ b/hicn-light/src/strategies/CMakeLists.txt @@ -0,0 +1,36 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/strategyImpl.h + ${CMAKE_CURRENT_SOURCE_DIR}/loadBalancer.h + ${CMAKE_CURRENT_SOURCE_DIR}/loadBalancerWithPD.h + ${CMAKE_CURRENT_SOURCE_DIR}/nexthopState.h + ${CMAKE_CURRENT_SOURCE_DIR}/nexthopStateWithPD.h + ${CMAKE_CURRENT_SOURCE_DIR}/rnd.h + ${CMAKE_CURRENT_SOURCE_DIR}/rndSegment.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/loadBalancer.c + ${CMAKE_CURRENT_SOURCE_DIR}/loadBalancerWithPD.c + ${CMAKE_CURRENT_SOURCE_DIR}/nexthopState.c + ${CMAKE_CURRENT_SOURCE_DIR}/nexthopStateWithPD.c + ${CMAKE_CURRENT_SOURCE_DIR}/rnd.c + ${CMAKE_CURRENT_SOURCE_DIR}/rndSegment.c +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) diff --git a/hicn-light/src/strategies/loadBalancer.c b/hicn-light/src/strategies/loadBalancer.c new file mode 100755 index 000000000..14e907770 --- /dev/null +++ b/hicn-light/src/strategies/loadBalancer.c @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_HashMap.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_Unsigned.h> + +#include <src/strategies/loadBalancer.h> +#include <src/strategies/nexthopState.h> + +static void _strategyLoadBalancer_ReceiveObject(StrategyImpl *strategy, + const NumberSet *egressId, + const Message *objectMessage, + Ticks rtt); +static void _strategyLoadBalancer_OnTimeout(StrategyImpl *strategy, + const NumberSet *egressId); +static NumberSet *_strategyLoadBalancer_LookupNexthop( + StrategyImpl *strategy, const Message *interestMessage); +static NumberSet *_strategyLoadBalancer_ReturnNexthops(StrategyImpl *strategy); +static unsigned _strategyLoadBalancer_CountNexthops(StrategyImpl *strategy); +static void _strategyLoadBalancer_AddNexthop(StrategyImpl *strategy, + unsigned connectionId); +static void _strategyLoadBalancer_RemoveNexthop(StrategyImpl *strategy, + unsigned connectionId); +static void _strategyLoadBalancer_ImplDestroy(StrategyImpl **strategyPtr); +static strategy_type _strategyLoadBalancer_GetStrategy(StrategyImpl *strategy); + +static StrategyImpl _template = { + .context = NULL, + .receiveObject = &_strategyLoadBalancer_ReceiveObject, + .onTimeout = &_strategyLoadBalancer_OnTimeout, + .lookupNexthop = &_strategyLoadBalancer_LookupNexthop, + .returnNexthops = &_strategyLoadBalancer_ReturnNexthops, + .countNexthops = &_strategyLoadBalancer_CountNexthops, + .addNexthop = &_strategyLoadBalancer_AddNexthop, + .removeNexthop = &_strategyLoadBalancer_RemoveNexthop, + .destroy = &_strategyLoadBalancer_ImplDestroy, + .getStrategy = &_strategyLoadBalancer_GetStrategy, +}; + +struct strategy_load_balancer; +typedef struct strategy_load_balancer StrategyLoadBalancer; + +struct strategy_load_balancer { + double weights_sum; + // hash map from connectionId to StrategyNexthopState + PARCHashMap *strategy_state; + NumberSet *nexthops; +}; + +StrategyImpl *strategyLoadBalancer_Create() { + StrategyLoadBalancer *strategy = + parcMemory_AllocateAndClear(sizeof(StrategyLoadBalancer)); + parcAssertNotNull(strategy, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(StrategyLoadBalancer)); + + strategy->weights_sum = 0.0; + strategy->strategy_state = parcHashMap_Create(); + strategy->nexthops = numberSet_Create(); + srand(time(NULL)); + + StrategyImpl *impl = parcMemory_AllocateAndClear(sizeof(StrategyImpl)); + parcAssertNotNull(impl, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(StrategyImpl)); + memcpy(impl, &_template, sizeof(StrategyImpl)); + impl->context = strategy; + + return impl; +} + +// ======================================================= +// Dispatch API + +strategy_type _strategyLoadBalancer_GetStrategy(StrategyImpl *strategy) { + return SET_STRATEGY_LOADBALANCER; +} + +static void _update_Stats(StrategyLoadBalancer *strategy, + StrategyNexthopState *state, bool inc) { + const double ALPHA = 0.9; + double w = strategyNexthopState_GetWeight(state); + strategy->weights_sum -= w; + w = strategyNexthopState_UpdateState(state, inc, ALPHA); + strategy->weights_sum += w; +} + +static unsigned _select_Nexthop(StrategyLoadBalancer *strategy) { + double rnd = (double)rand() / (double)RAND_MAX; + double start_range = 0.0; + + PARCIterator *it = parcHashMap_CreateKeyIterator(strategy->strategy_state); + + unsigned nexthop = 100000; + while (parcIterator_HasNext(it)) { + PARCUnsigned *cid = parcIterator_Next(it); + const StrategyNexthopState *elem = + parcHashMap_Get(strategy->strategy_state, cid); + + double w = strategyNexthopState_GetWeight(elem); + + double prob = w / strategy->weights_sum; + if ((rnd >= start_range) && (rnd < (start_range + prob))) { + nexthop = parcUnsigned_GetUnsigned(cid); + break; + } else { + start_range += prob; + } + } + + parcIterator_Release(&it); + + // if no face is selected by the algorithm (for example because of a wrong + // round in the weights) we may always select the last face here. Double check + // this! + return nexthop; +} + +static void _strategyLoadBalancer_ReceiveObject(StrategyImpl *strategy, + const NumberSet *egressId, + const Message *objectMessage, + Ticks rtt) { + _strategyLoadBalancer_OnTimeout(strategy, egressId); +} + +static void _strategyLoadBalancer_OnTimeout(StrategyImpl *strategy, + const NumberSet *egressId) { + StrategyLoadBalancer *lb = (StrategyLoadBalancer *)strategy->context; + + for (unsigned i = 0; i < numberSet_Length(egressId); i++) { + unsigned outId = numberSet_GetItem(egressId, i); + PARCUnsigned *cid = parcUnsigned_Create(outId); + + const StrategyNexthopState *state = + parcHashMap_Get(lb->strategy_state, cid); + if (state != NULL) { + _update_Stats(lb, (StrategyNexthopState *)state, false); + } else { + // this may happen if we remove a face/route while downloading a file + // we should ignore this timeout + } + parcUnsigned_Release(&cid); + } +} + +static NumberSet *_strategyLoadBalancer_LookupNexthop( + StrategyImpl *strategy, const Message *interestMessage) { + StrategyLoadBalancer *lb = (StrategyLoadBalancer *)strategy->context; + + unsigned in_connection = message_GetIngressConnectionId(interestMessage); + PARCUnsigned *in = parcUnsigned_Create(in_connection); + + unsigned mapSize = parcHashMap_Size(lb->strategy_state); + NumberSet *outList = numberSet_Create(); + + if ((mapSize == 0) || + ((mapSize == 1) && parcHashMap_Contains(lb->strategy_state, in))) { + // there are no output faces or the input face is also the only output face. + // return null to avoid loops + parcUnsigned_Release(&in); + return outList; + } + + unsigned out_connection; + do { + out_connection = _select_Nexthop(lb); + } while (out_connection == in_connection); + + PARCUnsigned *out = parcUnsigned_Create(out_connection); + + const StrategyNexthopState *state = parcHashMap_Get(lb->strategy_state, out); + if (state == NULL) { + // this is an error and should not happen! + parcTrapNotImplemented( + "Try to send an interest on a face that does not exists"); + } + + _update_Stats(lb, (StrategyNexthopState *)state, true); + + parcUnsigned_Release(&in); + parcUnsigned_Release(&out); + + numberSet_Add(outList, out_connection); + return outList; +} + +static NumberSet *_strategyLoadBalancer_ReturnNexthops(StrategyImpl *strategy) { + StrategyLoadBalancer *lb = (StrategyLoadBalancer *)strategy->context; + return lb->nexthops; +} + +unsigned _strategyLoadBalancer_CountNexthops(StrategyImpl *strategy) { + StrategyLoadBalancer *lb = (StrategyLoadBalancer *)strategy->context; + return numberSet_Length(lb->nexthops); +} + +static void _strategyLoadBalancer_resetState(StrategyImpl *strategy) { + StrategyLoadBalancer *lb = (StrategyLoadBalancer *)strategy->context; + lb->weights_sum = 0.0; + PARCIterator *it = parcHashMap_CreateKeyIterator(lb->strategy_state); + + while (parcIterator_HasNext(it)) { + PARCUnsigned *cid = parcIterator_Next(it); + StrategyNexthopState *elem = + (StrategyNexthopState *)parcHashMap_Get(lb->strategy_state, cid); + + strategyNexthopState_Reset(elem); + lb->weights_sum += strategyNexthopState_GetWeight(elem); + } + + parcIterator_Release(&it); +} + +static void _strategyLoadBalancer_AddNexthop(StrategyImpl *strategy, + unsigned connectionId) { + StrategyNexthopState *state = strategyNexthopState_Create(); + + PARCUnsigned *cid = parcUnsigned_Create(connectionId); + + StrategyLoadBalancer *lb = (StrategyLoadBalancer *)strategy->context; + + if (!parcHashMap_Contains(lb->strategy_state, cid)) { + parcHashMap_Put(lb->strategy_state, cid, state); + numberSet_Add(lb->nexthops, connectionId); + _strategyLoadBalancer_resetState(strategy); + } +} + +static void _strategyLoadBalancer_RemoveNexthop(StrategyImpl *strategy, + unsigned connectionId) { + StrategyLoadBalancer *lb = (StrategyLoadBalancer *)strategy->context; + + PARCUnsigned *cid = parcUnsigned_Create(connectionId); + + if (parcHashMap_Contains(lb->strategy_state, cid)) { + parcHashMap_Remove(lb->strategy_state, cid); + numberSet_Remove(lb->nexthops, connectionId); + _strategyLoadBalancer_resetState(strategy); + } + + parcUnsigned_Release(&cid); +} + +static void _strategyLoadBalancer_ImplDestroy(StrategyImpl **strategyPtr) { + parcAssertNotNull(strategyPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*strategyPtr, + "Parameter must dereference to non-null pointer"); + + StrategyImpl *impl = *strategyPtr; + StrategyLoadBalancer *strategy = (StrategyLoadBalancer *)impl->context; + + parcHashMap_Release(&(strategy->strategy_state)); + numberSet_Release(&(strategy->nexthops)); + + parcMemory_Deallocate((void **)&strategy); + parcMemory_Deallocate((void **)&impl); + *strategyPtr = NULL; +} diff --git a/hicn-light/src/strategies/loadBalancer.h b/hicn-light/src/strategies/loadBalancer.h new file mode 100755 index 000000000..1178c30fe --- /dev/null +++ b/hicn-light/src/strategies/loadBalancer.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Forward on the less loaded path + */ + +#ifndef loadBalancer_h +#define loadBalancer_h + +#include <src/strategies/strategyImpl.h> + +StrategyImpl *strategyLoadBalancer_Create(); +#endif // loadBalancer_h diff --git a/hicn-light/src/strategies/loadBalancerWithPD.c b/hicn-light/src/strategies/loadBalancerWithPD.c new file mode 100755 index 000000000..1aad8fd89 --- /dev/null +++ b/hicn-light/src/strategies/loadBalancerWithPD.c @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <limits.h> +#include <src/config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_HashMap.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_Unsigned.h> + +#include <src/strategies/loadBalancerWithPD.h> +#include <src/strategies/nexthopStateWithPD.h> + +const unsigned PROBE_FREQUENCY = 1024; + +static void _strategyLoadBalancerWithPD_ReceiveObject( + StrategyImpl *strategy, const NumberSet *egressId, + const Message *objectMessage, Ticks rtt); +static void _strategyLoadBalancerWithPD_OnTimeout(StrategyImpl *strategy, + const NumberSet *egressId); +static NumberSet *_strategyLoadBalancerWithPD_LookupNexthop( + StrategyImpl *strategy, const Message *interestMessage); +static NumberSet *_strategyLoadBalancerWithPD_ReturnNexthops( + StrategyImpl *strategy); +static unsigned _strategyLoadBalancerWithPD_CountNexthops( + StrategyImpl *strategy); +static void _strategyLoadBalancerWithPD_AddNexthop(StrategyImpl *strategy, + unsigned connectionId); +static void _strategyLoadBalancerWithPD_RemoveNexthop(StrategyImpl *strategy, + unsigned connectionId); +static void _strategyLoadBalancerWithPD_ImplDestroy(StrategyImpl **strategyPtr); +static strategy_type _strategyLoadBalancerWithPD_GetStrategy( + StrategyImpl *strategy); + +static StrategyImpl _template = { + .context = NULL, + .receiveObject = &_strategyLoadBalancerWithPD_ReceiveObject, + .onTimeout = &_strategyLoadBalancerWithPD_OnTimeout, + .lookupNexthop = &_strategyLoadBalancerWithPD_LookupNexthop, + .returnNexthops = &_strategyLoadBalancerWithPD_ReturnNexthops, + .countNexthops = &_strategyLoadBalancerWithPD_CountNexthops, + .addNexthop = &_strategyLoadBalancerWithPD_AddNexthop, + .removeNexthop = &_strategyLoadBalancerWithPD_RemoveNexthop, + .destroy = &_strategyLoadBalancerWithPD_ImplDestroy, + .getStrategy = &_strategyLoadBalancerWithPD_GetStrategy, +}; + +struct strategy_load_balancer_with_pd; +typedef struct strategy_load_balancer_with_pd StrategyLoadBalancerWithPD; + +struct strategy_load_balancer_with_pd { + double weights_sum; + unsigned min_delay; + // hash map from connectionId to StrategyNexthopState + PARCHashMap *strategy_state; + NumberSet *nexthops; + ConnectionTable *connTable; + bool toInit; + unsigned int fwdPackets; +}; + +StrategyImpl *strategyLoadBalancerWithPD_Create() { + StrategyLoadBalancerWithPD *strategy = + parcMemory_AllocateAndClear(sizeof(StrategyLoadBalancerWithPD)); + parcAssertNotNull(strategy, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(StrategyLoadBalancerWithPD)); + + strategy->weights_sum = 0.0; + strategy->min_delay = INT_MAX; + strategy->strategy_state = parcHashMap_Create(); + strategy->nexthops = numberSet_Create(); + srand(time(NULL)); + + StrategyImpl *impl = parcMemory_AllocateAndClear(sizeof(StrategyImpl)); + parcAssertNotNull(impl, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(StrategyImpl)); + memcpy(impl, &_template, sizeof(StrategyImpl)); + impl->context = strategy; + strategy->connTable = NULL; + strategy->fwdPackets = 0; + strategy->toInit = true; + + return impl; +} + +void strategyLoadBalancerWithPD_SetConnectionTable(StrategyImpl *strategy, + ConnectionTable *connTable) { + StrategyLoadBalancerWithPD *lb = + (StrategyLoadBalancerWithPD *)strategy->context; + lb->connTable = connTable; +} + +// ======================================================= +// Dispatch API + +strategy_type _strategyLoadBalancerWithPD_GetStrategy(StrategyImpl *strategy) { + return SET_STRATEGY_LOADBALANCER_WITH_DELAY; +} + +static void _update_Stats(StrategyLoadBalancerWithPD *strategy, + StrategyNexthopStateWithPD *state, bool inc, + Ticks rtt) { + const double ALPHA = 0.9; + double w = strategyNexthopStateWithPD_GetWeight(state); + strategy->weights_sum -= w; + w = strategyNexthopStateWithPD_UpdateState(state, inc, strategy->min_delay, + ALPHA); + strategy->weights_sum += w; +} + +static void _sendProbes(StrategyLoadBalancerWithPD *strategy) { + unsigned size = numberSet_Length(strategy->nexthops); + for (unsigned i = 0; i < size; i++) { + unsigned nhop = numberSet_GetItem(strategy->nexthops, i); + Connection *conn = + (Connection *)connectionTable_FindById(strategy->connTable, nhop); + if (conn != NULL) { + connection_Probe(conn); + unsigned delay = connection_GetDelay(conn); + PARCUnsigned *cid = parcUnsigned_Create(nhop); + StrategyNexthopStateWithPD *elem = + (StrategyNexthopStateWithPD *)parcHashMap_Get( + strategy->strategy_state, cid); + strategyNexthopStateWithPD_SetDelay(elem, delay); + if (delay < strategy->min_delay && delay != 0) { + strategy->min_delay = delay; + } + + parcUnsigned_Release(&cid); + } + } +} + +static unsigned _select_Nexthop(StrategyLoadBalancerWithPD *strategy) { + strategy->fwdPackets++; + if (strategy->toInit || strategy->fwdPackets == PROBE_FREQUENCY) { + strategy->toInit = false; + strategy->fwdPackets = 0; + _sendProbes(strategy); + } + double rnd = (double)rand() / (double)RAND_MAX; + double start_range = 0.0; + + PARCIterator *it = parcHashMap_CreateKeyIterator(strategy->strategy_state); + + unsigned nexthop = 100000; + while (parcIterator_HasNext(it)) { + PARCUnsigned *cid = parcIterator_Next(it); + const StrategyNexthopStateWithPD *elem = + parcHashMap_Get(strategy->strategy_state, cid); + + double w = strategyNexthopStateWithPD_GetWeight(elem); + + // printf("next = %u .. pi %u avgpi %f w %f avgrtt + // %f\n",parcUnsigned_GetUnsigned(cid), + // strategyNexthopStateWithPD_GetPI(elem), + // strategyNexthopStateWithPD_GetWeight(elem), + // strategyNexthopStateWithPD_GetWeight(elem), + // strategyNexthopStateWithPD_GetAvgRTT(elem)); + + double prob = w / strategy->weights_sum; + if ((rnd >= start_range) && (rnd < (start_range + prob))) { + nexthop = parcUnsigned_GetUnsigned(cid); + break; + } else { + start_range += prob; + } + } + + parcIterator_Release(&it); + + // if no face is selected by the algorithm (for example because of a wrong + // round in the weights) we may always select the last face here. Double check + // this! + return nexthop; +} + +static void _strategyLoadBalancerWithPD_ReceiveObject( + StrategyImpl *strategy, const NumberSet *egressId, + const Message *objectMessage, Ticks rtt) { + StrategyLoadBalancerWithPD *lb = + (StrategyLoadBalancerWithPD *)strategy->context; + + for (unsigned i = 0; i < numberSet_Length(egressId); i++) { + unsigned outId = numberSet_GetItem(egressId, i); + PARCUnsigned *cid = parcUnsigned_Create(outId); + + const StrategyNexthopStateWithPD *state = + parcHashMap_Get(lb->strategy_state, cid); + if (state != NULL) { + _update_Stats(lb, (StrategyNexthopStateWithPD *)state, false, 0); + } else { + // this may happen if we remove a face/route while downloading a file + // we should ignore this timeout + } + parcUnsigned_Release(&cid); + } +} + +static void _strategyLoadBalancerWithPD_OnTimeout(StrategyImpl *strategy, + const NumberSet *egressId) { + StrategyLoadBalancerWithPD *lb = + (StrategyLoadBalancerWithPD *)strategy->context; + + for (unsigned i = 0; i < numberSet_Length(egressId); i++) { + unsigned outId = numberSet_GetItem(egressId, i); + PARCUnsigned *cid = parcUnsigned_Create(outId); + + const StrategyNexthopStateWithPD *state = + parcHashMap_Get(lb->strategy_state, cid); + if (state != NULL) { + _update_Stats(lb, (StrategyNexthopStateWithPD *)state, false, 0); + } else { + // this may happen if we remove a face/route while downloading a file + // we should ignore this timeout + } + parcUnsigned_Release(&cid); + } +} + +// ATTENTION!! This interface force us to create a NumberSet which need to be +// delited somewhere The specification in the interface requires that this +// function never returns NULL. in case we have no output face we need to return +// an empty NumberSet +static NumberSet *_strategyLoadBalancerWithPD_LookupNexthop( + StrategyImpl *strategy, const Message *interestMessage) { + StrategyLoadBalancerWithPD *lb = + (StrategyLoadBalancerWithPD *)strategy->context; + + unsigned in_connection = message_GetIngressConnectionId(interestMessage); + PARCUnsigned *in = parcUnsigned_Create(in_connection); + + unsigned mapSize = parcHashMap_Size(lb->strategy_state); + NumberSet *outList = numberSet_Create(); + + if ((mapSize == 0) || + ((mapSize == 1) && parcHashMap_Contains(lb->strategy_state, in))) { + // there are no output faces or the input face is also the only output face. + // return null to avoid loops + parcUnsigned_Release(&in); + return outList; + } + + unsigned out_connection; + do { + out_connection = _select_Nexthop(lb); + } while (out_connection == in_connection); + + PARCUnsigned *out = parcUnsigned_Create(out_connection); + + const StrategyNexthopStateWithPD *state = + parcHashMap_Get(lb->strategy_state, out); + if (state == NULL) { + // this is an error and should not happen! + parcTrapNotImplemented( + "Try to send an interest on a face that does not exists"); + } + + _update_Stats(lb, (StrategyNexthopStateWithPD *)state, true, 0); + + parcUnsigned_Release(&in); + parcUnsigned_Release(&out); + + numberSet_Add(outList, out_connection); + return outList; +} + +static NumberSet *_strategyLoadBalancerWithPD_ReturnNexthops( + StrategyImpl *strategy) { + StrategyLoadBalancerWithPD *lb = + (StrategyLoadBalancerWithPD *)strategy->context; + return lb->nexthops; +} + +unsigned _strategyLoadBalancerWithPD_CountNexthops(StrategyImpl *strategy) { + StrategyLoadBalancerWithPD *lb = + (StrategyLoadBalancerWithPD *)strategy->context; + return numberSet_Length(lb->nexthops); +} + +static void _strategyLoadBalancerWithPD_resetState(StrategyImpl *strategy) { + StrategyLoadBalancerWithPD *lb = + (StrategyLoadBalancerWithPD *)strategy->context; + lb->weights_sum = 0.0; + lb->min_delay = INT_MAX; + lb->toInit = true; + PARCIterator *it = parcHashMap_CreateKeyIterator(lb->strategy_state); + + while (parcIterator_HasNext(it)) { + PARCUnsigned *cid = parcIterator_Next(it); + StrategyNexthopStateWithPD *elem = + (StrategyNexthopStateWithPD *)parcHashMap_Get(lb->strategy_state, cid); + + strategyNexthopStateWithPD_Reset(elem); + lb->weights_sum += strategyNexthopStateWithPD_GetWeight(elem); + } + + parcIterator_Release(&it); +} + +static void _strategyLoadBalancerWithPD_AddNexthop(StrategyImpl *strategy, + unsigned connectionId) { + StrategyNexthopStateWithPD *state = strategyNexthopStateWithPD_Create(); + + PARCUnsigned *cid = parcUnsigned_Create(connectionId); + + StrategyLoadBalancerWithPD *lb = + (StrategyLoadBalancerWithPD *)strategy->context; + + if (!parcHashMap_Contains(lb->strategy_state, cid)) { + parcHashMap_Put(lb->strategy_state, cid, state); + numberSet_Add(lb->nexthops, connectionId); + _strategyLoadBalancerWithPD_resetState(strategy); + } +} + +static void _strategyLoadBalancerWithPD_RemoveNexthop(StrategyImpl *strategy, + unsigned connectionId) { + StrategyLoadBalancerWithPD *lb = + (StrategyLoadBalancerWithPD *)strategy->context; + + PARCUnsigned *cid = parcUnsigned_Create(connectionId); + + if (parcHashMap_Contains(lb->strategy_state, cid)) { + parcHashMap_Remove(lb->strategy_state, cid); + numberSet_Remove(lb->nexthops, connectionId); + _strategyLoadBalancerWithPD_resetState(strategy); + } + + parcUnsigned_Release(&cid); +} + +static void _strategyLoadBalancerWithPD_ImplDestroy( + StrategyImpl **strategyPtr) { + parcAssertNotNull(strategyPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*strategyPtr, + "Parameter must dereference to non-null pointer"); + + StrategyImpl *impl = *strategyPtr; + StrategyLoadBalancerWithPD *strategy = + (StrategyLoadBalancerWithPD *)impl->context; + + parcHashMap_Release(&(strategy->strategy_state)); + numberSet_Release(&(strategy->nexthops)); + + parcMemory_Deallocate((void **)&strategy); + parcMemory_Deallocate((void **)&impl); + *strategyPtr = NULL; +} diff --git a/hicn-light/src/strategies/loadBalancerWithPD.h b/hicn-light/src/strategies/loadBalancerWithPD.h new file mode 100755 index 000000000..6ea7f0785 --- /dev/null +++ b/hicn-light/src/strategies/loadBalancerWithPD.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Forward on the less loaded path taking into account the propagation delay of + * the first hop + */ + +#ifndef loadBalancerWithPD_h +#define loadBalancerWithPD_h + +#include <src/core/connectionTable.h> +#include <src/strategies/strategyImpl.h> + +StrategyImpl *strategyLoadBalancerWithPD_Create(); +void strategyLoadBalancerWithPD_SetConnectionTable(StrategyImpl *strategy, + ConnectionTable *connTable); +#endif // loadBalancerWithPD_h diff --git a/hicn-light/src/strategies/nexthopState.c b/hicn-light/src/strategies/nexthopState.c new file mode 100755 index 000000000..ef0ffe982 --- /dev/null +++ b/hicn-light/src/strategies/nexthopState.c @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> +#include <stdio.h> + +#include <parc/algol/parc_DisplayIndented.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> + +#include <parc/assert/parc_Assert.h> +#include <src/strategies/nexthopState.h> + +struct strategy_nexthop_state { + unsigned int pi; + double avg_pi; + double weight; +}; + +static bool _strategyNexthopState_Destructor( + StrategyNexthopState **instancePtr) { + return true; +} + +parcObject_ImplementAcquire(strategyNexthopState, StrategyNexthopState); + +parcObject_ImplementRelease(strategyNexthopState, StrategyNexthopState); + +parcObject_Override( + StrategyNexthopState, PARCObject, + .destructor = (PARCObjectDestructor *)_strategyNexthopState_Destructor, + .copy = (PARCObjectCopy *)strategyNexthopState_Copy, + .display = (PARCObjectDisplay *)strategyNexthopState_Display, + .toString = (PARCObjectToString *)strategyNexthopState_ToString, + .equals = (PARCObjectEquals *)strategyNexthopState_Equals, + .compare = (PARCObjectCompare *)strategyNexthopState_Compare, + .hashCode = (PARCObjectHashCode *)strategyNexthopState_HashCode, + .display = (PARCObjectDisplay *)strategyNexthopState_Display); + +void strategyNexthopState_AssertValid(const StrategyNexthopState *instance) { + parcAssertTrue(strategyNexthopState_IsValid(instance), + "StrategyNexthopState is not valid."); +} + +StrategyNexthopState *strategyNexthopState_Create() { + StrategyNexthopState *result = + parcObject_CreateInstance(StrategyNexthopState); + if (result != NULL) { + result->pi = 0; + result->avg_pi = 0.0; + result->weight = 1; + } + return result; +} + +void strategyNexthopState_Reset(StrategyNexthopState *x) { + x->pi = 0; + x->avg_pi = 0.0; + x->weight = 1; +} + +int strategyNexthopState_Compare(const StrategyNexthopState *val, + const StrategyNexthopState *other) { + if (val == NULL) { + if (other != NULL) { + return -1; + } + } else if (other == NULL) { + return 1; + } else { + strategyNexthopState_OptionalAssertValid(val); + strategyNexthopState_OptionalAssertValid(other); + + if (val->pi < other->pi) { + return -1; + } else if (val->pi > other->pi) { + return 1; + } + + if (val->avg_pi < other->avg_pi) { + return -1; + } else if (val->avg_pi > other->avg_pi) { + return 1; + } + + if (val->weight < other->weight) { + return -1; + } else if (val->weight > other->weight) { + return 1; + } + } + + return 0; +} + +StrategyNexthopState *strategyNexthopState_Copy( + const StrategyNexthopState *original) { + StrategyNexthopState *result = strategyNexthopState_Create(); + result->pi = original->pi; + result->avg_pi = original->avg_pi; + result->weight = original->weight; + + return result; +} + +void strategyNexthopState_Display(const StrategyNexthopState *instance, + int indentation) { + parcDisplayIndented_PrintLine(indentation, "StrategyNexthopState@%p {", + instance); + parcDisplayIndented_PrintLine(indentation + 1, "%d", instance->pi); + parcDisplayIndented_PrintLine(indentation + 1, "%f", instance->avg_pi); + parcDisplayIndented_PrintLine(indentation + 1, "%f", instance->weight); + parcDisplayIndented_PrintLine(indentation, "}"); +} + +bool strategyNexthopState_Equals(const StrategyNexthopState *x, + const StrategyNexthopState *y) { + bool result = false; + + if (x == y) { + result = true; + } else if (x == NULL || y == NULL) { + result = false; + } else { + strategyNexthopState_OptionalAssertValid(x); + strategyNexthopState_OptionalAssertValid(y); + + if (strategyNexthopState_Compare(x, y) == 0) { + result = true; + } + } + + return result; +} + +PARCHashCode strategyNexthopState_HashCode(const StrategyNexthopState *x) { + PARCHashCode result = 0; + char str[128]; + sprintf(str, "PI:%d: AVG_PI:%f: W:%f", x->pi, x->avg_pi, x->weight); + result = parcHashCode_Hash((uint8_t *)&str, strlen(str)); + return result; +} + +bool strategyNexthopState_IsValid(const StrategyNexthopState *x) { + bool result = false; + + if (x != NULL) { + result = true; + } + + return result; +} + +char *strategyNexthopState_ToString(const StrategyNexthopState *x) { + // this is not implemented + parcTrapNotImplemented("strategyNexthopState_ToString is not implemented"); + return NULL; +} + +unsigned strategyNexthopState_GetPI(const StrategyNexthopState *x) { + strategyNexthopState_OptionalAssertValid(x); + + return x->pi; +} + +double strategyNexthopState_GetAvgPI(const StrategyNexthopState *x) { + strategyNexthopState_OptionalAssertValid(x); + + return x->avg_pi; +} + +double strategyNexthopState_GetWeight(const StrategyNexthopState *x) { + strategyNexthopState_OptionalAssertValid(x); + + return x->weight; +} + +double strategyNexthopState_UpdateState(StrategyNexthopState *x, bool inc, + double alpha) { + if (inc) { + x->pi++; + } else { + if (x->pi > 0) { + x->pi--; + } + } + x->avg_pi = (x->avg_pi * alpha) + (x->pi * (1 - alpha)); + if (x->avg_pi == 0.0) { + x->avg_pi = 0.1; + } + x->weight = 1 / x->avg_pi; + + return x->weight; +} diff --git a/hicn-light/src/strategies/nexthopState.h b/hicn-light/src/strategies/nexthopState.h new file mode 100755 index 000000000..35a9f497b --- /dev/null +++ b/hicn-light/src/strategies/nexthopState.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef nexthopstate_h +#define nexthopstate_h + +#include <parc/algol/parc_HashCode.h> +#include <parc/algol/parc_Object.h> + +struct strategy_nexthop_state; +typedef struct strategy_nexthop_state StrategyNexthopState; +extern parcObjectDescriptor_Declaration(StrategyNexthopState); + +/** + */ +StrategyNexthopState *strategyNexthopState_Acquire( + const StrategyNexthopState *instance); + +#ifdef PARCLibrary_DISABLE_VALIDATION +#define strategyNexthopState_OptionalAssertValid(_instance_) +#else +#define strategyNexthopState_OptionalAssertValid(_instance_) \ + strategyNexthopState_AssertValid(_instance_) +#endif + +/** + */ +void strategyNexthopState_AssertValid(const StrategyNexthopState *instance); + +/** + */ +StrategyNexthopState *strategyNexthopState_Create(); + +void strategyNexthopState_Reset(StrategyNexthopState *x); +/** + */ +int strategyNexthopState_Compare(const StrategyNexthopState *instance, + const StrategyNexthopState *other); + +/** + */ +StrategyNexthopState *strategyNexthopState_Copy( + const StrategyNexthopState *original); + +/** + */ +void strategyNexthopState_Display(const StrategyNexthopState *instance, + int indentation); + +/** + */ +bool strategyNexthopState_Equals(const StrategyNexthopState *x, + const StrategyNexthopState *y); + +/** + */ +PARCHashCode strategyNexthopState_HashCode( + const StrategyNexthopState *instance); + +/** + */ +bool strategyNexthopState_IsValid(const StrategyNexthopState *instance); + +/** + */ +void strategyNexthopState_Release(StrategyNexthopState **instancePtr); + +/** + */ +char *strategyNexthopState_ToString(const StrategyNexthopState *instance); + +/** + */ +unsigned strategyNexthopState_GetPI(const StrategyNexthopState *x); + +double strategyNexthopState_GetAvgPI(const StrategyNexthopState *x); + +double strategyNexthopState_GetWeight(const StrategyNexthopState *x); + +double strategyNexthopState_UpdateState(StrategyNexthopState *x, bool inc, + double alpha); +#endif diff --git a/hicn-light/src/strategies/nexthopStateWithPD.c b/hicn-light/src/strategies/nexthopStateWithPD.c new file mode 100755 index 000000000..2eecb0c64 --- /dev/null +++ b/hicn-light/src/strategies/nexthopStateWithPD.c @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <float.h> +#include <limits.h> +#include <src/config.h> +#include <stdio.h> + +#include <parc/algol/parc_DisplayIndented.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> +#include <parc/assert/parc_Assert.h> + +#include <src/strategies/nexthopStateWithPD.h> + +struct strategy_nexthop_state_with_pd { + unsigned int pi; + unsigned delay; + double weight; + double avg_pi; +}; + +static bool _strategyNexthopStateWithPD_Destructor( + StrategyNexthopStateWithPD **instancePtr) { + return true; +} + +parcObject_ImplementAcquire(strategyNexthopStateWithPD, + StrategyNexthopStateWithPD); + +parcObject_ImplementRelease(strategyNexthopStateWithPD, + StrategyNexthopStateWithPD); + +parcObject_Override( + StrategyNexthopStateWithPD, PARCObject, + .destructor = (PARCObjectDestructor *) + _strategyNexthopStateWithPD_Destructor, + .copy = (PARCObjectCopy *)strategyNexthopStateWithPD_Copy, + .display = (PARCObjectDisplay *)strategyNexthopStateWithPD_Display, + .toString = (PARCObjectToString *)strategyNexthopStateWithPD_ToString, + .equals = (PARCObjectEquals *)strategyNexthopStateWithPD_Equals, + .compare = (PARCObjectCompare *)strategyNexthopStateWithPD_Compare, + .hashCode = (PARCObjectHashCode *)strategyNexthopStateWithPD_HashCode, + .display = (PARCObjectDisplay *)strategyNexthopStateWithPD_Display); + +void strategyNexthopStateWithPD_AssertValid( + const StrategyNexthopStateWithPD *instance) { + parcAssertTrue(strategyNexthopStateWithPD_IsValid(instance), + "StrategyNexthopStateWithPD is not valid."); +} + +StrategyNexthopStateWithPD *strategyNexthopStateWithPD_Create() { + StrategyNexthopStateWithPD *result = + parcObject_CreateInstance(StrategyNexthopStateWithPD); + if (result != NULL) { + result->pi = 0; + result->avg_pi = 1.0; + result->weight = 1; + result->delay = 0; + } + return result; +} + +void strategyNexthopStateWithPD_Reset(StrategyNexthopStateWithPD *x) { + x->pi = 0; + x->avg_pi = 1.0; + x->weight = 1; + x->delay = 0; +} + +int strategyNexthopStateWithPD_Compare( + const StrategyNexthopStateWithPD *val, + const StrategyNexthopStateWithPD *other) { + if (val == NULL) { + if (other != NULL) { + return -1; + } + } else if (other == NULL) { + return 1; + } else { + strategyNexthopStateWithPD_OptionalAssertValid(val); + strategyNexthopStateWithPD_OptionalAssertValid(other); + + if (val->pi < other->pi) { + return -1; + } else if (val->pi > other->pi) { + return 1; + } + + if (val->avg_pi < other->avg_pi) { + return -1; + } else if (val->avg_pi > other->avg_pi) { + return 1; + } + + if (val->weight < other->weight) { + return -1; + } else if (val->weight > other->weight) { + return 1; + } + + if (val->delay < other->delay) { + return -1; + } else if (val->delay > other->delay) { + return 1; + } + } + + return 0; +} + +StrategyNexthopStateWithPD *strategyNexthopStateWithPD_Copy( + const StrategyNexthopStateWithPD *original) { + StrategyNexthopStateWithPD *result = strategyNexthopStateWithPD_Create(); + result->pi = original->pi; + result->avg_pi = original->avg_pi; + result->weight = original->weight; + result->delay = original->delay; + + return result; +} + +void strategyNexthopStateWithPD_Display( + const StrategyNexthopStateWithPD *instance, int indentation) { + parcDisplayIndented_PrintLine(indentation, "StrategyNexthopStateWithPD@%p {", + instance); + parcDisplayIndented_PrintLine(indentation + 1, "%d", instance->pi); + parcDisplayIndented_PrintLine(indentation + 1, "%f", instance->avg_pi); + parcDisplayIndented_PrintLine(indentation + 1, "%f", instance->weight); + parcDisplayIndented_PrintLine(indentation + 1, "%f", instance->delay); + parcDisplayIndented_PrintLine(indentation, "}"); +} + +bool strategyNexthopStateWithPD_Equals(const StrategyNexthopStateWithPD *x, + const StrategyNexthopStateWithPD *y) { + bool result = false; + + if (x == y) { + result = true; + } else if (x == NULL || y == NULL) { + result = false; + } else { + strategyNexthopStateWithPD_OptionalAssertValid(x); + strategyNexthopStateWithPD_OptionalAssertValid(y); + + if (strategyNexthopStateWithPD_Compare(x, y) == 0) { + result = true; + } + } + + return result; +} + +PARCHashCode strategyNexthopStateWithPD_HashCode( + const StrategyNexthopStateWithPD *x) { + PARCHashCode result = 0; + char str[128]; + sprintf(str, "PI:%d: AVG_PI:%f: W:%f D:%d", x->pi, x->avg_pi, x->weight, + x->delay); + result = parcHashCode_Hash((uint8_t *)&str, strlen(str)); + return result; +} + +bool strategyNexthopStateWithPD_IsValid(const StrategyNexthopStateWithPD *x) { + bool result = false; + + if (x != NULL) { + result = true; + } + + return result; +} + +char *strategyNexthopStateWithPD_ToString(const StrategyNexthopStateWithPD *x) { + // this is not implemented + parcTrapNotImplemented( + "strategyNexthopStateWithPD_ToString is not implemented"); + return NULL; +} + +unsigned strategyNexthopStateWithPD_GetPI(const StrategyNexthopStateWithPD *x) { + strategyNexthopStateWithPD_OptionalAssertValid(x); + + return x->pi; +} + +double strategyNexthopStateWithPD_GetAvgPI( + const StrategyNexthopStateWithPD *x) { + strategyNexthopStateWithPD_OptionalAssertValid(x); + + return x->avg_pi; +} + +double strategyNexthopStateWithPD_GetWeight( + const StrategyNexthopStateWithPD *x) { + strategyNexthopStateWithPD_OptionalAssertValid(x); + + return x->weight; +} + +unsigned strategyNexthopStateWithPD_GetDelay( + const StrategyNexthopStateWithPD *x) { + strategyNexthopStateWithPD_OptionalAssertValid(x); + + return x->delay; +} + +void strategyNexthopStateWithPD_SetDelay(StrategyNexthopStateWithPD *x, + unsigned delay) { + strategyNexthopStateWithPD_OptionalAssertValid(x); + if (delay != 0) { + x->delay = delay; + } +} + +double strategyNexthopStateWithPD_UpdateState(StrategyNexthopStateWithPD *x, + bool inc, unsigned min_delay, + double alpha) { + strategyNexthopStateWithPD_OptionalAssertValid(x); + + if (inc) { + x->pi++; + } else { + if (x->pi > 0) { + x->pi--; + } + } + + x->avg_pi = (x->avg_pi * alpha) + (x->pi * (1 - alpha)); + if (x->avg_pi == 0.0) { + x->avg_pi = 0.1; + } + + double factor = 1.0; + if (min_delay != INT_MAX && x->delay != 0) { + factor = ((double)min_delay / (double)x->delay); + } + + x->weight = 1 / (x->avg_pi * factor); + + return x->weight; +} diff --git a/hicn-light/src/strategies/nexthopStateWithPD.h b/hicn-light/src/strategies/nexthopStateWithPD.h new file mode 100755 index 000000000..4d8bd6d15 --- /dev/null +++ b/hicn-light/src/strategies/nexthopStateWithPD.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef nexthopstatewithpd_h +#define nexthopstatewithpd_h + +#include <parc/algol/parc_HashCode.h> +#include <parc/algol/parc_Object.h> + +struct strategy_nexthop_state_with_pd; +typedef struct strategy_nexthop_state_with_pd StrategyNexthopStateWithPD; +extern parcObjectDescriptor_Declaration(StrategyNexthopStateWithPD); + +/** + */ +StrategyNexthopStateWithPD *strategyNexthopStateWithPD_Acquire( + const StrategyNexthopStateWithPD *instance); + +#ifdef PARCLibrary_DISABLE_VALIDATION +#define strategyNexthopStateWithPD_OptionalAssertValid(_instance_) +#else +#define strategyNexthopStateWithPD_OptionalAssertValid(_instance_) \ + strategyNexthopStateWithPD_AssertValid(_instance_) +#endif + +/** + */ +void strategyNexthopStateWithPD_AssertValid( + const StrategyNexthopStateWithPD *instance); + +/** + */ +StrategyNexthopStateWithPD *strategyNexthopStateWithPD_Create(); + +void strategyNexthopStateWithPD_Reset(StrategyNexthopStateWithPD *x); +/** + */ +int strategyNexthopStateWithPD_Compare( + const StrategyNexthopStateWithPD *instance, + const StrategyNexthopStateWithPD *other); + +/** + */ +StrategyNexthopStateWithPD *strategyNexthopStateWithPD_Copy( + const StrategyNexthopStateWithPD *original); + +/** + */ +void strategyNexthopStateWithPD_Display( + const StrategyNexthopStateWithPD *instance, int indentation); + +/** + */ +bool strategyNexthopStateWithPD_Equals(const StrategyNexthopStateWithPD *x, + const StrategyNexthopStateWithPD *y); + +/** + */ +PARCHashCode strategyNexthopStateWithPD_HashCode( + const StrategyNexthopStateWithPD *instance); + +/** + */ +bool strategyNexthopStateWithPD_IsValid( + const StrategyNexthopStateWithPD *instance); + +/** + */ +void strategyNexthopStateWithPD_Release( + StrategyNexthopStateWithPD **instancePtr); + +/** + */ +char *strategyNexthopStateWithPD_ToString( + const StrategyNexthopStateWithPD *instance); + +/** + */ +unsigned strategyNexthopStateWithPD_GetPI(const StrategyNexthopStateWithPD *x); + +double strategyNexthopStateWithPD_GetAvgPI(const StrategyNexthopStateWithPD *x); + +double strategyNexthopStateWithPD_GetWeight( + const StrategyNexthopStateWithPD *x); + +unsigned strategyNexthopStateWithPD_GetDelay( + const StrategyNexthopStateWithPD *x); +void strategyNexthopStateWithPD_SetDelay(StrategyNexthopStateWithPD *x, + unsigned delay); + +double strategyNexthopStateWithPD_UpdateState(StrategyNexthopStateWithPD *x, + bool inc, unsigned min_delay, + double alpha); +#endif diff --git a/hicn-light/src/strategies/rnd.c b/hicn-light/src/strategies/rnd.c new file mode 100755 index 000000000..37f3f6f30 --- /dev/null +++ b/hicn-light/src/strategies/rnd.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_HashMap.h> +#include <parc/algol/parc_Memory.h> + +#include <src/strategies/rnd.h> + +static void _strategyRnd_ReceiveObject(StrategyImpl *strategy, + const NumberSet *egressId, + const Message *objectMessage, Ticks rtt); +static void _strategyRnd_OnTimeout(StrategyImpl *strategy, + const NumberSet *egressId); +static NumberSet *_strategyRnd_LookupNexthop(StrategyImpl *strategy, + const Message *interestMessage); +static NumberSet *_strategyRnd_ReturnNexthops(StrategyImpl *strategy); +static unsigned _strategyRnd_CountNexthops(StrategyImpl *strategy); +static void _strategyRnd_AddNexthop(StrategyImpl *strategy, + unsigned connectionId); +static void _strategyRnd_RemoveNexthop(StrategyImpl *strategy, + unsigned connectionId); +static void _strategyRnd_ImplDestroy(StrategyImpl **strategyPtr); +static strategy_type _strategyRnd_GetStrategy(StrategyImpl *strategy); + +static StrategyImpl _template = { + .context = NULL, + .receiveObject = &_strategyRnd_ReceiveObject, + .onTimeout = &_strategyRnd_OnTimeout, + .lookupNexthop = &_strategyRnd_LookupNexthop, + .returnNexthops = &_strategyRnd_ReturnNexthops, + .countNexthops = &_strategyRnd_CountNexthops, + .addNexthop = &_strategyRnd_AddNexthop, + .removeNexthop = &_strategyRnd_RemoveNexthop, + .destroy = &_strategyRnd_ImplDestroy, + .getStrategy = &_strategyRnd_GetStrategy, +}; + +struct strategy_rnd; +typedef struct strategy_rnd StrategyRnd; + +struct strategy_rnd { + NumberSet *nexthops; +}; + +StrategyImpl *strategyRnd_Create() { + StrategyRnd *strategy = parcMemory_AllocateAndClear(sizeof(StrategyRnd)); + parcAssertNotNull(strategy, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(StrategyRnd)); + + strategy->nexthops = numberSet_Create(); + srand(time(NULL)); + + StrategyImpl *impl = parcMemory_AllocateAndClear(sizeof(StrategyImpl)); + parcAssertNotNull(impl, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(StrategyImpl)); + memcpy(impl, &_template, sizeof(StrategyImpl)); + impl->context = strategy; + return impl; +} + +// ======================================================= +// Dispatch API + +strategy_type _strategyRnd_GetStrategy(StrategyImpl *strategy) { + return SET_STRATEGY_RANDOM; +} + +static int _select_Nexthop(StrategyRnd *strategy) { + unsigned len = numberSet_Length(strategy->nexthops); + if (len == 0) { + return -1; + } + + int rnd = (rand() % len); + return numberSet_GetItem(strategy->nexthops, rnd); +} + +static void _strategyRnd_ReceiveObject(StrategyImpl *strategy, + const NumberSet *egressId, + const Message *objectMessage, + Ticks rtt) {} + +static void _strategyRnd_OnTimeout(StrategyImpl *strategy, + const NumberSet *egressId) {} + +static NumberSet *_strategyRnd_LookupNexthop(StrategyImpl *strategy, + const Message *interestMessage) { + StrategyRnd *srnd = (StrategyRnd *)strategy->context; + + unsigned in_connection = message_GetIngressConnectionId(interestMessage); + unsigned nexthopSize = numberSet_Length(srnd->nexthops); + + NumberSet *out = numberSet_Create(); + if ((nexthopSize == 0) || + ((nexthopSize == 1) && + numberSet_Contains(srnd->nexthops, in_connection))) { + // there are no output faces or the input face is also the only output face. + // return null to avoid loops + return out; + } + + unsigned out_connection; + do { + out_connection = _select_Nexthop(srnd); + } while (out_connection == in_connection); + + if (out_connection == -1) { + return out; + } + + numberSet_Add(out, out_connection); + return out; +} + +static NumberSet *_strategyRnd_ReturnNexthops(StrategyImpl *strategy) { + StrategyRnd *srnd = (StrategyRnd *)strategy->context; + return srnd->nexthops; +} + +unsigned _strategyRnd_CountNexthops(StrategyImpl *strategy) { + StrategyRnd *srnd = (StrategyRnd *)strategy->context; + return numberSet_Length(srnd->nexthops); +} + +static void _strategyRnd_AddNexthop(StrategyImpl *strategy, + unsigned connectionId) { + StrategyRnd *srnd = (StrategyRnd *)strategy->context; + if (!numberSet_Contains(srnd->nexthops, connectionId)) { + numberSet_Add(srnd->nexthops, connectionId); + } +} + +static void _strategyRnd_RemoveNexthop(StrategyImpl *strategy, + unsigned connectionId) { + StrategyRnd *srnd = (StrategyRnd *)strategy->context; + + if (numberSet_Contains(srnd->nexthops, connectionId)) { + numberSet_Remove(srnd->nexthops, connectionId); + } +} + +static void _strategyRnd_ImplDestroy(StrategyImpl **strategyPtr) { + parcAssertNotNull(strategyPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*strategyPtr, + "Parameter must dereference to non-null pointer"); + + StrategyImpl *impl = *strategyPtr; + StrategyRnd *strategy = (StrategyRnd *)impl->context; + + numberSet_Release(&(strategy->nexthops)); + + parcMemory_Deallocate((void **)&strategy); + parcMemory_Deallocate((void **)&impl); + *strategyPtr = NULL; +} diff --git a/hicn-light/src/strategies/rnd.h b/hicn-light/src/strategies/rnd.h new file mode 100755 index 000000000..69bedc1a5 --- /dev/null +++ b/hicn-light/src/strategies/rnd.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Forward randomly + */ + +#ifndef rnd_h +#define rnd_h + +#include <src/strategies/strategyImpl.h> + +StrategyImpl* strategyRnd_Create(); +#endif // rnd_h diff --git a/hicn-light/src/strategies/rndSegment.c b/hicn-light/src/strategies/rndSegment.c new file mode 100755 index 000000000..2000ed7b7 --- /dev/null +++ b/hicn-light/src/strategies/rndSegment.c @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_HashMap.h> +#include <parc/algol/parc_Memory.h> +#include <src/core/nameBitvector.h> +#include <src/strategies/rndSegment.h> + +static void _strategyRndSegment_ReceiveObject(StrategyImpl *strategy, + const NumberSet *egressId, + const Message *objectMessage, + Ticks rtt); +static void _strategyRndSegment_OnTimeout(StrategyImpl *strategy, + const NumberSet *egressId); +static NumberSet *_strategyRndSegment_LookupNexthop( + StrategyImpl *strategy, const Message *interestMessage); +static NumberSet *_strategyRndSegment_ReturnNexthops(StrategyImpl *strategy); +static unsigned _strategyRndSegment_CountNexthops(StrategyImpl *strategy); +static void _strategyRndSegment_AddNexthop(StrategyImpl *strategy, + unsigned connectionId); +static void _strategyRndSegment_RemoveNexthop(StrategyImpl *strategy, + unsigned connectionId); +static void _strategyRndSegment_ImplDestroy(StrategyImpl **strategyPtr); +static strategy_type _strategyRndSegment_GetStrategy(StrategyImpl *strategy); + +static StrategyImpl _template = { + .context = NULL, + .receiveObject = &_strategyRndSegment_ReceiveObject, + .onTimeout = &_strategyRndSegment_OnTimeout, + .lookupNexthop = &_strategyRndSegment_LookupNexthop, + .returnNexthops = &_strategyRndSegment_ReturnNexthops, + .countNexthops = &_strategyRndSegment_CountNexthops, + .addNexthop = &_strategyRndSegment_AddNexthop, + .removeNexthop = &_strategyRndSegment_RemoveNexthop, + .destroy = &_strategyRndSegment_ImplDestroy, + .getStrategy = &_strategyRndSegment_GetStrategy, +}; + +struct strategy_rnd_segment; +typedef struct strategy_rnd_segment StrategyRndSegment; + +struct strategy_rnd_segment { + NumberSet *nexthops; + NameBitvector *segmentName; + int last_used_face; +}; + +StrategyImpl *strategyRndSegment_Create() { + StrategyRndSegment *strategy = + parcMemory_AllocateAndClear(sizeof(StrategyRndSegment)); + parcAssertNotNull(strategy, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(StrategyRndSegment)); + + strategy->nexthops = numberSet_Create(); + strategy->segmentName = NULL; + strategy->last_used_face = 0; + srand(time(NULL)); + + StrategyImpl *impl = parcMemory_AllocateAndClear(sizeof(StrategyImpl)); + parcAssertNotNull(impl, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(StrategyImpl)); + memcpy(impl, &_template, sizeof(StrategyImpl)); + impl->context = strategy; + + return impl; +} + +// ======================================================= +// Dispatch API + +strategy_type _strategyRndSegment_GetStrategy(StrategyImpl *strategy) { + return SET_STRATEGY_RANDOM_PER_DASH_SEGMENT; +} + +static int _select_Nexthop(StrategyRndSegment *strategy) { + unsigned len = numberSet_Length(strategy->nexthops); + if (len == 0) { + return -1; + } + + int rnd = (rand() % len); + return numberSet_GetItem(strategy->nexthops, rnd); +} + +static void _strategyRndSegment_ReceiveObject(StrategyImpl *strategy, + const NumberSet *egressId, + const Message *objectMessage, + Ticks rtt) {} + +static void _strategyRndSegment_OnTimeout(StrategyImpl *strategy, + const NumberSet *egressId) {} + +static NumberSet *_strategyRndSegment_LookupNexthop( + StrategyImpl *strategy, const Message *interestMessage) { + StrategyRndSegment *srnd = (StrategyRndSegment *)strategy->context; + + unsigned in_connection = message_GetIngressConnectionId(interestMessage); + unsigned nexthopSize = numberSet_Length(srnd->nexthops); + + NumberSet *out = numberSet_Create(); + if ((nexthopSize == 0) || + ((nexthopSize == 1) && + numberSet_Contains(srnd->nexthops, in_connection))) { + // there are no output faces or the input face is also the only output face. + // return null to avoid loops + return out; + } + + NameBitvector *interestName = + name_GetContentName(message_GetName(interestMessage)); + + if (srnd->segmentName == NULL) { + srnd->segmentName = nameBitvector_Copy(interestName); + } else if (!nameBitvector_Equals(srnd->segmentName, interestName)) { + nameBitvector_Destroy(&srnd->segmentName); + srnd->segmentName = nameBitvector_Copy(interestName); + } else { + // here we need to check if the output face still exists or if someone erase + // it + if (numberSet_Contains(srnd->nexthops, srnd->last_used_face)) { + // face exists, so keep using it! + numberSet_Add(out, srnd->last_used_face); + return out; + } else { + // the face does not exists anymore, try to find a new face but keep the + // name of the dash segment + } + } + + int out_connection; + do { + out_connection = _select_Nexthop(srnd); + } while (out_connection == in_connection); + + if (out_connection == -1) { + return out; + } + + srnd->last_used_face = out_connection; + numberSet_Add(out, out_connection); + return out; +} + +static NumberSet *_strategyRndSegment_ReturnNexthops(StrategyImpl *strategy) { + StrategyRndSegment *srnd = (StrategyRndSegment *)strategy->context; + return srnd->nexthops; +} + +unsigned _strategyRndSegment_CountNexthops(StrategyImpl *strategy) { + StrategyRndSegment *srnd = (StrategyRndSegment *)strategy->context; + return numberSet_Length(srnd->nexthops); +} + +static void _strategyRndSegment_AddNexthop(StrategyImpl *strategy, + unsigned connectionId) { + StrategyRndSegment *srnd = (StrategyRndSegment *)strategy->context; + if (!numberSet_Contains(srnd->nexthops, connectionId)) { + numberSet_Add(srnd->nexthops, connectionId); + } +} + +static void _strategyRndSegment_RemoveNexthop(StrategyImpl *strategy, + unsigned connectionId) { + StrategyRndSegment *srnd = (StrategyRndSegment *)strategy->context; + + if (numberSet_Contains(srnd->nexthops, connectionId)) { + numberSet_Remove(srnd->nexthops, connectionId); + } +} + +static void _strategyRndSegment_ImplDestroy(StrategyImpl **strategyPtr) { + parcAssertNotNull(strategyPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*strategyPtr, + "Parameter must dereference to non-null pointer"); + + StrategyImpl *impl = *strategyPtr; + StrategyRndSegment *strategy = (StrategyRndSegment *)impl->context; + + numberSet_Release(&(strategy->nexthops)); + if (strategy->segmentName != NULL) { + nameBitvector_Destroy(&strategy->segmentName); + } + + parcMemory_Deallocate((void **)&strategy); + parcMemory_Deallocate((void **)&impl); + *strategyPtr = NULL; +} diff --git a/hicn-light/src/strategies/rndSegment.h b/hicn-light/src/strategies/rndSegment.h new file mode 100755 index 000000000..0749692f7 --- /dev/null +++ b/hicn-light/src/strategies/rndSegment.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Forward randomly, selects a path every time the client ask for a new dash + * segment + */ + +#ifndef rnd_Segment_h +#define rnd_Segment_h + +#include <src/strategies/strategyImpl.h> + +StrategyImpl* strategyRndSegment_Create(); +#endif // rnd_Segment_h diff --git a/hicn-light/src/strategies/strategyImpl.h b/hicn-light/src/strategies/strategyImpl.h new file mode 100755 index 000000000..c089e0b2b --- /dev/null +++ b/hicn-light/src/strategies/strategyImpl.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file strategyImpl.h + * @brief Defines the function structure for a Strategy implementation + * + * <#Detailed Description#> + * + */ + +/** + * A dispatch structure for a concrete implementation of a forwarding strategy. + */ + +#ifndef strategyImpl_h +#define strategyImpl_h + +#include <src/core/message.h> +#include <src/core/numberSet.h> + +struct strategy_impl; +typedef struct strategy_impl StrategyImpl; + +/** + * @typedef StrategyImpl + * @abstract Forwarding strategy implementation + * @constant receiveObject is called when we receive an object and have a + * measured round trip time. This allows a strategy to update its performance + * data. + * @constant lookupNexthop Find the set of nexthops to use for the Interest. + * May be empty, should not be NULL. Must be destroyed. + * @constant addNexthop Add a nexthop to the list of available nexthops with a + * routing protocol-specific cost. + * @constant destroy cleans up the strategy, freeing all memory and state. A + * strategy is reference counted, so the final destruction only happens after + * the last reference is released. + * @discussion <#Discussion#> + */ +struct strategy_impl { + void *context; + void (*receiveObject)(StrategyImpl *strategy, const NumberSet *egressId, + const Message *objectMessage, Ticks rtt); + void (*onTimeout)(StrategyImpl *strategy, const NumberSet *egressId); + NumberSet *(*lookupNexthop)(StrategyImpl *strategy, + const Message *interestMessage); + NumberSet *(*returnNexthops)(StrategyImpl *strategy); + unsigned (*countNexthops)(StrategyImpl *strategy); + void (*addNexthop)(StrategyImpl *strategy, unsigned connectionId); + void (*removeNexthop)(StrategyImpl *strategy, unsigned connectionId); + void (*destroy)(StrategyImpl **strategyPtr); + strategy_type (*getStrategy)(StrategyImpl *strategy); +}; +#endif // strategyImpl_h diff --git a/hicn-light/src/utils/CMakeLists.txt b/hicn-light/src/utils/CMakeLists.txt new file mode 100755 index 000000000..7d438d157 --- /dev/null +++ b/hicn-light/src/utils/CMakeLists.txt @@ -0,0 +1,36 @@ +# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/address.h + ${CMAKE_CURRENT_SOURCE_DIR}/addressList.h + ${CMAKE_CURRENT_SOURCE_DIR}/commands.h + ${CMAKE_CURRENT_SOURCE_DIR}/interface.h + ${CMAKE_CURRENT_SOURCE_DIR}/interfaceSet.h + ${CMAKE_CURRENT_SOURCE_DIR}/punting.h + ${CMAKE_CURRENT_SOURCE_DIR}/utils.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/address.c + ${CMAKE_CURRENT_SOURCE_DIR}/addressList.c + ${CMAKE_CURRENT_SOURCE_DIR}/interface.c + ${CMAKE_CURRENT_SOURCE_DIR}/interfaceSet.c + ${CMAKE_CURRENT_SOURCE_DIR}/punting.c + ${CMAKE_CURRENT_SOURCE_DIR}/utils.c +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE)
\ No newline at end of file diff --git a/hicn-light/src/utils/address.c b/hicn-light/src/utils/address.c new file mode 100755 index 000000000..3f6fe2591 --- /dev/null +++ b/hicn-light/src/utils/address.c @@ -0,0 +1,419 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <arpa/inet.h> +#include <errno.h> +#include <src/config.h> +#include <stdio.h> +#include <strings.h> +#include <unistd.h> + +#include <src/utils/address.h> + +#include <parc/algol/parc_Base64.h> +#include <parc/algol/parc_BufferComposer.h> +#include <parc/algol/parc_Hash.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Network.h> +#include <parc/algol/parc_Object.h> + +#include <parc/assert/parc_Assert.h> + +struct address { + address_type addressType; + PARCBuffer *blob; +}; + +static struct address_type_str { + address_type type; + const char *str; +} addressTypeString[] = { + {.type = ADDR_INET, .str = "INET"}, {.type = ADDR_INET6, .str = "INET6"}, + {.type = ADDR_LINK, .str = "LINK"}, {.type = ADDR_IFACE, .str = "IFACE"}, + {.type = ADDR_UNIX, .str = "UNIX"}, {.type = 0, .str = NULL}}; + +void addressDestroy(Address **addressPtr) { + parcAssertNotNull(addressPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*addressPtr, + "Parameter must dereference to non-null pointer"); + + Address *address = *addressPtr; + parcBuffer_Release(&address->blob); + parcMemory_Deallocate((void **)&address); + *addressPtr = NULL; +} + +void addressAssertValid(const Address *address) { + parcAssertNotNull(address, "Parameter must be non-null Address *"); +} + +const char *addressTypeToString(address_type type) { + for (int i = 0; addressTypeString[i].str != NULL; i++) { + if (addressTypeString[i].type == type) { + return addressTypeString[i].str; + } + } + parcTrapIllegalValue(type, "Unknown value: %d", type); + const char *result = NULL; + return result; +} + +address_type addressStringToType(const char *str) { + for (int i = 0; addressTypeString[i].str != NULL; i++) { + if (strcasecmp(addressTypeString[i].str, str) == 0) { + return addressTypeString[i].type; + } + } + parcTrapIllegalValue(str, "Unknown type '%s'", str); + return 0; +} + +static Address *_addressCreate(address_type addressType, PARCBuffer *buffer) { + Address *result = parcMemory_AllocateAndClear(sizeof(Address)); + + parcAssertNotNull(result, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Address)); + if (result != NULL) { + result->addressType = addressType; + result->blob = buffer; + } + return result; +} + +Address *addressCreateFromInet(struct sockaddr_in *addr_in) { + parcAssertNotNull(addr_in, "Parameter must be non-null"); + + addr_in->sin_family = AF_INET; + + PARCBuffer *buffer = parcBuffer_Allocate(sizeof(struct sockaddr_in)); + parcBuffer_PutArray(buffer, sizeof(struct sockaddr_in), (uint8_t *)addr_in); + parcBuffer_Flip(buffer); + + Address *result = _addressCreate(ADDR_INET, buffer); + + return result; +} + +Address *addressCreateFromInet6(struct sockaddr_in6 *addr_in6) { + parcAssertNotNull(addr_in6, "Parameter must be non-null"); + + PARCBuffer *buffer = parcBuffer_Allocate(sizeof(struct sockaddr_in6)); + parcBuffer_PutArray(buffer, sizeof(struct sockaddr_in6), (uint8_t *)addr_in6); + parcBuffer_Flip(buffer); + + Address *result = _addressCreate(ADDR_INET6, buffer); + + return result; +} + +Address *addressCreateFromLink(const uint8_t *linkaddr, size_t length) { + parcAssertNotNull(linkaddr, "Parameter must be non-null"); + + PARCBuffer *buffer = parcBuffer_Allocate(sizeof(struct sockaddr_in6)); + parcBuffer_PutArray(buffer, length, linkaddr); + parcBuffer_Flip(buffer); + + Address *result = _addressCreate(ADDR_LINK, buffer); + return result; +} + +Address *addressCreateFromInterface(unsigned interfaceIndex) { + unsigned netbyteorder = htonl(interfaceIndex); + + PARCBuffer *buffer = parcBuffer_Allocate(sizeof(netbyteorder)); + parcBuffer_PutArray(buffer, sizeof(netbyteorder), (uint8_t *)&netbyteorder); + parcBuffer_Flip(buffer); + + Address *result = _addressCreate(ADDR_IFACE, buffer); + return result; +} + +Address *addressCreateFromUnix(struct sockaddr_un *addr_un) { + parcAssertNotNull(addr_un, "Parameter must be non-null"); + + PARCBuffer *buffer = parcBuffer_Allocate(sizeof(struct sockaddr_un)); + parcBuffer_PutArray(buffer, sizeof(struct sockaddr_un), (uint8_t *)addr_un); + parcBuffer_Flip(buffer); + + Address *result = _addressCreate(ADDR_UNIX, buffer); + return result; +} + +Address *addressCopy(const Address *original) { + addressAssertValid(original); + + Address *result = + _addressCreate(original->addressType, parcBuffer_Copy(original->blob)); + return result; +} + +bool addressEquals(const Address *a, const Address *b) { + if (a == b) { + return true; + } + + if (a == NULL || b == NULL) { + return false; + } + + if (a->addressType == b->addressType) { + if (parcBuffer_Equals(a->blob, b->blob)) { + return true; + } + } + + return false; +} + +address_type addressGetType(const Address *address) { + addressAssertValid(address); + + return address->addressType; +} + +// The Get functions need better names, what they do (Get from what? Put to +// what?) is not clear from their names. Case 1028 +bool addressGetInet(const Address *address, struct sockaddr_in *addr_in) { + addressAssertValid(address); + parcAssertNotNull(addr_in, "Parameter addr_in must be non-null"); + + if (address->addressType == ADDR_INET) { + parcAssertTrue( + parcBuffer_Remaining(address->blob) == sizeof(struct sockaddr_in), + "Address corrupted. Expected length %zu, actual length %zu", + sizeof(struct sockaddr_in), parcBuffer_Remaining(address->blob)); + + memcpy(addr_in, parcBuffer_Overlay(address->blob, 0), + sizeof(struct sockaddr_in)); + return true; + } + return false; +} + +bool addressGetInet6(const Address *address, struct sockaddr_in6 *addr_in6) { + addressAssertValid(address); + parcAssertNotNull(addr_in6, "Parameter addr_in6 must be non-null"); + + if (address->addressType == ADDR_INET6) { + parcAssertTrue( + parcBuffer_Remaining(address->blob) == sizeof(struct sockaddr_in6), + "Address corrupted. Expected length %zu, actual length %zu", + sizeof(struct sockaddr_in6), parcBuffer_Remaining(address->blob)); + + memcpy(addr_in6, parcBuffer_Overlay(address->blob, 0), + sizeof(struct sockaddr_in6)); + return true; + } + return false; +} + +bool addressGetUnix(const Address *address, struct sockaddr_un *addr_un) { + addressAssertValid(address); + parcAssertNotNull(addr_un, "Parameter addr_in6 must be non-null"); + + if (address->addressType == ADDR_UNIX) { + parcAssertTrue( + parcBuffer_Remaining(address->blob) == sizeof(struct sockaddr_un), + "Address corrupted. Expected length %zu, actual length %zu", + sizeof(struct sockaddr_un), parcBuffer_Remaining(address->blob)); + + memcpy(addr_un, parcBuffer_Overlay(address->blob, 0), + sizeof(struct sockaddr_un)); + return true; + } + return false; +} + +bool addressGetInterfaceIndex(const Address *address, uint32_t *ifidx) { + addressAssertValid(address); + parcAssertNotNull(ifidx, "Parameter ifidx must be non-null"); + + if (address->addressType == ADDR_IFACE) { + parcAssertTrue(parcBuffer_Remaining(address->blob) == sizeof(uint32_t), + "Address corrupted. Expected length %zu, actual length %zu", + sizeof(uint32_t), parcBuffer_Remaining(address->blob)); + + uint32_t netbyteorder; + memcpy(&netbyteorder, parcBuffer_Overlay(address->blob, 0), + sizeof(uint32_t)); + *ifidx = ntohl(netbyteorder); + return true; + } + return false; +} + +PARCBuffer *addressGetLinkAddress(const Address *address) { + addressAssertValid(address); + if (address->addressType == ADDR_LINK) { + return address->blob; + } + return NULL; +} + +static PARCBufferComposer *_Inet_BuildString(const Address *address, + PARCBufferComposer *composer) { + addressAssertValid(address); + + struct sockaddr_in *saddr = + (struct sockaddr_in *)parcBuffer_Overlay(address->blob, 0); + return parcNetwork_SockInet4Address_BuildString(saddr, composer); +} + +static PARCBufferComposer *_Inet6_BuildString(const Address *address, + PARCBufferComposer *composer) { + addressAssertValid(address); + + struct sockaddr_in6 *saddr = + (struct sockaddr_in6 *)parcBuffer_Overlay(address->blob, 0); + return parcNetwork_SockInet6Address_BuildString(saddr, composer); +} + +static PARCBufferComposer *_Link_BuildString(const Address *address, + PARCBufferComposer *composer) { + addressAssertValid(address); + + const unsigned char *addr = parcBuffer_Overlay(address->blob, 0); + + size_t length = parcBuffer_Remaining(address->blob); + + return parcNetwork_LinkAddress_BuildString(addr, length, composer); +} + +static ssize_t _UnixToString(char *output, size_t remaining_size, + const PARCBuffer *addr) { + parcAssertNotNull(output, "parameter output must be non-null"); + parcBuffer_AssertValid(addr); + + parcAssertTrue(parcBuffer_Remaining(addr) == sizeof(struct sockaddr_un), + "Address corrupted. Expected %zu actual %zu", + sizeof(struct sockaddr_un), parcBuffer_Remaining(addr)); + + // sockaddr length for the path, 16 for the ascii stuff, 3 for the length + // number + struct sockaddr_un *saddr = + (struct sockaddr_un *)parcBuffer_Overlay((PARCBuffer *)addr, 0); + size_t min_remaining = strlen(saddr->sun_path) + 16 + 3; + parcAssertTrue(remaining_size >= min_remaining, + "Remaining size too small, need at least %zu", min_remaining); + + ssize_t output_length = sprintf(output, "{ .path=%s, .len=%zu }", + saddr->sun_path, strlen(saddr->sun_path)); + return output_length; +} + +static ssize_t _IfaceToString(char *output, size_t remaining_size, + const PARCBuffer *addr) { + parcAssertNotNull(output, "parameter output must be non-null"); + parcBuffer_AssertValid(addr); + + parcAssertTrue(parcBuffer_Remaining(addr) == sizeof(uint32_t), + "Address corrupted. Expected %zu actual %zu", sizeof(uint32_t), + parcBuffer_Remaining(addr)); + + uint32_t *ifidx = (uint32_t *)parcBuffer_Overlay((PARCBuffer *)addr, 0); + + ssize_t output_length = sprintf(output, "{ .ifidx=%u }", ntohl(*ifidx)); + + return output_length; +} + +PARCBufferComposer *addressBuildString(const Address *address, + PARCBufferComposer *composer) { + if (address != NULL) { + char *str = addressToString(address); + parcBufferComposer_PutString(composer, str); + parcMemory_Deallocate((void **)&str); + } + return composer; +} + +char *addressToString(const Address *address) { + addressAssertValid(address); + + char addrstr[256]; + + switch (address->addressType) { + case ADDR_INET: { + PARCBufferComposer *composer = parcBufferComposer_Create(); + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer( + _Inet_BuildString(address, composer)); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + parcBufferComposer_Release(&composer); + return result; + } break; + + case ADDR_INET6: { + PARCBufferComposer *composer = parcBufferComposer_Create(); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer( + _Inet6_BuildString(address, composer)); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + + parcBufferComposer_Release(&composer); + return result; + } break; + + case ADDR_LINK: + _UnixToString(addrstr, 256, address->blob); + break; + + case ADDR_IFACE: { + PARCBufferComposer *composer = parcBufferComposer_Create(); + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer( + _Link_BuildString(address, composer)); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + + parcBufferComposer_Release(&composer); + return result; + } break; + + case ADDR_UNIX: + _IfaceToString(addrstr, 256, address->blob); + break; + + default: + sprintf(addrstr, "UNKNOWN type = %d", address->addressType); + break; + } + + ssize_t alloc_size = 1024; + char *output = parcMemory_Allocate(alloc_size); + parcAssertNotNull(output, "parcMemory_Allocate(%zu) returned NULL", + alloc_size); + ssize_t output_length = + snprintf(output, alloc_size, "{ .type=%s, .data=%s }", + addressTypeToString(address->addressType), addrstr); + + parcAssertTrue(output_length < alloc_size, + "allocated size too small, needed %zd", output_length); + parcAssertFalse(output_length < 0, "snprintf error: (%d) %s", errno, + strerror(errno)); + + return output; +} + +PARCHashCode addressHashCode(const Address *address) { + addressAssertValid(address); + + PARCHashCode hash = parcBuffer_HashCode(address->blob); + hash = parcHashCode_HashImpl((uint8_t *)&address->addressType, + sizeof(address->addressType), hash); + + return hash; +} diff --git a/hicn-light/src/utils/address.h b/hicn-light/src/utils/address.h new file mode 100755 index 000000000..a98d15084 --- /dev/null +++ b/hicn-light/src/utils/address.h @@ -0,0 +1,498 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief Represents an endpoint address. + * + * Represents an endpoint address. May be INET, INET6, or a multi-byte LINK, + * or an Interface Index. + * + * INET and INET6 must contain the .sa_addr member, and other members as needed + * by the use of the address. + * + * The Interface Index address is essentially a pointer to a device. + * + * + * Example: + * @code + * <#example#> + * @endcode + */ +#ifndef address_h +#define address_h + +#include <netinet/in.h> +#include <stdbool.h> +#include <sys/un.h> + +#include <parc/algol/parc_Buffer.h> +#include <parc/algol/parc_BufferComposer.h> +#include <src/utils/commands.h> + +/** + * Return a string representation of the given `address_type` + * + * @param [in] type A valid address_type value. + * + * @return NULL An error occurred + * @return non-NULL A pointer to a static string representation of the + * `address_type`. + * + * Example: + * @code + * { + * const char *typeAsString = addressTypeToString(commandAddrType_INET); + * } + * @endcode + * + * @see addressStringToType + */ +const char *addressTypeToString(address_type type); + +/** + * Return a `address_type` from the given nul-terminated C string. + * + * @param [in] typeAsString A nul-terminated, C string representation of a + * `address_type`. + * + * @return A address_type + * + * Example: + * @code + * { + * address_type type = addressTypeToString("INET"); + * } + * @endcode + * + * @see addressTypeToString + */ +address_type addressStringToType(const char *typeAsString); + +struct address; +typedef struct address Address; + +/** + * Create a new `Address` instance from an IPv4 IP address, the port is + * optional. + * + * The sockaddr_in should be filled in network byte order. The newly created + * instance must eventually be destroyed by calling {@link addressDestroy}(). + * + * @param [in] addr_in The `sockaddr_in` representing the IPv4 IP address with + * which to initialize the new `Address` instance. + * @return A new instance of `Address` that must eventually be destroyed by + * calling {@link addressDestroy}(). + * + * Example: + * @code + * { + * Address *dest = addressCreateFromInet( + * &(struct sockaddr_in) { + * .sa_addr = + * inet_addr("foo.bar.com"), .sa_port = htons(9695) } ); addressDestroy(&dest); + * } + * @endcode + * @see addressDestroy + */ +Address *addressCreateFromInet(struct sockaddr_in *addr_in); + +/** + * Create a new `Address` instance from an IPv6 IP address, the port is + * optional. + * + * + * The sockaddr_in should be filled in network byte order. The newly created + * instance must eventually be destroyed by calling {@link addressDestroy}(). + * + * @param [in] addr_in6 A `sockaddr_in6` from which to initialize a new instance + * of Address + * @return A new instance of `Address` that must eventually be destroyed by + * calling {@link addressDestroy}() + * + * Example: + * @code + * { + * struct sockaddr_in6 addr_in6; + * memset(&addr_in6, 0, sizeof(struct sockaddr_in6)); + * + * inet_pton(AF_INET6, "2001:720:1500:1::a100", &(addr_in6.sin6_addr)); + * addr_in6.sin6_family = AF_INET6; + * addr_in6.sin6_port = 0x0A0B; + * addr_in6.sin6_flowinfo = 0x01020304; + * + * Address *address = addressCreateFromInet6(&addr_in6); + * + * addressDestroy(&address); + * } + * @endcode + * @see addressDestroy + */ +Address *addressCreateFromInet6(struct sockaddr_in6 *addr_in6); + +/** + * Create a new `Address` instance, initialized from a Link address. + * + * User must know the link address format (i.e. token ring vs ethernet) and have + * the address in a byte array. The array is encoded in left-to-right order. The + * newly created instance must eventually be destroyed by calling {@link + * addressDestroy}(). + * + * @param [in] linkaddr A byte array containing the link address + * @param [in] length The length of the link address byte array + * @return A new instance of `Address` that must eventually be destroyed by + * calling {@link addressDestroy}() + * + * Example: + * @code + * { + * uint8_t mac[] = { 0x14, 0x10, 0x9f, 0xd7, 0x0b, 0x89 }; + * Address *address = addressCreateFromLink(mac, sizeof(mac)); + * + * addressDestroy(&address); + * } + * @endcode + * @see addressDestroy + */ +Address *addressCreateFromLink(const uint8_t *linkaddr, size_t length); + +/** + * Create a new `Address` instance from a network interface index. + * + * The interfaceIndex should be in host byte order. The newly created instance + * must eventually be destroyed by calling {@link addressDestroy}(). + * + * @param [in] interfaceIndex The index of the interface to encode + * @return A new instance of `Address` that must eventually be destroyed by + * calling {@link addressDestroy}() + * + * Example: + * @code + * { + * Address *address = addressCreateFromInterface(2); + * + * addressDestroy(&address); + * } + * @endcode + * @see addressDestroy + */ +Address *addressCreateFromInterface(uint32_t interfaceIndex); + +/** + * Create a new Address instance from a PF_UNIX address domain. + * + * The newly created instance must eventually be destroyed by calling {@link + * addressDestroy}(). + * + * @param [in] addr_un The `struct sockaddr_un` specifying the local PF_UNIX + * socket address + * @return A new instance of `Address` that must eventually be destroyed by + * calling {@link addressDestroy}() + * + * Example: + * @code + * { + * struct sockaddr_un addr_unix; + * memset(&addr_unix, 0, sizeof(struct sockaddr_un)); + * char path[] = "/Hello/Cruel/World"; + * strcpy(addr_un.sun_path, path); + * addr_un.sun_family = AF_UNIX; + * + * Address *address = addressCreateFromUnix(&addr_un); + * + * addressDestroy(&address); + * } + * @endcode + * @see addressDestroy + */ +Address *addressCreateFromUnix(struct sockaddr_un *addr_un); + +/** + * Create a deep copy of an instance of a `Address`. A completely new, + * indedependent instance is created. + * + * The newly created instance must eventually be destroyed by calling {@link + * addressDestroy}(). + * + * @param [in] original A pointer to a `Address` instance to be copied. + * @return A new instance of a Address, deep copied from the `original` + * instance. + * + * Example: + * @code + * { + * Address *address = addressCreateFromInterface(2); + * + * Address *copy = addressCopy(address); + * + * addressDestroy(&address); + * addressDestroy(©); + * } + * @endcode + * @see addressDestroy + */ +Address *addressCopy(const Address *original); + +/** + * Deallocate an instance of a Address. + * + * The Address instance is deallocated, and any referenced data is also + * deallocated. The referenced pointer is set to NULL upon return. + * + * @param [in] addressPtr A pointer to a pointer to an instance of Address. + * + * Example: + * @code + * { + * Address *address = addressCreateFromInterface(2); + * + * addressDestroy(&address); + * } + * @endcode + */ +void addressDestroy(Address **addressPtr); + +/** + * Determine if two Address instances are equal. + * + * + * The following equivalence relations on non-null `Address` instances are + * maintained: + * + * * It is reflexive: for any non-null reference value x, `addressEquals(x, x)` + * must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `addressEquals(x, y)` must return true if and only if + * `addressEquals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `addressEquals(x, y)` returns true and + * `addressEquals(y, z)` returns true, + * then `addressEquals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `addressEquals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `addressEquals(x, NULL)` must + * return false. + * + * If one address specifies more information than other, + * e.g. a is INET with a port and b is not, they are not equal. + * + * `a` and `b` may be NULL, and NULL == NULL. + * + * @param a A pointer to a Address instance + * @param b A pointer to a Address instance + * @return true if the two instances are equal + * @return false if the two instances are not equal + * + * Example: + * @code + * { + * Address *address = addressCreateFromInterface(2); + * Address *copy = addressCopy(address); + * + * if (addressEquals(address, copy)) { + * // true + * } else { + * // false + * } + * + * addressDestroy(&address); + * addressDestroy(©); + * } + * @endcode + */ +bool addressEquals(const Address *a, const Address *b); + +/** + * Return the {@link address_type} from a specified Address. + * + * @param [in] A pointer to a Address instance + * + * @return the {@link address_type} of the specified Address instance + * + * Example: + * @code + * { + * Address *address = addressCreateFromInterface(2); + * + * address_type type = addressGetType(address); + * + * addressDestroy(&address); + * } + * @endcode + * + * @see address_type + */ +address_type addressGetType(const Address *address); + +/** + * Fills in the output parameter with an INET address. + * + * @param addr_in must be non-NULL + * @return true if INET address and output filled in, false otherwise. + * + */ +bool addressGetInet(const Address *address, struct sockaddr_in *addr_in); + +/** + * Retrieve the INET6 address associated with a `Address` instance. + * + * If the specified Address instance is of type {@link commandAddrType_INET6}, + * then populate the supplied `struct sockaddr_in6` from the Address and return + * true. If the Address is not of type `commandAddrType_INET6`, this function + * returns false. + * + * @param [in] address A pointer to a `Address` instance of type {@link + * commandAddrType_INET6}. + * @param [in] addr_in6 A pointer to a `struct sockaddr_in6`. Must be non-NULL. + * @return true If the Address instance is of type `commandAddrType_INET6` and + * `addr_in6` was filled in + * @return false If the Address instance was not of type `commandAddrType_INET6` + * or `addr_in6` could not be filled in. + * + * @see addressGetType + */ +bool addressGetInet6(const Address *address, struct sockaddr_in6 *addr_in6); + +/** + * Retrieve the interface index associated with a `Address` instance. + * + * If the specified `Address` instance is of type {@link commandAddrType_IFACE}, + * then populate the supplied `uint32_t` from the Address and return true. If + * the `Address` is not of type `commandAddrType_INET6`, this function returns + * false. + * + * @param [in] address A pointer to a `Address` instance of type {@link + * commandAddrType_IFACE}. + * @param [in] interfaceIndex A pointer to a `uint32_t` to fill in. Must be + * non-NULL. + * @return true If the Address instance is of type `commandAddrType_IFACE` and + * `interfaceIndex` was filled in. + * @return false If the Address instance was not of type `commandAddrType_IFACE` + * or `interfaceIndex` could not be filled in. + * + * @see addressGetType + */ +bool addressGetInterfaceIndex(const Address *address, uint32_t *interfaceIndex); + +/** + * Retrieve the link address associated with a `Address` instance. + * + * If the specified `Address` instance is of type {@link commandAddrType_LINK}, + * then return a pointer to the {@link PARCBuffer} containing the link address. + * If the `Address` is not of type {@link commandAddrType_LINK}, then return + * NULL. The returned PARCBuffer pointer points to memory managed by the Address + * instance, and does not need to be destroyed or released on its own. + * + * @param [in] address A pointer to a `Address` instance of type {@link + * commandAddrType_LINK}. + * @return A pointer to the {@link PARCBuffer} containing the link address. + * + * Example: + * @code + * { + * uint8_t mac[] = { 0x14, 0x10, 0x9f, 0xd7, 0x0b, 0x89 }; + * Address *address = addressCreateFromLink(mac, sizeof(mac)); + * + * PARCBuffer *macBuffer = addressGetLinkAddress(address); + * + * addressDestroy(&address); + * } + * @endcode + * @see addressGetType + */ +PARCBuffer *addressGetLinkAddress(const Address *address); + +/** + * Append the string representation of a `Address` to a specified + * `PARCBufferComposer`. + * + * @param [in] address A pointer to a `Address` instance. + * @param [in] composer A pointer to a `PARCBufferComposer` instance to which to + * append the string. + * + * @return The `PARCBufferComposer` instance that was passed in. + * + * Example: + * @code + * { + * Address *address = addressCreateFromInterface(1); + * PARCBufferComposer *composer = addressBuildString(address, + * parcBufferComposer_Create()); parcBufferComposer_Release(&composer); + * addressDestroy(&address); + * } + * @endcode + * + * @see PARCBufferComposer + */ +PARCBufferComposer *addressBuildString(const Address *address, + PARCBufferComposer *composer); + +/** + * Produce a nil-terminated string representation of the specified instance. + * + * The result must be freed by the caller via {@link parcMemory_Deallocate}. + * + * @param [in] interest A pointer to the instance. + * + * @return NULL Cannot allocate memory. + * @return non-NULL A pointer to an allocated, nul-terminated C string that must + * be deallocated via {@link parcMemory_Deallocate}(). + * + * Example: + * @code + * { + * Address *address = addressCreateFromInterface(1); + * + * char *string = addressToString(address); + * + * if (string != NULL) { + * printf("Address looks like: %s\n", string); + * parcMemory_Deallocate(string); + * } else { + * printf("Cannot allocate memory\n"); + * } + * + * addressDestroy(&address); + * } + * @endcode + * @see parcMemory_Deallocate + * @see addressBuildString + */ +char *addressToString(const Address *address); + +/** + * Return a non-cryptographic hash code consistent with Equals + * + * If commandAddrA == commandAddrB, then addressHashCode(commandAddrA) == + * addressHashCode(commandAddrB) + * + * @param [in] address A pointer to a Address instance. + * @return A 32-bit hashcode for the specified Address instance. + * + * Example: + * @code + * Address *address = addressCreateFromInterface(1); + * + * uint32_t hashCode = addressHashCode(address); + * + * addressDestroy(&address); + * @endcode + */ +PARCHashCode addressHashCode(const Address *address); +#endif // address_h diff --git a/hicn-light/src/utils/addressList.c b/hicn-light/src/utils/addressList.c new file mode 100755 index 000000000..4f51a11bf --- /dev/null +++ b/hicn-light/src/utils/addressList.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> +#include <stdio.h> + +#include <parc/assert/parc_Assert.h> + +#include <src/utils/addressList.h> + +#include <parc/algol/parc_ArrayList.h> +#include <parc/algol/parc_Buffer.h> +#include <parc/algol/parc_Memory.h> + +struct address_list { + PARCArrayList *listOfAddress; +}; + +static void _addressListFreeAddress(void **addressVoidPtr) { + Address **addressPtr = (Address **)addressVoidPtr; + addressDestroy(addressPtr); +} + +AddressList *addressListCreate() { + AddressList *list = parcMemory_AllocateAndClear(sizeof(AddressList)); + parcAssertNotNull(list, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(AddressList)); + list->listOfAddress = parcArrayList_Create(_addressListFreeAddress); + parcAssertNotNull(list->listOfAddress, "Got null from parcArrayList_Create"); + + return list; +} + +void addressListDestroy(AddressList **addressListPtr) { + parcAssertNotNull(addressListPtr, + "Parameter must be non-null double pointer"); + parcAssertNotNull(*addressListPtr, + "Parameter must dereference to non-null pointer"); + AddressList *list = *addressListPtr; + + parcArrayList_Destroy(&list->listOfAddress); + parcMemory_Deallocate((void **)&list); + *addressListPtr = NULL; +} + +AddressList *addressListAppend(AddressList *list, Address *address) { + parcAssertNotNull(list, "Parameter list must be non-null"); + parcAssertNotNull(address, "Parameter address must be non-null"); + + parcArrayList_Add(list->listOfAddress, (PARCObject *)address); + return list; +} + +AddressList *addressListCopy(const AddressList *original) { + parcAssertNotNull(original, "Parameter must be non-null"); + + AddressList *copy = addressListCreate(); + for (int i = 0; i < parcArrayList_Size(original->listOfAddress); i++) { + Address *address = (Address *)parcArrayList_Get(original->listOfAddress, i); + parcArrayList_Add(copy->listOfAddress, (PARCObject *)addressCopy(address)); + } + + return copy; +} + +bool addressListEquals(const AddressList *a, const AddressList *b) { + parcAssertNotNull(a, "Parameter a must be non-null"); + parcAssertNotNull(b, "Parameter b must be non-null"); + + if (a == b) { + return true; + } + + if (parcArrayList_Size(a->listOfAddress) != + parcArrayList_Size(b->listOfAddress)) { + return false; + } + + for (size_t i = 0; i < parcArrayList_Size(a->listOfAddress); i++) { + const Address *addr_a = (Address *)parcArrayList_Get(a->listOfAddress, i); + const Address *addr_b = (Address *)parcArrayList_Get(b->listOfAddress, i); + if (!addressEquals(addr_a, addr_b)) { + return false; + } + } + return true; +} + +size_t addressListLength(const AddressList *list) { + parcAssertNotNull(list, "Parameter must be non-null"); + return parcArrayList_Size(list->listOfAddress); +} + +const Address *addressListGetItem(const AddressList *list, size_t item) { + parcAssertNotNull(list, "Parameter must be non-null"); + parcAssertTrue(item < addressListLength(list), + "Asked for item %zu beyond end of list %zu", item, + addressListLength(list)); + + return (Address *)parcArrayList_Get(list->listOfAddress, item); +} + +char *addressListToString(const AddressList *list) { + PARCBufferComposer *composer = parcBufferComposer_Create(); + + for (size_t i = 0; i < addressListLength(list); i++) { + char *addressString = addressToString(addressListGetItem(list, i)); + parcBufferComposer_PutString(composer, addressString); + if (i < (addressListLength(list) - 1)) { + parcBufferComposer_PutString(composer, " "); + } + parcMemory_Deallocate((void **)&addressString); + } + + PARCBuffer *buffer = parcBufferComposer_ProduceBuffer(composer); + char *result = parcBuffer_ToString(buffer); + parcBuffer_Release(&buffer); + parcBufferComposer_Release(&composer); + + return result; +} diff --git a/hicn-light/src/utils/addressList.h b/hicn-light/src/utils/addressList.h new file mode 100755 index 000000000..bcb312c14 --- /dev/null +++ b/hicn-light/src/utils/addressList.h @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief A list of Address instances. + * + * An AddressList is a list of addresses. + * It wraps a PARCLinkedList for type saftey with Address. + * + */ +#ifndef address_list_h +#define address_list_h + +#include <src/utils/address.h> + +struct address_list; +/** + * @typedef AddressList + * @abstract A list of Address instance pointers. + */ +typedef struct address_list AddressList; + +/** + * Create an instance of {@link AddressList} + * + * @return NULL An error occurred + * @return non-NULL A pointer to a valid AddressList instance. + * + * Example: + * @code + * { + * AddressList *list = addressListCreate(); + * + * } + * @endcode + * + * @see addressListDestroy + */ +AddressList *addressListCreate(void); + +/** + * Dellocate and destroy a AddressList instance. + * + * @param [in] addressListPtr A pointer to a pointer to a valid {@link + * AddressList}. + * + * + * Example: + * @code + * { + * AddressList *list = addressListCreate(void); + * addressListDestroy(&list); + * } + * @endcode + * + * @see addressListCreate + */ +void addressListDestroy(AddressList **addressListPtr); + +/** + * Appends the address, taking ownership of the memory + * + * @param list A pointer to a AddressList. + * @param address must be non-null + * @return The input list + * + * Example: + * @code + * <#example#> + * @endcode + */ +AddressList *addressListAppend(AddressList *list, Address *address); + +/** + * Creates a reference counted copy + * + * @param list A pointer to a valid {@link AddressList}. + * + * @return An allocated list, you must destroy it. + * + * Example: + * @code + * <#example#> + * @endcode + */ +AddressList *addressListCopy(const AddressList *list); + +/** + * Determine if two AddressList instances are equal. + * + * Two AddressList instances are equal if, and only if, they have the same + * length, with the same elements in the same order. + * + * + * The following equivalence relations on non-null `AddressList` instances are + * maintained: + * + * * It is reflexive: for any non-null reference value x, + * `AddressList_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `AddressList_Equals(x, y)` must return true if and only if + * `addressListEquals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `addressListEquals(x, y)` returns true and + * `addressListEquals(y, z)` returns true, + * then `addressListEquals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `addressListEquals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `addressListEquals(x, NULL)` must + * return false. + * + * @param a A pointer to a `AddressList` instance. + * @param b A pointer to a `AddressList` instance. + * @return true if the two `AddressList` instances are equal. + * + * Example: + * @code + * { + * AddressList *a = addressListCreate(); + * AddressList *b = addressListCreate(); + * + * if (addressListEquals(a, b)) { + * // true + * } else { + * // false + * } + * } + * @endcode + */ +bool addressListEquals(const AddressList *a, const AddressList *b); + +/** + * Get the number of items in the list + * + * @param list A pointer to a {@link AddressList}. + * @return The number of items in the list. + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t addressListLength(const AddressList *list); + +/** + * Returns a const reference to an item. + * Use addressCopy if needed. + * + * Do not free or modify the returned value. + * Use addressCopy if you need a mutable instance. + * + * @param list A pointer to a AddressList. + * @param item A value less than the number of items in the given {@link + * AddressList}. + * @return Asserts if item off end of list. + * + * Example: + * @code + * <#example#> + * @endcode + */ +const Address *addressListGetItem(const AddressList *list, size_t item); + +/** + * Get a nul-terminated, C-string representation of the given {@link + * AddressList}. + * + * @param list A pointer to a valid {@link AddressList} instance. + * + * @return An allocate string representation of the {@link AddressList} that + * must be freed via `parcMemory_Deallocate()`. + * + * Example: + * @code + * <#example#> + * @endcode + */ +char *addressListToString(const AddressList *list); +#endif // address_list_h diff --git a/hicn-light/src/utils/commands.h b/hicn-light/src/utils/commands.h new file mode 100755 index 000000000..2f8ebcbfe --- /dev/null +++ b/hicn-light/src/utils/commands.h @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @file commands.h + * @brief All hicn-light commands: 14 in total. + * + * Header and payload in binary format. + */ + +#ifndef commands_h +#define commands_h + +#include <netinet/in.h> +#include <stdint.h> +#include <stdlib.h> +#include <sys/socket.h> + +typedef struct in6_addr ipv6_addr_t; +typedef uint32_t ipv4_addr_t; + +union commandAddr { + ipv4_addr_t ipv4; + ipv6_addr_t ipv6; +}; + +typedef enum { + REQUEST_LIGHT = 100, + RESPONSE_LIGHT, + ACK_LIGHT, + NACK_LIGHT, + LAST_MSG_TYPE_VALUE +} message_type; + +typedef enum { + ADD_LISTENER, + ADD_CONNECTION, + LIST_CONNECTIONS, + ADD_ROUTE, + LIST_ROUTES, + REMOVE_CONNECTION, + REMOVE_ROUTE, + CACHE_STORE, + CACHE_SERVE, + CACHE_CLEAR, + SET_STRATEGY, + SET_WLDR, + ADD_PUNTING, + LIST_LISTENERS, + MAPME_ENABLE, + MAPME_DISCOVERY, + MAPME_TIMESCALE, + MAPME_RETX, + LAST_COMMAND_VALUE +} command_id; + +typedef enum { + ADDR_INET = 1, + ADDR_INET6, + ADDR_LINK, + ADDR_IFACE, + ADDR_UNIX /* PF_UNIX */ +} address_type; + +typedef enum { + UDP_CONN, + TCP_CONN, + GRE_CONN, // not implemented + HICN_CONN +} connection_type; + +typedef enum { ACTIVATE_ON, ACTIVATE_OFF } activate_type; + +//========== HEADER ========== + +typedef struct { + uint8_t messageType; + uint8_t commandID; + uint16_t length; // tells the number of structures in the payload + uint32_t seqNum; +} header_control_message; +// for the moment has to be at least 8 bytes + +// SIZE=8 + +//========== [00] ADD LISTENER ========== + +typedef enum { ETHER_MODE, IP_MODE, HICN_MODE } listener_mode; + +typedef struct { + char symbolic[16]; + // char interfaceName[16]; + union commandAddr address; + uint16_t port; + // uint16_t etherType; + uint8_t addressType; + uint8_t listenerMode; + uint8_t connectionType; +} add_listener_command; + +// SIZE=40 + +//========== [01] ADD CONNECTION ========== + +typedef struct { + char symbolic[16]; + union commandAddr remoteIp; + union commandAddr localIp; + uint16_t remotePort; + uint16_t localPort; + uint8_t ipType; + uint8_t connectionType; +} add_connection_command; + +// SIZE=56 + +//========== [02] LIST CONNECTIONS ========== + +typedef enum { + CONN_GRE, + CONN_TCP, + CONN_UDP, + CONN_MULTICAST, + CONN_L2, + CONN_HICN +} list_connections_type; + +typedef enum { + IFACE_UP = 0, + IFACE_DOWN = 1, + IFACE_UNKNOWN = 2 // not used actually +} connection_state; + +typedef struct { + add_connection_command connectionData; + uint32_t connid; + uint8_t state; +} list_connections_command; + +// SIZE=64 + +//========== [03] ADD ROUTE ========== + +typedef struct { + char symbolicOrConnid[16]; + union commandAddr address; + uint16_t cost; + uint8_t addressType; + uint8_t len; +} add_route_command; + +// SIZE=36 + +//========== [04] LIST ROUTE ========== + +typedef struct { + union commandAddr address; + uint32_t connid; + uint16_t cost; + uint8_t addressType; + uint8_t len; +} list_routes_command; + +// SIZE=24 + +//========== [05] REMOVE CONNECTION ========== + +typedef struct { + char symbolicOrConnid[16]; +} remove_connection_command; + +// SIZE=16 + +//========== [06] REMOVE ROUTE ========== + +typedef struct { + char symbolicOrConnid[16]; + union commandAddr address; + uint8_t addressType; + uint8_t len; +} remove_route_command; + +// SIZE=36 + +//========== [07] CACHE STORE ========== + +typedef struct { + uint8_t activate; +} cache_store_command; + +// SIZE=1 + +//========== [08] CACHE SERVE ========== + +typedef struct { + uint8_t activate; +} cache_serve_command; + +// SIZE=1 + +//========== [09] SET STRATEGY ========== + +typedef enum { + SET_STRATEGY_LOADBALANCER, + SET_STRATEGY_RANDOM, + SET_STRATEGY_RANDOM_PER_DASH_SEGMENT, + SET_STRATEGY_LOADBALANCER_WITH_DELAY, + SET_STRATEGY_LOADBALANCER_BY_RATE, + SET_STRATEGY_LOADBALANCER_BEST_ROUTE, + LAST_STRATEGY_VALUE +} strategy_type; + +typedef struct { + union commandAddr address; + uint8_t strategyType; + uint8_t addressType; + uint8_t len; +} set_strategy_command; + +// SIZE=20 + +//========== [11] SET WLDR ========== + +typedef struct { + char symbolicOrConnid[16]; + uint8_t activate; +} set_wldr_command; + +// SIZE=17 + +//========== [12] ADD PUNTING ========== + +typedef struct { + char symbolicOrConnid[16]; + union commandAddr address; + uint8_t addressType; + uint8_t len; +} add_punting_command; + +// SIZE=36 + +//========== [13] LIST LISTENER ========== + +typedef struct { + union commandAddr address; + uint32_t connid; + uint16_t port; + uint8_t addressType; + uint8_t encapType; +} list_listeners_command; + +// SIZE=24 + +//========== [14] MAPME ========== + +// (enable/discovery/timescale/retx) + +typedef struct { + uint8_t activate; +} mapme_activator_command; + +// SIZE=1 + +typedef struct { + uint32_t timePeriod; +} mapme_timing_command; + +// SIZE=1 + +#endif diff --git a/hicn-light/src/utils/interface.c b/hicn-light/src/utils/interface.c new file mode 100755 index 000000000..ab7a88f0f --- /dev/null +++ b/hicn-light/src/utils/interface.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <parc/algol/parc_ArrayList.h> +#include <parc/algol/parc_BufferComposer.h> +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> +#include <src/utils/addressList.h> +#include <src/utils/interface.h> + +#include <parc/assert/parc_Assert.h> +#include <src/utils/commands.h> + +struct interface { + char *name; + unsigned interfaceIndex; + bool loopback; + bool supportMulticast; + unsigned mtu; + + AddressList *addressList; +}; + +char *interfaceToString(const Interface *interface) { + PARCBufferComposer *composer = parcBufferComposer_Create(); + + parcBufferComposer_Format( + composer, "%3u %10s %1s%1s %8u ", interface->interfaceIndex, + interface->name, interface->loopback ? "l" : " ", + interface->supportMulticast ? "m" : " ", interface->mtu); + + for (size_t i = 0; i < addressListLength(interface->addressList); i++) { + addressBuildString(addressListGetItem(interface->addressList, i), composer); + if (i < (addressListLength(interface->addressList) - 1)) { + parcBufferComposer_PutStrings(composer, "\n", NULL); + } + } + + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + parcBufferComposer_Release(&composer); + return result; +} + +Interface *interfaceCreate(const char *name, unsigned interfaceIndex, + bool loopback, bool supportMulticast, unsigned mtu) { + Interface *iface = parcMemory_AllocateAndClear(sizeof(Interface)); + + parcAssertNotNull(iface, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(Interface)); + iface->name = parcMemory_StringDuplicate(name, 64); + iface->interfaceIndex = interfaceIndex; + iface->loopback = loopback; + iface->supportMulticast = supportMulticast; + iface->mtu = mtu; + iface->addressList = addressListCreate(); + + return iface; +} + +void interfaceDestroy(Interface **interfacePtr) { + parcAssertNotNull(interfacePtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*interfacePtr, + "Parameter must dereference to non-null pointer"); + + Interface *iface = *interfacePtr; + parcMemory_Deallocate((void **)&iface->name); + addressListDestroy(&iface->addressList); + parcMemory_Deallocate((void **)&iface); + interfacePtr = NULL; +} + +void interfaceAddAddress(Interface *iface, Address *address) { + parcAssertNotNull(iface, "Parameter iface must be non-null"); + + size_t length = addressListLength(iface->addressList); + for (size_t i = 0; i < length; i++) { + const Address *a = addressListGetItem(iface->addressList, i); + if (addressEquals(a, address)) { + return; + } + } + + addressListAppend(iface->addressList, address); +} + +const AddressList *interfaceGetAddresses(const Interface *iface) { + parcAssertNotNull(iface, "Parameter iface must be non-null"); + return iface->addressList; +} + +unsigned interfaceGetInterfaceIndex(const Interface *iface) { + parcAssertNotNull(iface, "Parameter iface must be non-null"); + return iface->interfaceIndex; +} + +bool interfaceNameEquals(const Interface *iface, const char *name) { + parcAssertNotNull(iface, "Parameter iface must be non-null"); + + if (strcasecmp(iface->name, name) == 0) { + return true; + } + return false; +} + +bool interfaceEquals(const Interface *a, const Interface *b) { + if (a == NULL && b == NULL) { + return true; + } + + if (a == NULL || b == NULL) { + return false; + } + + if (a->interfaceIndex == b->interfaceIndex) { + if (a->loopback == b->loopback) { + if (a->supportMulticast == b->supportMulticast) { + if (a->mtu == b->mtu) { + if (strcasecmp(a->name, b->name) == 0) { + if (addressListEquals(a->addressList, b->addressList)) { + return true; + } + } + } + } + } + } + return false; +} + +// static const char light_Iface[] = "Interface"; +// static const char light_IfName[] = "Name"; +// static const char light_IFIDX[] = "Index"; +// static const char light_IsLoopback[] = "Loopback"; +// static const char light_Multicast[] = "Multicast"; +// static const char light_MTU[] = "MTU"; + +// static const char light_True[] = "true"; +// static const char light_False[] = "false"; +// static const char light_Addrs[] = "Addrs"; + +const char *interfaceGetName(const Interface *iface) { + parcAssertNotNull(iface, "Parameter iface must be non-null"); + return iface->name; +} + +unsigned interfaceGetMTU(const Interface *iface) { + parcAssertNotNull(iface, "Parameter iface must be non-null"); + return iface->mtu; +} diff --git a/hicn-light/src/utils/interface.h b/hicn-light/src/utils/interface.h new file mode 100755 index 000000000..0810ec053 --- /dev/null +++ b/hicn-light/src/utils/interface.h @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + */ +#ifndef interface_h +#define interface_h + +#include <src/utils/address.h> +#include <src/utils/addressList.h> + +struct interface; +typedef struct interface Interface; + +/** + * Creates a representation of an interface + * + * The name is copied. Creates a representation of a system interface. + * + * @param <#param1#> + * @return An allocated object, you must call <code>interfaceDestroy()</code> + * + * Example: + * @code + * <#example#> + * @endcode + */ +Interface *interfaceCreate(const char *name, unsigned interfaceIndex, + bool loopback, bool supportMulticast, unsigned mtu); + +void interfaceDestroy(Interface **interfacePtr); + +/** + * Adds an address to an interface + * + * Does not allow duplicates, if already exists is not added again + * + * @param <#param1#> + * @return <#return#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +void interfaceAddAddress(Interface *iface, Address *address); + +/** + * Retrieves a list of interface addresses + * + * <#Discussion#> + * + * @param <#param1#> + * @return Will not be NULL, but may be empty + * + * Example: + * @code + * <#example#> + * @endcode + */ +const AddressList *interfaceGetAddresses(const Interface *iface); + +/** + * The interface index + * + * <#Discussion#> + * + * @param <#param1#> + * @return <#return#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +unsigned interfaceGetInterfaceIndex(const Interface *iface); + +/** + * Returns the interface name, e.g. "eth0" + * + * <#Paragraphs Of Explanation#> + * + * @param [in] iface An allocated Interface + * + * @return non-null The interface Name as a C-string + * @return null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +const char *interfaceGetName(const Interface *iface); + +/** + * Returns the Maximum Transmission Unit (MTU) of the interface + * + * <#Paragraphs Of Explanation#> + * + * @param [in] iface An allocated Interface + * + * @return number The MTU as reported by the kernel + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +unsigned interfaceGetMTU(const Interface *iface); + +/** + * Determine if two InterfaceName instances are equal. + * + * + * The following equivalence relations on non-null `InterfaceName` instances are + * maintained: + * + * * It is reflexive: for any non-null reference value x, + * `InterfaceName_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `InterfaceName_Equals(x, y)` must return true if and only if + * `InterfaceName_Equals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `InterfaceName_Equals(x, y)` returns true and + * `InterfaceName_Equals(y, z)` returns true, + * then `InterfaceName_Equals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `InterfaceName_Equals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `InterfaceName_Equals(x, NULL)` must + * return false. + * + * @param a A pointer to a `InterfaceName` instance. + * @param b A pointer to a `InterfaceName` instance. + * @return true if the two `InterfaceName` instances are equal. + * + * Example: + * @code + * { + * InterfaceName *a = InterfaceName_Create(); + * InterfaceName *b = InterfaceName_Create(); + * + * if (InterfaceName_Equals(a, b)) { + * // true + * } else { + * // false + * } + * } + * @endcode + */ +bool interfaceNameEquals(const Interface *iface, const char *name); + +/** + * Two Interfaces are idential + * + * All properties must be the same. The order of addresses matters, and + * they must have been added to the address list in the same order. + * + * The interface name match is case in-sensitive. + * + * @param <#param1#> + * @return <#return#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool interfaceEquals(const Interface *a, const Interface *b); + +/** + * <#OneLineDescription#> + * + * <#Discussion#> + * + * @param interface A Interface structure pointer. + * @return An allocate string representation of the Interface that must be freed + * via parcMemory_Deallocate(). + * + * Example: + * @code + * <#example#> + * @endcode + */ +char *interfaceToString(const Interface *interface); +#endif // interface_h diff --git a/hicn-light/src/utils/interfaceSet.c b/hicn-light/src/utils/interfaceSet.c new file mode 100755 index 000000000..3f56ec167 --- /dev/null +++ b/hicn-light/src/utils/interfaceSet.c @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> +#include <stdio.h> + +#include <src/utils/interfaceSet.h> + +#include <parc/algol/parc_ArrayList.h> +#include <parc/algol/parc_Memory.h> + +#include <parc/assert/parc_Assert.h> + +struct interfaceSet { + PARCArrayList *listOfInterfaces; +}; + +static void _destroyInterface(void **ifaceVoidPtr) { + interfaceDestroy((Interface **)ifaceVoidPtr); +} + +InterfaceSet *interfaceSetCreate(void) { + InterfaceSet *set = parcMemory_AllocateAndClear(sizeof(InterfaceSet)); + parcAssertNotNull(set, "parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(InterfaceSet)); + set->listOfInterfaces = parcArrayList_Create(_destroyInterface); + return set; +} + +void interfaceSetDestroy(InterfaceSet **setPtr) { + parcAssertNotNull(setPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*setPtr, "Parameter must dereference to non-null pointer"); + + InterfaceSet *set = *setPtr; + parcArrayList_Destroy(&set->listOfInterfaces); + parcMemory_Deallocate((void **)&set); + *setPtr = NULL; +} + +bool interfaceSetAdd(InterfaceSet *set, Interface *iface) { + parcAssertNotNull(set, "Parameter set must be non-null"); + parcAssertNotNull(iface, "Parameter iface must be non-null"); + + unsigned ifaceIndex = interfaceGetInterfaceIndex(iface); + size_t length = parcArrayList_Size(set->listOfInterfaces); + for (size_t i = 0; i < length; i++) { + Interface *listEntry = + (Interface *)parcArrayList_Get(set->listOfInterfaces, i); + unsigned entryInterfaceIndex = interfaceGetInterfaceIndex(listEntry); + if (entryInterfaceIndex == ifaceIndex) { + return false; + } + } + + parcArrayList_Add(set->listOfInterfaces, (PARCObject *)iface); + return true; +} + +size_t interfaceSetLength(const InterfaceSet *set) { + parcAssertNotNull(set, "Parameter set must be non-null"); + return parcArrayList_Size(set->listOfInterfaces); +} + +Interface *interfaceSetGetByOrdinalIndex(InterfaceSet *set, + size_t ordinalIndex) { + parcAssertNotNull(set, "Parameter set must be non-null"); + return (Interface *)parcArrayList_Get(set->listOfInterfaces, ordinalIndex); +} + +Interface *interfaceSetGetByInterfaceIndex(const InterfaceSet *set, + unsigned interfaceIndex) { + size_t length = parcArrayList_Size(set->listOfInterfaces); + for (size_t i = 0; i < length; i++) { + Interface *listEntry = + (Interface *)parcArrayList_Get(set->listOfInterfaces, i); + unsigned entryInterfaceIndex = interfaceGetInterfaceIndex(listEntry); + if (entryInterfaceIndex == interfaceIndex) { + return listEntry; + } + } + return NULL; +} + +/** + * Uses the system name (e.g. "en0") + * + * <#Discussion#> + * + * @param <#param1#> + * @return NULL if not found + * + * Example: + * @code + * <#example#> + * @endcode + */ +Interface *interfaceSetGetByName(InterfaceSet *set, const char *name) { + size_t length = parcArrayList_Size(set->listOfInterfaces); + for (size_t i = 0; i < length; i++) { + Interface *listEntry = + (Interface *)parcArrayList_Get(set->listOfInterfaces, i); + if (interfaceNameEquals(listEntry, name)) { + return listEntry; + } + } + return NULL; +} + +bool interfaceSetEquals(const InterfaceSet *a, const InterfaceSet *b) { + if (a == NULL && b == NULL) { + return true; + } + + if (a == NULL || b == NULL) { + return false; + } + + size_t length_a = parcArrayList_Size(a->listOfInterfaces); + size_t length_b = parcArrayList_Size(b->listOfInterfaces); + + if (length_a == length_b) { + for (size_t i = 0; i < length_a; i++) { + Interface *iface_a = + (Interface *)parcArrayList_Get(a->listOfInterfaces, i); + + // the set is unique by interface id, so if it exists in set b, it + // exists there by interface id + Interface *iface_b = interfaceSetGetByInterfaceIndex( + b, interfaceGetInterfaceIndex(iface_a)); + if (!interfaceEquals(iface_b, iface_b)) { + return false; + } + } + return true; + } + return false; +} diff --git a/hicn-light/src/utils/interfaceSet.h b/hicn-light/src/utils/interfaceSet.h new file mode 100755 index 000000000..8eb8397fb --- /dev/null +++ b/hicn-light/src/utils/interfaceSet.h @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + */ +#ifndef InterfaceSet_h +#define InterfaceSet_h + +#include <src/utils/interface.h> + +struct interfaceSet; +/** + * + * @see interfaceSetCreate + */ +typedef struct interfaceSet InterfaceSet; + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +InterfaceSet *interfaceSetCreate(void); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +void interfaceSetDestroy(InterfaceSet **setPtr); + +/** + * Adds interface to set, does not allow duplicates + * + * Takes ownership of the iface memory if added + * + * Duplicates are two entries with the same interface index + * + * @param <#param1#> + * @return true if added, false if not (likely a duplicate) + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool interfaceSetAdd(InterfaceSet *set, Interface *iface); + +/** + * The number of interfaces in the set + * + * <#Discussion#> + * + * @param <#param1#> + * @return <#return#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +size_t interfaceSetLength(const InterfaceSet *set); + +/** + * Uses the ordinal index of the interface in the Set + * + * Ranges from 0 .. <code>interfaceSetLength()-1</code>. + * + * @param <#param1#> + * @return NULL if not found + * + * Example: + * @code + * <#example#> + * @endcode + */ +Interface *interfaceSetGetByOrdinalIndex(InterfaceSet *set, + size_t ordinalIndex); + +/** + * Retreives by the assigned interface index + * + * <#Discussion#> + * + * @param <#param1#> + * @return NULL if not found + * + * Example: + * @code + * <#example#> + * @endcode + */ +Interface *interfaceSetGetByInterfaceIndex(const InterfaceSet *set, + unsigned interfaceIndex); + +/** + * Uses the system name (e.g. "en0") + * + * <#Discussion#> + * + * @param <#param1#> + * @return NULL if not found + * + * Example: + * @code + * <#example#> + * @endcode + */ +Interface *interfaceSetGetByName(InterfaceSet *set, const char *name); + +/** + * Determine if two InterfaceSet instances are equal. + * + * Two InterfaceSet instances are equal if, and only if, the sets contain the + * same elements + * - order independent. + * Each element is compared via <code>interfaceEquals()</code> + * + * The following equivalence relations on non-null `InterfaceSet` instances are + * maintained: + * + * * It is reflexive: for any non-null reference value x, + * `InterfaceSet_Equals(x, x)` must return true. + * + * * It is symmetric: for any non-null reference values x and y, + * `InterfaceSet_Equals(x, y)` must return true if and only if + * `interfaceSetEquals(y, x)` returns true. + * + * * It is transitive: for any non-null reference values x, y, and z, if + * `interfaceSetEquals(x, y)` returns true and + * `interfaceSetEquals(y, z)` returns true, + * then `interfaceSetEquals(x, z)` must return true. + * + * * It is consistent: for any non-null reference values x and y, multiple + * invocations of `interfaceSetEquals(x, y)` consistently return true or + * consistently return false. + * + * * For any non-null reference value x, `interfaceSetEquals(x, NULL)` must + * return false. + * + * @param a A pointer to a `InterfaceSet` instance. + * @param b A pointer to a `InterfaceSet` instance. + * @return true if the two `InterfaceSet` instances are equal. + * + * Example: + * @code + * { + * InterfaceSet *a = interfaceSetCreate(); + * InterfaceSet *b = interfaceSetCreate(); + * + * if (interfaceSetEquals(a, b)) { + * // true + * } else { + * // false + * } + * } + * @endcode + */ +bool interfaceSetEquals(const InterfaceSet *a, const InterfaceSet *b); +#endif // InterfaceSet_h diff --git a/hicn-light/src/utils/punting.c b/hicn-light/src/utils/punting.c new file mode 100755 index 000000000..9352732fe --- /dev/null +++ b/hicn-light/src/utils/punting.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <src/config.h> +#include <stdio.h> + +#include <parc/assert/parc_Assert.h> + +#include <parc/algol/parc_Memory.h> +#include <src/utils/punting.h> + +struct punting { + char *symbolic; + Address *prefix; + uint32_t len; +}; + +Punting *puntingCreate(const char *listenerName, Address *prefix, + uint32_t len) { + parcAssertNotNull(listenerName, "Parameter listenerName must be non-null"); + parcAssertNotNull(prefix, "Parameter prefix must be non-null"); + + Punting *punting = parcMemory_AllocateAndClear(sizeof(Punting)); + if (punting) { + punting->symbolic = + parcMemory_StringDuplicate(listenerName, strlen(listenerName)); + punting->prefix = addressCopy(prefix); + punting->len = len; + } + + return punting; +} + +void puntingRelease(Punting **puntingPtr) { + parcAssertNotNull(puntingPtr, + "Parameter puntingPtr must be non-null double pointer"); + parcAssertNotNull(*puntingPtr, + "Parameter puntingPtr dereference to non-null pointer"); + + Punting *punting = *puntingPtr; + + if (punting->symbolic) { + parcMemory_Deallocate((void **)&punting->symbolic); + } + + if (punting->prefix) { + addressDestroy(&punting->prefix); + } + + parcMemory_Deallocate((void **)&punting); + *puntingPtr = NULL; +} + +bool puntingEquals(const Punting *a, const Punting *b) { + if ((a == NULL && b == NULL) || a == b) { + // both null or identically equal + return true; + } + + if (a == NULL || b == NULL) { + // only one is null + return false; + } + + if ((strcmp(a->symbolic, b->symbolic) == 0) && + (addressEquals(a->prefix, b->prefix)) && (a->len == b->len)) { + return true; + } + + return false; +} + +const char *puntingGetSymbolicName(const Punting *punting) { + parcAssertNotNull(punting, "Parameter listener must be non-null"); + return punting->symbolic; +} + +Address *puntingGetAddress(const Punting *punting) { + parcAssertNotNull(punting, "Parameter listener must be non-null"); + return punting->prefix; +} + +uint32_t puntingPrefixLen(const Punting *punting) { + parcAssertNotNull(punting, "Parameter listener must be non-null"); + return punting->len; +} diff --git a/hicn-light/src/utils/punting.h b/hicn-light/src/utils/punting.h new file mode 100755 index 000000000..03841c5ad --- /dev/null +++ b/hicn-light/src/utils/punting.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef punting_h +#define punting_h + +struct punting; +typedef struct punting Punting; + +#include <src/utils/address.h> + +/** + * Creates a Punting object + * + * The symbolic name represents this listener and may be used by other commands. + * It must be unique, otherwise the command will fail when sent to the + * forwarder. + * + * @param [in] symbolic name of the listener + * @param [in] prefix address to add to the punting rule + * @param [in] len prefix length + * + * @return non-null An Allocated object + * @return null An error + * + */ +Punting *puntingCreate(const char *symbolic, Address *prefix, uint32_t len); + +/** + * Releases a reference count to the object + * + * <#Paragraphs Of Explanation#> + * + * @param [in,out] etherConnPtr A pointer to an etherConn object, will be + * null'd. + * + */ +void puntingRelease(Punting **puntingPtr); + +/** + * Determine if two light Punting are equal. + * + */ + +bool puntingEquals(const Punting *a, const Punting *b); + +/** + * Returns the symbolic name + * + */ +const char *puntingGetSymbolicName(const Punting *punting); + +/** + * Returns the address (INET or INET6 ip address) + * + */ +Address *puntingGetAddress(const Punting *punting); + +uint32_t puntingPrefixLen(const Punting *punting); +#endif // punting_h diff --git a/hicn-light/src/utils/utils.c b/hicn-light/src/utils/utils.c new file mode 100755 index 000000000..a41478a4b --- /dev/null +++ b/hicn-light/src/utils/utils.c @@ -0,0 +1,258 @@ +// Utility function for commands + +#include <ctype.h> +#include <netinet/in.h> +#include <pthread.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Network.h> +#include <parc/assert/parc_Assert.h> +#include <src/utils/utils.h> + +// This is the unique sequence number used by all messages and its thread locks +static pthread_mutex_t nextSequenceNumberMutex = PTHREAD_MUTEX_INITIALIZER; +static uint32_t nextSequenceNumber = 1; + +uint32_t utils_GetNextSequenceNumber(void) { + uint32_t seqnum; + + int result = pthread_mutex_lock(&nextSequenceNumberMutex); + parcAssertTrue(result == 0, "Got error from pthread_mutex_lock: %d", result); + + seqnum = nextSequenceNumber++; + + result = pthread_mutex_unlock(&nextSequenceNumberMutex); + parcAssertTrue(result == 0, "Got error from pthread_mutex_unlock: %d", + result); + + return seqnum; +} + +/** + * Return true if string is purely an integer + */ +bool utils_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. + */ +bool utils_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; +} + +Address *utils_AddressFromInet(in_addr_t *addr4, in_port_t *port) { + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = *port; + addr.sin_addr.s_addr = *addr4; + + Address *result = addressCreateFromInet(&addr); + return result; +} + +Address *utils_AddressFromInet6(struct in6_addr *addr6, in_port_t *port) { + struct sockaddr_in6 addr; + memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_port = *port; + addr.sin6_addr = *addr6; + addr.sin6_scope_id = 0; + // Other 2 fields: scope_id and flowinfo, do not know what to put inside. + + Address *result = addressCreateFromInet6(&addr); + return result; +} + +struct iovec *utils_CreateAck(header_control_message *header, void *payload, + size_t payloadLen) { + struct iovec *response = + parcMemory_AllocateAndClear(sizeof(struct iovec) * 2); + + header->messageType = ACK_LIGHT; + + response[0].iov_base = header; + response[0].iov_len = sizeof(header_control_message); + response[1].iov_base = payload; + response[1].iov_len = payloadLen; + + return response; +} + +struct iovec *utils_CreateNack(header_control_message *header, void *payload, + size_t payloadLen) { + struct iovec *response = + parcMemory_AllocateAndClear(sizeof(struct iovec) * 2); + + header->messageType = NACK_LIGHT; + + response[0].iov_base = header; + response[0].iov_len = sizeof(header_control_message); + response[1].iov_base = payload; + response[1].iov_len = payloadLen; + + return response; +} + +char *utils_BuildStringFromInet(in_addr_t *addr4, in_port_t *port) { + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = *port; + addr.sin_addr.s_addr = *addr4; + + PARCBufferComposer *composer = parcBufferComposer_Create(); + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer( + parcNetwork_SockInet4Address_BuildString(&addr, composer)); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + parcBufferComposer_Release(&composer); + return result; +} + +char *utils_BuildStringFromInet6(struct in6_addr *addr6, in_port_t *port) { + struct sockaddr_in6 addr; + memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_port = *port; + addr.sin6_addr = *addr6; + + PARCBufferComposer *composer = parcBufferComposer_Create(); + PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer( + parcNetwork_SockInet6Address_BuildString(&addr, composer)); + char *result = parcBuffer_ToString(tempBuffer); + parcBuffer_Release(&tempBuffer); + parcBufferComposer_Release(&composer); + return result; +} + +char *utils_CommandAddressToString(address_type addressType, + union commandAddr *address, + in_port_t *port) { + char *result; + + switch (addressType) { + case ADDR_INET: { + result = utils_BuildStringFromInet(&address->ipv4, port); + break; + } + + case ADDR_INET6: { + result = utils_BuildStringFromInet6(&address->ipv6, port); + break; + } + + default: { + char *addrStr = (char *)parcMemory_Allocate(sizeof(char) * 32); + sprintf(addrStr, "Error: UNKNOWN address type = %d", addressType); + result = addrStr; + break; + } + } + return result; +} + +struct iovec *utils_SendRequest(ControlState *state, command_id command, + void *payload, size_t payloadLen) { + bool success = false; + + // get sequence number for the header + uint32_t currentSeqNum = utils_GetNextSequenceNumber(); + + // Allocate and fill the header + header_control_message *headerControlMessage = + parcMemory_AllocateAndClear(sizeof(header_control_message)); + headerControlMessage->messageType = REQUEST_LIGHT; + headerControlMessage->commandID = command; + headerControlMessage->seqNum = currentSeqNum; + if (payloadLen > 0) { + headerControlMessage->length = 1; + } + + struct iovec msg[2]; + msg[0].iov_base = headerControlMessage; + msg[0].iov_len = sizeof(header_control_message); + msg[1].iov_base = payload; + msg[1].iov_len = payloadLen; + + struct iovec *response = controlState_WriteRead(state, msg); + + header_control_message *receivedHeader = + (header_control_message *)response[0].iov_base; + if (receivedHeader->seqNum != currentSeqNum) { + printf("Seq number is NOT correct: expected %d got %d \n", currentSeqNum, + receivedHeader->seqNum); + // failure + } else { + if (receivedHeader->messageType == RESPONSE_LIGHT) { + return response; // command needs both payload and header + } else { + if (receivedHeader->messageType == ACK_LIGHT) { + success = true; + } else if (receivedHeader->messageType == NACK_LIGHT) { + success = true; + } else { + printf("Error: unrecognized message type"); // failure + } + } + } + + // deallocate when payload & header of the response are not needed + if (receivedHeader->length > 0) { + parcMemory_Deallocate(&response[1].iov_base); // free received payload + } + parcMemory_Deallocate(&response[0].iov_base); // free receivedHeader + + // return response + if (success) { + return response; + } else { + parcMemory_Deallocate(&response); // free iovec pointer + return NULL; // will generate a failure + } +} + +const char *utils_PrefixLenToString(address_type addressType, + union commandAddr *address, + uint8_t *prefixLen) { + char len[4]; // max size + 1 + sprintf(len, "%u", (unsigned)*prefixLen); + in_port_t port = htons(1234); // this is a random port number that is ignored + + char *prefix = utils_CommandAddressToString(addressType, address, &port); + char *prefixStr = malloc(strlen(prefix) + strlen(len) + 2); + strcpy(prefixStr, prefix); + strcat(prefixStr, "/"); + strcat(prefixStr, len); + + free(prefix); + + return prefixStr; +} diff --git a/hicn-light/src/utils/utils.h b/hicn-light/src/utils/utils.h new file mode 100755 index 000000000..1d2616941 --- /dev/null +++ b/hicn-light/src/utils/utils.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef utils_h +#define utils_h + +#include <src/config/controlState.h> +#include <src/utils/address.h> +#include <src/utils/commands.h> + +/** + * Return true if string is purely an integer + */ +bool utils_IsNumber(const char *string); + +/** + * A symbolic name must be at least 1 character and must begin with an alpha. + * The remainder must be an alphanum. + */ +bool utils_ValidateSymbolicName(const char *symbolic); + +/** + * Convert an internet address family (IPv4) to the address format used by the + * Fwd + */ +Address *utils_AddressFromInet(in_addr_t *addr4, in_port_t *port); + +/** + * Convert an internet address family (IPv6) to the address format used by the + * Fwd + */ +Address *utils_AddressFromInet6(struct in6_addr *addr6, in_port_t *port); + +/** + *Create an Ack message instance as a response of a control successfully + *completed. + */ +struct iovec *utils_CreateAck(header_control_message *header, void *payload, + size_t payloadLen); + +/** + *Create a Nack message instance as a response of a control unsuccessfully + *completed. + */ +struct iovec *utils_CreateNack(header_control_message *header, void *payload, + size_t payloadLen); + +/** + *Convert IPv4/IPv6 address from binary to text string. `uint8_t *ipAddress` has + *to be a `in_addr_t * or `a struct in6_addr *. + */ +char *utils_CommandAddressToString(address_type addressType, + union commandAddr *address, in_port_t *port); + +/** + *Given a command payload, it generates the header and send the request to the + *deamon. + */ +struct iovec *utils_SendRequest(ControlState *state, command_id command, + void *payload, size_t payloadLen); + +/** + *Convert a IPv4/IPv6 address plus Netmask len from binary to text string in the + *form [add]:[port]/[len]. + */ +const char *utils_PrefixLenToString(address_type addressType, + union commandAddr *address, + uint8_t *prefixLen); + +#endif
\ No newline at end of file |