int readNioCounters(HSP *sp, SFLHost_nio_counters *nio, char *devFilter, SFLAdaptorList *adList) { int interface_count = 0; size_t devFilterLen = devFilter ? strlen(devFilter) : 0; // may need to schedule intermediate calls to updateNioCounters() // too (to avoid undetected wraps), but at the very least we need to do // it here to make sure the data is up to the second. updateNioCounters(sp); for(int i = 0; i < sp->adaptorNIOList.num_adaptors; i++) { HSPAdaptorNIO *adaptor = sp->adaptorNIOList.adaptors[i]; if(devFilter == NULL || !strncmp(devFilter, adaptor->deviceName, devFilterLen)) { if(adList == NULL || adaptorListGet(adList, adaptor->deviceName) != NULL) { interface_count++; // report the sum over all devices that match the filter nio->bytes_in += adaptor->nio.bytes_in; nio->pkts_in += adaptor->nio.pkts_in; nio->errs_in += adaptor->nio.errs_in; nio->drops_in += adaptor->nio.drops_in; nio->bytes_out += adaptor->nio.bytes_out; nio->pkts_out += adaptor->nio.pkts_out; nio->errs_out += adaptor->nio.errs_out; nio->drops_out += adaptor->nio.drops_out; } } } return interface_count; }
void readVLANs(HSP *sp) { // mark interfaces that are specific to a VLAN FILE *procFile = fopen("/proc/net/vlan/config", "r"); if(procFile) { char line[MAX_PROC_LINE_CHARS]; int lineNo = 0; while(fgets(line, MAX_PROC_LINE_CHARS, procFile)) { // expect lines of the form "<device> VID: <vlan> ..." // (with a header line on the first row) char devName[MAX_PROC_LINE_CHARS]; int vlan; ++lineNo; if(lineNo > 1 && sscanf(line, "%s | %d", devName, &vlan) == 2) { SFLAdaptor *adaptor = adaptorListGet(sp->adaptorList, trimWhitespace(devName)); if(adaptor && adaptor->userData && vlan >= 0 && vlan < 4096) { HSPAdaptorNIO *niostate = (HSPAdaptorNIO *)adaptor->userData; niostate->vlan = vlan; if(debug) myLog(LOG_INFO, "adaptor %s has 802.1Q vlan %d", devName, vlan); } } } fclose(procFile); } }
static SFLHost_nio_counters *getNewNIO(HSP *sp, char *deviceName) { // perform the same name-cleanup here as we did on the deviceName // in readInterfaces. This is just in case we have the wrong list of // reserved characters. cleanNameForWMI(deviceName); //myLog(LOG_DEBUG, "looking up device <%s>...", deviceName); SFLAdaptor *adaptor = adaptorListGet(sp->adaptorList, deviceName); if(adaptor) { //myLog(LOG_DEBUG, "...found. userData=%p", adaptor->userData); HSPAdaptorNIO *nio = (HSPAdaptorNIO *)adaptor->userData; if(nio) return &nio->new_nio; } return NULL; }
void readIPv6Addresses(HSP *sp) { FILE *procFile = fopen("/proc/net/if_inet6", "r"); if(procFile) { char line[MAX_PROC_LINE_CHARS]; int lineNo = 0; while(fgets(line, MAX_PROC_LINE_CHARS, procFile)) { // expect lines of the form "<address> <netlink_no> <prefix_len(HEX)> <scope(HEX)> <flags(HEX)> <deviceName> // (with a header line on the first row) char devName[MAX_PROC_LINE_CHARS]; u_char addr[MAX_PROC_LINE_CHARS]; u_int devNo, maskBits, scope, flags; ++lineNo; if(sscanf(line, "%s %x %x %x %x %s", addr, &devNo, &maskBits, &scope, &flags, devName) == 6) { if(debug) { myLog(LOG_INFO, "adaptor %s has v6 address %s with scope 0x%x", devName, addr, scope); } SFLAdaptor *adaptor = adaptorListGet(sp->adaptorList, trimWhitespace(devName)); if(adaptor && adaptor->userData) { HSPAdaptorNIO *niostate = (HSPAdaptorNIO *)adaptor->userData; SFLAddress v6addr; v6addr.type = SFLADDRESSTYPE_IP_V6; if(hexToBinary(addr, v6addr.address.ip_v6.addr, 16) == 16) { // we interpret the scope from the address now // scope = remap_proc_net_if_inet6_scope(scope); EnumIPSelectionPriority ipPriority = agentAddressPriority(sp, &v6addr, niostate->vlan, niostate->loopback); if(ipPriority > niostate->ipPriority) { // write this in as the preferred sflow-agent-address for this adaptor niostate->ipAddr = v6addr; niostate->ipPriority = ipPriority; } } } } } fclose(procFile); } }
int selectAgentAddress(HSP *sp) { if(debug) myLog(LOG_INFO, "selectAgentAddress"); if(sp->sFlow->explicitAgentIP && sp->sFlow->agentIP.type) { // it was hard-coded in the config file if(debug) myLog(LOG_INFO, "selectAgentAddress hard-coded in config file"); return YES; } // it may have been defined as agent=<device> if(sp->sFlow->explicitAgentDevice && sp->sFlow->agentDevice) { SFLAdaptor *ad = adaptorListGet(sp->adaptorList, sp->sFlow->agentDevice); if(ad && ad->userData) { HSPAdaptorNIO *adaptorNIO = (HSPAdaptorNIO *)ad->userData; sp->sFlow->agentIP = adaptorNIO->ipAddr; if(debug) myLog(LOG_INFO, "selectAgentAddress pegged to device in config file"); return YES; } } // try to automatically choose an IP (or IPv6) address, based on the priority ranking. // We already used this ranking to prioritize L3 addresses per adaptor (in the case where // there are more than one) so now we are applying the same ranking globally to pick // the best candidate to represent the whole agent: SFLAdaptor *selectedAdaptor = NULL; EnumIPSelectionPriority ipPriority = IPSP_NONE; for(uint32_t i = 0; i < sp->adaptorList->num_adaptors; i++) { SFLAdaptor *adaptor = sp->adaptorList->adaptors[i]; if(adaptor && adaptor->userData) { HSPAdaptorNIO *adaptorNIO = (HSPAdaptorNIO *)adaptor->userData; if(adaptorNIO->ipPriority > ipPriority) { selectedAdaptor = adaptor; ipPriority = adaptorNIO->ipPriority; } } } if(selectedAdaptor && selectedAdaptor->userData) { // crown the winner HSPAdaptorNIO *adaptorNIO = (HSPAdaptorNIO *)selectedAdaptor->userData; sp->sFlow->agentIP = adaptorNIO->ipAddr; sp->sFlow->agentDevice = my_strdup(selectedAdaptor->deviceName); if(debug) myLog(LOG_INFO, "selectAgentAddress selected agentIP with highest priority"); return YES; } return NO; }
void agentCB_getCounters_interface(void *magic, SFLPoller *poller, SFL_COUNTERS_SAMPLE_TYPE *cs) { assert(poller->magic); HSP *sp = (HSP *)poller->magic; // device name was copied as userData char *devName = (char *)poller->userData; if(devName) { // look up the adaptor objects SFLAdaptor *adaptor = adaptorListGet(sp->adaptorList, devName); HSPAdaptorNIO *adaptorNIO = getAdaptorNIO(&sp->adaptorNIOList, devName); if(adaptor && adaptorNIO) { // make sure the counters are up to the second updateNioCounters(sp); // generic interface counters SFLCounters_sample_element elem = { 0 }; elem.tag = SFLCOUNTERS_GENERIC; elem.counterBlock.generic.ifIndex = poller->dsi.ds_index; elem.counterBlock.generic.ifType = 6; // assume ethernet elem.counterBlock.generic.ifSpeed = adaptor->ifSpeed; elem.counterBlock.generic.ifDirection = adaptor->ifDirection; elem.counterBlock.generic.ifStatus = 3; // ifAdminStatus==up, ifOperstatus==up elem.counterBlock.generic.ifPromiscuousMode = adaptor->promiscuous; elem.counterBlock.generic.ifInOctets = adaptorNIO->nio.bytes_in; elem.counterBlock.generic.ifInUcastPkts = adaptorNIO->nio.pkts_in; #define UNSUPPORTED_SFLOW_COUNTER32 (uint32_t)-1 elem.counterBlock.generic.ifInMulticastPkts = UNSUPPORTED_SFLOW_COUNTER32; elem.counterBlock.generic.ifInBroadcastPkts = UNSUPPORTED_SFLOW_COUNTER32; elem.counterBlock.generic.ifInDiscards = adaptorNIO->nio.drops_in; elem.counterBlock.generic.ifInErrors = adaptorNIO->nio.errs_in; elem.counterBlock.generic.ifInUnknownProtos = UNSUPPORTED_SFLOW_COUNTER32; elem.counterBlock.generic.ifOutOctets = adaptorNIO->nio.bytes_out; elem.counterBlock.generic.ifOutUcastPkts = adaptorNIO->nio.pkts_out; elem.counterBlock.generic.ifOutMulticastPkts = UNSUPPORTED_SFLOW_COUNTER32; elem.counterBlock.generic.ifOutBroadcastPkts = UNSUPPORTED_SFLOW_COUNTER32; elem.counterBlock.generic.ifOutDiscards = adaptorNIO->nio.drops_out; elem.counterBlock.generic.ifOutErrors = adaptorNIO->nio.errs_out; SFLADD_ELEMENT(cs, &elem); sfl_poller_writeCountersSample(poller, cs); } } }
int readNioCounters(HSP *sp, SFLHost_nio_counters *nio, char *devFilter, SFLAdaptorList *adList) { int interface_count = 0; size_t devFilterLen = devFilter ? strlen(devFilter) : 0; // may need to schedule intermediate calls to updateNioCounters() // too (to avoid undetected wraps), but at the very least we need to do // it here to make sure the data is up to the second. updateNioCounters(sp); for(int i = 0; i < sp->adaptorList->num_adaptors; i++) { SFLAdaptor *adaptor = sp->adaptorList->adaptors[i]; // note that the devFilter here is a prefix-match if(devFilter == NULL || !strncmp(devFilter, adaptor->deviceName, devFilterLen)) { if(adList == NULL || adaptorListGet(adList, adaptor->deviceName) != NULL) { HSPAdaptorNIO *niostate = (HSPAdaptorNIO *)adaptor->userData; // in the case where we are adding up across all // interfaces, be careful to avoid double-counting. // By leaving this test until now we make it possible // to know the counters for any interface or sub-interface // if required (e.g. for the readPackets() module). if(devFilter == NULL && (niostate->up == NO || niostate->vlan != HSP_VLAN_ALL || niostate->loopback || niostate->bond_master)) { continue; } interface_count++; // report the sum over all devices that match the filter nio->bytes_in += niostate->nio.bytes_in; nio->pkts_in += niostate->nio.pkts_in; nio->errs_in += niostate->nio.errs_in; nio->drops_in += niostate->nio.drops_in; nio->bytes_out += niostate->nio.bytes_out; nio->pkts_out += niostate->nio.pkts_out; nio->errs_out += niostate->nio.errs_out; nio->drops_out += niostate->nio.drops_out; } } } return interface_count; }
SFLAdaptor *adaptorListAdd(SFLAdaptorList *adList, char *dev, u_char *macBytes) { SFLAdaptor *ad = adaptorListGet(adList, dev); if(ad == NULL) { ad = (SFLAdaptor *)my_calloc(sizeof(SFLAdaptor)); ad->deviceName = strdup(dev); } if(adList->num_adaptors == adList->capacity) { // grow adList->capacity *= 2; adList->adaptors = (SFLAdaptor **)my_realloc(adList->adaptors, adList->capacity * sizeof(SFLAdaptor *)); } adList->adaptors[adList->num_adaptors++] = ad; if(macBytes) { memcpy(ad->macs[0].mac, macBytes, 6); ad->num_macs = 1; } return ad; }
static int containerLinkCB(void *magic, char *line) { HSPVMState *vm = (HSPVMState *)magic; if(debug) myLog(LOG_INFO, "containerLinkCB: line=<%s>", line); char deviceName[HSF_DOCKER_MAX_LINELEN]; char macStr[HSF_DOCKER_MAX_LINELEN]; uint32_t ifIndex; if(sscanf(line, "VNIC: %u %s %s", &ifIndex, deviceName, macStr) == 3) { u_char mac[6]; if(hexToBinary((u_char *)macStr, mac, 6) == 6) { SFLAdaptor *adaptor = adaptorListGet(vm->interfaces, deviceName); if(adaptor == NULL) { adaptor = adaptorListAdd(vm->interfaces, deviceName, mac, sizeof(HSPAdaptorNIO)); } // set ifIndex adaptor->ifIndex = ifIndex; // clear the mark so we don't free it below adaptor->marked = NO; } } return YES; }
int readPackets(HSP *sp) { int batch = 0; static uint32_t MySkipCount=1; if(sp->sFlow->sFlowSettings->ulogSubSamplingRate == 0) { // packet sampling was disabled by setting desired rate to 0 return 0; } if(sp->ulog_soc) { for( ; batch < HSP_READPACKET_BATCH; batch++) { char buf[HSP_MAX_MSG_BYTES]; socklen_t peerlen = sizeof(sp->ulog_peer); int len = recvfrom(sp->ulog_soc, buf, HSP_MAX_MSG_BYTES, 0, (struct sockaddr *)&sp->ulog_peer, &peerlen); if(len <= 0) break; if(debug > 1) myLog(LOG_INFO, "got ULOG msg: %u bytes", len); for(struct nlmsghdr *msg = (struct nlmsghdr *)buf; NLMSG_OK(msg, len); msg=NLMSG_NEXT(msg, len)) { if(debug > 1) { myLog(LOG_INFO, "netlink (%u bytes left) msg [len=%u type=%u flags=0x%x seq=%u pid=%u]", len, msg->nlmsg_len, msg->nlmsg_type, msg->nlmsg_flags, msg->nlmsg_seq, msg->nlmsg_pid); } switch(msg->nlmsg_type) { case NLMSG_NOOP: case NLMSG_ERROR: case NLMSG_OVERRUN: // ignore these break; case NLMSG_DONE: // last in multi-part default: { if(--MySkipCount == 0) { /* reached zero. Set the next skip */ MySkipCount = sfl_random((2 * sp->sFlow->sFlowSettings->ulogSubSamplingRate) - 1); /* and take a sample */ // we're seeing type==111 on Fedora14 //if(msg->nlmsg_flags & NLM_F_REQUEST) { } //if(msg->nlmsg_flags & NLM_F_MULTI) { } //if(msg->nlmsg_flags & NLM_F_ACK) { } //if(msg->nlmsg_flags & NLM_F_ECHO) { } ulog_packet_msg_t *pkt = NLMSG_DATA(msg); if(debug > 1) { myLog(LOG_INFO, "mark=%u ts=%s hook=%u in=%s out=%s len=%u prefix=%s maclen=%u\n", pkt->mark, ctime(&pkt->timestamp_sec), pkt->hook, pkt->indev_name, pkt->outdev_name, pkt->data_len, pkt->prefix, pkt->mac_len); if(pkt->mac_len == 14) { u_char macdst[12], macsrc[12]; printHex(pkt->mac+6,6,macsrc,12,NO); printHex(pkt->mac+0,6,macdst,12,NO); uint16_t ethtype = (pkt->mac[12] << 8) + pkt->mac[13]; myLog(LOG_INFO, "%s -> %s (ethertype=0x%04X)", macsrc, macdst, ethtype); } } SFL_FLOW_SAMPLE_TYPE fs = { 0 }; SFLSampler *sampler = NULL; // set the ingress and egress ifIndex numbers. // Can be "INTERNAL" (0x3FFFFFFF) or "UNKNOWN" (0). if(pkt->indev_name[0]) { SFLAdaptor *in = adaptorListGet(sp->adaptorList, pkt->indev_name); if(in && in->ifIndex) { fs.input = in->ifIndex; sampler = getSampler(sp, pkt->indev_name, in->ifIndex); } } else { fs.input = SFL_INTERNAL_INTERFACE; } if(pkt->outdev_name[0]) { SFLAdaptor *out = adaptorListGet(sp->adaptorList, pkt->outdev_name); if(out && out->ifIndex) { fs.output = out->ifIndex; sampler = getSampler(sp, pkt->outdev_name, out->ifIndex); } } else { fs.output = SFL_INTERNAL_INTERFACE; } if(sampler == NULL) { // maybe ULOG sent us a packet on device lo(?) if(debug > 1) myLog(LOG_INFO, "dropped sample with no ifIndex\n"); } else { SFLFlow_sample_element hdrElem = { 0 }; hdrElem.tag = SFLFLOW_HEADER; uint32_t FCS_bytes = 4; uint32_t maxHdrLen = sampler->sFlowFsMaximumHeaderSize; hdrElem.flowType.header.frame_length = pkt->data_len + FCS_bytes; hdrElem.flowType.header.stripped = FCS_bytes; u_char hdr[HSP_MAX_HEADER_BYTES]; if(pkt->mac_len == 14) { // set the header_protocol to ethernet and // reunite the mac header and payload in one buffer hdrElem.flowType.header.header_protocol = SFLHEADER_ETHERNET_ISO8023; memcpy(hdr, pkt->mac, 14); maxHdrLen -= 14; uint32_t payloadBytes = (pkt->data_len < maxHdrLen) ? pkt->data_len : maxHdrLen; memcpy(hdr+14, pkt->payload, payloadBytes); hdrElem.flowType.header.header_length = payloadBytes + 14; hdrElem.flowType.header.header_bytes = hdr; hdrElem.flowType.header.frame_length += 14; } else { // no need to copy - just point at the payload u_char ipversion = pkt->payload[0] >> 4; if(ipversion != 4 && ipversion != 6) { if(debug) myLog(LOG_ERR, "received non-IP packet. Encapsulation is unknown"); } else { hdrElem.flowType.header.header_protocol = (ipversion == 4) ? SFLHEADER_IPv4 : SFLHEADER_IPv6; hdrElem.flowType.header.stripped += 14; // assume ethernet was (or will be) the framing hdrElem.flowType.header.header_length = (pkt->data_len < maxHdrLen) ? pkt->data_len : maxHdrLen; hdrElem.flowType.header.header_bytes = pkt->payload; } } SFLADD_ELEMENT(&fs, &hdrElem); // submit the actual sampling rate so it goes out with the sFlow feed // otherwise the sampler object would fill in his own (sub-sampling) rate. uint32_t actualSamplingRate = sp->sFlow->sFlowSettings->ulogActualSamplingRate; fs.sampling_rate = actualSamplingRate; // estimate the sample pool from the samples. Could maybe do this // above with the (possibly more granular) ulogSamplingRate, but then // we would have to look up the sampler object every time, which // might be too expensive in the case where ulogSamplingRate==1. sampler->samplePool += actualSamplingRate; sfl_sampler_writeFlowSample(sampler, &fs); } } } } } } }
/** * Gets the switch port info from WMI (switch port friendly name, ifSpeed) * and merges into the list of existing ports. */ void readWMISwitchPorts(HSP *sp) { BSTR path = SysAllocString(WMI_VIRTUALIZATION_NS_V2); HRESULT hr = S_FALSE; IWbemServices *pNamespace = NULL; hr = connectToWMI(path, &pNamespace); if (FAILED(hr)) { //only try the v2 namespace since this will only be present //with the extensible switch that supports sampling. //don't try to get counters if there is no sampling. SysFreeString(path); myLog(LOG_INFO, "readWMISwitchPorts: virtualization namespace v2 not found"); return; } else { SysFreeString(path); } BSTR queryLang = SysAllocString(L"WQL"); wchar_t *query = L"SELECT * FROM Msvm_EthernetSwitchPort"; IEnumWbemClassObject *switchPortEnum = NULL; hr = pNamespace->ExecQuery(queryLang, query, WBEM_FLAG_FORWARD_ONLY, NULL, &switchPortEnum); SysFreeString(queryLang); if (FAILED(hr)) { myLog(LOG_ERR,"readWMISwitchPorts: ExecQuery() failed for query %S error=0x%x", query, hr); pNamespace->Release(); return; } if (sp->vAdaptorList == NULL) { sp->vAdaptorList = adaptorListNew(); } IWbemClassObject *switchPortObj = NULL; hr = WBEM_S_NO_ERROR; while (WBEM_S_NO_ERROR == hr) { SFLAdaptor *vAdaptor = NULL; ULONG uReturned = 1; hr = switchPortEnum->Next(WBEM_INFINITE, 1, &switchPortObj, &uReturned); if (0 == uReturned) { break; } wchar_t *guidString = stringFromWMIProperty(switchPortObj, PROP_NAME); if (guidString != NULL) { char portGuid[FORMATTED_GUID_LEN+1]; guidToString(guidString, (UCHAR *)portGuid, FORMATTED_GUID_LEN); myLog(LOG_INFO, "readWMISwitchPorts: portGuid=%s", portGuid); my_free(guidString); vAdaptor = adaptorListGet(sp->vAdaptorList, portGuid); } if (vAdaptor != NULL) { HVSVPortInfo *portInfo = (HVSVPortInfo *)vAdaptor->userData; wchar_t *switchName = stringFromWMIProperty(switchPortObj, PROP_SYSTEM_NAME); if (switchName != NULL) { if (portInfo->switchName != NULL) { my_free(portInfo->switchName); } portInfo->switchName = switchName; } wchar_t *friendlyName = stringFromWMIProperty(switchPortObj, PROP_ELEMENT_NAME); if (friendlyName != NULL) { if (portInfo->portFriendlyName != NULL) { my_free(portInfo->portFriendlyName); } portInfo->portFriendlyName = friendlyName; } setPortCountersInstance(vAdaptor); wchar_t *speedString = stringFromWMIProperty(switchPortObj, PROP_SPEED); if (speedString != NULL) { ULONGLONG ifSpeed = _wcstoui64(speedString, NULL, 10); vAdaptor->ifSpeed = ifSpeed; my_free(speedString); } //could also get ifDirection but FullDuplex=True always //Get the MACs and VM system name when we enumerate the vms. myLog(LOG_INFO, "readWMISwitchPorts: updated switch port %s %S portId=%u ifIndex=%u ifSpeed=%llu counterName=%S", vAdaptor->deviceName, portInfo->portFriendlyName, portInfo->portId, vAdaptor->ifIndex, vAdaptor->ifSpeed, portInfo->portCountersInstance); } else { myLog(LOG_INFO, "readWMISwitchPorts: vAdapter not found"); } switchPortObj->Release(); } switchPortEnum->Release(); pNamespace->Release(); }
/** * Updates the switch port list with the information in pSwitchConfig * obtained from the filter. */ void updateSwitchPorts(HSP *sp, PAllSwitchesConfig config) { if (config->revision <= sp->portInfoRevision) { return; } if (sp->vAdaptorList == NULL) { sp->vAdaptorList = adaptorListNew(); } else { adaptorListMarkAll(sp->vAdaptorList); } PSwitchConfig switchConfig; for (uint32_t switchNum = 0; switchNum < config->numSwitches; switchNum++) { if (switchNum == 0) { switchConfig = GET_FIRST_SWITCH_CONFIG(config); } else { switchConfig = GET_NEXT_SWITCH_CONFIG(switchConfig); } wchar_t *switchName = ndiswcsdup(&switchConfig->switchName); uint64_t switchId = switchConfig->switchID; for (uint32_t portNum = 0; portNum < switchConfig->numPorts; portNum++) { PPortEntry portEntry = GET_PORT_ENTRY_AT(switchConfig, portNum); uint32_t portId = portEntry->portID; wchar_t *portName = ndiswcsdup(&portEntry->portName); char portGuid[FORMATTED_GUID_LEN+1]; guidToString(portName, (UCHAR *)portGuid, FORMATTED_GUID_LEN); SFLAdaptor *switchPort = adaptorListGet(sp->vAdaptorList, portGuid); if (switchPort == NULL) { //new port so add to the vadaptor list //convert GUID to uuid format to look up ifIndex/dsIndex char uuid[16]; hexToBinary((UCHAR *)portGuid, (UCHAR *)uuid, 33); uint32_t ifIndex = assign_dsIndex(&sp->portStore, uuid, &sp->maxIfIndex, &sp->portStoreInvalid); switchPort = addVAdaptor(sp->vAdaptorList, portGuid, ifIndex); HVSVPortInfo *portInfo = (HVSVPortInfo *)switchPort->userData; portInfo->filterEnabled = TRUE; portInfo->portId = portId; portInfo->revision = portEntry->revision; switchPort->marked = FALSE; updatePortSwitchName(switchPort, switchName); portInfo->switchId = switchConfig->switchID; myLog(LOG_INFO, "updateSwitchPorts: Added new portId=%u ifIndex=%u deviceName=%s switchId=%llu switchName=%S", portInfo->portId, switchPort->ifIndex, switchPort->deviceName, portInfo->switchId, portInfo->switchName); addPoller(sp, switchPort); } else { //we already know about this port, so make sure we have a poller //and the current info SFLDataSource_instance dsi; SFL_DS_SET(dsi, 0, switchPort->ifIndex, 0); SFLPoller *poller = sfl_agent_getPoller(sp->sFlow->agent, &dsi); if (poller == NULL) { poller = addPoller(sp, switchPort); } HVSVPortInfo *portInfo = (HVSVPortInfo *)switchPort->userData; if (portEntry->revision > portInfo->revision) { updatePortSwitchName(switchPort, switchName); portInfo->revision = portEntry->revision; if (poller != NULL) { sfl_poller_resetCountersSeqNo(poller); } myLog(LOG_INFO, "updateSwitchPorts: revision changed: portId=%u ifIndex=%u deviceName=%s switchId=%llu switchName=%S", portInfo->portId, switchPort->ifIndex, switchPort->deviceName, portInfo->switchId, portInfo->switchName); } portInfo->filterEnabled = TRUE; switchPort->marked = FALSE; } my_free(portName); } my_free(switchName); } //now sweep //remove the pollers for non-sampling ports for (uint32_t i = 0; i < sp->vAdaptorList->num_adaptors; i++) { SFLAdaptor *vAdaptor = sp->vAdaptorList->adaptors[i]; if (vAdaptor->marked) { HVSVPortInfo *portInfo = (HVSVPortInfo *)vAdaptor->userData; if (portInfo->filterEnabled) { //filter (ie sampling) has been disabled in the switch with this port ((HVSVPortInfo *)vAdaptor->userData)->portId = 0; removePoller(sp, vAdaptor); portInfo->filterEnabled = FALSE; //Clear the mark so this port will not be deleted, the VM and adaptor may still exist. //If the adaptor does not exist, it will be removed when we next refresh the VMs. vAdaptor->marked = FALSE; } else { //this was a port added for a vm on a switch with the filter disabled, so //just clear the mark so that it will not be deleted. vAdaptor->marked = FALSE; } } } //Now remove the marked adaptors and their port info from the list adaptorListFreeMarked(sp->vAdaptorList, freePortInfo); //TODO ageout the persistent ifIndex->GUID mapping and remove from vAdaptor list. sp->portInfoRevision = config->revision; readWMISwitchPorts(sp); //update the ifSpeed, MAC, VM name sp->refreshVms = TRUE; }
int readInterfaces(HSP *sp, uint32_t *p_added, uint32_t *p_removed, uint32_t *p_cameup, uint32_t *p_wentdown, uint32_t *p_changed) { uint32_t ad_added=0, ad_removed=0, ad_cameup=0, ad_wentdown=0, ad_changed=0; if(sp->adaptorList == NULL) sp->adaptorList = adaptorListNew(); else adaptorListMarkAll(sp->adaptorList); // Walk the interfaces and collect the non-loopback interfaces so that we // have a list of MAC addresses for each interface (usually only 1). // // May need to come back and run a variation of this where we supply // a domain and collect the virtual interfaces for that domain in a // similar way. It looks like we do that by just parsing the numbers // out of the interface name. int fd = socket (PF_INET, SOCK_DGRAM, 0); if (fd < 0) { fprintf (stderr, "error opening socket: %d (%s)\n", errno, strerror(errno)); return 0; } FILE *procFile = fopen("/proc/net/dev", "r"); if(procFile) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); char line[MAX_PROC_LINE_CHARS]; int lineNo = 0; while(fgets(line, MAX_PROC_LINE_CHARS, procFile)) { if(lineNo++ < 2) continue; // skip headers // the device name is always the first token before the ":" char buf[MAX_PROC_LINE_CHARS]; char *p = line; char *devName = parseNextTok(&p, " \t:", NO, '\0', NO, buf, MAX_PROC_LINE_CHARS); if(devName && my_strlen(devName) < IFNAMSIZ) { devName = trimWhitespace(devName); if(devName && strlen(devName) < IFNAMSIZ) { // we set the ifr_name field to make our queries strncpy(ifr.ifr_name, devName, sizeof(ifr.ifr_name)); if(debug > 1) { myLog(LOG_INFO, "reading interface %s", devName); } // Get the flags for this interface if(ioctl(fd,SIOCGIFFLAGS, &ifr) < 0) { myLog(LOG_ERR, "device %s Get SIOCGIFFLAGS failed : %s", devName, strerror(errno)); } else { int up = (ifr.ifr_flags & IFF_UP) ? YES : NO; int loopback = (ifr.ifr_flags & IFF_LOOPBACK) ? YES : NO; int promisc = (ifr.ifr_flags & IFF_PROMISC) ? YES : NO; int bond_master = (ifr.ifr_flags & IFF_MASTER) ? YES : NO; int bond_slave = (ifr.ifr_flags & IFF_SLAVE) ? YES : NO; //int hasBroadcast = (ifr.ifr_flags & IFF_BROADCAST); //int pointToPoint = (ifr.ifr_flags & IFF_POINTOPOINT); // used to ignore loopback interfaces here, and interfaces // that are currently marked down, but now those are // filtered at the point where we roll together the // counters, or build the list for export // Get the MAC Address for this interface if(ioctl(fd,SIOCGIFHWADDR, &ifr) < 0) { myLog(LOG_ERR, "device %s Get SIOCGIFHWADDR failed : %s", devName, strerror(errno)); } // for now just assume that each interface has only one MAC. It's not clear how we can // learn multiple MACs this way anyhow. It seems like there is just one per ifr record. // find or create a new "adaptor" entry SFLAdaptor *adaptor = adaptorListGet(sp->adaptorList, devName); if(adaptor == NULL) { ad_added++; adaptor = adaptorListAdd(sp->adaptorList, devName, (u_char *)&ifr.ifr_hwaddr.sa_data, sizeof(HSPAdaptorNIO)); } // clear the mark so we don't free it below adaptor->marked = NO; // this flag might belong in the adaptorNIO struct adaptor->promiscuous = promisc; // remember some useful flags in the userData structure HSPAdaptorNIO *adaptorNIO = (HSPAdaptorNIO *)adaptor->userData; if(adaptorNIO->up != up) { if(up) ad_cameup++; else ad_wentdown++; if(debug) { myLog(LOG_INFO, "adaptor %s %s", adaptor->deviceName, up ? "came up" : "went down"); } } adaptorNIO->up = up; adaptorNIO->loopback = loopback; adaptorNIO->bond_master = bond_master; adaptorNIO->bond_slave = bond_slave; adaptorNIO->vlan = HSP_VLAN_ALL; // may be modified below #ifdef HSP_SWITCHPORT_REGEX if(regexec(&sp->swp_regex, devName, 0, NULL, 0) == 0) { adaptorNIO->switchPort = YES; } #endif // Try and get the ifIndex for this interface if(ioctl(fd,SIOCGIFINDEX, &ifr) < 0) { // only complain about this if we are debugging if(debug) { myLog(LOG_ERR, "device %s Get SIOCGIFINDEX failed : %s", devName, strerror(errno)); } } else { adaptor->ifIndex = ifr.ifr_ifindex; } // Try to get the IP address for this interface if(ioctl(fd,SIOCGIFADDR, &ifr) < 0) { // only complain about this if we are debugging if(debug) { myLog(LOG_ERR, "device %s Get SIOCGIFADDR failed : %s", devName, strerror(errno)); } } else { if (ifr.ifr_addr.sa_family == AF_INET) { struct sockaddr_in *s = (struct sockaddr_in *)&ifr.ifr_addr; // IP addr is now s->sin_addr adaptorNIO->ipAddr.type = SFLADDRESSTYPE_IP_V4; adaptorNIO->ipAddr.address.ip_v4.addr = s->sin_addr.s_addr; } //else if (ifr.ifr_addr.sa_family == AF_INET6) { // not sure this ever happens - on a linux system IPv6 addresses // are picked up from /proc/net/if_inet6 // struct sockaddr_in6 *s = (struct sockaddr_in6 *)&ifr.ifr_addr; // IP6 addr is now s->sin6_addr; //} } // use ethtool to get info about direction/speed and more if(read_ethtool_info(&ifr, fd, adaptor) == YES) { ad_changed++; } } } } } fclose(procFile); } close (fd); // now remove and free any that are still marked ad_removed = adaptorListFreeMarked(sp->adaptorList); // check in case any of the survivors are specific // to a particular VLAN readVLANs(sp); // now that we have the evidence gathered together, we can // set the L3 address priorities (used for auto-selecting // the sFlow-agent-address if requrired to by the config. setAddressPriorities(sp); // now we can read IPv6 addresses too - they come from a // different place. Depending on the address priorities this // may cause the adaptor's best-choice ipAddress to be // overwritten. readIPv6Addresses(sp); if(p_added) *p_added = ad_added; if(p_removed) *p_removed = ad_removed; if(p_cameup) *p_cameup = ad_cameup; if(p_wentdown) *p_wentdown = ad_wentdown; if(p_changed) *p_changed = ad_changed; return sp->adaptorList->num_adaptors; }
int HSPReadConfigFile(HSP *sp) { EnumHSPObject level[HSP_MAX_CONFIG_DEPTH + 5]; int depth = 0; level[depth] = HSPOBJ_HSP; // could have used something like bison to make a complete parser with // strict rules, but for simplicity we just allow the current object // to double as a state variable that determines what is allowed next. for(HSPToken *tok = readTokens(sp); tok; tok = tok->nxt) { if(depth > HSP_MAX_CONFIG_DEPTH) { // depth overrun parseError(sp, tok, "too many '{'s", ""); return NO; } else if(tok->stok == HSPTOKEN_ENDOBJ) { // end of level, pop the stack if(depth > 0) --depth; else { parseError(sp, tok, "too many '}'s ", ""); return NO; } } else switch(level[depth]) { case HSPOBJ_HSP: // must start by opening an sFlow object if((tok = expectToken(sp, tok, HSPTOKEN_SFLOW)) == NULL) return NO; if((tok = expectToken(sp, tok, HSPTOKEN_STARTOBJ)) == NULL) return NO; newSFlow(sp); level[++depth] = HSPOBJ_SFLOW; break; case HSPOBJ_SFLOW: switch(tok->stok) { case HSPTOKEN_DNSSD: if((tok = expectDNSSD(sp, tok)) == NULL) return NO; break; case HSPTOKEN_DNSSD_DOMAIN: if((tok = expectDNSSD_domain(sp, tok)) == NULL) return NO; break; case HSPTOKEN_COLLECTOR: if((tok = expectToken(sp, tok, HSPTOKEN_STARTOBJ)) == NULL) return NO; newCollector(sp->sFlow->sFlowSettings_file); level[++depth] = HSPOBJ_COLLECTOR; break; case HSPTOKEN_SAMPLING: case HSPTOKEN_PACKETSAMPLINGRATE: if((tok = expectInteger32(sp, tok, &sp->sFlow->sFlowSettings_file->samplingRate, 0, 65535)) == NULL) return NO; break; case HSPTOKEN_POLLING: case HSPTOKEN_COUNTERPOLLINGINTERVAL: if((tok = expectInteger32(sp, tok, &sp->sFlow->sFlowSettings_file->pollingInterval, 0, 300)) == NULL) return NO; break; case HSPTOKEN_AGENTIP: if((tok = expectIP(sp, tok, &sp->sFlow->agentIP, NULL)) == NULL) return NO; break; case HSPTOKEN_AGENT: if((tok = expectDevice(sp, tok, &sp->sFlow->agentDevice)) == NULL) return NO; break; case HSPTOKEN_SUBAGENTID: if((tok = expectInteger32(sp, tok, &sp->sFlow->subAgentId, 0, HSP_MAX_SUBAGENTID)) == NULL) return NO; break; case HSPTOKEN_UUID: if((tok = expectUUID(sp, tok, sp->uuid)) == NULL) return NO; break; case HSPTOKEN_HEADERBYTES: if((tok = expectInteger32(sp, tok, &sp->sFlow->sFlowSettings_file->headerBytes, 0, HSP_MAX_HEADER_BYTES)) == NULL) return NO; break; case HSPTOKEN_ULOGGROUP: if((tok = expectInteger32(sp, tok, &sp->sFlow->sFlowSettings_file->ulogGroup, 1, 32)) == NULL) return NO; break; case HSPTOKEN_ULOGPROBABILITY: if((tok = expectDouble(sp, tok, &sp->sFlow->sFlowSettings_file->ulogProbability, 0.0, 1.0)) == NULL) return NO; break; default: parseError(sp, tok, "unexpected sFlow setting", ""); return NO; break; } break; case HSPOBJ_COLLECTOR: { HSPCollector *col = sp->sFlow->sFlowSettings_file->collectors; switch(tok->stok) { case HSPTOKEN_IP: if((tok = expectIP(sp, tok, &col->ipAddr, (struct sockaddr *)&col->sendSocketAddr)) == NULL) return NO; break; case HSPTOKEN_UDPPORT: if((tok = expectInteger32(sp, tok, &col->udpPort, 1, 65535)) == NULL) return NO; break; default: parseError(sp, tok, "unexpected collector setting", ""); return NO; break; } } break; default: parseError(sp, tok, "unexpected state", ""); } } // OK we consumed all the tokens, but we still need to run some sanity checks to make sure // we have a usable configuration... int parseOK = YES; if(sp->sFlow == NULL) { myLog(LOG_ERR, "parse error in %s : sFlow not found", sp->configFile); parseOK = NO; } else { //////////////////////// sFlow ///////////////////////// if(sp->sFlow->agentIP.type == 0) { // it may have been defined as agent=<device> if(sp->sFlow->agentDevice) { SFLAdaptor *ad = adaptorListGet(sp->adaptorList, sp->sFlow->agentDevice); if(ad && ad->ipAddr.addr) { sp->sFlow->agentIP.type = SFLADDRESSTYPE_IP_V4; sp->sFlow->agentIP.address.ip_v4 = ad->ipAddr; } } } if(sp->sFlow->agentIP.type == 0) { // nae luck - try to automatically choose the first non-loopback IP address for(uint32_t i = 0; i < sp->adaptorList->num_adaptors; i++) { SFLAdaptor *adaptor = sp->adaptorList->adaptors[i]; // only the non-loopback devices should be listed here, so just take the first if(adaptor && adaptor->ipAddr.addr) { sp->sFlow->agentIP.type = SFLADDRESSTYPE_IP_V4; sp->sFlow->agentIP.address.ip_v4 = adaptor->ipAddr; // fill in the device that we picked too sp->sFlow->agentDevice = strdup(adaptor->deviceName); break; } } } if(sp->sFlow->agentIP.type == SFLADDRESSTYPE_IP_V4 && sp->sFlow->agentDevice == NULL) { // try to fill in the device field too (because we need to give that one to open vswitch). for(uint32_t i = 0; i < sp->adaptorList->num_adaptors; i++) { SFLAdaptor *adaptor = sp->adaptorList->adaptors[i]; if(adaptor && (adaptor->ipAddr.addr == sp->sFlow->agentIP.address.ip_v4.addr)) { sp->sFlow->agentDevice = strdup(adaptor->deviceName); break; } } } if(sp->sFlow->agentIP.type == 0) { // still no agentIP. That's a showstopper. myLog(LOG_ERR, "parse error in %s : agentIP not defined", sp->configFile); parseOK = NO; } if(sp->sFlow->sFlowSettings_file->numCollectors == 0 && sp->DNSSD == NO) { myLog(LOG_ERR, "parse error in %s : DNS-SD is off and no collectors are defined", sp->configFile); parseOK = NO; } for(HSPCollector *coll = sp->sFlow->sFlowSettings_file->collectors; coll; coll = coll->nxt) { //////////////////////// collector ///////////////////////// if(coll->ipAddr.type == 0) { myLog(LOG_ERR, "parse error in %s : collector has no IP", sp->configFile); parseOK = NO; } } if(sp->sFlow->sFlowSettings_file->ulogProbability > 0) { sp->sFlow->sFlowSettings_file->ulogSamplingRate = (uint32_t)(1.0 / sp->sFlow->sFlowSettings_file->ulogProbability); } } return parseOK; }
/** * Gets the switch port info from WMI (switch port friendly name, ifSpeed) * and merges into the list of existing ports. */ void readWMISwitchPorts(HSP *sp) { myLog(LOG_INFO, "entering readWMISwitchPorts"); BSTR path = SysAllocString(WMI_VIRTUALIZATION_NS_V2); HRESULT hr = S_FALSE; IWbemServices *pNamespace = NULL; hr = connectToWMI(path, &pNamespace); if (FAILED(hr)) { //only try the v2 namespace since this will only be present //with the extensible switch that supports sampling. //don't try to get counters if there is no sampling. SysFreeString(path); myLog(LOG_INFO, "readWMISwitchPorts: virtualization namespace v2 not found"); return; } else { SysFreeString(path); } BSTR queryLang = SysAllocString(L"WQL"); BSTR query = SysAllocString(L"SELECT * FROM Msvm_EthernetSwitchPort"); IEnumWbemClassObject *switchPortEnum = NULL; hr = pNamespace->ExecQuery(queryLang, query, WBEM_FLAG_FORWARD_ONLY, NULL, &switchPortEnum); SysFreeString(queryLang); SysFreeString(query); if (FAILED(hr)) { myLog(LOG_ERR,"readWMISwitchPorts: ExecQuery() failed for query %S error=0x%x", query, hr); CoUninitialize(); return; } if (sp->vAdaptorList == NULL) { sp->vAdaptorList = adaptorListNew(); } IWbemClassObject *switchPortObj = NULL; BSTR propElementName = SysAllocString(L"ElementName"); BSTR propSystemName = SysAllocString(L"SystemName"); BSTR propName = SysAllocString(L"Name"); BSTR propSpeed = SysAllocString(L"Speed"); //could also get ifDirection but FullDuplex=True always VARIANT systemVal; VARIANT elementVal; VARIANT nameVal; VARIANT speedVal; hr = WBEM_S_NO_ERROR; while (WBEM_S_NO_ERROR == hr) { SFLAdaptor *vAdaptor = NULL; ULONG uReturned = 1; hr = switchPortEnum->Next(WBEM_INFINITE, 1, &switchPortObj, &uReturned); if (0 == uReturned) { break; } HRESULT portHr; portHr = switchPortObj->Get(propName, 0, &nameVal, 0, 0); char portGuid[FORMATTED_GUID_LEN+1]; guidToString(nameVal.bstrVal, (UCHAR *)portGuid, FORMATTED_GUID_LEN); myLog(LOG_INFO, "readWMISwitchPorts: portGuid=%s", portGuid); VariantClear(&nameVal); vAdaptor = adaptorListGet(sp->vAdaptorList, portGuid); if (vAdaptor != NULL) { portHr = switchPortObj->Get(propSystemName, 0, &systemVal, 0, 0); portHr = switchPortObj->Get(propElementName, 0, &elementVal, 0, 0); portHr = switchPortObj->Get(propSpeed, 0, &speedVal, 0, 0); int length = SysStringLen(systemVal.bstrVal)+1; //include room for terminating null wchar_t *switchName = (wchar_t *)my_calloc(length*sizeof(wchar_t)); wcscpy_s(switchName, length, systemVal.bstrVal); length = SysStringLen(elementVal.bstrVal)+1; wchar_t *friendlyName = (wchar_t *)my_calloc(length*sizeof(wchar_t)); wcscpy_s(friendlyName, length, elementVal.bstrVal); ULONGLONG ifSpeed = _wcstoui64(speedVal.bstrVal, NULL, 10); VariantClear(&systemVal); VariantClear(&elementVal); VariantClear(&speedVal); HVSVPortInfo *portInfo = (HVSVPortInfo *)vAdaptor->userData; if (portInfo->switchName != NULL) { my_free(portInfo->switchName); } portInfo->switchName = switchName; if (portInfo->portFriendlyName != NULL) { my_free(portInfo->portFriendlyName); } portInfo->portFriendlyName = friendlyName; setPortCountersInstance(vAdaptor); vAdaptor->ifSpeed = ifSpeed; //Get the MACs and VM system name when we enumerate the vms. myLog(LOG_INFO, "readWMISwitchPorts: updated switch port %s %S portId=%u ifIndex=%u ifSpeed=%llu counterName=%S", vAdaptor->deviceName, portInfo->portFriendlyName, portInfo->portId, vAdaptor->ifIndex, vAdaptor->ifSpeed, portInfo->portCountersInstance); } else { myLog(LOG_INFO, "readWMISwitchPorts: vAdapter not found"); } switchPortObj->Release(); } switchPortEnum->Release(); pNamespace->Release(); CoUninitialize(); SysFreeString(propElementName); SysFreeString(propSystemName); SysFreeString(propName); SysFreeString(propSpeed); return; }
void updateBondCounters(HSP *sp, SFLAdaptor *bond) { char procFileName[256]; snprintf(procFileName, 256, "/proc/net/bonding/%s", bond->deviceName); FILE *procFile = fopen(procFileName, "r"); if(procFile) { // limit the number of chars we will read from each line // (there can be more than this - fgets will chop for us) #define MAX_PROC_LINE_CHARS 240 char line[MAX_PROC_LINE_CHARS]; SFLAdaptor *currentSlave = NULL; HSPAdaptorNIO *slave_nio = NULL; HSPAdaptorNIO *bond_nio = (HSPAdaptorNIO *)bond->userData; HSPAdaptorNIO *aggregator_slave_nio = NULL; bond_nio->lacp.attachedAggID = bond->ifIndex; uint32_t aggID = 0; // make sure we don't hold on to stale data - may need // to pick up actorSystemID from a slave port. memset(bond_nio->lacp.actorSystemID, 0, 6); memset(bond_nio->lacp.partnerSystemID, 0, 6); int readingMaster = YES; // bond master data comes first int gotActorID = NO; while(fgets(line, MAX_PROC_LINE_CHARS, procFile)) { char buf_var[MAX_PROC_LINE_CHARS]; char buf_val[MAX_PROC_LINE_CHARS]; // buf_var is up to first ':', buf_val is the rest if(sscanf(line, "%[^:]:%[^\n]", buf_var, buf_val) == 2) { char *tok_var = trimWhitespace(buf_var); char *tok_val = trimWhitespace(buf_val); if(readingMaster) { if(my_strequal(tok_var, "MII Status")) { if(my_strequal(tok_val, "up")) { bond_nio->lacp.portState.v.actorAdmin = 2; // dot3adAggPortActorAdminState bond_nio->lacp.portState.v.actorOper = 2; bond_nio->lacp.portState.v.partnerAdmin = 2; bond_nio->lacp.portState.v.partnerOper = 2; } else { bond_nio->lacp.portState.all = 0; } } if(my_strequal(tok_var, "System Identification")) { if(debug) { myLog(LOG_INFO, "updateBondCounters: %s system identification %s", bond->deviceName, tok_val); } char sys_mac[MAX_PROC_LINE_CHARS]; uint64_t code; if(sscanf(tok_val, "%"SCNu64" %s", &code, sys_mac) == 2) { if(hexToBinary((u_char *)sys_mac,bond_nio->lacp.actorSystemID, 6) != 6) { myLog(LOG_ERR, "updateBondCounters: system mac read error: %s", sys_mac); } else if(!isAllZero(bond_nio->lacp.actorSystemID, 6)) { gotActorID = YES; } } } if(my_strequal(tok_var, "Partner Mac Address")) { if(debug) { myLog(LOG_INFO, "updateBondCounters: %s partner mac is %s", bond->deviceName, tok_val); } if(hexToBinary((u_char *)tok_val,bond_nio->lacp.partnerSystemID, 6) != 6) { myLog(LOG_ERR, "updateBondCounters: partner mac read error: %s", tok_val); } } if(my_strequal(tok_var, "Aggregator ID")) { aggID = strtol(tok_val, NULL, 0); if(debug) { myLog(LOG_INFO, "updateBondCounters: %s aggID %u", bond->deviceName, aggID); } } } // initially the data is for the bond, but subsequently // we get info about each slave. So we started with // (readingMaster=YES,currentSlave=NULL), and now we // detect transitions to slave data: if(my_strequal(tok_var, "Slave Interface")) { readingMaster = NO; currentSlave = adaptorListGet(sp->adaptorList, trimWhitespace(tok_val)); slave_nio = currentSlave ? (HSPAdaptorNIO *)currentSlave->userData : NULL; if(debug) { myLog(LOG_INFO, "updateBondCounters: bond %s slave %s %s", bond->deviceName, tok_val, currentSlave ? "found" : "not found"); } if(slave_nio) { // initialize from bond slave_nio->lacp.attachedAggID = bond->ifIndex; memcpy(slave_nio->lacp.partnerSystemID, bond_nio->lacp.partnerSystemID, 6); memcpy(slave_nio->lacp.actorSystemID, bond_nio->lacp.actorSystemID, 6); // make sure the parent is going to export separate // counters if the slave is going to (because it was // marked as a switchPort): if(slave_nio->switchPort) { bond_nio->switchPort = YES; } // and vice-versa if(bond_nio->switchPort) { slave_nio->switchPort = YES; } } } if(readingMaster == NO && slave_nio) { if(my_strequal(tok_var, "MII Status")) { if(my_strequal(tok_val, "up")) { slave_nio->lacp.portState.v.actorAdmin = 2; // dot3adAggPortActorAdminState slave_nio->lacp.portState.v.actorOper = 2; slave_nio->lacp.portState.v.partnerAdmin = 2; slave_nio->lacp.portState.v.partnerOper = 2; } else { slave_nio->lacp.portState.all = 0; } } if(my_strequal(tok_var, "Permanent HW addr")) { if(!gotActorID) { // Still looking for our actorSystemID, so capture this here in case we // decide below that it is the one we want. Note that this mac may not be the // same as the mac associated with this port that we read back in readInterfaces.c. if(hexToBinary((u_char *)tok_val,slave_nio->lacp.actorSystemID, 6) != 6) { myLog(LOG_ERR, "updateBondCounters: permanent HW addr read error: %s", tok_val); } } } if(my_strequal(tok_var, "Aggregator ID")) { uint32_t slave_aggID = strtol(tok_val, NULL, 0); if(slave_aggID == aggID) { // remember that is the slave port that has the same aggregator ID as the bond aggregator_slave_nio = slave_nio; } } } } } if(aggregator_slave_nio && !gotActorID) { // go back and fill in the actorSystemID on all the slave ports shareActorIDFromSlave(sp, bond_nio, aggregator_slave_nio); } fclose(procFile); } }
void updateNioCounters(HSP *sp) { // don't do anything if we already refreshed the numbers less than a second ago if(sp->nio_last_update == sp->clk) { return; } sp->nio_last_update = sp->clk; FILE *procFile; procFile= fopen("/proc/net/dev", "r"); if(procFile) { #ifdef HSP_ETHTOOL_STATS int fd = socket (PF_INET, SOCK_DGRAM, 0); struct ifreq ifr; memset (&ifr, 0, sizeof(ifr)); #endif // ASCII numbers in /proc/diskstats may be 64-bit (if not now // then someday), so it seems safer to read into // 64-bit ints with scanf first, then copy them // into the host_nio structure from there. uint64_t bytes_in = 0; uint64_t pkts_in = 0; uint64_t errs_in = 0; uint64_t drops_in = 0; uint64_t bytes_out = 0; uint64_t pkts_out = 0; uint64_t errs_out = 0; uint64_t drops_out = 0; // limit the number of chars we will read from each line // (there can be more than this - fgets will chop for us) #define MAX_PROC_LINE_CHARS 240 char line[MAX_PROC_LINE_CHARS]; while(fgets(line, MAX_PROC_LINE_CHARS, procFile)) { char deviceName[MAX_PROC_LINE_CHARS]; // assume the format is: // Inter-| Receive | Transmit // face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed if(sscanf(line, "%[^:]:%"SCNu64" %"SCNu64" %"SCNu64" %"SCNu64" %*u %*u %*u %*u %"SCNu64" %"SCNu64" %"SCNu64" %"SCNu64"", deviceName, &bytes_in, &pkts_in, &errs_in, &drops_in, &bytes_out, &pkts_out, &errs_out, &drops_out) == 9) { SFLAdaptor *adaptor = adaptorListGet(sp->adaptorList, trimWhitespace(deviceName)); if(adaptor && adaptor->userData) { HSPAdaptorNIO *niostate = (HSPAdaptorNIO *)adaptor->userData; #ifdef HSP_ETHTOOL_STATS HSP_ethtool_counters et_ctrs = { 0 }, et_delta = { 0 }; if (niostate->et_nfound) { // get the latest stats block for this device via ethtool // and read out the counters that we located by name. uint32_t bytes = sizeof(struct ethtool_stats); bytes += niostate->et_nctrs * sizeof(uint64_t); bytes += 32; // pad - just in case driver wants to write more struct ethtool_stats *et_stats = (struct ethtool_stats *)my_calloc(bytes); et_stats->cmd = ETHTOOL_GSTATS; et_stats->n_stats = niostate->et_nctrs; // now issue the ioctl strncpy(ifr.ifr_name, adaptor->deviceName, sizeof(ifr.ifr_name)); ifr.ifr_data = (char *)et_stats; if(ioctl(fd, SIOCETHTOOL, &ifr) >= 0) { if(debug > 2) { for(int xx = 0; xx < et_stats->n_stats; xx++) { myLog(LOG_INFO, "ethtool counter for %s at index %d == %"PRIu64, adaptor->deviceName, xx, et_stats->data[xx]); } } if(niostate->et_idx_mcasts_in) { et_ctrs.mcasts_in = et_stats->data[niostate->et_idx_mcasts_in - 1]; et_delta.mcasts_in = et_ctrs.mcasts_in - niostate->et_last.mcasts_in; } if(niostate->et_idx_mcasts_out) { et_ctrs.mcasts_out = et_stats->data[niostate->et_idx_mcasts_out - 1]; et_delta.mcasts_out = et_ctrs.mcasts_out - niostate->et_last.mcasts_out; } if(niostate->et_idx_bcasts_in) { et_ctrs.bcasts_in = et_stats->data[niostate->et_idx_bcasts_in - 1]; et_delta.bcasts_in = et_ctrs.bcasts_in - niostate->et_last.bcasts_in; } if(niostate->et_idx_bcasts_out) { et_ctrs.bcasts_out = et_stats->data[niostate->et_idx_bcasts_out - 1]; et_delta.bcasts_out = et_ctrs.bcasts_out - niostate->et_last.bcasts_out; } } my_free(et_stats); } #endif // have to detect discontinuities here, so use a full // set of latched counters and accumulators. int accumulate = niostate->last_update ? YES : NO; niostate->last_update = sp->clk; uint64_t maxDeltaBytes = HSP_MAX_NIO_DELTA64; SFLHost_nio_counters delta; #define NIO_COMPUTE_DELTA(field) delta.field = field - niostate->last_nio.field NIO_COMPUTE_DELTA(pkts_in); NIO_COMPUTE_DELTA(errs_in); NIO_COMPUTE_DELTA(drops_in); NIO_COMPUTE_DELTA(pkts_out); NIO_COMPUTE_DELTA(errs_out); NIO_COMPUTE_DELTA(drops_out); if(sp->nio_polling_secs == 0) { // 64-bit byte counters NIO_COMPUTE_DELTA(bytes_in); NIO_COMPUTE_DELTA(bytes_out); } else { // for case where byte counters are 32-bit, we need // to use 32-bit unsigned arithmetic to avoid spikes delta.bytes_in = (uint32_t)bytes_in - niostate->last_bytes_in32; delta.bytes_out = (uint32_t)bytes_out - niostate->last_bytes_out32; niostate->last_bytes_in32 = bytes_in; niostate->last_bytes_out32 = bytes_out; maxDeltaBytes = HSP_MAX_NIO_DELTA32; // if we detect that the OS is using 64-bits then we can turn off the faster // NIO polling. This should probably be done based on the kernel version or some // other include-file definition, but it's not expensive to do it here like this: if(bytes_in > 0xFFFFFFFF || bytes_out > 0xFFFFFFFF) { myLog(LOG_INFO, "detected 64-bit counters in /proc/net/dev"); sp->nio_polling_secs = 0; } } if(accumulate) { // sanity check in case the counters were reset under out feet. // normally we leave this to the upstream collector, but these // numbers might be getting passed through from the hardware(?) // so we treat them with particular distrust. if(delta.bytes_in > maxDeltaBytes || delta.bytes_out > maxDeltaBytes || delta.pkts_in > HSP_MAX_NIO_DELTA32 || delta.pkts_out > HSP_MAX_NIO_DELTA32) { myLog(LOG_ERR, "detected counter discontinuity in /proc/net/dev for %s: deltaBytes=%"PRIu64",%"PRIu64" deltaPkts=%u,%u", adaptor->deviceName, delta.bytes_in, delta.bytes_out, delta.pkts_in, delta.pkts_out); accumulate = NO; } #ifdef HSP_ETHTOOL_STATS if(et_delta.mcasts_in > HSP_MAX_NIO_DELTA64 || et_delta.mcasts_out > HSP_MAX_NIO_DELTA64 || et_delta.bcasts_in > HSP_MAX_NIO_DELTA64 || et_delta.bcasts_out > HSP_MAX_NIO_DELTA64) { myLog(LOG_ERR, "detected counter discontinuity in ethtool stats"); accumulate = NO; } #endif } if(accumulate) { #define NIO_ACCUMULATE(field) niostate->nio.field += delta.field NIO_ACCUMULATE(bytes_in); NIO_ACCUMULATE(pkts_in); NIO_ACCUMULATE(errs_in); NIO_ACCUMULATE(drops_in); NIO_ACCUMULATE(bytes_out); NIO_ACCUMULATE(pkts_out); NIO_ACCUMULATE(errs_out); NIO_ACCUMULATE(drops_out); #ifdef HSP_ETHTOOL_STATS #define ET_ACCUMULATE(field) niostate->et_total.field += et_delta.field ET_ACCUMULATE(mcasts_in); ET_ACCUMULATE(mcasts_out); ET_ACCUMULATE(bcasts_in); ET_ACCUMULATE(bcasts_out); #endif } #define NIO_LATCH(field) niostate->last_nio.field = field NIO_LATCH(bytes_in); NIO_LATCH(pkts_in); NIO_LATCH(errs_in); NIO_LATCH(drops_in); NIO_LATCH(bytes_out); NIO_LATCH(pkts_out); NIO_LATCH(errs_out); NIO_LATCH(drops_out); #ifdef HSP_ETHTOOL_STATS niostate->et_last = et_ctrs; // struct copy #endif } } } #ifdef HSP_ETHTOOL_STATS if(fd >= 0) close(fd); #endif fclose(procFile); } }
void agentCB_getCounters_interface(void *magic, SFLPoller *poller, SFL_COUNTERS_SAMPLE_TYPE *cs) { assert(poller->magic); HSP *sp = (HSP *)poller->magic; // device name was copied as userData char *devName = (char *)poller->userData; if(devName) { // look up the adaptor objects SFLAdaptor *adaptor = adaptorListGet(sp->adaptorList, devName); if(adaptor && adaptor->userData) { HSPAdaptorNIO *adaptorNIO = (HSPAdaptorNIO *)adaptor->userData; // make sure the counters are up to the second updateNioCounters(sp); // see if we were able to discern multicast and broadcast counters // by polling for ethtool stats. Be careful to use unsigned 32-bit // arithmetic here: #define UNSUPPORTED_SFLOW_COUNTER32 (uint32_t)-1 uint32_t pkts_in = adaptorNIO->nio.pkts_in; uint32_t pkts_out = adaptorNIO->nio.pkts_out; uint32_t mcasts_in = UNSUPPORTED_SFLOW_COUNTER32; uint32_t mcasts_out = UNSUPPORTED_SFLOW_COUNTER32; uint32_t bcasts_in = UNSUPPORTED_SFLOW_COUNTER32; uint32_t bcasts_out = UNSUPPORTED_SFLOW_COUNTER32; #ifdef HSP_ETHTOOL_STATS // only do this if we were able to find all four // via ethtool, otherwise it would just be too weird... if(adaptorNIO->et_nfound == 4) { mcasts_in = (uint32_t)adaptorNIO->et_total.mcasts_in; bcasts_in = (uint32_t)adaptorNIO->et_total.bcasts_in; pkts_in -= (mcasts_in + bcasts_in); mcasts_out = (uint32_t)adaptorNIO->et_total.mcasts_out; bcasts_out = (uint32_t)adaptorNIO->et_total.bcasts_out; pkts_out -= (mcasts_out + bcasts_out); } #endif // generic interface counters SFLCounters_sample_element elem = { 0 }; elem.tag = SFLCOUNTERS_GENERIC; elem.counterBlock.generic.ifIndex = poller->dsi.ds_index; elem.counterBlock.generic.ifType = 6; // assume ethernet elem.counterBlock.generic.ifSpeed = adaptor->ifSpeed; elem.counterBlock.generic.ifDirection = adaptor->ifDirection; elem.counterBlock.generic.ifStatus = adaptorNIO->up ? (SFLSTATUS_ADMIN_UP | SFLSTATUS_OPER_UP) : 0; elem.counterBlock.generic.ifPromiscuousMode = adaptor->promiscuous; elem.counterBlock.generic.ifInOctets = adaptorNIO->nio.bytes_in; elem.counterBlock.generic.ifInUcastPkts = pkts_in; elem.counterBlock.generic.ifInMulticastPkts = mcasts_in; elem.counterBlock.generic.ifInBroadcastPkts = bcasts_in; elem.counterBlock.generic.ifInDiscards = adaptorNIO->nio.drops_in; elem.counterBlock.generic.ifInErrors = adaptorNIO->nio.errs_in; elem.counterBlock.generic.ifInUnknownProtos = UNSUPPORTED_SFLOW_COUNTER32; elem.counterBlock.generic.ifOutOctets = adaptorNIO->nio.bytes_out; elem.counterBlock.generic.ifOutUcastPkts = pkts_out; elem.counterBlock.generic.ifOutMulticastPkts = mcasts_out; elem.counterBlock.generic.ifOutBroadcastPkts = bcasts_out; elem.counterBlock.generic.ifOutDiscards = adaptorNIO->nio.drops_out; elem.counterBlock.generic.ifOutErrors = adaptorNIO->nio.errs_out; SFLADD_ELEMENT(cs, &elem); // add optional interface name struct SFLCounters_sample_element pn_elem = { 0 }; pn_elem.tag = SFLCOUNTERS_PORTNAME; pn_elem.counterBlock.portName.portName.len = my_strlen(devName); pn_elem.counterBlock.portName.portName.str = devName; SFLADD_ELEMENT(cs, &pn_elem); // possibly include LACP struct for bond master or slave SFLCounters_sample_element lacp_elem = { 0 }; if(adaptorNIO->bond_master || adaptorNIO->bond_slave) { updateBondCounters(sp, adaptor); lacp_elem.tag = SFLCOUNTERS_LACP; lacp_elem.counterBlock.lacp = adaptorNIO->lacp; // struct copy SFLADD_ELEMENT(cs, &lacp_elem); } sfl_poller_writeCountersSample(poller, cs); } } }
int readPackets(HSP *sp) { int batch = 0; static uint32_t MySkipCount=1; if(sp->sFlow->sFlowSettings == NULL) { // config was turned off return 0; } if(sp->sFlow->sFlowSettings->ulogSubSamplingRate == 0) { // packet sampling was disabled by setting desired rate to 0 return 0; } if(sp->ulog_soc) { for( ; batch < HSP_READPACKET_BATCH; batch++) { char buf[HSP_MAX_ULOG_MSG_BYTES]; socklen_t peerlen = sizeof(sp->ulog_peer); int len = recvfrom(sp->ulog_soc, buf, HSP_MAX_ULOG_MSG_BYTES, 0, (struct sockaddr *)&sp->ulog_peer, &peerlen); if(len <= 0) break; if(debug > 1) myLog(LOG_INFO, "got ULOG msg: %u bytes", len); for(struct nlmsghdr *msg = (struct nlmsghdr *)buf; NLMSG_OK(msg, len); msg=NLMSG_NEXT(msg, len)) { if(debug > 1) { myLog(LOG_INFO, "netlink (%u bytes left) msg [len=%u type=%u flags=0x%x seq=%u pid=%u]", len, msg->nlmsg_len, msg->nlmsg_type, msg->nlmsg_flags, msg->nlmsg_seq, msg->nlmsg_pid); } // check for drops indicated by sequence no uint32_t droppedSamples = 0; if(sp->ulog_seqno) { droppedSamples = msg->nlmsg_seq - sp->ulog_seqno - 1; if(droppedSamples) { sp->ulog_drops += droppedSamples; } } sp->ulog_seqno = msg->nlmsg_seq; switch(msg->nlmsg_type) { case NLMSG_NOOP: case NLMSG_ERROR: case NLMSG_OVERRUN: // ignore these break; case NLMSG_DONE: // last in multi-part default: { if(--MySkipCount == 0) { /* reached zero. Set the next skip */ MySkipCount = sfl_random((2 * sp->sFlow->sFlowSettings->ulogSubSamplingRate) - 1); /* and take a sample */ // we're seeing type==111 on Fedora14 //if(msg->nlmsg_flags & NLM_F_REQUEST) { } //if(msg->nlmsg_flags & NLM_F_MULTI) { } //if(msg->nlmsg_flags & NLM_F_ACK) { } //if(msg->nlmsg_flags & NLM_F_ECHO) { } ulog_packet_msg_t *pkt = NLMSG_DATA(msg); if(debug > 1) { myLog(LOG_INFO, "mark=%u ts=%s hook=%u in=%s out=%s len=%u prefix=%s maclen=%u", pkt->mark, ctime(&pkt->timestamp_sec), pkt->hook, pkt->indev_name, pkt->outdev_name, pkt->data_len, pkt->prefix, pkt->mac_len); if(pkt->mac_len == 14) { u_char macdst[12], macsrc[12]; printHex(pkt->mac+6,6,macsrc,12,NO); printHex(pkt->mac+0,6,macdst,12,NO); uint16_t ethtype = (pkt->mac[12] << 8) + pkt->mac[13]; myLog(LOG_INFO, "%s -> %s (ethertype=0x%04X)", macsrc, macdst, ethtype); } } SFL_FLOW_SAMPLE_TYPE fs = { 0 }; SFLAdaptor *sampler_dev = NULL; int inIsLoopback=0, outIsLoopback=0; // set the ingress and egress ifIndex numbers. // Can be "INTERNAL" (0x3FFFFFFF) or "UNKNOWN" (0). if(pkt->indev_name[0]) { SFLAdaptor *in = adaptorListGet(sp->adaptorList, pkt->indev_name); if(in) { fs.input = in->ifIndex; // record whether this was a loopback or not - used below HSPAdaptorNIO *inNIO = (HSPAdaptorNIO *)in->userData; inIsLoopback = inNIO->loopback; #ifdef HSF_CUMULUS // On Cumulus Linux the sampling direction is indicated in the low // bit of the pkt->hook field: 0==ingress,1==egress if((pkt->hook & 1) == 0) { sampler_dev = in; } #else // set this provisionally - may be overidden below sampler_dev = in; #endif } } else { fs.input = SFL_INTERNAL_INTERFACE; } if(pkt->outdev_name[0]) { SFLAdaptor *out = adaptorListGet(sp->adaptorList, pkt->outdev_name); if(out && out->ifIndex) { fs.output = out->ifIndex; HSPAdaptorNIO *outNIO = (HSPAdaptorNIO *)out->userData; outIsLoopback = outNIO->loopback; #ifdef HSF_CUMULUS // On Cumulus Linux the sampling direction is indicated in the low // bit of the pkt->hook field: 0==ingress,1==egress if((pkt->hook & 1) == 1) { sampler_dev = out; } #else // If one of them is not a loopback interface, then assume the // sample was taken there. In a typical scenario most samples // will be "lo" -> "eth0" or "eth0" -> "lo", so this ensures that // that we make that look like bidirectional sampling on eth0. if(sampler_dev == NULL || (inIsLoopback && !outIsLoopback)) { sampler_dev = out; } #endif } } else { fs.output = SFL_INTERNAL_INTERFACE; } // must have a sampler_dev with an ifIndex if(sampler_dev && sampler_dev->ifIndex) { HSPAdaptorNIO *samplerNIO = (HSPAdaptorNIO *)sampler_dev->userData; if(debug > 2) { myLog(LOG_INFO, "selected sampler %s (loopback in=%d out=%d)", sampler_dev->deviceName, inIsLoopback, outIsLoopback); } SFLSampler *sampler = getSampler(sp, sampler_dev); if(sampler) { SFLFlow_sample_element hdrElem = { 0 }; hdrElem.tag = SFLFLOW_HEADER; uint32_t FCS_bytes = 4; uint32_t maxHdrLen = sampler->sFlowFsMaximumHeaderSize; hdrElem.flowType.header.frame_length = pkt->data_len + FCS_bytes; hdrElem.flowType.header.stripped = FCS_bytes; u_char hdr[HSP_MAX_HEADER_BYTES]; if(pkt->mac_len == 14) { // set the header_protocol to ethernet and // reunite the mac header and payload in one buffer hdrElem.flowType.header.header_protocol = SFLHEADER_ETHERNET_ISO8023; memcpy(hdr, pkt->mac, 14); maxHdrLen -= 14; uint32_t payloadBytes = (pkt->data_len < maxHdrLen) ? pkt->data_len : maxHdrLen; memcpy(hdr+14, pkt->payload, payloadBytes); hdrElem.flowType.header.header_length = payloadBytes + 14; hdrElem.flowType.header.header_bytes = hdr; hdrElem.flowType.header.frame_length += 14; } else { // no need to copy - just point at the payload u_char ipversion = pkt->payload[0] >> 4; if(ipversion != 4 && ipversion != 6) { if(debug) myLog(LOG_ERR, "received non-IP packet. Encapsulation is unknown"); } else { hdrElem.flowType.header.header_protocol = (ipversion == 4) ? SFLHEADER_IPv4 : SFLHEADER_IPv6; hdrElem.flowType.header.stripped += 14; // assume ethernet was (or will be) the framing hdrElem.flowType.header.header_length = (pkt->data_len < maxHdrLen) ? pkt->data_len : maxHdrLen; hdrElem.flowType.header.header_bytes = pkt->payload; } } SFLADD_ELEMENT(&fs, &hdrElem); // submit the actual sampling rate so it goes out with the sFlow feed // otherwise the sampler object would fill in his own (sub-sampling) rate. // If it's a switch port then samplerNIO->sampling_n will be set, so that // takes precendence (allows different ports to have different sampling // settings). uint32_t actualSamplingRate = samplerNIO->sampling_n ?: sp->sFlow->sFlowSettings->ulogActualSamplingRate; fs.sampling_rate = actualSamplingRate; // estimate the sample pool from the samples. Could maybe do this // above with the (possibly more granular) ulogSamplingRate, but then // we would have to look up the sampler object every time, which // might be too expensive in the case where ulogSamplingRate==1. sampler->samplePool += actualSamplingRate; // accumulate any dropped-samples we detected against whichever sampler // sends the next sample. This is not perfect, but is likely to accrue // drops against the point whose sampling-rate needs to be adjusted. samplerNIO->ulog_drops += droppedSamples; fs.drops = samplerNIO->ulog_drops; sfl_sampler_writeFlowSample(sampler, &fs); } } } } } } }
int selectAgentAddress(HSP *sp, int *p_changed) { int selected = NO; SFLAddress previous = sp->sFlow->agentIP; if(debug) myLog(LOG_INFO, "selectAgentAddress"); if(sp->sFlow->explicitAgentIP && sp->sFlow->agentIP.type) { // it was hard-coded in the config file if(debug) myLog(LOG_INFO, "selectAgentAddress hard-coded in config file"); selected = YES; } else if(sp->sFlow->explicitAgentDevice && sp->sFlow->agentDevice) { // it may have been defined as agent=<device> SFLAdaptor *ad = adaptorListGet(sp->adaptorList, sp->sFlow->agentDevice); if(ad && ad->userData) { HSPAdaptorNIO *adaptorNIO = (HSPAdaptorNIO *)ad->userData; sp->sFlow->agentIP = adaptorNIO->ipAddr; if(debug) myLog(LOG_INFO, "selectAgentAddress pegged to device in config file"); selected = YES; } } else { // try to automatically choose an IP (or IPv6) address, based on the priority ranking. // We already used this ranking to prioritize L3 addresses per adaptor (in the case where // there are more than one) so now we are applying the same ranking globally to pick // the best candidate to represent the whole agent: SFLAdaptor *selectedAdaptor = NULL; EnumIPSelectionPriority ipPriority = IPSP_NONE; for(uint32_t i = 0; i < sp->adaptorList->num_adaptors; i++) { SFLAdaptor *adaptor = sp->adaptorList->adaptors[i]; if(adaptor && adaptor->userData) { HSPAdaptorNIO *adaptorNIO = (HSPAdaptorNIO *)adaptor->userData; // take the highest priority one, but if we have more than one with the same // priority then choose the one with the lowest (non-zero) ifIndex number: if(adaptorNIO->ipPriority > ipPriority || (adaptorNIO->ipPriority == ipPriority && adaptor->ifIndex && selectedAdaptor && (selectedAdaptor->ifIndex == 0 || adaptor->ifIndex < selectedAdaptor->ifIndex))) { selectedAdaptor = adaptor; ipPriority = adaptorNIO->ipPriority; } } } if(selectedAdaptor && selectedAdaptor->userData) { // crown the winner HSPAdaptorNIO *adaptorNIO = (HSPAdaptorNIO *)selectedAdaptor->userData; sp->sFlow->agentIP = adaptorNIO->ipAddr; sp->sFlow->agentDevice = my_strdup(selectedAdaptor->deviceName); if(debug) myLog(LOG_INFO, "selectAgentAddress selected agentIP with highest priority"); selected = YES; } } if(p_changed) { if(SFLAddress_equal(&previous, &sp->sFlow->agentIP)) { *p_changed = YES; } else { *p_changed = NO; } } return selected; }
/** * Call back function called when it is time to sample the Hyper-V switch interface counters. * Uses the poller->userData to find the device name (ie switch port guid) which identifies * the counter instance for the switch port. */ void getCounters_interface(void *magic, SFLPoller *poller, SFL_COUNTERS_SAMPLE_TYPE *cs) { HSP *sp = (HSP *)poller->magic; // device name was copied as userData char *deviceName = (char *)poller->userData; myLog(LOG_INFO, "getCounters_interface: dsClass=%u, dsIndex=%u, deviceName=%s", poller->dsi.ds_class, poller->dsi.ds_index, deviceName); if (deviceName) { //find the vadaptor for the deviceName SFLAdaptor *vAdaptor = adaptorListGet(sp->vAdaptorList, deviceName); if (vAdaptor != NULL) { myLog(LOG_INFO, "getCounters_interface: found adaptor index=%u", vAdaptor->ifIndex); wchar_t *instance = ((HVSVPortInfo *)vAdaptor->userData)->portCountersInstance; if (!instance) { myLog(LOG_INFO, "getCounters_interface: counters instance name not set for %s", deviceName); return; } //Get the counter data SFLCounters_sample_element elem = { 0 }; elem.tag = SFLCOUNTERS_GENERIC; elem.counterBlock.generic.ifIndex = poller->dsi.ds_index; elem.counterBlock.generic.ifType = 6; // assume ethernet elem.counterBlock.generic.ifSpeed = vAdaptor->ifSpeed; elem.counterBlock.generic.ifDirection = vAdaptor->ifDirection; elem.counterBlock.generic.ifStatus = 3; // ifAdminStatus==up, ifOperstatus==up elem.counterBlock.generic.ifPromiscuousMode = vAdaptor->promiscuous; PDH_HQUERY query; if (PdhOpenQuery(NULL, 0, &query) == ERROR_SUCCESS) { PDH_HCOUNTER bytesIn, pktsIn, mcastsIn, bcastsIn, discardsIn; PDH_HCOUNTER bytesOut, pktsOut, mcastsOut, bcastsOut, discardsOut; if (addCounter(instance, IF_COUNTER_BYTES_IN, &query, &bytesIn) == ERROR_SUCCESS && addCounter(instance, IF_COUNTER_PACKETS_IN, &query, &pktsIn) == ERROR_SUCCESS && addCounter(instance, IF_COUNTER_MULTICASTS_IN, &query, &mcastsIn) == ERROR_SUCCESS && addCounter(instance, IF_COUNTER_BROADCASTS_IN, &query, &bcastsIn) == ERROR_SUCCESS && addCounter(instance, IF_COUNTER_DISCARDS_IN, &query, &discardsIn) == ERROR_SUCCESS && addCounter(instance, IF_COUNTER_BYTES_OUT, &query, &bytesOut) == ERROR_SUCCESS && addCounter(instance, IF_COUNTER_PACKETS_OUT, &query, &pktsOut) == ERROR_SUCCESS && addCounter(instance, IF_COUNTER_MULTICASTS_OUT, &query, &mcastsOut) == ERROR_SUCCESS && addCounter(instance, IF_COUNTER_BROADCASTS_OUT, &query, &bcastsOut) == ERROR_SUCCESS && addCounter(instance, IF_COUNTER_DISCARDS_OUT, &query, &discardsOut) == ERROR_SUCCESS && PdhCollectQueryData(query) == ERROR_SUCCESS) { elem.counterBlock.generic.ifInOctets = getRawCounterValue(&bytesIn); elem.counterBlock.generic.ifInUcastPkts = (uint32_t)getRawCounterValue(&pktsIn); elem.counterBlock.generic.ifInMulticastPkts = (uint32_t)getRawCounterValue(&mcastsIn); elem.counterBlock.generic.ifInBroadcastPkts = (uint32_t)getRawCounterValue(&bcastsIn); elem.counterBlock.generic.ifInDiscards = (uint32_t)getRawCounterValue(&discardsIn); elem.counterBlock.generic.ifOutOctets = getRawCounterValue(&bytesOut); elem.counterBlock.generic.ifOutUcastPkts = (uint32_t)getRawCounterValue(&pktsOut); elem.counterBlock.generic.ifOutMulticastPkts = (uint32_t)getRawCounterValue(&mcastsOut); elem.counterBlock.generic.ifOutBroadcastPkts = (uint32_t)getRawCounterValue(&bcastsOut); elem.counterBlock.generic.ifOutDiscards = (uint32_t) getRawCounterValue(&discardsOut); } PdhCloseQuery(query); } elem.counterBlock.generic.ifInErrors = UNKNOWN_COUNTER; elem.counterBlock.generic.ifOutErrors = UNKNOWN_COUNTER; elem.counterBlock.generic.ifInUnknownProtos = UNKNOWN_COUNTER; SFLADD_ELEMENT(cs, &elem); myLog(LOG_INFO, "getCounters_interface:\n\tifIndex:\t%u\n\tifType:\t%u\n\tifSpeed:\t%I64u\n" "\tifDirection:\t%u\n\tifStatus:\t%u\n\tpromiscuous:\t%u\n\tinOctets:\t%I64u\n\tinUcast:\t%u\n" "\tinMulticast:\t%u\n\tinBroadcast:\t%u\n\tinDiscards:\t%u\n\tinErrors:\t%u\n\toutOctets:\t%I64u\n" "\toutUcast:\t%u\n\toutMulticast:\t%u\n\toutBroadcast:\t%u\n\toutDiscards:\t%u\n\toutErrors:\t%u", elem.counterBlock.generic.ifIndex, elem.counterBlock.generic.ifType, elem.counterBlock.generic.ifSpeed, elem.counterBlock.generic.ifDirection, elem.counterBlock.generic.ifStatus, elem.counterBlock.generic.ifPromiscuousMode, elem.counterBlock.generic.ifInOctets, elem.counterBlock.generic.ifInUcastPkts, elem.counterBlock.generic.ifInMulticastPkts, elem.counterBlock.generic.ifInBroadcastPkts, elem.counterBlock.generic.ifInDiscards, elem.counterBlock.generic.ifInErrors, elem.counterBlock.generic.ifOutOctets, elem.counterBlock.generic.ifOutUcastPkts, elem.counterBlock.generic.ifOutMulticastPkts, elem.counterBlock.generic.ifOutBroadcastPkts, elem.counterBlock.generic.ifOutDiscards, elem.counterBlock.generic.ifOutErrors); sfl_poller_writeCountersSample(poller, cs); } } }