--- netmgmt-1.0.4-orig/common/defines.h 2016-07-28 12:22:38.000000000 -0700 +++ netmgmt-1.0.4/common/defines.h 2018-11-06 15:39:37.455023627 -0800 @@ -20,3 +20,6 @@ goto error; \ } \ } while(0) + +#define IS_VALID_INTERFACE_NAME(_pstr) \ + ((_pstr) && (*_pstr) && (strnlen(_pstr, IFNAMSIZ) < IFNAMSIZ)) --- netmgmt-1.0.4-orig/common/includes.h 2016-07-28 12:22:38.000000000 -0700 +++ netmgmt-1.0.4/common/includes.h 2018-11-06 15:52:47.078930154 -0800 @@ -17,6 +17,8 @@ #include <stdint.h> #include <string.h> #include <errno.h> +#include <stdarg.h> +#include <regex.h> #include "defines.h" #include "prototypes.h" --- netmgmt-1.0.4-orig/common/memory.c 2016-07-28 12:22:38.000000000 -0700 +++ netmgmt-1.0.4/common/memory.c 2018-11-06 15:37:32.443078010 -0800 @@ -140,6 +140,64 @@ goto cleanup; } +uint32_t +netmgr_alloc_string_printf( + char** ppszDst, + const char* pszFmt, + ... + ) +{ + uint32_t err = 0; + size_t nSize = 0; + char* pszDst = NULL; + char chDstTest = '\0'; + va_list argList; + + if (!ppszDst || !pszFmt) + { + err = EINVAL; + bail_on_error(err); + } + + //Find size + va_start(argList, pszFmt); + nSize = vsnprintf(&chDstTest, 1, pszFmt, argList); + va_end(argList); + + if (nSize <= 0) + { + err = errno; + bail_on_error(err); + } + nSize = nSize + 1; + + err = netmgr_alloc(nSize, (void**)&pszDst); + bail_on_error(err); + + va_start(argList, pszFmt); + nSize = vsnprintf(pszDst, nSize, pszFmt, argList); + va_end(argList); + + if (nSize <= 0) + { + err = errno; + bail_on_error(err); + } + *ppszDst = pszDst; + +cleanup: + return err; + +error: + if (ppszDst) + { + *ppszDst = NULL; + } + netmgr_free(pszDst); + goto cleanup; +} + + void netmgr_free( void* pMemory --- netmgmt-1.0.4-orig/common/prototypes.h 2016-07-28 12:22:38.000000000 -0700 +++ netmgmt-1.0.4/common/prototypes.h 2018-11-06 16:18:36.033573046 -0800 @@ -14,6 +14,8 @@ // memory.c +#define IS_NULL_OR_EMPTY(_pstr) (!(_pstr) || !(*(_pstr))) + uint32_t netmgr_alloc( size_t size, @@ -38,3 +40,9 @@ void* pMemory ); +uint32_t +netmgr_alloc_string_printf( + char** ppszDst, + const char* pszFmt, + ... + ); --- netmgmt-1.0.4-orig/include/public/netmgr.h 2016-07-28 12:22:38.000000000 -0700 +++ netmgmt-1.0.4/include/public/netmgr.h 2018-11-06 15:41:59.227552112 -0800 @@ -15,6 +15,27 @@ #ifndef __NETMGR_H__ #define __NETMGR_H__ +/* + * Error codes + */ +#define NM_BASE_ERROR 4096U +#define NM_ERR_INVALID_PARAMETER (NM_BASE_ERROR + 1) +#define NM_ERR_NOT_SUPPORTED (NM_BASE_ERROR + 2) +#define NM_ERR_OUT_OF_MEMORY (NM_BASE_ERROR + 3) +#define NM_ERR_VALUE_NOT_FOUND (NM_BASE_ERROR + 4) +#define NM_ERR_VALUE_EXISTS (NM_BASE_ERROR + 5) +#define NM_ERR_INVALID_INTERFACE (NM_BASE_ERROR + 6) +#define NM_ERR_INVALID_ADDRESS (NM_BASE_ERROR + 7) +#define NM_ERR_INVALID_MODE (NM_BASE_ERROR + 8) +#define NM_ERR_BAD_CONFIG_FILE (NM_BASE_ERROR + 9) +#define NM_ERR_WRITE_FAILED (NM_BASE_ERROR + 10) +#define NM_ERR_TIME_OUT (NM_BASE_ERROR + 11) +#define NM_ERR_DHCP_TIME_OUT (NM_BASE_ERROR + 12) +#define NM_MAX_ERROR (NM_BASE_ERROR + 100) + +/* + * Interface configuration structs + */ typedef struct _NETMGR_INTERFACE { char* pszName; --- netmgmt-1.0.4-orig/src/defines.h 2016-07-28 12:22:38.000000000 -0700 +++ netmgmt-1.0.4/src/defines.h 2018-11-06 15:56:52.260219253 -0800 @@ -26,13 +26,14 @@ #define SECTION_RESOLVE "Resolve" #define SECTION_NETWORK "Network" #define SECTION_DHCP "DHCP" +#define SECTION_MATCH "Match" #define KEY_IAID "IAID" #define KEY_DUID_TYPE "DUIDType" #define KEY_DUID_RAWDATA "DUIDRawData" #define KEY_DNS "DNS" #define KEY_USE_DNS "UseDNS" - +#define KEY_NAME "Name" #define bail_on_error(errcode) \ do { \ --- netmgmt-1.0.4-orig/src/includes.h 2016-07-28 12:22:38.000000000 -0700 +++ netmgmt-1.0.4/src/includes.h 2018-11-06 15:53:32.298923020 -0800 @@ -26,6 +26,7 @@ #include <iniparser.h> #include "../common/prototypes.h" +#include "../common/includes.h" #include "defines.h" #include "structs.h" #include "prototypes.h" --- netmgmt-1.0.4-orig/src/netmgr.c 2016-07-28 12:22:38.000000000 -0700 +++ netmgmt-1.0.4/src/netmgr.c 2018-11-06 16:28:46.766109486 -0800 @@ -14,11 +14,308 @@ #include "includes.h" +static uint32_t +nm_regex_match_ifname( + const char *pszIfName, + const char *pszMatchName +) +{ + uint32_t err = 0; + regex_t rx; + regmatch_t rm; + size_t patternLen, ifNameLen, n; + char *q, *pszPattern = NULL; + const char *p; + + if (!IS_VALID_INTERFACE_NAME(pszIfName) || + !IS_VALID_INTERFACE_NAME(pszMatchName)) + { + err = NM_ERR_INVALID_INTERFACE; + bail_on_error(err); + } + + ifNameLen = strlen(pszIfName); + patternLen = strlen(pszMatchName); + for (p = strchr(pszMatchName, '*'), n = 0; p; p = strchr(++p, '*'), n++); + + err = netmgr_alloc(patternLen + n + 1, (void **)&pszPattern); + bail_on_error(err); + + for (p = pszMatchName, q = pszPattern; *p; p++, q++) + { + if (*p == '*') + { + *q++ = '.'; + } + *q = *p; + } + + err = regcomp(&rx, pszPattern, 0); + bail_on_error(err); + + err = regexec(&rx, pszIfName, 1, &rm, 0); + bail_on_error(err); + if ((rm.rm_eo - rm.rm_so) < ifNameLen) + { + err = NM_ERR_VALUE_NOT_FOUND; + bail_on_error(err); + } +cleanup: + netmgr_free(pszPattern); + regfree(&rx); + return err; +error: + goto cleanup; +} + +static uint32_t +nm_alloc_conf_filename( + char **ppszFilename, + const char *pszPath, + const char *pszFname +) +{ + uint32_t err = 0; + size_t len = 0; + char *pszFilename = NULL; + + if (!ppszFilename || !pszPath || !pszFname) + { + err = NM_ERR_INVALID_PARAMETER; + bail_on_error(err); + } + + len = strlen(pszPath) + strlen (pszFname) + 1; + err = netmgr_alloc(len, (void **)&pszFilename); + bail_on_error(err); + + sprintf(pszFilename, "%s%s", pszPath, pszFname); + + *ppszFilename = pszFilename; + +cleanup: + return err; +error: + netmgr_free(pszFilename); + if (ppszFilename != NULL) + { + *ppszFilename = NULL; + } + goto cleanup; +} + +static uint32_t +nm_get_network_conf_filename_match( + const char *pszIfName, + char **ppszFilename, + size_t *pMatchLen +) +{ + uint32_t err = 0; + size_t matchLen = 0; + struct dirent *hFile; + DIR *dirFile = NULL; + char *p, *pszFileName = NULL, *pszMatchName = NULL, *pszCfgFileName = NULL; + + dirFile = opendir(SYSTEMD_NET_PATH); + if (dirFile == NULL) + { + err = errno; + bail_on_error(err); + } + + // TODO: Looks fine but closely vet this logic with systemd + errno = 0; + while ((hFile = readdir(dirFile)) != NULL) + { + if (!strcmp(hFile->d_name, ".")) continue; + if (!strcmp(hFile->d_name, "..")) continue; + if (hFile->d_name[0] == '.') continue; + if ((((p = strstr(hFile->d_name, ".network")) != NULL) && + (strlen(p) == strlen(".network"))) || + (((p = strstr(hFile->d_name, ".network.manual")) != NULL) && + (strlen(p) == strlen(".network.manual")))) + { + err = nm_alloc_conf_filename(&pszFileName, + SYSTEMD_NET_PATH, + hFile->d_name); + bail_on_error(err); + err = nm_get_key_value(pszFileName, + SECTION_MATCH, + KEY_NAME, + &pszMatchName); + if ((err == NM_ERR_VALUE_NOT_FOUND) || + (err == NM_ERR_BAD_CONFIG_FILE)) + { + /* Ignore cfg file with invalid/missing Match section */ + err = 0; + } + bail_on_error(err); + + if (pszMatchName && !nm_regex_match_ifname(pszIfName, pszMatchName)) + { + if (pszCfgFileName == NULL) + { + pszCfgFileName = pszFileName; + matchLen = strlen(pszMatchName); + pszFileName = NULL; + continue; + } + if (strcmp(pszCfgFileName, pszFileName) > 0) + { + netmgr_free(pszCfgFileName); + pszCfgFileName = pszFileName; + matchLen = strlen(pszMatchName); + pszFileName = NULL; + } + } + + netmgr_free(pszMatchName); + pszMatchName = NULL; + netmgr_free(pszFileName); + pszFileName = NULL; + } + } + + if (!pszCfgFileName) + { + err = NM_ERR_VALUE_NOT_FOUND; + bail_on_error(err); + } + + *ppszFilename = pszCfgFileName; + if (pMatchLen) + { + *pMatchLen = matchLen; + } + +cleanup: + if (dirFile != NULL) + { + closedir(dirFile); + } + netmgr_free(pszMatchName); + netmgr_free(pszFileName); + return err; + +error: + if (ppszFilename) + { + *ppszFilename = NULL; + } + if (pMatchLen) + { + *pMatchLen = 0; + } + netmgr_free(pszCfgFileName); + goto cleanup; +} + +static uint32_t +nm_get_network_conf_filename_for_update( + const char *pszIfName, + char **ppszFilename +) +{ + uint32_t err = 0; + size_t ifNameLen, matchLen = 0; + char fName[IFNAMSIZ+strlen(SYSTEMD_NET_PATH)+strlen("00-.network")+1]; + char *pszFilename = NULL, *pszNewFilename = NULL, *pszCmd = NULL; + + err = nm_get_network_conf_filename_match(pszIfName, &pszFilename, &matchLen); + if (err == NM_ERR_VALUE_NOT_FOUND) + { + err = 0; + } + bail_on_error(err); + + ifNameLen = strlen(pszIfName); + if ((pszFilename == NULL) || (matchLen < ifNameLen)) + { + sprintf(fName, "%s00-%s.network", SYSTEMD_NET_PATH, pszIfName); + if (access(fName, F_OK) == 0) + { + /* Designated conf file for this interface exists with bad match */ + err = NM_ERR_BAD_CONFIG_FILE; + bail_on_error(err); + } + + err = netmgr_alloc_string(fName, &pszNewFilename); + bail_on_error(err); + + /* Create dedicated conf for interface based on best match conf file */ + if (pszFilename != NULL) + { + err = netmgr_alloc_string_printf(&pszCmd, + "/usr/bin/cp -f -p %s %s", + pszFilename, + pszNewFilename); + bail_on_error(err); + + err = nm_run_command(pszCmd); + bail_on_error(err); + } + + err = nm_set_key_value(pszNewFilename, + SECTION_MATCH, + KEY_NAME, + pszIfName, + F_CREATE_CFG_FILE); + bail_on_error(err); + } + else + { + err = netmgr_alloc_string(pszFilename, &pszNewFilename); + bail_on_error(err); + } + + *ppszFilename = pszNewFilename; + +cleanup: + netmgr_free(pszFilename); + netmgr_free(pszCmd); + return err; + +error: + if (ppszFilename) + { + *ppszFilename = NULL; + } + netmgr_free(pszNewFilename); + goto cleanup; +} + +static uint32_t +nm_get_network_conf_filename( + const char *pszIfName, + char **ppszFilename) +{ + return nm_get_network_conf_filename_match(pszIfName, ppszFilename, NULL); +} + +/* + * Service management APIs + */ +uint32_t +nm_restart_network_service() +{ + uint32_t err = 0; + const char command[] = "systemctl restart systemd-networkd"; + + err = nm_run_command(command); + bail_on_error(err); + +clean: + return err; +error: + goto clean; +} + uint32_t enum_interfaces( int nFamily, PNETMGR_INTERFACE* ppInterfaces - ) +) { uint32_t err = 0; int fd = 0; @@ -836,30 +1133,31 @@ ) { uint32_t err = 0; - char cfgFileName[MAX_LINE]; + char *pszCfgFileName = NULL; const char *duidType; uint16_t n1, n2; char szDuid[MAX_LINE]; - if (pszInterfaceName != NULL) + if (!pszInterfaceName) { - /* TODO: Add support */ - err = ENOTSUP; - bail_on_error(err); + err = EINVAL; } else { - sprintf(cfgFileName, "%snetworkd.conf", SYSTEMD_PATH); + err = nm_get_network_conf_filename_for_update(pszInterfaceName, + &pszCfgFileName); } + bail_on_error(err); if ((pszDuid == NULL) || (strlen(pszDuid) == 0)) { - err = set_key_value(cfgFileName, SECTION_DHCP, KEY_DUID_TYPE, NULL, + err = set_key_value(pszCfgFileName, SECTION_DHCP, KEY_DUID_TYPE, NULL, F_CREATE_CFG_FILE); bail_on_error(err); - err = set_key_value(cfgFileName, SECTION_DHCP, KEY_DUID_RAWDATA, NULL, + err = set_key_value(pszCfgFileName, SECTION_DHCP, KEY_DUID_RAWDATA, NULL, F_CREATE_CFG_FILE); + bail_on_error(err); } else { @@ -877,16 +1175,19 @@ } /* TODO: Validate DUID length and DUID bytes */ - err = set_key_value(cfgFileName, SECTION_DHCP, KEY_DUID_TYPE, duidType, + err = set_key_value(pszCfgFileName, SECTION_DHCP, KEY_DUID_TYPE, duidType, F_CREATE_CFG_FILE); bail_on_error(err); - err = set_key_value(cfgFileName, SECTION_DHCP, KEY_DUID_RAWDATA, szDuid, + err = set_key_value(pszCfgFileName, SECTION_DHCP, KEY_DUID_RAWDATA, szDuid, F_CREATE_CFG_FILE); + bail_on_error(err); } + err = nm_restart_network_service(); bail_on_error(err); error: + netmgr_free(pszCfgFileName); return err; } @@ -897,7 +1198,7 @@ ) { uint32_t err = 0; - char cfgFileName[MAX_LINE]; + char *pszCfgFileName = NULL; uint16_t duidType; char *pszDuidType = NULL; char *pszDuid = NULL; @@ -908,18 +1209,17 @@ bail_on_error(err); } - if (pszInterfaceName != NULL) + if (pszInterfaceName == NULL) { - /* TODO: Add support */ - err = ENOTSUP; - bail_on_error(err); + err = EINVAL; } else { - sprintf(cfgFileName, "%snetworkd.conf", SYSTEMD_PATH); + err = nm_get_network_conf_filename(pszInterfaceName, &pszCfgFileName); } + bail_on_error(err); - err = get_key_value(cfgFileName, SECTION_DHCP, KEY_DUID_TYPE, &pszDuidType); + err = get_key_value(pszCfgFileName, SECTION_DHCP, KEY_DUID_TYPE, &pszDuidType); bail_on_error(err); duidType = duid_type_from_strtype(pszDuidType); @@ -929,7 +1229,7 @@ bail_on_error(err); } - err = get_key_value(cfgFileName, SECTION_DHCP, KEY_DUID_RAWDATA, &pszDuid); + err = get_key_value(pszCfgFileName, SECTION_DHCP, KEY_DUID_RAWDATA, &pszDuid); bail_on_error(err); err = netmgr_alloc((strlen(pszDuid) + 8), (void *)ppszDuid); @@ -945,6 +1245,7 @@ { netmgr_free(pszDuidType); } + netmgr_free(pszCfgFileName); return err; error: --- netmgmt-1.0.4-orig/src/utils.c 2016-07-28 12:22:38.000000000 -0700 +++ netmgmt-1.0.4/src/utils.c 2018-11-06 16:16:56.081230589 -0800 @@ -169,3 +169,200 @@ return err; } +uint32_t +nm_run_command( + const char *pszCommand +) +{ + uint32_t err = 0; + FILE *pipe_fp = NULL; + + if (pszCommand == NULL) + { + err = NM_ERR_INVALID_PARAMETER; + bail_on_error(err); + } + + pipe_fp = popen(pszCommand, "r"); + if (pipe_fp == NULL) + { + err = errno; + bail_on_error(err); + } + +clean: + if (pipe_fp != NULL) + { + if (pclose(pipe_fp) == -1) + { + err = errno; + } + } + return err; +error: + goto clean; +} + +uint32_t +nm_get_key_value( + const char *pszConfigFileName, + const char *pszSection, + const char *pszKey, + char **ppszValue +) +{ + uint32_t err = 0, dwNumSections = 0; + PCONFIG_INI pConfig = NULL; + PSECTION_INI *ppSections = NULL, pSection = NULL; + PKEYVALUE_INI pKeyValue = NULL; + *ppszValue = NULL; + + if (IS_NULL_OR_EMPTY(pszConfigFileName) || IS_NULL_OR_EMPTY(pszSection) || + IS_NULL_OR_EMPTY(pszKey)) + { + err = NM_ERR_INVALID_PARAMETER; + bail_on_error(err); + } + + err = ini_cfg_read(pszConfigFileName, &pConfig); + bail_on_error(err); + + err = ini_cfg_find_sections(pConfig, pszSection, &ppSections, &dwNumSections); + bail_on_error(err); + + if (dwNumSections > 1) + { + /* TODO: Log error */ + err = NM_ERR_BAD_CONFIG_FILE; + bail_on_error(err); + } + else if (dwNumSections == 0) + { + err = NM_ERR_VALUE_NOT_FOUND; + bail_on_error(err); + } + + pSection = ppSections[0]; + + pKeyValue = ini_cfg_find_key(pSection, pszKey); + if (pKeyValue == NULL) + { + err = NM_ERR_VALUE_NOT_FOUND; + bail_on_error(err); + } + /* malloc return memory - caller to free */ + err = netmgr_alloc_string(pKeyValue->pszValue, ppszValue); + +error: + if (ppSections != NULL) + { + ini_cfg_free_sections(ppSections, dwNumSections); + } + if (pConfig != NULL) + { + ini_cfg_free_config(pConfig); + } + return err; +} + +uint32_t +nm_set_key_value( + const char *pszConfigFileName, + const char *pszSection, + const char *pszKey, + const char *pszValue, + uint32_t flags +) +{ + uint32_t err = 0, dwNumSections = 0; + PCONFIG_INI pConfig = NULL; + PSECTION_INI *ppSections = NULL, pSection = NULL; + PKEYVALUE_INI pKeyValue = NULL; + FILE *fp; + + if (IS_NULL_OR_EMPTY(pszConfigFileName) || IS_NULL_OR_EMPTY(pszSection) || + IS_NULL_OR_EMPTY(pszKey)) + { + err = NM_ERR_INVALID_PARAMETER; + bail_on_error(err); + } + + if (TEST_FLAG(flags, F_CREATE_CFG_FILE)) + { + /* 'touch' the file if it does't exist */ + if ((fp = fopen(pszConfigFileName, "a+")) != NULL) + { + fclose(fp); + } + else + { + err = errno; + bail_on_error(err); + } + } + + err = ini_cfg_read(pszConfigFileName, &pConfig); + bail_on_error(err); + + err = ini_cfg_find_sections(pConfig, pszSection, &ppSections, &dwNumSections); + bail_on_error(err); + + if (dwNumSections > 1) + { + err = NM_ERR_BAD_CONFIG_FILE; + bail_on_error(err); + } + else if (dwNumSections == 0) + { + if (pszValue != NULL) + { + err = ini_cfg_add_section(pConfig, pszSection, &pSection); + bail_on_error(err); + } + else + { + /* Bug out with success - nothing to set, no section found. */ + err = 0; + goto error; + } + } + else + { + pSection = ppSections[0]; + } + + pKeyValue = ini_cfg_find_key(pSection, pszKey); + if (pKeyValue == NULL) + { + if (pszValue != NULL) + { + err = ini_cfg_add_key(pSection, pszKey, pszValue); + } + } + else + { + if (pszValue != NULL) + { + err = ini_cfg_set_value(pSection, pszKey, pszValue); + } + else + { + err = ini_cfg_delete_key(pSection, pszKey); + } + } + bail_on_error(err); + + err = ini_cfg_save(pszConfigFileName, pConfig); + bail_on_error(err); + +error: + if (ppSections != NULL) + { + ini_cfg_free_sections(ppSections, dwNumSections); + } + if (pConfig != NULL) + { + ini_cfg_free_config(pConfig); + } + return err; +} --- netmgmt-1.0.4-orig/src/utils.h 2016-07-28 12:22:38.000000000 -0700 +++ netmgmt-1.0.4/src/utils.h 2018-11-06 16:13:04.745741356 -0800 @@ -34,4 +34,26 @@ char **ppszValue ); +uint32_t +nm_get_key_value( + const char *pszConfigFileName, + const char *pszSection, + const char *pszKey, + char **ppszValue +); + +uint32_t +nm_set_key_value( + const char *pszConfigFileName, + const char *pszSection, + const char *pszKey, + const char *pszValue, + uint32_t flags +); + +uint32_t +nm_run_command( + const char *pszCommand +); + #endif /* __UTILS_H__ */ --- netmgmt-1.0.4-orig/tools/netmgr/main.c 2016-07-28 12:22:38.000000000 -0700 +++ netmgmt-1.0.4/tools/netmgr/main.c 2018-11-06 16:40:29.936816688 -0800 @@ -362,13 +362,13 @@ bail_on_error(err); } - if(pCmdArgs->nCmdCount < 2) + if(pCmdArgs->nCmdCount < 3) { err = EDOM; bail_on_error(err); } - err = set_duid(NULL, pCmdArgs->ppszCmds[1]); + err = set_duid(pCmdArgs->ppszCmds[1], pCmdArgs->ppszCmds[2]); bail_on_error(err); cleanup: @@ -379,7 +379,7 @@ { fprintf( stderr, - "Usage: set_duid <duid>\n"); + "Usage: set_duid <ifname> <duid>\n"); } goto cleanup; } @@ -398,13 +398,13 @@ bail_on_error(err); } - if(pCmdArgs->nCmdCount < 1) + if(pCmdArgs->nCmdCount < 2) { err = EDOM; bail_on_error(err); } - err = get_duid(NULL, &duid); + err = get_duid(pCmdArgs->ppszCmds[1], &duid); bail_on_error(err); fprintf(stdout, "DUID=%s\n", duid); @@ -421,7 +421,7 @@ { fprintf( stderr, - "Usage: get_duid\n"); + "Usage: get_duid <ifname> \n"); } goto cleanup; }