From be61b1e53e2baf9c1e21ca7169ba418904225bc4 Mon Sep 17 00:00:00 2001 From: uglymotha Date: Sat, 20 Jul 2024 12:52:37 +0000 Subject: [PATCH] Enable CLI for Monitor --- src/cli.c | 77 +++++++++++++++++++++++++++--------------- src/config.c | 86 +++++++++++++++-------------------------------- src/ifvc.c | 50 ++++++++++++--------------- src/igmp.c | 4 +++ src/igmpv3proxy.c | 83 ++++++++++++++++++++++----------------------- src/igmpv3proxy.h | 9 ++--- src/kern.c | 48 +++++++++++++++++--------- src/lib.c | 6 ++-- src/timers.c | 10 ++++-- 9 files changed, 194 insertions(+), 179 deletions(-) diff --git a/src/cli.c b/src/cli.c index 971a423..5569b2f 100644 --- a/src/cli.c +++ b/src/cli.c @@ -43,16 +43,15 @@ static void cliSignalHandler(int sig); // Daemon CLI socket address. -extern volatile sig_atomic_t sighandled; // From igmpv3proxy.c signal handler. +static int cli_fd = -1; static struct sockaddr_un cli_sa; +extern volatile sig_atomic_t sighandled; // From igmpv3proxy.c signal handler. /** * Opens and binds a socket for cli connections. */ int openCliFd(void) { struct stat st; - int cli_fd; - memset(&cli_sa, 0, sizeof(struct sockaddr_un)); cli_sa.sun_family = AF_UNIX; @@ -86,66 +85,90 @@ int openCliFd(void) { /** * Close and unlink CLI socket. */ -int closeCliFd(int fd) { - if (!RESTART) { - shutdown(fd, SHUT_RDWR); - close(fd); - unlink(cli_sa.sun_path); - return -1; +int closeCliFd(void) { + if (!RESTART && cli_fd >= 0) { + if (!STARTUP) + shutdown(cli_fd, SHUT_RDWR); + if (close(cli_fd) < 0) + LOG(LOG_WARNING, errno, "closeCliFd: CLOSE %s", cli_sa.sun_path); + else { + LOG(LOG_INFO, 0, "closeCliFd: Closed CLI socket %s.", cli_sa.sun_path); + cli_fd = -1; + } + if (!STARTUP) + unlink(cli_sa.sun_path); } - return fd; + return cli_fd; } /** * Processes an incoming cli connection. Requires the fd of the cli socket. */ -void acceptCli(int fd) +void acceptCli(void) { - int pid = 0, cli_fd = -1, len = 0, s = sizeof(struct sockaddr); + int pid = 0, fd = -1, len = 0, s = sizeof(struct sockaddr); uint32_t addr = (uint32_t)-1, mask = (uint32_t)-1; char buf[CLI_CMD_BUF] = {0}, msg[CLI_CMD_BUF]; struct sockaddr cli_sa; struct IfDesc *IfDp = NULL; // Receive and answer the cli request. - if ((cli_fd = accept(fd, &cli_sa, (socklen_t *)&s)) < 0) { + if ((fd = accept(cli_fd, &cli_sa, (socklen_t *)&s)) < 0) { LOG(LOG_WARNING, errno, "acceptCli: Failed accept()"); return; } else { - if ((len = recv(cli_fd, &buf, CLI_CMD_BUF, MSG_DONTWAIT)) <= 0 || len > CLI_CMD_BUF || + if ((len = recv(fd, &buf, CLI_CMD_BUF, MSG_DONTWAIT)) <= 0 || len > CLI_CMD_BUF || (buf[0] == 'r' && len > 2 && (!parseSubnetAddress(&buf[buf[1] == 'h' ? 3 : 2], &addr, &mask) || !IN_MULTICAST(ntohl(addr))))) { LOG(LOG_DEBUG, 0, "acceptCli: Invalid command received."); } else if (buf[0] == 'c' || buf[0] == 'b') { sighandled |= buf[0] == 'c' ? GOT_SIGUSR1 : GOT_SIGUSR2; - buf[0] == 'c' ? send(cli_fd, "Reloading Configuration.\n", 26, MSG_DONTWAIT) - : send(cli_fd, "Rebuilding Interfaces.\n", 24, MSG_DONTWAIT); + buf[0] == 'c' ? send(fd, "Reloading Configuration.\n", 26, MSG_DONTWAIT) + : send(fd, "Rebuilding Interfaces.\n", 24, MSG_DONTWAIT); +#ifdef __linux__ + IF_FOR_IF(mrt_tbl < 0, int i = 0; i < chld.nr; i++, chld.c[i].pid > 0) + kill(chld.c[i].pid, buf[0] == 'c' ? SIGUSR1 : SIGUSR2); +#endif } else if ((pid = fork()) != 0) pid < 0 ? LOG(LOG_WARNING, eNOFORK, "Cannot fork().") : LOG(LOG_DEBUG, 0, "acceptCli: Forked PID: %d", pid); if (pid != 0 || buf[0] == 'c' || buf[0] == 'b') { - close(cli_fd); + close(fd); return; } } if (buf[0] == 'r') { - logRouteTable("", buf[1] == 'h' ? 0 : 1, cli_fd, addr, mask); + logRouteTable("", buf[1] == 'h' ? 0 : 1, fd, addr, mask); } else if ((buf[0] == 'i' || buf[0] == 'f') && len > 2 && ! (IfDp = getIf(0, &buf[buf[1] == 'h' ? 3 : 2], 2))) { sprintf(msg, "Interface %s Not Found\n", &buf[buf[1] == 'h' ? 3 : 2]); - send(cli_fd, msg, strlen(msg), MSG_DONTWAIT); + send(fd, msg, strlen(msg), MSG_DONTWAIT); } else if (buf[0] == 'i') { - getIfStats(IfDp, buf[1] == 'h' ? 0 : 1, cli_fd); + getIfStats(IfDp, buf[1] == 'h' ? 0 : 1, fd); } else if (buf[0] == 'f') { - getIfFilters(IfDp, len > 1 && buf[1] == 'h' ? 0 : 1, cli_fd); + getIfFilters(IfDp, len > 1 && buf[1] == 'h' ? 0 : 1, fd); } else if (buf[0] == 't') { - debugQueue("", len > 1 && buf[1] == 'h' ? 0 : 1, cli_fd); + debugQueue("", len > 1 && buf[1] == 'h' ? 0 : 1, fd); } else if (buf[0] == 'm') { - getMemStats(buf[1] == 'h' ? 0 : 1, cli_fd); + getMemStats(buf[1] == 'h' ? 0 : 1, fd); +#ifdef __linux__ + } else if (buf[0] == 'p' && mrt_tbl < 0) { + send(fd, "Currently Running Processes:\n", 29, MSG_DONTWAIT); + for (int i = 0; i < chld.nr; i++) { + if (chld.c[i].pid > 0) + sprintf(msg, "Table: %d - PID: %d\n", chld.c[i].tbl, chld.c[i].pid); + else + sprintf(msg, "Table: %d - %s\n", chld.c[i].tbl, exitmsg[chld.c[i].st]); + send(fd, msg, strlen(msg), MSG_DONTWAIT); + } + } else if (buf[0] == 'p' && mrt_tbl >= 0) { + sprintf(msg, "Table: %d - PID: %d\n", mrt_tbl, getppid()); + send(fd, msg, strlen(msg), MSG_DONTWAIT); +#endif } else - send(cli_fd, "GO AWAY\n", 9, MSG_DONTWAIT); + send(fd, "GO AWAY\n", 9, MSG_DONTWAIT); // Close connection. - close(cli_fd); + close(fd); LOG(LOG_DEBUG, 0, "acceptCli: Finished command %s.", buf); exit(0); } @@ -181,13 +204,13 @@ void cliCmd(char *cmd, int tbl) { while (path) { sprintf(tpath, "%s/%s/root", path, fileName); if (lstat(tpath, &st) == 0 && (S_ISLNK(st.st_mode) || S_ISDIR(st.st_mode))) { - if (tbl) + if (tbl != 0) sprintf(srv_sa.sun_path, "/%s/%s/root/cli-%d.sock", path, fileName, tbl); else sprintf(srv_sa.sun_path, "/%s/%s/root/cli.sock", path, fileName); break; } - if (tbl) + if (tbl != 0) sprintf(tpath, "%s/%s/cli-%d.sock", path, fileName, tbl); else sprintf(tpath, "%s/%s/cli.sock", path, fileName); diff --git a/src/config.c b/src/config.c index 9863deb..f72355b 100644 --- a/src/config.c +++ b/src/config.c @@ -49,8 +49,8 @@ static inline void parseFilters(char *in, char *token, struct filters ***filP, static inline bool parsePhyintToken(char *token); // All valid configuration options. Prepend whitespace to allow for strstr() exact token matching. -static const char *options = " include phyint user group chroot defaultquickleave quickleave maxorigins hashtablesize routetables defaultdown defaultup defaultupdown defaultthreshold defaultratelimit defaultquerierver defaultquerierip defaultrobustness defaultqueryinterval defaultqueryrepsonseinterval defaultlastmemberinterval defaultlastmembercount bwcontrol rescanvif rescanconf loglevel logfile defaultproxylocalmc defaultnoquerierelection proxylocalmc noproxylocalmc upstream downstream disabled ratelimit threshold querierver querierip robustness queryinterval queryrepsonseinterval lastmemberinterval lastmembercount defaultnocksumverify nocksumverify cksumverify noquerierelection querierelection nocksumverify cksumverify noquerierelection querierelection defaultfilterany nodefaultfilter filter altnet whitelist reqqueuesize kbufsize pbufsize maxtbl defaulttable disableipmrules"; -static const char *phyintopt = " table updownstream upstream downstream disabled proxylocalmc noproxylocalmc quickleave noquickleave ratelimit threshold nocksumverify cksumverify noquerierelection querierelection querierip querierver robustnessvalue queryinterval queryrepsonseinterval lastmemberinterval lastmembercount defaultfilter filter altnet whitelist"; +static const char *options = " include phyint user group chroot defaultquickleave quickleave maxorigins hashtablesize routetables defaultdown defaultup defaultupdown defaultthreshold defaultratelimit defaultquerierver defaultquerierip defaultrobustness defaultqueryinterval defaultqueryrepsonseinterval defaultlastmemberinterval defaultlastmembercount bwcontrol rescanvif rescanconf loglevel logfile defaultproxylocalmc defaultnoquerierelection proxylocalmc noproxylocalmc upstream downstream disabled ratelimit threshold querierver querierip robustness queryinterval queryrepsonseinterval lastmemberinterval lastmembercount defaultnocksumverify nocksumverify cksumverify noquerierelection querierelection nocksumverify cksumverify noquerierelection querierelection defaultfilterany nodefaultfilter filter altnet whitelist reqqueuesize kbufsize pbufsize maxtbl defaulttable defaultdisableipmrules"; +static const char *phyintopt = " table updownstream upstream downstream disabled proxylocalmc noproxylocalmc quickleave noquickleave ratelimit threshold nocksumverify cksumverify noquerierelection querierelection querierip querierver robustnessvalue queryinterval queryrepsonseinterval lastmemberinterval lastmembercount defaultfilter filter altnet whitelist disableipmrules"; // Daemon Configuration. static struct Config conf, oldconf; @@ -62,11 +62,6 @@ uint32_t uVifs; // Keeps timer ids for configurable timed functions. static struct timers timers = { 0, 0, 0 }; -// Keeps the tables seen in the config file. -#ifdef __linux__ -static int *tbl = NULL, ntbl = 0, tblsz = 32 * sizeof(int); -#endif - // Macro to get a token which should be integer. #define INTTOKEN ((nextToken(token)) && ((intToken = atoll(token + 1)) || !intToken)) @@ -404,25 +399,21 @@ static inline bool parsePhyintToken(char *token) { else { tmpPtr->tbl = intToken; LOG(LOG_INFO, 0, "Config (%s): Assigning to table %d.", tmpPtr->name, intToken); - if (intToken > 0 && mrt_tbl < 0) { - if (ntbl % 32 == 0 && ! _recalloc(tbl, var, ((ntbl / 32) + 1) * tblsz, (ntbl / 32) * tblsz)) - LOG(LOG_ERR, eNOMEM, "Config (%s): Out of Memory", tmpPtr->name); // Freed by loadConfig() - if (!ntbl) { - tbl[ntbl++] = CONF->defaultTable; - igmpProxyFork(0); - } - if (mrt_tbl < 0) { // Check again becasue of fork(). - for (i = 0; i < ntbl && tbl[i] != intToken; i++); - if (i >= ntbl) - tbl[ntbl++] = intToken; - igmpProxyFork(intToken); - } - } + if (intToken > 0 && mrt_tbl < 0) + igmpProxyFork(0); + if (mrt_tbl < 0) // Check again becasue of fork(). + igmpProxyFork(intToken); } #else LOG(LOG_NOTICE, 0, "Config (%s): Table id is only valid on linux.", tmpPtr->name); #endif - + } else if (strcmp(" disableipmrules", token) == 0) { +#ifdef __linux__ + LOG(LOG_NOTICE, 0, "Config (%s): Will disable ip mrules.", tmpPtr->name); + tmpPtr->disableIpMrules = true; +#else + LOG(LOG_NOTICE, 0, "disableipmrules is ony valid for linux."); +#endif } else if (strcmp(" updownstream", token) == 0) { tmpPtr->state = IF_STATE_UPDOWNSTREAM; LOG(LOG_NOTICE, 0, "Config (%s): Setting to Updownstream.", tmpPtr->name); @@ -670,21 +661,10 @@ bool loadConfig(char *cfgFile) { LOG(LOG_NOTICE, 0, "Config: Default table id should be between 0 and 999999999."); else { conf.defaultTable = intToken; - if (mrt_tbl < 0 && ntbl % 32 == 0 && ! _recalloc(tbl, var, ((ntbl / 32) + 1) * tblsz, (ntbl / 32) * tblsz)) - // Freed by Self - LOG(LOG_ERR, eNOMEM, "Config: Out of Memory."); - else if (mrt_tbl < 0) { - int i; - if (!ntbl) - tbl[ntbl++] = 0; - for (i = 0; i < ntbl && tbl[i] != conf.defaultTable; i++); - if (i >= ntbl) - tbl[ntbl++] = conf.defaultTable; - if (conf.defaultTable > 0) { - igmpProxyFork(0); - if (mrt_tbl < 0) // Check again because of fork(). - igmpProxyFork(conf.defaultTable); - } + if (mrt_tbl < 0 && conf.defaultTable > 0) { + igmpProxyFork(0); + if (mrt_tbl < 0) // Check again because of fork(). + igmpProxyFork(conf.defaultTable); } LOG(LOG_NOTICE, 0, "Config: Default to table %d for interfaces.", conf.defaultTable); } @@ -702,12 +682,12 @@ bool loadConfig(char *cfgFile) { LOG(LOG_NOTICE, 0, "Config: Run as user '%s' is only valid for linux.", token + 1); #endif - } else if (strcmp(" disableipmrules", token) == 0) { + } else if (strcmp(" defaultdisableipmrules", token) == 0) { #ifdef __linux__ LOG(LOG_NOTICE, 0, "Config: Will disable ip mrules for mc route tables."); conf.disableIpMrules = true; #else - LOG(LOG_NOTICE, 0, "disableipmrules is ony valid for linux."); + LOG(LOG_NOTICE, 0, "defaultdisableipmrules is ony valid for linux."); #endif } else if (strcmp(" group", token) == 0 && nextToken(token) && (STARTUP || (token[1] = '\0'))) { if (! (conf.group = getgrnam(token + 1))) @@ -919,19 +899,6 @@ bool loadConfig(char *cfgFile) { if (confFilePtr && (confFilePtr = configFile(NULL, 0))) LOG(LOG_WARNING, errno, "Config: Failed to close config file (%d) '%s'.", conf.cnt, cfgFile); -#ifdef __linux__ - IF_FOR_IF(mrt_tbl < 0 && chld.c && !logwarning, int i = 0; i < chld.nr; i++, chld.c[i].pid > 0) { - // Check if any proxy needs to be stopped because it is no longer used. - int j; - for (j = 0; j < ntbl && chld.c[i].tbl != tbl[j]; j++); - if (j >= ntbl) { - kill(chld.c[i].pid, SIGINT); // SIGINT so process will not be restarted in SIGCHLD - LOG(LOG_NOTICE, 0, "Stopping PID: %d (%d) for table %d.", chld.c[i].pid, i, chld.c[i].tbl); - } - } - _free(tbl, var, (((ntbl - 1) / 32) + 1) * tblsz); // Alloced by Self - tbl = NULL, ntbl = 0; -#endif if (--conf.cnt > 0 || logwarning) return !logwarning; @@ -985,7 +952,10 @@ bool loadConfig(char *cfgFile) { timers.bwControl = timer_setTimer(conf.bwControlInterval * 10, "Bandwidth Control", bwControl, &timers.bwControl); } - +#ifdef __linux__ + if (mrt_tbl < 0 && !chld.nr) + mrt_tbl = 0; +#endif return !logwarning; } @@ -1129,11 +1099,11 @@ void configureVifs(void) { } // Do maintenance on vifs according to their old and new state. - if ( IS_DISABLED(oldstate) && IS_UPSTREAM(newstate) ) { ctrlQuerier(1, IfDp); clearGroups(IfDp); } - else if ( IS_DISABLED(oldstate) && IS_DOWNSTREAM(newstate)) { ctrlQuerier(1, IfDp); } - else if (!IS_DISABLED(oldstate) && IS_DISABLED(newstate) ) { ctrlQuerier(0, IfDp); clearGroups(IfDp); } - else if ( oldstate != newstate) { ctrlQuerier(2, IfDp); clearGroups(IfDp); } - else if ( oldstate == newstate && !IS_DISABLED(newstate) ) { if (!IFREBUILD) clearGroups(IfDp); } + if ( IS_DISABLED(oldstate) && IS_UPSTREAM(newstate)) { ctrlQuerier(1, IfDp); clearGroups(IfDp); } + else if ((STARTUP || IS_DISABLED(oldstate)) && IS_DOWNSTREAM(newstate)) { ctrlQuerier(1, IfDp); } + else if (!STARTUP && !IS_DISABLED(oldstate) && IS_DISABLED(newstate)) { ctrlQuerier(0, IfDp); clearGroups(IfDp); } + else if (!STARTUP && oldstate != newstate) { ctrlQuerier(2, IfDp); clearGroups(IfDp); } + else if ( oldstate == newstate && !IS_DISABLED(newstate)) { if (!IFREBUILD) clearGroups(IfDp); } IfDp->filCh = false; // Check if vif needs to be removed. diff --git a/src/ifvc.c b/src/ifvc.c index 7301477..21536d2 100644 --- a/src/ifvc.c +++ b/src/ifvc.c @@ -45,16 +45,15 @@ static struct IfDesc *IfDescL = NULL; /** * Frees the IfDesc table and cleans up on interface removal. */ -void freeIfDescL(bool force) { +void freeIfDescL(void) { struct IfDesc *IfDp = IfDescL, *fIfDp; while (IfDp) { - if (force || IfDp->state & 0x80 || (IfDp->next && IfDp->next->state & 0x80)) { + if (SHUTDOWN || IfDp->state & 0x80 || (IfDp->next && IfDp->next->state & 0x80)) { // Remove interface marked for deletion. if (!STARTUP) - LOG(SHUTDOWN ? LOG_WARNING : LOG_NOTICE, 0, "Interface %s was removed.", - force | (IfDp->state & 0x80) ? IfDp->Name : IfDp->next->Name); - fIfDp = force || (IfDp->state & 0x80) ? IfDescL : IfDp->next; - if (force || IfDp->state & 0x80) + LOG(LOG_NOTICE, 0, "Interface %s was removed.", IfDp->state & 0x80 ? IfDp->Name : IfDp->next->Name); + fIfDp = SHUTDOWN || (IfDp->state & 0x80) ? IfDescL : IfDp->next; + if (SHUTDOWN || IfDp->state & 0x80) IfDescL = IfDp = IfDp->next; else IfDp->next = IfDp->next->next; @@ -81,10 +80,10 @@ void rebuildIfVc(uint64_t *tid) { configureVifs(); // Free removed interfaces. - freeIfDescL(false); + freeIfDescL(); // Restart timer when doing timed reload. - if (CONF->rescanVif && tid) + if (!SHUTDOWN && CONF->rescanVif && tid) *tid = timer_setTimer(CONF->rescanVif * 10, "Rebuild Interfaces", rebuildIfVc, tid); if (IFREBUILD || STARTUP) { sigstatus &= ~GOT_SIGUSR2; @@ -145,26 +144,21 @@ void buildIfVc(void) { memset(&ifr, 0, sizeof(struct ifreq)); memcpy(ifr.ifr_name, tmpIfAddrsP->ifa_name, strlen(tmpIfAddrsP->ifa_name)); -#ifdef __linux__ - if (mrt_tbl < 0) - IfDp->mtu = 1; - else -#endif - if (ioctl(MROUTERFD, SIOCGIFMTU, &ifr) < 0) { - LOG(LOG_WARNING, errno, "Failed to get MTU for %s, disabling.", IfDp->Name); - IfDp->mtu = 0; - } else - IfDp->mtu = ifr.ifr_mtu; - // Enable multicast if necessary. - if (! (IfDp->Flags & IFF_MULTICAST)) { - ifr.ifr_flags = IfDp->Flags | IFF_MULTICAST; - if (ioctl(MROUTERFD, SIOCSIFFLAGS, &ifr) < 0) - LOG(LOG_WARNING, errno, "Failed to enable multicast on %s, disabling.", IfDp->Name); - else { - IfDp->Flags = ifr.ifr_flags; - LOG(LOG_NOTICE, 0, "Multicast enabled on %s.", IfDp->Name); - } - } + if (ioctl(MROUTERFD, SIOCGIFMTU, &ifr) < 0) { + LOG(LOG_WARNING, errno, "Failed to get MTU for %s, disabling.", IfDp->Name); + IfDp->mtu = 0; + } else + IfDp->mtu = ifr.ifr_mtu; + // Enable multicast if necessary. + if (! (IfDp->Flags & IFF_MULTICAST)) { + ifr.ifr_flags = IfDp->Flags | IFF_MULTICAST; + if (ioctl(MROUTERFD, SIOCSIFFLAGS, &ifr) < 0) + LOG(LOG_WARNING, errno, "Failed to enable multicast on %s, disabling.", IfDp->Name); + else { + IfDp->Flags = ifr.ifr_flags; + LOG(LOG_NOTICE, 0, "Multicast enabled on %s.", IfDp->Name); + } + } // Log the result... LOG(LOG_INFO, 0, "buildIfVc: Interface %s, IP: %s/%d, Flags: 0x%04x, MTU: %d", diff --git a/src/igmp.c b/src/igmp.c index 144364f..6b2b13e 100644 --- a/src/igmp.c +++ b/src/igmp.c @@ -70,6 +70,10 @@ int initIgmp(bool activate) { } if (fd == -1) fd = k_enableMRouter(); +#ifdef __linux__ + if (mrt_tbl < 0) + return fd; +#endif if (! _calloc(rcv_buf, 1, rcv, CONF->pBufsz) || ! _calloc(snd_buf, 1, snd, CONF->pBufsz)) LOG(LOG_ERR, eNOMEM, "initIgmp: Out of Memory."); // Freed by Self struct ip *ip = (struct ip *)snd_buf; diff --git a/src/igmpv3proxy.c b/src/igmpv3proxy.c index fcaba94..8073426 100644 --- a/src/igmpv3proxy.c +++ b/src/igmpv3proxy.c @@ -79,7 +79,7 @@ int main(int ArgCn, char *ArgVc[]) { CONF->logLevel = LOG_WARNING; // Parse the commandline options and setup basic settings.. - for (c = getopt(ArgCn, ArgVc, "cvVdnht:"); c != -1; c = getopt(ArgCn, ArgVc, "cvVdnht:")) { + for (c = getopt(ArgCn, ArgVc, "cvVdnht::"); c != -1; c = getopt(ArgCn, ArgVc, "cvVdnht::")) { switch (c) { case 'v': if (CONF->logLevel == LOG_WARNING) @@ -94,24 +94,24 @@ int main(int ArgCn, char *ArgVc[]) { break; case 't': #ifdef __linux__ - tbl = atoll(optarg); + tbl = ! optarg ? -1 : atoll(optarg); break; #else fprintf(stderr, "Only linux supports multiple tables.\n"); exit(1); #endif case 'c': - c = getopt(ArgCn, ArgVc, "cbr::i::mf::th"); + c = getopt(ArgCn, ArgVc, "cbr::i::mf::thp"); while (c != -1 && c != '?') { uint32_t addr, mask, h = 0; memset(cmd, 0, sizeof(cmd)); cmd[0] = c; - if (c != 'r' && c != 'i' && c!= 'f' && (h = getopt(j ? 2 : ArgCn, j ? opts : ArgVc, "cbr::i::mf::th")) == 'h') + if (c != 'r' && c != 'i' && c!= 'f' && (h = getopt(j ? 2 : ArgCn, j ? opts : ArgVc, "cbr::i::mf::thp")) == 'h') strcat(cmd, "h"); else if (h == '?') break; else if ((c == 'r' || c == 'i' || c == 'f') && optarg) { - if (optarg[0] == 'h') { + if (optarg[0] == 'h' && optarg[1] == '\0') { strcat(cmd, "h"); optarg++; h = 'h'; @@ -131,11 +131,11 @@ int main(int ArgCn, char *ArgVc[]) { } } cliCmd(cmd, tbl); - c = (h == 'h' || c == 'r' || c == 'i' || c == 'f') ? getopt(j ? 2 : ArgCn, j ? opts : ArgVc, "cbr::i::mf::th") : h; + c = (h == 'h' || c == 'r' || c == 'i' || c == 'f') ? getopt(j ? 2 : ArgCn, j ? opts : ArgVc, "cbr::i::mf::thp") : h; if (c == -1 && j == 1) { free(opts[1]); // Alloced by Self optind = i, j = 0; - c = getopt(ArgCn, ArgVc, "cbr::i::mf::t"); + c = getopt(ArgCn, ArgVc, "cbr::i::mf::tp"); } if (c != -1 && c != '?') fprintf(stdout, "\n"); @@ -239,8 +239,8 @@ static void igmpProxyRun(void) { sighandled &= ~GOT_SIGURG & ~GOT_SIGTERM; break; } else if (sighandled & GOT_SIGHUP || sighandled & GOT_SIGUSR1) { - sigstatus = GOT_SIGHUP ? GOT_SIGHUP : GOT_SIGUSR1; - sighandled &= ~GOT_SIGHUP & GOT_SIGUSR1; + sigstatus = sighandled & GOT_SIGHUP ? GOT_SIGHUP : GOT_SIGUSR1; + sighandled &= ~GOT_SIGHUP & ~GOT_SIGUSR1; LOG(LOG_DEBUG, 0, "%s: Reloading config%s.", SHUP ? "SIGHUP" : "SIGUSR1", SHUP ? " and rebuilding interfaces" : ""); reloadConfig(NULL); } else if (sighandled & GOT_SIGUSR2) { @@ -286,7 +286,7 @@ static void igmpProxyRun(void) { // Check if any cli connection needs to be handled. if (pollFD[1].revents & POLLIN) { LOG(LOG_DEBUG, 0, "igmpProxyRun: RECV CLI Request %d.", i); - acceptCli(pollFD[1].fd); + acceptCli(); } clock_gettime(CLOCK_REALTIME, &curtime); @@ -326,7 +326,6 @@ void igmpProxyFork(int tbl) { _free(chld.c, var, (((chld.nr - 1) / 32) + 1) * 32 * sizeof(struct pt)); // Alloced by Self. chld.c = NULL; sigstatus = 1; - freeIfDescL(true); } else { // Parent sets the new child info in table. chld.c[i].tbl = tbl; @@ -343,13 +342,13 @@ void igmpProxyFork(int tbl) { * signalHandler will restart processes if the exit. loadConfig may start new procxies if needed. */ static void igmpProxyMonitor(void) { - rebuildIfVc(NULL); - struct timespec timeout = timer_ageQueue(); + struct timespec timeout = (struct timespec){ 0, 0 }; + FOR_IF(int i = 0; i < chld.nr; i++, chld.c[i].tbl > 0 && !CONF->disableIpMrules) + ipRules(chld.c[i].tbl, true); LOG(LOG_NOTICE, 0, "Monitoring %d proxy processes.", chld.nr); sigstatus = 0; - // Simple busy sleeping loop here, it suits our needs. - do { + while (sighandled || ppoll(&pollFD[1], 1, &timeout, NULL) || true) { if (sighandled) { if (sighandled & GOT_SIGCHLD) { sighandled &= ~GOT_SIGCHLD; @@ -357,14 +356,12 @@ static void igmpProxyMonitor(void) { FOR_IF(int i = 0; i < chld.nr; i++, chld.c[i].sig == 1) { LOG(chld.c[i].st == 0 ? LOG_NOTICE : LOG_WARNING, 0, "Child: %d PID: %d for table: %d %s (%i)", i + 1, chld.c[i].pid, chld.c[i].tbl, exitmsg[chld.c[i].st], chld.c[i].st); - if (chld.c[i].st < 0) - chld.c[i].st = 0 - (chld.c[i].st); chld.c[i].pid = chld.c[i].sig = 0; - if (chld.c[i].tbl > 0 && !CONF->disableIpMrules) - ipRules(chld.c[i].tbl, false); if (chld.c[i].st == 15 || chld.c[i].st == 6 || chld.c[i].st == 11 || chld.c[i].st == 9) // Start new proxy in case of unexpected shutdown. igmpProxyFork(chld.c[i].tbl); + else if (chld.c[i].tbl > 0 && !CONF->disableIpMrules) + ipRules(chld.c[i].tbl, false); } } else if (sighandled & GOT_SIGTERM || sighandled & GOT_SIGURG || sighandled & GOT_SIGHUP || sighandled & GOT_SIGUSR1) { sigstatus = sighandled & GOT_SIGTERM ? GOT_SIGTERM : sighandled & GOT_SIGURG ? GOT_SIGURG : @@ -384,16 +381,25 @@ static void igmpProxyMonitor(void) { sighandled &= ~GOT_SIGPIPE; LOG(LOG_WARNING, 0, "Ceci n'est pas une SIGPIPE."); } - sigstatus = 0; } - if (mrt_tbl < 0) + // Check if any cli connection needs to be handled. + if (pollFD[1].revents & POLLIN) { + LOG(LOG_DEBUG, 0, "igmpProxyMonitor: RECV CLI Request."); + acceptCli(); + } + if (mrt_tbl < 0 && !sighandled) timeout = timer_ageQueue(); - if (mrt_tbl >= 0) + if (mrt_tbl >= 0) { // SIGCHLD, ageQueue() or loadConfig() may have forked new proxy. + pollFD[0].fd = initIgmp(false), pollFD[1].fd = closeCliFd(); + pollFD[0].fd = initIgmp(true), pollFD[1].fd = openCliFd(); + rebuildIfVc(NULL); return; // To igmpProxyInit() - if (timeout.tv_sec < 0) - timeout = (struct timespec){ 2147483647, 0 }; - } while (sighandled || timeout.tv_nsec < 0 || nanosleep(&timeout, NULL) >= 0 || true); + } + if (timeout.tv_nsec < 0) + timeout.tv_nsec = 0; + errno = sigstatus = pollFD[1].revents = 0; + } LOG(LOG_ERR, eABNRML, "igmpProxyMonitor: Proceses exited."); } @@ -461,20 +467,14 @@ static void igmpProxyInit(void) { LOG(LOG_WARNING, errno, "Failed to chown log file %s to %s.", CONF->logFilePath, CONF->user->pw_name); SETSIGS; -#ifdef __linux__ - // When multiple tables are in use, process for default table 0 is forked off here. - if (mrt_tbl < 0 && chld.nr) - igmpProxyMonitor(); - else if (mrt_tbl < 0) - mrt_tbl = 0; -#endif // Enable mroute and open cli socket and add ip mrules while still running as root. pollFD[0].fd = initIgmp(true); pollFD[1].fd = openCliFd(); rebuildIfVc(NULL); #ifdef __linux__ - if (!CONF->disableIpMrules) - ipRules(mrt_tbl, true); + // When multiple tables are in use, process for default table 0 is forked off here. + if (mrt_tbl < 0) + igmpProxyMonitor(); #endif // Make sure logfile and chroot directory are owned by configured user and switch ids. if (CONF->user && geteuid() == 0) { @@ -503,10 +503,11 @@ void igmpProxyCleanUp(int code) { pid_t pid; // Wait for all childs. Cli processes are not tracked, their fds are closed. LOG(LOG_INFO, 0, "Waiting for %d processes to finish.", chld.nr); - while ((pid = wait(NULL)) > 0 && --chld.nr) { - FOR_IF(int i = 0; i < chld.nr + 1; i++, chld.c[i].pid == pid && chld.c[i].tbl > 0) + while ((pid = wait(NULL)) > 0 && chld.nr) { + FOR_IF(int i = 0; i < chld.nr; i++, chld.c[i].pid == pid && chld.c[i].tbl > 0 && --chld.nr) { ipRules(chld.c[i].tbl, false); - LOG(LOG_NOTICE, 0, "Still waiting for %d process%s to finish.", chld.nr, chld.nr > 1 ? "es" : ""); + LOG(LOG_NOTICE, 0, "Still waiting for %d process%s to finish.", chld.nr, chld.nr > 1 ? "es" : ""); + } } _free(chld.c, var, size); // Alloced by igmpProxyFork() LOG(LOG_NOTICE, 0, "All processes finished."); @@ -516,10 +517,7 @@ void igmpProxyCleanUp(int code) { rebuildIfVc(NULL); // Remove MRT, CLI socket and PID file and Config in main daemon only. -#ifdef __linux__ - if (mrt_tbl >= 0) -#endif - pollFD[1].fd = closeCliFd(pollFD[1].fd), pollFD[0].fd = initIgmp(false); + pollFD[1].fd = closeCliFd(), pollFD[0].fd = initIgmp(false); #ifdef __linux__ if (mrt_tbl < 0) #endif @@ -529,7 +527,7 @@ void igmpProxyCleanUp(int code) { remove(rFile); sprintf(rFile, "%scli.sock", CONF->runPath); remove(rFile); - if (CONF->chroot && rmdir(CONF->runPath) < 0) + if (rmdir(CONF->runPath) < 0) LOG(LOG_WARNING, errno, "Cannot remove run dir %s.", CONF->runPath); } if (!RESTART) @@ -544,6 +542,7 @@ void igmpProxyCleanUp(int code) { LOG(LOG_WARNING, 0, "%s on %s. Running since %s (%ds).", RESTART ? "Restarting" : "Shutting down", tE, tS, timeDiff(starttime, endtime).tv_sec); if (SHUTDOWN) { + timer_clearTimer((uint64_t)-1); free(CONF->runPath); // Alloced by main() free(CONF->chroot); // Alloced by loadConfig() free(CONF->logFilePath); // Alloced by loadConfig() diff --git a/src/igmpv3proxy.h b/src/igmpv3proxy.h index 2b54e1e..88b0e7b 100644 --- a/src/igmpv3proxy.h +++ b/src/igmpv3proxy.h @@ -189,6 +189,7 @@ struct vifConfig { uint8_t threshold; // Interface MC TTL uint64_t ratelimit; // Interface ratelimit struct queryParam qry; // Configured query parameters + bool disableIpMrules; // Disable ip mrules actions for interface bool noDefaultFilter; // Do not add default filters to interface bool cksumVerify; // Do not validate igmp checksums on interface bool quickLeave; // Fast upstream leave @@ -198,7 +199,7 @@ struct vifConfig { struct vifConfig *next; }; #define VIFSZ (sizeof(struct vifConfig)) -#define DEFAULT_VIFCONF (struct vifConfig){ "", conf.defaultTable, conf.defaultInterfaceState, conf.defaultThreshold, conf.defaultRatelimit, {conf.querierIp, conf.querierVer, conf.querierElection, conf.robustnessValue, conf.queryInterval, conf.queryResponseInterval, conf.lastMemberQueryInterval, conf.lastMemberQueryCount, 0, 0}, false, conf.cksumVerify, conf.quickLeave, conf.proxyLocalMc, NULL, NULL, vifConf } +#define DEFAULT_VIFCONF (struct vifConfig){ "", conf.defaultTable, conf.defaultInterfaceState, conf.defaultThreshold, conf.defaultRatelimit, {conf.querierIp, conf.querierVer, conf.querierElection, conf.robustnessValue, conf.queryInterval, conf.queryResponseInterval, conf.lastMemberQueryInterval, conf.lastMemberQueryCount, 0, 0}, conf.disableIpMrules, false, conf.cksumVerify, conf.quickLeave, conf.proxyLocalMc, NULL, NULL, vifConf } // Running querier status for interface. struct querier { // igmp querier status for interface @@ -474,8 +475,8 @@ void configureVifs(void); * cli.c */ int openCliFd(void); -int closeCliFd(int fd); -void acceptCli(int fd); +int closeCliFd(void); +void acceptCli(void); void cliCmd(char *cmd, int tbl); /** @@ -486,7 +487,7 @@ void cliCmd(char *cmd, int tbl); #define IFGETIFL(y, x) if (y) GETIFL(x) #define GETIFLIF(x, y) GETIFL(x) if (y) #define IFGETIFLIF(x, y, z) if (x) GETIFLIF(y, z) -void freeIfDescL(bool force); +void freeIfDescL(void); void rebuildIfVc(uint64_t *tid); void buildIfVc(void); struct IfDesc *getIfL(void); diff --git a/src/kern.c b/src/kern.c index b25a760..0ed46b6 100644 --- a/src/kern.c +++ b/src/kern.c @@ -174,14 +174,22 @@ inline int k_getMrouterFD(void) { int k_enableMRouter(void) { int Va = 1; - if ((mrouterFD = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0) +#ifdef __linux__ + if (mrt_tbl < 0 && (mrouterFD = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + LOG(LOG_ERR, errno, "Failed to open UDP socket."); + else if (mrt_tbl < 0) { + LOG(LOG_INFO, 0, "k_enableMRouter: Opened UDP socket."); + return mrouterFD; + } +#endif + if ((mrouterFD = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0) LOG(LOG_ERR, eNOINIT, "IGMP socket open Failed"); else if (setsockopt(mrouterFD, IPPROTO_IP, IP_HDRINCL, (void *)&Va, sizeof(Va)) < 0) LOG(LOG_ERR, eNOINIT, "IGMP socket IP_HDRINCL Failed"); #ifdef __linux__ else if (setsockopt(mrouterFD, IPPROTO_IP, MRT_TABLE, &mrt_tbl, sizeof(mrt_tbl)) < 0) - errno == ENOPROTOOPT ? LOG(LOG_ERR, eNOINIT, "IGMP socket MRT_TABLE Failed. Make sure your kernel has CONFIG_IP_MROUTE_MULTIPLE_TABLES=y") - : LOG(LOG_ERR, eNOINIT, "IGMP socket MRT_TABLE Failed."); + errno == ENOPROTOOPT ? LOG(LOG_ERR, eNOINIT, "IGMP socket MRT_TABLE Failed. Make sure your kernel has CONFIG_IP_MROUTE_MULTIPLE_TABLES=y") + : LOG(LOG_ERR, eNOINIT, "IGMP socket MRT_TABLE Failed."); #endif else if (setsockopt(mrouterFD, IPPROTO_IP, MRT_INIT, (void *)&Va, sizeof(Va)) < 0) LOG(LOG_ERR, eNOINIT, "IGMP socket MRT_INIT Failed"); @@ -189,7 +197,7 @@ int k_enableMRouter(void) { LOG(LOG_ERR, eNOINIT, "IGMP socket IP_IFINFO Failed"); #ifdef HAVE_STRUCT_BW_UPCALL_BU_SRC if (((Va = MRT_MFC_BW_UPCALL) && setsockopt(mrouterFD, IPPROTO_IP, MRT_API_CONFIG, (void *)&Va, sizeof(Va)) < 0) - || ! (Va & MRT_MFC_BW_UPCALL)) { + || ! (Va & MRT_MFC_BW_UPCALL)) { LOG(LOG_WARNING, errno, "IGMP socket MRT_API_CONFIG Failed. Disabling bandwidth control."); CONF->bwControlInterval = 0; } @@ -204,10 +212,17 @@ int k_enableMRouter(void) { * Disable the mrouted API and relases by this the lock. */ int k_disableMRouter(void) { - if (setsockopt(mrouterFD, IPPROTO_IP, MRT_DONE, NULL, 0) != 0 || close(mrouterFD) < 0) - LOG(LOG_WARNING, errno, "MRT_DONE/close"); - else +#ifdef __linux__ + if (mrt_tbl >= 0 && !STARTUP) +#endif + if (setsockopt(mrouterFD, IPPROTO_IP, MRT_DONE, NULL, 0) != 0) + LOG(LOG_WARNING, errno, "k_disableMRouter: MRT_DONE"); + if (close(mrouterFD) < 0) + LOG(LOG_WARNING, errno, "k_disableMRouter: CLOSE"); + else { + LOG(LOG_INFO, 0, "k_disableMRouter: Closed Socket"); mrouterFD = -1; + } return mrouterFD; } @@ -236,14 +251,13 @@ bool k_addVIF(struct IfDesc *IfDp) { struct vifctl vifCtl; uint8_t Ix; + // Find available vif index. if (vifBits == (uint32_t)-1) { LOG(LOG_WARNING, 0, "Out of VIF space"); + IfDp->state &= ~0x03; return false; } for (Ix = 0; Ix < MAXVIFS && (vifBits & (1 << Ix)); Ix++); - IfDp->index = Ix; - BIT_SET(vifBits, IfDp->index); - IfDp->bytes = IfDp->rate = 0; // Set the vif parameters, reset bw counters. memset(&vifCtl, 0, sizeof(struct vifctl)); @@ -252,15 +266,17 @@ bool k_addVIF(struct IfDesc *IfDp) { #else vifCtl = (struct vifctl){ Ix, 0, IfDp->conf->threshold, 0, {IfDp->InAdr.s_addr}, {INADDR_ANY} }; #endif - - // Log the VIF information and add. - LOG(LOG_NOTICE, 0, "Adding VIF: %s, Ix: %d, Fl: 0x%x, IP: %s, Threshold: %d, Ratelimit: %d", IfDp->Name, vifCtl.vifc_vifi, - vifCtl.vifc_flags, inetFmt(vifCtl.vifc_lcl_addr.s_addr, 1), vifCtl.vifc_threshold, IfDp->conf->ratelimit); + // Add the vif. if (setsockopt(mrouterFD, IPPROTO_IP, MRT_ADD_VIF, (char *)&vifCtl, sizeof(vifCtl)) < 0) { - LOG(LOG_WARNING, errno, "Error adding VIF %d:%s", IfDp->index, IfDp->Name); - IfDp->index = (uint8_t)-1; + LOG(LOG_WARNING, errno, "Error adding VIF %d:%s", Ix, IfDp->Name); + IfDp->state &= ~0x03; return false; } + IfDp->bytes = IfDp->rate = 0; + IfDp->index = Ix; + BIT_SET(vifBits, IfDp->index); + LOG(LOG_NOTICE, 0, "Adding VIF: %s, Ix: %d, Fl: 0x%x, IP: %s, Threshold: %d, Ratelimit: %d", IfDp->Name, vifCtl.vifc_vifi, + vifCtl.vifc_flags, inetFmt(vifCtl.vifc_lcl_addr.s_addr, 1), vifCtl.vifc_threshold, IfDp->conf->ratelimit); return true; } diff --git a/src/lib.c b/src/lib.c index 0382ebf..a1f1896 100644 --- a/src/lib.c +++ b/src/lib.c @@ -52,6 +52,7 @@ char Usage[] = " -c Daemon control and statistics.\n" " -c Reload Configuration.\n" " -b Rebuild Interfaces.\n" +" -p Display Multiple Routing Table Information.\n" " -r Display routing table.\n" " -i Display interface statistics.\n" " -f Display configured filters.\n" @@ -323,8 +324,8 @@ void ipRules(int tbl, bool activate) struct IfDesc *IfDp; char msg[12]; sprintf(msg, "%d", tbl); - LOG(LOG_INFO, 0, "ipRules: %s mrules%s%s.", activate ? "Adding" : "Removing", activate ? "" : " for table ", activate ? "" : msg); - GETIFLIF(IfDp, IfDp->conf->tbl == tbl && !IS_DISABLED(IfDp->state)) { + LOG(LOG_INFO, 0, "ipRules: %s mrules for table %s.", activate ? "Adding" : "Removing", msg); + GETIFLIF(IfDp, IfDp->conf->tbl == tbl && !IS_DISABLED(IfDp->state) && !IfDp->conf->disableIpMrules) { pid_t pid; LOG(LOG_NOTICE, 0, "%s ip mrules for interface %s.", activate ? "Adding" : "Removing", IfDp->Name); for (int i = 0; i < 2; i++) { @@ -332,6 +333,7 @@ void ipRules(int tbl, bool activate) LOG(LOG_ERR, eNOFORK, "ipRules: Cannot fork."); } else if (pid == 0) { execlp("ip", "ip", "mrule", activate ? "add" : "del", i ? "iif" : "oif", IfDp->Name, "table", msg, NULL); + LOG(LOG_ERR, eNOFORK, "ipRules: Cannot exec."); exit(-1); } } diff --git a/src/timers.c b/src/timers.c index 01da1fc..795ac89 100644 --- a/src/timers.c +++ b/src/timers.c @@ -62,12 +62,12 @@ struct timespec timer_ageQueue(void) { uint64_t i = 1; clock_gettime(CLOCK_REALTIME, &curtime); - for (size_t n = 0; !sighandled && i <= CONF->tmQsz && node && timeDiff(curtime, node->time).tv_nsec == -1; i++) { + for (size_t n = 0; !sighandled && !STARTUP && i <= CONF->tmQsz && node && timeDiff(curtime, node->time).tv_nsec == -1; i++) { LOG(LOG_INFO, 0, "About to call timeout %d (#%d) - %s - Missed by %dus", node->id, i, node->name, timeDiff(node->time, curtime).tv_nsec / 1000); n = strlen(node->name); - queue = node->next; node->func(node->data, node->id); + queue = node->next; _free(node, tmr, TMSZ); // Alloced by timer_setTimer() node = queue; } @@ -113,6 +113,12 @@ void *timer_clearTimer(uint64_t tid) { struct timeOutQueue *node, *pnode; uint64_t i; + // If tid = -1 clear all timers. + if (tid == (uint64_t)-1) { + while (queue) + timer_clearTimer(queue->id); + return NULL; + } // Find the timer and remove it if found. for (pnode = NULL, i = 1, node = queue; node && node->id != tid; pnode = node, node = node->next, i++); if (node) {