diff --git a/hsflowd.spec b/hsflowd.spec index 81bd92c6..c8d2e3c3 100644 --- a/hsflowd.spec +++ b/hsflowd.spec @@ -1,7 +1,7 @@ Summary: host sFlow daemon Name: hsflowd Version: 2.0.5 -Release: 5 +Release: 6 License: http://sflow.net/license.html Group: Applications/Internet URL: http://sflow.net diff --git a/src/Linux/hsflowconfig.c b/src/Linux/hsflowconfig.c index c3e3c260..2384ee6d 100644 --- a/src/Linux/hsflowconfig.c +++ b/src/Linux/hsflowconfig.c @@ -135,7 +135,7 @@ extern "C" { // expectInteger32 - static uint32_t getMultiplier(char *str) + static uint32_t getMultiplier32(char *str) { uint32_t mult = 1; uint32_t len = my_strlen(str); @@ -159,7 +159,7 @@ extern "C" { return NULL; } char *str = my_strdup(t->str); // take a copy so we can modify it - uint32_t mult = getMultiplier(str); + uint32_t mult = getMultiplier32(str); *arg = (mult * strtol(str, NULL, 0)); my_free(str); if(*arg < minVal || *arg > maxVal) { @@ -169,6 +169,89 @@ extern "C" { return t; } + + static uint64_t getMultiplier64(char *str) + { + uint64_t mult = 1; + uint32_t len = my_strlen(str); + char last = toupper(str[len - 1]); + if(last == 'K' || last == 'M' || last == 'G' || last == 'T' || last == 'P') { + // number of the form "100M" or "1G" + str[len - 1] = '\0'; // blat the K, M, G, T or P + if(last == 'K') mult = 1000LL; + if(last == 'M') mult = 1000000LL; + if(last == 'G') mult = 1000000000LL; + if(last == 'T') mult = 1000000000000LL; + if(last == 'P') mult = 1000000000000000LL; + } + return mult; + } + +#if 0 // expectInteger64 not needed yet + + static HSPToken *expectInteger64(HSP *sp, HSPToken *tok, uint64_t *arg, uint64_t minVal, uint64_t maxVal) + { + HSPToken *t = tok; + t = t->nxt; + if(t == NULL || !isdigit(t->str[0])) { + parseError(sp, tok, "expected integer", ""); + return NULL; + } + char *str = my_strdup(t->str); // take a copy so we can modify it + uint64_t mult = getMultiplier64(str); + *arg = (mult * strtoll(str, NULL, 0)); + my_free(str); + if(*arg < minVal || *arg > maxVal) { + parseError(sp, tok, "range error", ""); + return NULL; + } + return t; + } + +#endif // expectInteger64 + + static HSPToken *expectIntegerRange64(HSP *sp, HSPToken *tok, uint64_t *arg1, uint64_t *arg2, uint64_t minVal, uint64_t maxVal) + { + HSPToken *t = tok; + t = t->nxt; + if(t == NULL || !isdigit(t->str[0])) { + parseError(sp, tok, "expected integer", ""); + return NULL; + } + char *str = my_strdup(t->str); // take a copy so we can modify it + int len = my_strlen(str); + int len1 = strcspn(str, "-"); + str[len1] = '\0'; + uint64_t mult1 = getMultiplier64(str); + *arg1 = (mult1 * strtoll(str, NULL, 0)); + if(*arg1 < minVal || *arg1 > maxVal) { + parseError(sp, tok, "range error", ""); + return NULL; + } + if(len > len1) { + // we have at least a trailing '-' such as "1G-" + char *str2 = str + len1 + 1; + if(my_strlen(str2) == 0) { + // trailing dash. Allow that to mean "" + *arg2 = maxVal; + } + else { + uint64_t mult2 = getMultiplier64(str2); + *arg2 = (mult2 * strtoll(str2, NULL, 0)); + if(*arg2 < minVal || *arg2 > maxVal) { + parseError(sp, tok, "range error", ""); + return NULL; + } + } + } + else { + // no second number - indicate by setting arg2 to 0 + *arg2 = 0; + } + my_free(str); + return t; + } + // expectDouble static HSPToken *expectDouble(HSP *sp, HSPToken *tok, double *arg, double minVal, double maxVal) { @@ -1199,6 +1282,10 @@ extern "C" { if((tok = expectONOFF(sp, tok, &pc->vport)) == NULL) return NO; pc->vport_set = YES; break; + case HSPTOKEN_SPEED: + if((tok = expectIntegerRange64(sp, tok, &pc->speed_min, &pc->speed_max, 0, LLONG_MAX)) == NULL) return NO; + pc->speed_set = YES; + break; default: unexpectedToken(sp, tok, level[depth]); return NO; diff --git a/src/Linux/hsflowd.c b/src/Linux/hsflowd.c index de8b4320..e6a355f8 100644 --- a/src/Linux/hsflowd.c +++ b/src/Linux/hsflowd.c @@ -16,6 +16,7 @@ extern "C" { FILE *f_crash = NULL; static void installSFlowSettings(HSP *sp, HSPSFlowSettings *settings); + static bool updatePollingInterval(HSP *sp); /*_________________---------------------------__________________ _________________ agent callbacks __________________ @@ -916,6 +917,33 @@ extern "C" { return UTStrBuf_unwrap(buf); } + /*_________________---------------------------__________________ + _________________ pre_config_first __________________ + -----------------___________________________------------------ + */ + + static void pre_config_first(HSP *sp) { + // make sure we are ready for someone to call getSampler/getPoller + updatePollingInterval(sp); + + // before we do anything else, read the interfaces again - this time with a full discovery + // so that modules can weigh in if required, and, for example, sampling-rates can be set + // correctly. + readInterfaces(sp, YES, NULL, NULL, NULL, NULL, NULL); + + // print some stats to help us size HSP_RLIMIT_MEMLOCK etc. + if(debug(1)) + malloc_stats(); + + // add a poller to represent the whole physical host + SFLDataSource_instance dsi; + // ds_class = , ds_index = , ds_instance = 0 + SFL_DS_SET(dsi, SFL_DSCLASS_PHYSICAL_ENTITY, HSP_DEFAULT_PHYSICAL_DSINDEX, 0); + sp->poller = sfl_agent_addPoller(sp->agent, &dsi, sp, agentCB_getCounters_request); + sfl_poller_set_sFlowCpInterval(sp->poller, sp->actualPollingInterval); + sfl_poller_set_sFlowCpReceiver(sp->poller, HSP_SFLOW_RECEIVER_INDEX); + } + /*_________________---------------------------__________________ _________________ installSFlowSettings __________________ -----------------___________________________------------------ @@ -945,7 +973,14 @@ extern "C" { sp->sFlowSettings = settings; // announce the change - if(firstConfig) EVEventTxAll(sp->rootModule, HSPEVENT_CONFIG_FIRST, NULL, 0); + if(firstConfig) { + // make sure certain things are in place before we proceed. This + // could be done with an event such as CONFIG_PRE, but then + // we would have to handshake before raising CONFIG_FIRST + pre_config_first(sp); + // now offer it to the modules + EVEventTxAll(sp->rootModule, HSPEVENT_CONFIG_FIRST, NULL, 0); + } EVEventTxAll(sp->rootModule, HSPEVENT_CONFIG_CHANGED, NULL, 0); // delay the config-done event until every thread has processed the // config change. This is especially important the first time because @@ -1131,26 +1166,15 @@ extern "C" { if(!sp->DNSSD.DNSSD) abort(); } + } - // make sure we are ready for someone to call getSampler/getPoller - updatePollingInterval(sp); - - // before we do anything else, read the interfaces again - this time with a full discovery - // so that modules can weigh in if required, and, for example, sampling-rates can be set - // correctly. - readInterfaces(sp, YES, NULL, NULL, NULL, NULL, NULL); - - // print some stats to help us size HSP_RLIMIT_MEMLOCK etc. - if(debug(1)) - malloc_stats(); + /*_________________---------------------------__________________ + _________________ evt_config_changed __________________ + -----------------___________________________------------------ + */ - // add a poller to represent the whole physical host - SFLDataSource_instance dsi; - // ds_class = , ds_index = , ds_instance = 0 - SFL_DS_SET(dsi, SFL_DSCLASS_PHYSICAL_ENTITY, HSP_DEFAULT_PHYSICAL_DSINDEX, 0); - sp->poller = sfl_agent_addPoller(sp->agent, &dsi, sp, agentCB_getCounters_request); - sfl_poller_set_sFlowCpInterval(sp->poller, sp->actualPollingInterval); - sfl_poller_set_sFlowCpReceiver(sp->poller, HSP_SFLOW_RECEIVER_INDEX); + static void evt_config_changed(EVMod *mod, EVEvent *evt, void *data, size_t dataLen) { + myDebug(1, "main: evt_config_changed()"); } /*_________________---------------------------__________________ @@ -1550,6 +1574,33 @@ extern "C" { // initialize event bus sp->rootModule = EVInit(sp); + // convenience ptr to the poll-bus + sp->pollBus = EVGetBus(sp->rootModule, HSPBUS_POLL, YES); + + // register for events that we are going to handle here in the main pollBus thread. The + // events that form the config sequence are requested here before the modules are loaded + // so that these functions are called first for each event. For example, a module callback + // for HSPEVENT_CONFIG_FIRST will be called after evt_config_first() here, but before + // evt_config_done(). + + // Events to feed lines of configuration in one line at a time + EVEventRx(sp->rootModule, EVGetEvent(sp->pollBus, HSPEVENT_CONFIG_START), evt_config_start); + EVEventRx(sp->rootModule, EVGetEvent(sp->pollBus, HSPEVENT_CONFIG_LINE), evt_config_line); + EVEventRx(sp->rootModule, EVGetEvent(sp->pollBus, HSPEVENT_CONFIG_END), evt_config_end); + + // An event that is called once, after the config is settled and + // interfaces have been fully discovered, but before privileges are dropped + EVEventRx(sp->rootModule, EVGetEvent(sp->pollBus, HSPEVENT_CONFIG_FIRST), evt_config_first); + + // An event that is called for every config change. + EVEventRx(sp->rootModule, EVGetEvent(sp->pollBus, HSPEVENT_CONFIG_CHANGED), evt_config_changed); + + // A handshake event for sync across threads - to make sure CONFIG_FIRST and CONFIG_CHANGED + // have been processed to completion by all threads (all buses) before CONFIG_DONE is sent. + EVEventRx(sp->rootModule, EVGetEvent(sp->pollBus, HSPEVENT_CONFIG_SHAKE), evt_config_shake); + // CONFIG_DONE is where privileges are dropped (the first time). + EVEventRx(sp->rootModule, EVGetEvent(sp->pollBus, HSPEVENT_CONFIG_DONE), evt_config_done); + // load modules (except DNSSD - loaded below). // The module init functions can assume that the // config is loaded, but they can't assume anything @@ -1581,17 +1632,6 @@ extern "C" { if(sp->os10.os10) EVLoadModule(sp->rootModule, "mod_os10", sp->modulesPath); - // convenience ptr to the poll-bus - sp->pollBus = EVGetBus(sp->rootModule, HSPBUS_POLL, YES); - - // register for events that we are going to handle here in the main pollBus thread - EVEventRx(sp->rootModule, EVGetEvent(sp->pollBus, HSPEVENT_CONFIG_START), evt_config_start); - EVEventRx(sp->rootModule, EVGetEvent(sp->pollBus, HSPEVENT_CONFIG_LINE), evt_config_line); - EVEventRx(sp->rootModule, EVGetEvent(sp->pollBus, HSPEVENT_CONFIG_END), evt_config_end); - EVEventRx(sp->rootModule, EVGetEvent(sp->pollBus, HSPEVENT_CONFIG_FIRST), evt_config_first); - // EVEventRx(sp->rootModule, EVGetEvent(sp->pollBus, HSPEVENT_CONFIG_CHANGED), evt_config_changed); - EVEventRx(sp->rootModule, EVGetEvent(sp->pollBus, HSPEVENT_CONFIG_SHAKE), evt_config_shake); - EVEventRx(sp->rootModule, EVGetEvent(sp->pollBus, HSPEVENT_CONFIG_DONE), evt_config_done); EVEventRx(sp->rootModule, EVGetEvent(sp->pollBus, EVEVENT_TICK), evt_poll_tick); EVEventRx(sp->rootModule, EVGetEvent(sp->pollBus, EVEVENT_TOCK), evt_poll_tock); diff --git a/src/Linux/hsflowd.h b/src/Linux/hsflowd.h index 77290b1a..187ba27c 100644 --- a/src/Linux/hsflowd.h +++ b/src/Linux/hsflowd.h @@ -32,7 +32,7 @@ extern "C" { #include // for getpwnam() #include #include // for setrlimit() -#include // for UINT_MAX +#include // for UINT_MAX, LLONG_MAX #if defined(__GLIBC__) || defined(__UCLIBC__) // for signal backtrace, if supported by libc @@ -138,6 +138,9 @@ extern "C" { bool promisc; bool vport; bool vport_set; + uint64_t speed_min; + uint64_t speed_max; + bool speed_set; } HSPPcap; typedef struct _HSPCIDR { @@ -535,6 +538,7 @@ extern "C" { // read functions int readInterfaces(HSP *sp, bool full_discovery, uint32_t *p_added, uint32_t *p_removed, uint32_t *p_cameup, uint32_t *p_wentdown, uint32_t *p_changed); + const char *devTypeName(EnumHSPDevType devType); int readCpuCounters(SFLHost_cpu_counters *cpu); int readMemoryCounters(SFLHost_mem_counters *mem); int readDiskCounters(HSP *sp, SFLHost_dsk_counters *dsk); diff --git a/src/Linux/hsflowtokens.h b/src/Linux/hsflowtokens.h index caa14c6f..32d2d862 100644 --- a/src/Linux/hsflowtokens.h +++ b/src/Linux/hsflowtokens.h @@ -39,6 +39,7 @@ HSPTOKEN_DATA( HSPTOKEN_SAMPLINGDIRECTION, "samplingDirection", HSPTOKENTYPE_ATT HSPTOKEN_DATA( HSPTOKEN_FORGET_VMS, "forgetVMs", HSPTOKENTYPE_ATTRIB, NULL) HSPTOKEN_DATA( HSPTOKEN_PCAP, "pcap", HSPTOKENTYPE_OBJ, NULL) HSPTOKEN_DATA( HSPTOKEN_DEV, "dev", HSPTOKENTYPE_ATTRIB, NULL) +HSPTOKEN_DATA( HSPTOKEN_SPEED, "speed", HSPTOKENTYPE_ATTRIB, NULL) HSPTOKEN_DATA( HSPTOKEN_PROMISC, "promisc", HSPTOKENTYPE_ATTRIB, NULL) HSPTOKEN_DATA( HSPTOKEN_VPORT, "vport", HSPTOKENTYPE_ATTRIB, NULL) HSPTOKEN_DATA( HSPTOKEN_KVM, "kvm", HSPTOKENTYPE_OBJ, NULL) diff --git a/src/Linux/mod_pcap.c b/src/Linux/mod_pcap.c index ba3eaae9..f7d5a144 100644 --- a/src/Linux/mod_pcap.c +++ b/src/Linux/mod_pcap.c @@ -238,7 +238,8 @@ extern "C" { BPFSoc *bpfs; UTARRAY_WALK(mdata->bpf_socs, bpfs) { struct pcap_stat stats; - if(pcap_stats(bpfs->pcap, &stats) == 0) { + if(bpfs->pcap + && pcap_stats(bpfs->pcap, &stats) == 0) { bpfs->drops = stats.ps_drop; } } @@ -253,12 +254,6 @@ extern "C" { HSP_mod_PCAP *mdata = (HSP_mod_PCAP *)mod->data; HSP *sp = (HSP *)EVROOTDATA(mod); - bpfs->adaptor = adaptorByName(sp, bpfs->deviceName); - if(bpfs->adaptor == NULL) { - myLog(LOG_ERR, "PCAP: device %s not found", bpfs->deviceName); - return; - } - bpfs->samplingRate = lookupPacketSamplingRate(bpfs->adaptor, sp->sFlowSettings); bpfs->subSamplingRate = bpfs->samplingRate; bpfs->pcap = pcap_open_live(bpfs->deviceName, @@ -298,23 +293,76 @@ extern "C" { } /*_________________---------------------------__________________ - _________________ evt_config_first __________________ + _________________ addBPFSocket __________________ + -----------------___________________________------------------ + */ + static void addBPFSocket(EVMod *mod, HSPPcap *pcap, SFLAdaptor *adaptor) { + HSP_mod_PCAP *mdata = (HSP_mod_PCAP *)mod->data; + myDebug(1, "PCAP addBPFSocket(%s) speed=%"PRIu64, adaptor->deviceName, adaptor->ifSpeed); + BPFSoc *bpfs = (BPFSoc *)my_calloc(sizeof(BPFSoc)); + UTArrayAdd(mdata->bpf_socs, bpfs); + bpfs->module = mod; + bpfs->adaptor = adaptor; + bpfs->deviceName = adaptor->deviceName; + bpfs->promisc = pcap->promisc; + bpfs->vport = pcap->vport; + bpfs->vport_set = pcap->vport_set; + tap_open(mod, bpfs); + } + + /*_________________---------------------------__________________ + _________________ evt_config_first __________________ -----------------___________________________------------------ */ static void evt_config_first(EVMod *mod, EVEvent *evt, void *data, size_t dataLen) { - HSP_mod_PCAP *mdata = (HSP_mod_PCAP *)mod->data; HSP *sp = (HSP *)EVROOTDATA(mod); + // the list of pcap {} sections may expand to a longer list of BPFSoc + // objects if we are matching with patterns or on ifSpeed etc. for(HSPPcap *pcap = sp->pcap.pcaps; pcap; pcap = pcap->nxt) { - BPFSoc *bpfs = (BPFSoc *)my_calloc(sizeof(BPFSoc)); - UTArrayAdd(mdata->bpf_socs, bpfs); - bpfs->module = mod; - bpfs->deviceName = my_strdup(pcap->dev); - bpfs->promisc = pcap->promisc; - bpfs->vport = pcap->vport; - bpfs->vport_set = pcap->vport_set; - tap_open(mod, bpfs); + if(pcap->dev) { + SFLAdaptor *adaptor = adaptorByName(sp, pcap->dev); + if(adaptor == NULL) { + myLog(LOG_ERR, "PCAP: device %s not found", pcap->dev); + continue; + } + addBPFSocket(mod, pcap, adaptor); + } + else if(pcap->speed_set) { + if(debug(1)) { + char sp1[20], sp2[20]; + printSpeed(pcap->speed_min, sp1, 20); + printSpeed(pcap->speed_max, sp2, 20); + myDebug(1, "PCAP: searching devices with speed %s-%s", sp1, sp2); + } + SFLAdaptor *adaptor; + UTHASH_WALK(sp->adaptorsByName, adaptor) { + if((adaptor->ifSpeed == pcap->speed_min && pcap->speed_max == 0) + || (adaptor->ifSpeed >= pcap->speed_min + && adaptor->ifSpeed <= pcap->speed_max)) { + // passed the speed test, but there may be other + // reasons to reject this one: + HSPAdaptorNIO *nio = (HSPAdaptorNIO *)adaptor->userData; + if(nio->bond_master) { + myDebug(1, "not %s (bond_master)", adaptor->deviceName); + } + else if(nio->vlan != HSP_VLAN_ALL) { + myDebug(1, "not %s (vlan=%u)", adaptor->deviceName, nio->vlan); + } + else if(nio->devType != HSPDEV_PHYSICAL + && nio->devType != HSPDEV_OTHER) { + myDebug(1, "not %s (devType=%s)", + adaptor->deviceName, + devTypeName(nio->devType)); + } + else { + // passed all the tests + addBPFSocket(mod, pcap, adaptor); + } + } + } + } } } diff --git a/src/Linux/mod_tcp.c b/src/Linux/mod_tcp.c index 2bb2ba80..1389dfd8 100644 --- a/src/Linux/mod_tcp.c +++ b/src/Linux/mod_tcp.c @@ -206,6 +206,18 @@ extern "C" { { HSP_mod_TCP *mdata = (HSP_mod_TCP *)mod->data; HSP *sp = (HSP *)EVROOTDATA(mod); + + // user info. Prefer getpwuid_r() if avaiable... + // struct passwd *uid_info = getpwuid(diag_msg->idiag_uid); + // myDebug(1, "diag_msg: UID=%u(%s) inode=%u", + // diag_msg->idiag_uid, + // uid_info->pw_name, + // diag_msg->idiag_inode); + // Theoretically we could follow the inode back to + // the socket and get the application (command line) + // but there does not seem to be a direct lookup + // for that. + if(rtalen > 0) { struct rtattr *attr = (struct rtattr *)(diag_msg + 1); diff --git a/src/Linux/readInterfaces.c b/src/Linux/readInterfaces.c index af4167b7..a5f31aff 100644 --- a/src/Linux/readInterfaces.c +++ b/src/Linux/readInterfaces.c @@ -379,6 +379,25 @@ extern "C" { } #endif /* HSP_OPTICAL_STATS && ETHTOOL_GMODULEINFO */ + +/*________________---------------------------__________________ + ________________ HSPDevTypeName __________________ + ----------------___________________________------------------ +*/ + + const char *devTypeName(EnumHSPDevType devType) { + switch(devType) { + case HSPDEV_OTHER: return "OTHER"; + case HSPDEV_PHYSICAL: return "PHYSICAL"; + case HSPDEV_VETH: return "VETH"; + case HSPDEV_VIF: return "VIF"; + case HSPDEV_OVS: return "OVS"; + case HSPDEV_BRIDGE: return "BRIDGE"; + default: break; + } + return ""; + } + /*________________---------------------------__________________ ________________ ethtool_get_GDRVINFO __________________ ----------------___________________________------------------