/* * Copyright(C) 2002-07 Luca Deri * * http://www.ntop.org/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* This plugin works only with threads */ #include "ntop.h" #include "globals-report.h" static void* netflowMainLoop(void* _deviceId); #ifdef HAVE_SNMP static void* netflowUtilsLoop(void* _deviceId); #endif /* #define DEBUG_FLOWS */ #define CONST_NETFLOW_STATISTICS_HTML "statistics.html" #define valueOf(a) (a == NULL ? "" : a) #define isEmpty(a) ((a == NULL) || (a[0] == '\0') ? 1 : 0) /* ********************************* */ /* Forward */ static int setNetFlowInSocket(int); static void setNetFlowInterfaceMatrix(int); static void freeNetFlowMatrixMemory(int); static void setPluginStatus(char * status); static int initNetFlowFunct(void); static void termNetflowFunct(u_char termNtop /* 0=term plugin, 1=term ntop */); static void termNetflowDevice(int deviceId); static void initNetFlowDevice(int deviceId); #ifdef DEBUG_FLOWS static void handleNetFlowPacket(u_char *_deviceId, const struct pcap_pkthdr *h, const u_char *p); #endif static void handleNetflowHTTPrequest(char* url); static void printNetFlowStatisticsRcvd(int deviceId); static void printNetFlowConfiguration(int deviceId); static int createNetFlowDevice(int netFlowDeviceId); static int mapNetFlowDeviceToNtopDevice(int deviceId); struct generic_netflow_record { /* v5 */ u_int32_t srcaddr; /* Source IP Address */ u_int32_t dstaddr; /* Destination IP Address */ u_int32_t nexthop; /* Next hop router's IP Address */ u_int16_t input; /* Input interface index */ u_int16_t output; /* Output interface index */ u_int32_t sentPkts, rcvdPkts; u_int32_t sentOctets, rcvdOctets; u_int32_t first; /* SysUptime at start of flow */ u_int32_t last; /* and of last packet of the flow */ u_int16_t srcport; /* TCP/UDP source port number (.e.g, FTP, Telnet, etc.,or equivalent) */ u_int16_t dstport; /* TCP/UDP destination port number (.e.g, FTP, Telnet, etc.,or equivalent) */ u_int8_t tcp_flags; /* Cumulative OR of tcp flags */ u_int8_t proto; /* IP protocol, e.g., 6=TCP, 17=UDP, etc... */ u_int8_t tos; /* IP Type-of-Service */ u_int16_t dst_as; /* dst peer/origin Autonomous System */ u_int16_t src_as; /* source peer/origin Autonomous System */ u_int8_t dst_mask; /* destination route's mask bits */ u_int8_t src_mask; /* source route's mask bits */ /* v9 */ u_int16_t vlanId; /* Latency extensions */ u_int32_t nw_latency_sec, nw_latency_usec; /* VoIP Extensions */ char sip_call_id[50], sip_calling_party[50], sip_called_party[50]; }; /* ****************************** */ u_char static pluginActive = 0; static ExtraPage netflowExtraPages[] = { { NULL, CONST_NETFLOW_STATISTICS_HTML, "Statistics" }, { NULL, NULL, NULL } }; static PluginInfo netflowPluginInfo[] = { { VERSION, /* current ntop version */ "NetFlow", "This plugin is used to setup, activate and deactivate NetFlow support.
" "ntop can both collect and receive " "NetFlow " "V1/V5/V7/V9 and IPFIX (draft) data.
" "Received flow data is reported as a separate 'NIC' in the regular ntop " "reports.
Remember to switch the reporting NIC.", "4.1", /* version */ "L.Deri", "NetFlow", /* http://:/plugins/NetFlow */ 0, /* Active by default */ ViewConfigure, 1, /* Inactive setup */ initNetFlowFunct, /* InitFunc */ termNetflowFunct, /* TermFunc */ #ifdef DEBUG_FLOWS handleNetFlowPacket, #else NULL, /* PluginFunc */ #endif handleNetflowHTTPrequest, NULL, /* no host creation/deletion handle */ #ifdef DEBUG_FLOWS "udp and (port 2055)", #else NULL, /* no capture */ #endif NULL, /* no status */ netflowExtraPages } }; #ifdef MAX_NETFLOW_FLOW_BUFFER static float netflowflowBuffer[MAX_NETFLOW_FLOW_BUFFER]; static int netflowflowBufferCount; static float netflowfmaxTime; #endif #ifdef MAX_NETFLOW_PACKET_BUFFER static float netflowpacketBuffer[MAX_NETFLOW_PACKET_BUFFER]; static int netflowpacketBufferCount; static float netflowpmaxTime; #endif /* ****************************** */ static char* nfValue(int deviceId, char *name, int appendDeviceId) { static char buf[64]; if(appendDeviceId) { safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "netflow.%d.%s", myGlobals.device[deviceId].netflowGlobals->netFlowDeviceId, name); } else { safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "netflow.%s", name); } #ifdef DEBUG traceEvent(CONST_TRACE_INFO, "NETFLOW: nfValue=%s", buf); #endif return(buf); } /* ****************************** */ static void freeNetFlowMatrixMemory(int deviceId) { /* NOTE: wee need to lock something here(TBD) */ if((!myGlobals.device[deviceId].activeDevice) ||(deviceId == -1)) return; if(myGlobals.device[deviceId].ipTrafficMatrix != NULL) { int j; /* Courtesy of Wies-Software */ for(j=0; j<(myGlobals.device[deviceId].numHosts * myGlobals.device[deviceId].numHosts); j++) if(myGlobals.device[deviceId].ipTrafficMatrix[j] != NULL) free(myGlobals.device[deviceId].ipTrafficMatrix[j]); free(myGlobals.device[deviceId].ipTrafficMatrix); } if(myGlobals.device[deviceId].ipTrafficMatrixHosts != NULL) free(myGlobals.device[deviceId].ipTrafficMatrixHosts); } /* ************************************************** */ static void setNetFlowInterfaceMatrix(int deviceId) { if((!myGlobals.device[deviceId].activeDevice) || (deviceId == -1)) return; myGlobals.device[deviceId].numHosts = 0xFFFFFFFF - myGlobals.device[deviceId].netflowGlobals->netFlowIfMask.s_addr+1; myGlobals.device[deviceId].ifAddr.s_addr = myGlobals.device[deviceId].netflowGlobals->netFlowIfAddress.s_addr; myGlobals.device[deviceId].network.s_addr = myGlobals.device[deviceId].netflowGlobals->netFlowIfAddress.s_addr; myGlobals.device[deviceId].netmask.s_addr = myGlobals.device[deviceId].netflowGlobals->netFlowIfMask.s_addr; if(myGlobals.device[deviceId].numHosts > MAX_SUBNET_HOSTS) { myGlobals.device[deviceId].numHosts = MAX_SUBNET_HOSTS; traceEvent(CONST_TRACE_WARNING, "NETFLOW: Truncated network size(device %s) to %d hosts(real netmask %s).", myGlobals.device[deviceId].name, myGlobals.device[deviceId].numHosts, intoa(myGlobals.device[deviceId].netmask)); } myGlobals.device[deviceId].ipTrafficMatrix = (TrafficEntry**)calloc(myGlobals.device[deviceId].numHosts* myGlobals.device[deviceId].numHosts, sizeof(TrafficEntry*)); myGlobals.device[deviceId].ipTrafficMatrixHosts = (struct hostTraffic**)calloc(sizeof(struct hostTraffic*), myGlobals.device[deviceId].numHosts); } /* ************************************** */ static int setNetFlowInSocket(int deviceId) { struct sockaddr_in sockIn; int sockopt = 1; if(myGlobals.device[deviceId].netflowGlobals->netFlowInSocket > 0) { traceEvent(CONST_TRACE_ALWAYSDISPLAY, "NETFLOW: Collector terminated"); closeNwSocket(&myGlobals.device[deviceId].netflowGlobals->netFlowInSocket); #ifdef HAVE_SCTP if(myGlobals.device[deviceId].netflowGlobals->netFlowInSctpSocket > 0) closeNwSocket(&myGlobals.device[deviceId].netflowGlobals->netFlowInSctpSocket); #endif } if(myGlobals.device[deviceId].netflowGlobals->netFlowInPort > 0) { errno = 0; myGlobals.device[deviceId].netflowGlobals->netFlowInSocket = socket(AF_INET, SOCK_DGRAM, 0); if((myGlobals.device[deviceId].netflowGlobals->netFlowInSocket <= 0) || (errno != 0) ) { traceEvent(CONST_TRACE_INFO, "NETFLOW: Unable to create a UDP socket - returned %d, error is '%s'(%d)", myGlobals.device[deviceId].netflowGlobals->netFlowInSocket, strerror(errno), errno); setPluginStatus("Disabled - Unable to create listening socket."); return(-1); } #ifdef HAVE_SCTP myGlobals.device[deviceId].netflowGlobals->netFlowInSctpSocket = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP); if((myGlobals.device[deviceId].netflowGlobals->netFlowInSctpSocket <= 0) || (errno != 0)) { traceEvent(CONST_TRACE_INFO, "NETFLOW: Unable to create a SCTP socket - returned %d, error is '%s'(%d)", myGlobals.device[deviceId].netflowGlobals->netFlowInSocket, strerror(errno), errno); /* setPluginStatus("SCTP disabled - Unable to create listening socket."); */ } #endif traceEvent(CONST_TRACE_INFO, "NETFLOW: Created a UDP socket (%d)", myGlobals.device[deviceId].netflowGlobals->netFlowInSocket); #ifdef HAVE_SCTP if(myGlobals.device[deviceId].netflowGlobals->netFlowInSctpSocket > 0) traceEvent(CONST_TRACE_INFO, "NETFLOW: Created a SCTP socket (%d)", myGlobals.device[deviceId].netflowGlobals->netFlowInSctpSocket); #endif setsockopt(myGlobals.device[deviceId].netflowGlobals->netFlowInSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&sockopt, sizeof(sockopt)); sockIn.sin_family = AF_INET; sockIn.sin_port =(int)htons(myGlobals.device[deviceId].netflowGlobals->netFlowInPort); sockIn.sin_addr.s_addr = INADDR_ANY; if((bind(myGlobals.device[deviceId].netflowGlobals->netFlowInSocket, (struct sockaddr *)&sockIn, sizeof(sockIn)) < 0) #ifdef HAVE_SCTP || ((myGlobals.device[deviceId].netflowGlobals->netFlowInSctpSocket > 0) && (bind(myGlobals.device[deviceId].netflowGlobals->netFlowInSctpSocket, (struct sockaddr *)&sockIn, sizeof(sockIn)) < 0)) #endif ) { traceEvent(CONST_TRACE_ERROR, "NETFLOW: Collector port %d already in use", myGlobals.device[deviceId].netflowGlobals->netFlowInPort); closeNwSocket(&myGlobals.device[deviceId].netflowGlobals->netFlowInSocket); myGlobals.device[deviceId].netflowGlobals->netFlowInSocket = 0; #ifdef HAVE_SCTP if(myGlobals.device[deviceId].netflowGlobals->netFlowInSctpSocket) closeNwSocket(&myGlobals.device[deviceId].netflowGlobals->netFlowInSctpSocket); myGlobals.device[deviceId].netflowGlobals->netFlowInSctpSocket = 0; #endif return(0); } #ifdef HAVE_SCTP if(myGlobals.device[deviceId].netflowGlobals->netFlowInSctpSocket > 0) { if(listen(myGlobals.device[deviceId].netflowGlobals->netFlowInSctpSocket, 100) == -1) { traceEvent(CONST_TRACE_ERROR, "NETFLOW: listen on SCTP socket failed [%s]", strerror(errno)); } } #endif traceEvent(CONST_TRACE_ALWAYSDISPLAY, "NETFLOW: Collector listening on port %d", myGlobals.device[deviceId].netflowGlobals->netFlowInPort); } if((myGlobals.device[deviceId].netflowGlobals->netFlowInPort != 0) && (!myGlobals.device[deviceId].netflowGlobals->threadActive)) { /* This plugin works only with threads */ createThread(&myGlobals.device[deviceId].netflowGlobals->netFlowThread, netflowMainLoop, (void*)((long)deviceId)); #ifdef HAVE_SNMP createThread(&myGlobals.device[deviceId].netflowGlobals->netFlowUtilsThread, netflowUtilsLoop, (void*)((long)deviceId)); #endif traceEvent(CONST_TRACE_INFO, "THREADMGMT[t%lu]: NETFLOW: Started thread for receiving flows on port %d", (long)myGlobals.device[deviceId].netflowGlobals->netFlowThread, myGlobals.device[deviceId].netflowGlobals->netFlowInPort); } return(0); } /* *************************** */ #ifdef HAVE_SNMP static void updateInterfaceName(InterfaceStats *ifStats) { char buf[32]; struct in_addr addr; addr.s_addr = ifStats->netflow_device_ip; getIfName(_intoa(addr, buf, sizeof(buf)), "public", ifStats->interface_id, ifStats->interface_name, sizeof(ifStats->interface_name)); } #endif /* *************************** */ static void updateNetFlowIfStats(u_int32_t netflow_device_ip, int deviceId, u_int32_t ifId, u_char selfUpdate, u_char sentStats, u_int32_t _pkts, u_int32_t _octets) { if(_pkts == 0) return; else { u_char found = 0; InterfaceStats *ifStats, *prev = NULL; Counter pkts = (Counter)_pkts; Counter octets = (Counter)_octets; accessMutex(&myGlobals.device[deviceId].netflowGlobals->ifStatsMutex, "rrdPluginNetflow"); ifStats = myGlobals.device[deviceId].netflowGlobals->ifStats; while(ifStats != NULL) { if((ifStats->interface_id == ifId) && (ifStats->netflow_device_ip == netflow_device_ip)) { found = 1; break; } else if(ifStats->interface_id > ifId) break; else { prev = ifStats; ifStats = ifStats->next; } } if(!found) { if((ifStats = (InterfaceStats*)malloc(sizeof(InterfaceStats))) == NULL) { traceEvent(CONST_TRACE_ERROR, "NETFLOW: not enough memory"); releaseMutex(&myGlobals.device[deviceId].netflowGlobals->ifStatsMutex); return; } memset(ifStats, 0, sizeof(InterfaceStats)); ifStats->netflow_device_ip = netflow_device_ip, ifStats->interface_id = ifId; resetTrafficCounter(&ifStats->outBytes); resetTrafficCounter(&ifStats->outPkts); resetTrafficCounter(&ifStats->inBytes); resetTrafficCounter(&ifStats->inPkts); resetTrafficCounter(&ifStats->selfBytes); resetTrafficCounter(&ifStats->selfPkts); if(prev == NULL) { ifStats->next = myGlobals.device[deviceId].netflowGlobals->ifStats; myGlobals.device[deviceId].netflowGlobals->ifStats = ifStats; } else { ifStats->next = prev->next; prev->next = ifStats; } #ifdef HAVE_SNMP accessMutex(&myGlobals.device[deviceId].netflowGlobals->ifStatsQueueMutex, "netflowUtilsLoop"); if(myGlobals.device[deviceId].netflowGlobals->ifStatsQueue_len < (MAX_INTERFACE_STATS_QUEUE_LEN-1)) { myGlobals.device[deviceId].netflowGlobals->ifStatsQueue[myGlobals.device[deviceId].netflowGlobals->ifStatsQueue_len++] = ifStats; signalCondvar(&myGlobals.device[deviceId].netflowGlobals->ifStatsQueueCondvar); } releaseMutex(&myGlobals.device[deviceId].netflowGlobals->ifStatsQueueMutex); #else ifStats->interface_name[0] = '\0'; #endif } releaseMutex(&myGlobals.device[deviceId].netflowGlobals->ifStatsMutex); if(selfUpdate) { incrementTrafficCounter(&ifStats->selfBytes, octets); incrementTrafficCounter(&ifStats->selfPkts, pkts); } else { if(sentStats) { incrementTrafficCounter(&ifStats->outBytes, octets); incrementTrafficCounter(&ifStats->outPkts, pkts); } else { incrementTrafficCounter(&ifStats->inBytes, octets); incrementTrafficCounter(&ifStats->inPkts, pkts); } } } } /* *************************** */ static void updateInterfaceStats(u_int32_t netflow_device_ip, int deviceId, struct generic_netflow_record *record) { if((myGlobals.device[deviceId].netflowGlobals == NULL) || (record == NULL)) { traceEvent(CONST_TRACE_WARNING, "NETFLOW: internal error, NULL interface stats"); return; } if(0) traceEvent(CONST_TRACE_INFO, "NETFLOW: updateInterfaceStats(%d/%d) " "[sent_pkts=%d][sent_bytes=%d][rcvd_pkts=%d][rcvd_bytes=%d]", record->input, record->output, ntohl(record->sentPkts), ntohl(record->sentOctets), ntohl(record->rcvdPkts), ntohl(record->rcvdOctets)); updateNetFlowIfStats(netflow_device_ip, deviceId, record->output, 0, 1, ntohl(record->sentPkts), ntohl(record->sentOctets)); if(record->input == record->output) updateNetFlowIfStats(netflow_device_ip, deviceId, record->input, 1 /* self update */, 0, ntohl(2*record->sentPkts), ntohl(2*record->sentOctets)); else if(ntohl(record->rcvdPkts) != 0) { updateNetFlowIfStats(netflow_device_ip, deviceId, record->input, 0, 0, ntohl(record->rcvdPkts), ntohl(record->rcvdOctets)); } else { /* pre v9 */ updateNetFlowIfStats(netflow_device_ip, deviceId, record->input, 0, 0, ntohl(record->sentPkts), ntohl(record->sentOctets)); } } /* *************************** */ static int handleGenericFlow(u_int32_t netflow_device_ip, time_t recordActTime, time_t recordSysUpTime, struct generic_netflow_record *record, int deviceId, time_t *firstSeen, time_t *lastSeen) { int actualDeviceId; Counter len; char theFlags[256], srcPseudoLocal, dstPseudoLocal; u_int16_t srcAS, dstAS; struct in_addr a, b; HostAddr addr1, addr2; u_int numPkts; HostTraffic *srcHost=NULL, *dstHost=NULL; u_short sport, dport, proto, newSession = 0; TrafficCounter ctr; int skipSRC=0, skipDST=0; struct pcap_pkthdr h; struct tcphdr tp; IPSession *session = NULL; time_t initTime; #ifdef MAX_NETFLOW_FLOW_BUFFER float elapsed; struct timeval netflowStartOfFlowProcessing, netflowEndOfFlowProcessing; gettimeofday(&netflowStartOfFlowProcessing, NULL); #endif myGlobals.device[deviceId].netflowGlobals->numNetFlowsRcvd++; numPkts = ntohl(record->sentPkts); len = (Counter)ntohl(record->sentOctets); /* Bad flow(zero packets) */ if(numPkts == 0) { myGlobals.device[deviceId].netflowGlobals->numBadFlowPkts++; return(0); } /* Bad flow(zero length) */ if(len == 0) { myGlobals.device[deviceId].netflowGlobals->numBadFlowBytes++; return(0); } /* Bad flow(more packets than bytes) */ if(numPkts > len) { myGlobals.device[deviceId].netflowGlobals->numBadFlowReality++; return(0); } myGlobals.actTime = time(NULL); recordActTime = ntohl(recordActTime); recordSysUpTime = ntohl(recordSysUpTime); initTime = recordActTime-(recordSysUpTime/1000); *firstSeen = (ntohl(record->first)/1000) + initTime; *lastSeen = (ntohl(record->last)/1000) + initTime; /* Sanity check */ if(*lastSeen > myGlobals.actTime) *lastSeen = myGlobals.actTime; if(*firstSeen > *lastSeen) *firstSeen = *lastSeen; myGlobals.device[deviceId].netflowGlobals->numNetFlowsProcessed++; a.s_addr = ntohl(record->srcaddr); b.s_addr = ntohl(record->dstaddr); sport = ntohs(record->srcport); dport = ntohs(record->dstport); proto = record->proto; srcAS = ntohs(record->src_as); dstAS = ntohs(record->dst_as); #ifdef DEBUG_FLOWS if(0) { char buf1[256], buf[256]; traceEvent(CONST_TRACE_INFO, "[%s:%d <-> %s:%d][pkt=%u/len=%u][sAS=%d/dAS=%d][proto=%d]", _intoa(a, buf, sizeof(buf)), sport, _intoa(b, buf1, sizeof(buf1)), dport, numPkts, len, srcAS, dstAS, proto); } #endif switch(myGlobals.device[deviceId].netflowGlobals->netFlowAggregation) { case noAggregation: /* Nothing to do */ break; case portAggregation: a.s_addr = b.s_addr = 0; /* 0.0.0.0 */ break; case hostAggregation: sport = dport = 0; break; case protocolAggregation: skipDST = skipSRC = 1; a.s_addr = b.s_addr = 0; /* 0.0.0.0 */ sport = dport = 0; srcAS = dstAS = 0; break; case asAggregation: skipDST = skipSRC = 1; a.s_addr = b.s_addr = 0; /* 0.0.0.0 */ sport = dport = 0; proto = 17; /* UDP */ break; } if(myGlobals.device[deviceId].netflowGlobals->netFlowDebug) { theFlags[0] = '\0'; if(record->tcp_flags & TH_SYN) strncat(theFlags, "SYN ", (sizeof(theFlags) - strlen(theFlags) - 1)); if(record->tcp_flags & TH_FIN) strncat(theFlags, "FIN ", (sizeof(theFlags) - strlen(theFlags) - 1)); if(record->tcp_flags & TH_RST) strncat(theFlags, "RST ", (sizeof(theFlags) - strlen(theFlags) - 1)); if(record->tcp_flags & TH_ACK) strncat(theFlags, "ACK ", (sizeof(theFlags) - strlen(theFlags) - 1)); if(record->tcp_flags & TH_PUSH) strncat(theFlags, "PUSH", (sizeof(theFlags) - strlen(theFlags) - 1)); } /* traceEvent(CONST_TRACE_INFO, "NETFLOW_DEBUG: a=%u", record->srcaddr); */ actualDeviceId = deviceId; if((actualDeviceId == -1) ||(actualDeviceId >= myGlobals.numDevices)) { traceEvent(CONST_TRACE_ERROR, "NETFLOW: deviceId(%d) is out of range - ignored", actualDeviceId); return(-1); } myGlobals.device[actualDeviceId].receivedPkts.value += numPkts; myGlobals.device[actualDeviceId].ethernetPkts.value += numPkts; myGlobals.device[actualDeviceId].ipPkts.value += numPkts; /* Average number of packets */ updateDevicePacketStats((u_int)(len/numPkts), actualDeviceId); myGlobals.device[actualDeviceId].ethernetBytes.value += len; myGlobals.device[actualDeviceId].ipBytes.value += len; if (numPkts > 0) { u_int ratio = len/numPkts; if(ratio <= 64) myGlobals.device[actualDeviceId].rcvdPktStats.upTo64.value += numPkts; else if(ratio <= 128) myGlobals.device[actualDeviceId].rcvdPktStats.upTo128.value += numPkts; else if(ratio <= 256) myGlobals.device[actualDeviceId].rcvdPktStats.upTo256.value += numPkts; else if(ratio <= 512) myGlobals.device[actualDeviceId].rcvdPktStats.upTo512.value += numPkts; else if(ratio <= 1024) myGlobals.device[actualDeviceId].rcvdPktStats.upTo1024.value += numPkts; else if(ratio <= 1518) myGlobals.device[actualDeviceId].rcvdPktStats.upTo1518.value += numPkts; } /* accessMutex(&myGlobals.hostsHashMutex, "processNetFlowPacket"); */ record->input = ntohs(record->input), record->output = ntohs(record->output); updateInterfaceStats(netflow_device_ip, deviceId, record); if(!skipSRC) { switch((skipSRC = isOKtoSave(ntohl(record->srcaddr), myGlobals.device[deviceId].netflowGlobals->whiteNetworks, myGlobals.device[deviceId].netflowGlobals->blackNetworks, myGlobals.device[deviceId].netflowGlobals->numWhiteNets, myGlobals.device[deviceId].netflowGlobals->numBlackNets)) ) { case 1: myGlobals.device[deviceId].netflowGlobals->numSrcNetFlowsEntryFailedWhiteList++; break; case 2: myGlobals.device[deviceId].netflowGlobals->numSrcNetFlowsEntryFailedBlackList++; break; default: myGlobals.device[deviceId].netflowGlobals->numSrcNetFlowsEntryAccepted++; break; } } if(!skipDST) { switch((skipDST = isOKtoSave(ntohl(record->dstaddr), myGlobals.device[deviceId].netflowGlobals->whiteNetworks, myGlobals.device[deviceId].netflowGlobals->blackNetworks, myGlobals.device[deviceId].netflowGlobals->numWhiteNets, myGlobals.device[deviceId].netflowGlobals->numBlackNets)) ) { case 1: myGlobals.device[deviceId].netflowGlobals->numDstNetFlowsEntryFailedWhiteList++; break; case 2: myGlobals.device[deviceId].netflowGlobals->numDstNetFlowsEntryFailedBlackList++; break; default: myGlobals.device[deviceId].netflowGlobals->numDstNetFlowsEntryAccepted++; break; } } #ifdef DEBUG_FLOWS if(0) { traceEvent(CONST_TRACE_INFO, "DEBUG: isOKtoSave(%08x) - src - returned %s", ntohl(record->srcaddr), skipSRC == 0 ? "OK" : skipSRC == 1 ? "failed White list" : "failed Black list"); traceEvent(CONST_TRACE_INFO, "DEBUG: isOKtoSave(%08x) - dst - returned %s", ntohl(record->dstaddr), skipDST == 0 ? "OK" : skipDST == 1 ? "failed White list" : "failed Black list"); } #endif addrput(AF_INET,&addr1,&b); addrput(AF_INET,&addr2,&a); if(!skipDST) dstHost = lookupHost(&addr1, NULL, record->vlanId, 0, 1, deviceId); else dstHost = myGlobals.device[deviceId].netflowGlobals->dummyHost; if(!skipSRC) srcHost = lookupHost(&addr2, NULL, record->vlanId, 0, 1, deviceId); else srcHost = myGlobals.device[deviceId].netflowGlobals->dummyHost; if((srcHost == NULL) ||(dstHost == NULL)) { #ifdef DEBUG_FLOWS traceEvent(CONST_TRACE_INFO, "DEBUG: srcHost=%x, dstHost=%x\n", srcHost, dstHost); #endif return(0); } /* All hosts with a netmask are assumed to be (pseudo)local */ if((srcHost->network_mask == 0) && record->src_mask) { srcHost->network_mask = record->src_mask; FD_SET(FLAG_SUBNET_PSEUDO_LOCALHOST, &srcHost->flags); } if((dstHost->network_mask == 0) && record->dst_mask) { dstHost->network_mask = record->dst_mask; FD_SET(FLAG_SUBNET_PSEUDO_LOCALHOST, &dstHost->flags); } if(srcHost->firstSeen > *firstSeen) srcHost->firstSeen = *firstSeen; if(srcHost->lastSeen < *lastSeen) srcHost->lastSeen = *lastSeen; if(dstHost->firstSeen > *firstSeen) dstHost->firstSeen = *firstSeen; if(dstHost->lastSeen < *lastSeen) dstHost->lastSeen = *lastSeen; #ifdef DEBUG_FLOWS if(0) traceEvent(CONST_TRACE_INFO, "DEBUG: %s:%d -> %s:%d [last=%d][first=%d][last-first=%d]", srcHost->hostNumIpAddress, sport, dstHost->hostNumIpAddress, dport, ntohl(record->last), ntohl(record->first), (*lastSeen - *firstSeen)); #endif /* Commented out ... already done in updatePacketCount() */ /* srcHost->pktSent.value += numPkts, dstHost->pktRcvd.value += numPkts; */ /* srcHost->bytesSent.value += len, dstHost->bytesRcvd.value += len; */ srcHost->ipBytesSent.value += len, dstHost->ipBytesRcvd.value += len; if(srcAS != 0) srcHost->hostAS = srcAS; if(dstAS != 0) dstHost->hostAS = dstAS; srcHost->ifId = record->input, dstHost->ifId = record->output; #ifdef DEBUG_FLOWS if((srcAS == 13018) || (dstAS == 13018)) traceEvent(CONST_TRACE_ERROR, "************* AS %d/%d", srcHost->hostAS, dstHost->hostAS); #endif if((sport != 0) && (dport != 0)) { if(dport < sport) { if(handleIP(dport, srcHost, dstHost, len, 0, 0, 0, actualDeviceId, 1) == -1) { if(handleIP(sport, srcHost, dstHost, len, 0, 0, 0, actualDeviceId, 1) == -1) { if(myGlobals.device[deviceId].netflowGlobals->netFlowAssumeFTP) { /* If the user wants (via a run-time parm), as a last resort * we assume it's ftp-data traffic */ handleIP((u_short)CONST_FTPDATA, srcHost, dstHost, len, 0, 0, 0, actualDeviceId, 1); } } } } else { if(handleIP(sport, srcHost, dstHost, len, 0, 0, 0, actualDeviceId, 1) == -1) { if(handleIP(dport, srcHost, dstHost, len, 0, 0, 0, actualDeviceId, 1) == -1) { if(myGlobals.device[deviceId].netflowGlobals->netFlowAssumeFTP) { /* If the user wants (via a run-time parm), as a last resort * we assume it's ftp-data traffic */ handleIP((u_short)CONST_FTPDATA, srcHost, dstHost, len, 0, 0, 0, actualDeviceId, 1); } } } } } myGlobals.device[deviceId].netflowGlobals->flowProcessed++; myGlobals.device[deviceId].netflowGlobals->flowProcessedBytes += len; ctr.value = len; updatePacketCount(srcHost, &srcHost->hostIpAddress, dstHost, &dstHost->hostIpAddress, ctr, numPkts, actualDeviceId); srcPseudoLocal = subnetPseudoLocalHost(srcHost); dstPseudoLocal = subnetPseudoLocalHost(dstHost); if(srcPseudoLocal) { if(dstPseudoLocal) { updateTrafficMatrix(srcHost, dstHost, ctr, actualDeviceId); incrementTrafficCounter(&srcHost->bytesSentLoc, len); incrementTrafficCounter(&dstHost->bytesRcvdLoc, len); } else { incrementTrafficCounter(&srcHost->bytesSentRem, len); incrementTrafficCounter(&dstHost->bytesRcvdLoc, len); } } else { /* srcHost is remote */ if(dstPseudoLocal) { incrementTrafficCounter(&srcHost->bytesSentLoc, len); incrementTrafficCounter(&dstHost->bytesRcvdFromRem, len); } else { incrementTrafficCounter(&srcHost->bytesSentRem, len); incrementTrafficCounter(&dstHost->bytesRcvdFromRem, len); } } h.ts.tv_sec = recordActTime, h.ts.tv_usec = 0; switch(proto) { case 1: /* ICMP */ myGlobals.device[actualDeviceId].icmpBytes.value += len; srcHost->icmpSent.value += len, dstHost->icmpRcvd.value += len; myGlobals.device[actualDeviceId].netflowGlobals->numNetFlowsICMPRcvd++, myGlobals.device[actualDeviceId].netflowGlobals->totalNetFlowsICMPSize += len; break; case 6: /* TCP */ myGlobals.device[actualDeviceId].tcpBytes.value += len; myGlobals.device[actualDeviceId].netflowGlobals->numNetFlowsTCPRcvd++, myGlobals.device[actualDeviceId].netflowGlobals->totalNetFlowsTCPSize += len; allocateSecurityHostPkts(srcHost); allocateSecurityHostPkts(dstHost); incrementTrafficCounter(&myGlobals.device[actualDeviceId]. numEstablishedTCPConnections, 1); updateInterfacePorts(actualDeviceId, sport, dport, len); updateUsedPorts(srcHost, dstHost, sport, dport, len); if(srcPseudoLocal) { if(dstPseudoLocal) { incrementTrafficCounter(&srcHost->tcpSentLoc, len); incrementTrafficCounter(&dstHost->tcpRcvdLoc, len); incrementTrafficCounter(&myGlobals.device[actualDeviceId]. tcpGlobalTrafficStats.local, len); } else { incrementTrafficCounter(&srcHost->tcpSentRem, len); incrementTrafficCounter(&dstHost->tcpRcvdLoc, len); incrementTrafficCounter(&myGlobals.device[actualDeviceId]. tcpGlobalTrafficStats.local2remote, len); } } else { /* srcHost is remote */ if(dstPseudoLocal) { incrementTrafficCounter(&srcHost->tcpSentLoc, len); incrementTrafficCounter(&dstHost->tcpRcvdFromRem, len); incrementTrafficCounter(&myGlobals.device[actualDeviceId]. tcpGlobalTrafficStats.remote2local, len); } else { incrementTrafficCounter(&srcHost->tcpSentRem, len); incrementTrafficCounter(&dstHost->tcpRcvdFromRem, len); incrementTrafficCounter(&myGlobals.device[actualDeviceId]. tcpGlobalTrafficStats.remote, len); } } tp.th_sport = htons(sport), tp.th_dport = htons(dport); tp.th_flags = record->tcp_flags; #ifdef DEBUG_FLOWS /* traceEvent(CONST_TRACE_INFO, "handleSession(TCP)"); */ #endif if(myGlobals.device[deviceId].netflowGlobals->enableSessionHandling) session = handleSession(&h, 0, 0, srcHost, sport, dstHost, dport, len, &tp, 0, NULL, actualDeviceId, &newSession, 0); break; case 17: /* UDP */ myGlobals.device[actualDeviceId].netflowGlobals->numNetFlowsUDPRcvd++, myGlobals.device[actualDeviceId].netflowGlobals->totalNetFlowsUDPSize += len; incrementTrafficCounter(&myGlobals.device[actualDeviceId].udpBytes, len); updateInterfacePorts(actualDeviceId, sport, dport, len); updateUsedPorts(srcHost, dstHost, sport, dport, len); if(srcPseudoLocal) { if(dstPseudoLocal) { incrementTrafficCounter(&srcHost->udpSentLoc, len); incrementTrafficCounter(&dstHost->udpRcvdLoc, len); incrementTrafficCounter(&myGlobals.device[actualDeviceId]. udpGlobalTrafficStats.local, len); } else { incrementTrafficCounter(&srcHost->udpSentRem, len); incrementTrafficCounter(&dstHost->udpRcvdLoc, len); incrementTrafficCounter(&myGlobals.device[actualDeviceId]. udpGlobalTrafficStats.local2remote, len); } } else { /* srcHost is remote */ if(dstPseudoLocal) { incrementTrafficCounter(&srcHost->udpSentLoc, len); incrementTrafficCounter(&dstHost->udpRcvdFromRem, len); incrementTrafficCounter(&myGlobals.device[actualDeviceId]. udpGlobalTrafficStats.remote2local, len); } else { incrementTrafficCounter(&srcHost->udpSentRem, len); incrementTrafficCounter(&dstHost->udpRcvdFromRem, len); incrementTrafficCounter(&myGlobals.device[actualDeviceId]. udpGlobalTrafficStats.remote, len); } } #ifdef DEBUG_FLOWS /* traceEvent(CONST_TRACE_INFO, "handleSession(UDP)"); */ #endif if(myGlobals.device[deviceId].netflowGlobals->enableSessionHandling) session = handleSession(&h, 0, 0, srcHost, sport, dstHost, dport, len, NULL, 0, NULL, actualDeviceId, &newSession, 0); break; default: myGlobals.device[actualDeviceId].netflowGlobals->numNetFlowsOtherRcvd++, myGlobals.device[actualDeviceId].netflowGlobals->totalNetFlowsOtherSize += len; break; } if(session) { time_t timeDiff = recordActTime - (*lastSeen - *firstSeen); if(session->session_info == NULL) { if((!isEmpty(record->sip_call_id)) || (!isEmpty(record->sip_calling_party)) || (!isEmpty(record->sip_called_party))) { char tmpStr[256]; safe_snprintf(__FILE__, __LINE__, tmpStr, sizeof(tmpStr), "Call Id: %s
" "'%s' called '%s", valueOf(record->sip_call_id), valueOf(record->sip_calling_party), valueOf(record->sip_called_party)); traceEvent(CONST_TRACE_INFO, "DEBUG: ->>>>>>>> '%s'", tmpStr); session->session_info = strdup(tmpStr); } } #ifdef DEBUG_FLOWS if(0) traceEvent(CONST_TRACE_INFO, "DEBUG: %s:%d -> %s:%d [diff=%d]" "[recordActTime=%d][last-first=%d]", srcHost->hostNumIpAddress, sport, dstHost->hostNumIpAddress, dport, timeDiff, recordActTime, (*lastSeen - *firstSeen)); #endif if(session->firstSeen > timeDiff) session->firstSeen = timeDiff; session->lastSeen = recordActTime; record->nw_latency_sec = ntohl(record->nw_latency_sec), record->nw_latency_usec = ntohl(record->nw_latency_usec); if(record->nw_latency_sec || record->nw_latency_usec) { /* traceEvent(CONST_TRACE_INFO, "DEBUG: Nw Latency=%d.%d [%s:%d -> %s:%d]", record->nw_latency_sec, record->nw_latency_usec, srcHost->hostNumIpAddress, sport, dstHost->hostNumIpAddress, dport); */ session->nwLatency.tv_sec = record->nw_latency_sec, session->nwLatency.tv_usec = record->nw_latency_usec; } } /* releaseMutex(&myGlobals.hostsHashMutex); */ #ifdef MAX_NETFLOW_FLOW_BUFFER gettimeofday(&netflowEndOfFlowProcessing, NULL); elapsed = timeval_subtract(netflowEndOfFlowProcessing, netflowStartOfFlowProcessing); netflowflowBuffer[++netflowflowBufferCount & (MAX_NETFLOW_FLOW_BUFFER - 1)] = elapsed; if(elapsed > netflowfmaxTime) netflowfmaxTime = elapsed; #endif if(myGlobals.device[deviceId].netflowGlobals->saveFlowsIntoDB) insert_flow_record(deviceId, ntohl(record->srcaddr), ntohl(record->dstaddr), record->input, record->output, ntohl(record->sentPkts), ntohl(record->sentOctets), ntohl(record->rcvdPkts), ntohl(record->rcvdOctets), *firstSeen, *lastSeen, ntohs(record->srcport), ntohs(record->dstport), record->tcp_flags, record->proto, record->tos, record->vlanId); return(0); } /* *************************** */ static void dumpFlow(char *buffer, int bufferLen, int deviceId) { static char warningSent = 0; char nfDumpPath[512]; /* traceEvent(CONST_TRACE_INFO, "DEBUG: Dumping flow (len=%d)", bufferLen); */ /* Save flow on disk if configured */ if(myGlobals.device[deviceId].netflowGlobals->dumpInterval > 0) { time_t now = time(NULL); if(myGlobals.device[deviceId].netflowGlobals->dumpFd && ((now-myGlobals.device[deviceId].netflowGlobals->dumpFdCreationTime) > myGlobals.device[deviceId].netflowGlobals->dumpInterval)) { fclose(myGlobals.device[deviceId].netflowGlobals->dumpFd); myGlobals.device[deviceId].netflowGlobals->dumpFd = NULL; } if(myGlobals.device[deviceId].netflowGlobals->dumpFd == NULL) { /* Create the file */ safe_snprintf(__FILE__, __LINE__, nfDumpPath, sizeof(nfDumpPath), "%s/interfaces/%s/", myGlobals.device[deviceId].netflowGlobals->dumpPath, myGlobals.device[deviceId].uniqueIfName); mkdir_p("NETFLOW", nfDumpPath, 0700 /* CONST_RRD_D_PERMISSIONS_PRIVATE */); safe_snprintf(__FILE__, __LINE__, nfDumpPath, sizeof(nfDumpPath), "%s/interfaces/%s/%u.flow", myGlobals.device[deviceId].netflowGlobals->dumpPath, myGlobals.device[deviceId].uniqueIfName, time(NULL)); myGlobals.device[deviceId].netflowGlobals->dumpFd = fopen(nfDumpPath, "w+"); if(myGlobals.device[deviceId].netflowGlobals->dumpFd == NULL) { if(!warningSent) { warningSent = 1; traceEvent(CONST_TRACE_WARNING, "NETFLOW: Cannot create file %s", nfDumpPath); } } else { myGlobals.device[deviceId].netflowGlobals->dumpFdCreationTime = now; /* traceEvent(CONST_TRACE_WARNING, "NETFLOW: Created file @ %u", now); */ warningSent = 0; } } if(myGlobals.device[deviceId].netflowGlobals->dumpFd != NULL) { fprintf(myGlobals.device[deviceId].netflowGlobals->dumpFd, "%04d", bufferLen); if(fwrite(buffer, bufferLen, 1, myGlobals.device[deviceId]. netflowGlobals->dumpFd) != 1) { if(!warningSent) { warningSent = 1; traceEvent(CONST_TRACE_WARNING, "NETFLOW: Error while saving data into file %s", nfDumpPath); } } } } } /* ********************************************************* */ #ifdef DEBUG_FLOWS static char* nf_hex_dump(char *buf, u_short len) { static char staticbuf[256] = { 0 }; int i; staticbuf[0] = '\0'; for(i=0; i sizeof(the5Record) ? sizeof(the5Record): bufferLen); flowVersion = ntohs(the5Record.flowHeader.version); #ifdef DEBUG_FLOWS if(0) traceEvent(CONST_TRACE_INFO, "NETFLOW: +++++++ version=%d", flowVersion); #endif /* Convert V7 flows into V5 flows in order to make ntop able to handle V7 flows. Courtesy of Bernd Ziller */ if((flowVersion == 1) || (flowVersion == 7)) { int numFlows, i, j; NetFlow1Record the1Record; NetFlow7Record the7Record; if(flowVersion == 1) { memcpy(&the1Record, buffer, bufferLen > sizeof(the1Record) ? sizeof(the1Record): bufferLen); numFlows = ntohs(the1Record.flowHeader.count); if(numFlows > CONST_V1FLOWS_PER_PAK) numFlows = CONST_V1FLOWS_PER_PAK; myGlobals.device[deviceId].netflowGlobals->numNetFlowsV1Rcvd += numFlows; recordActTime = the1Record.flowHeader.unix_secs; recordSysUpTime = the1Record.flowHeader.sysUptime; } else { memcpy(&the7Record, buffer, bufferLen > sizeof(the7Record) ? sizeof(the7Record): bufferLen); numFlows = ntohs(the7Record.flowHeader.count); if(numFlows > CONST_V7FLOWS_PER_PAK) numFlows = CONST_V7FLOWS_PER_PAK; myGlobals.device[deviceId].netflowGlobals->numNetFlowsV7Rcvd += numFlows; recordActTime = the7Record.flowHeader.unix_secs; recordSysUpTime = the7Record.flowHeader.sysUptime; } #ifdef DEBUG_FLOWS if(0) traceEvent(CONST_TRACE_INFO, "NETFLOW: +++++++ flows=%d", numFlows); #endif the5Record.flowHeader.version = htons(5); the5Record.flowHeader.count = htons(numFlows); /* rest of flowHeader will not be used */ for(j=i=0; inumNetFlowsV9TemplRcvd++; if(bufferLen > (displ+sizeof(V9Template))) { FlowSetV9 *cursor = myGlobals.device[deviceId].netflowGlobals->templates; u_char found = 0; u_short len; int fieldId; if(!isOptionTemplate) { len = sizeof(V9Template); memcpy(&template, &buffer[displ], sizeof(V9Template)); template.templateId = ntohs(template.templateId); template.fieldCount = ntohs(template.fieldCount); template.flowsetLen = ntohs(template.flowsetLen); #ifdef DEBUG_FLOWS if(1) traceEvent(CONST_TRACE_INFO, "Template [id=%d] fields: %d", template.templateId, template.fieldCount); #endif /* Check the template before to handle it */ for(fieldId=0; (fieldId < template.fieldCount) && (len < template.flowsetLen); fieldId++) { #ifdef DEBUG_FLOWS V9FlowSet *set = (V9FlowSet*)&buffer[displ+sizeof(V9Template)+fieldId*sizeof(V9FlowSet)]; #endif len += 4; /* Field Type (2) + Field Length (2) */ #ifdef DEBUG_FLOWS if(1) traceEvent(CONST_TRACE_INFO, "[%d] fieldLen=%d/len=%d", 1+fieldId, htons(set->flowsetLen), len); #endif } if(len > template.flowsetLen) { static u_short lastBadTemplate = 0; if(template.templateId != lastBadTemplate) { traceEvent(CONST_TRACE_WARNING, "Template %d has wrong size [actual=%d/expected=%d]: skipped", template.templateId, len, template.flowsetLen); lastBadTemplate = template.templateId; } myGlobals.device[deviceId].netflowGlobals->numNetFlowsV9BadTemplRcvd++; } else { while(cursor != NULL) { if(cursor->templateInfo.templateId == template.templateId) { found = 1; break; } else cursor = cursor->next; } if(found) { #ifdef DEBUG_FLOWS traceEvent(CONST_TRACE_INFO, ">>>>> Redefined existing template [id=%d]", template.templateId); #endif free(cursor->fields); } else { #ifdef DEBUG_FLOWS traceEvent(CONST_TRACE_INFO, ">>>>> Found new flow template definition [id=%d]", template.templateId); #endif cursor = (FlowSetV9*)malloc(sizeof(FlowSetV9)); cursor->next = myGlobals.device[deviceId].netflowGlobals->templates; myGlobals.device[deviceId].netflowGlobals->templates = cursor; } memcpy(&cursor->templateInfo, &buffer[displ], sizeof(V9Template)); cursor->templateInfo.flowsetLen = ntohs(cursor->templateInfo.flowsetLen); cursor->templateInfo.templateId = ntohs(cursor->templateInfo.templateId); cursor->templateInfo.fieldCount = ntohs(cursor->templateInfo.fieldCount); cursor->fields = (V9TemplateField*)malloc(cursor->templateInfo.flowsetLen-sizeof(V9Template)); memcpy(cursor->fields, &buffer[displ+sizeof(V9Template)], cursor->templateInfo.flowsetLen-sizeof(V9Template)); } } else { u_short move_ahead; memcpy(&move_ahead, &buffer[displ+2], 2); template.flowsetLen = ntohs(move_ahead); } #ifdef DEBUG_FLOWS traceEvent(CONST_TRACE_INFO, "Moving ahead of %d bytes", template.flowsetLen); #endif /* Skip template definition */ displ += template.flowsetLen; } else { done = 1; myGlobals.device[deviceId].netflowGlobals->numNetFlowsV9BadTemplRcvd++; } } else { #ifdef DEBUG_FLOWS traceEvent(CONST_TRACE_INFO, "Found FlowSet [displ=%d]", displ); #endif foundRecord = 1; } if(foundRecord) { V9FlowSet fs; if(bufferLen > (displ+sizeof(V9FlowSet))) { FlowSetV9 *cursor = myGlobals.device[deviceId].netflowGlobals->templates; u_short tot_len = 0; memcpy(&fs, &buffer[displ], sizeof(V9FlowSet)); fs.flowsetLen = ntohs(fs.flowsetLen); fs.templateId = ntohs(fs.templateId); while(cursor != NULL) { if(cursor->templateInfo.templateId == fs.templateId) { break; } else cursor = cursor->next; } if(cursor != NULL) { /* Template found */ int fieldId, init_displ; V9TemplateField *fields = cursor->fields; time_t firstSeen, lastSeen; /* initialize to zero */ memset(&record, 0, sizeof(record)); record.vlanId = NO_VLAN; /* No VLAN */ init_displ = displ; displ += sizeof(V9FlowSet); #ifdef DEBUG_FLOWS if(0) traceEvent(CONST_TRACE_INFO, ">>>>> Rcvd flow with known template %d [%d...%d]", fs.templateId, displ, fs.flowsetLen); #endif while(displ < (init_displ + fs.flowsetLen)) { u_short accum_len = 0; /* Defaults */ record.nw_latency_sec = record.nw_latency_usec = htonl(0); #ifdef DEBUG_FLOWS if(0) traceEvent(CONST_TRACE_INFO, ">>>>> Stats [%d...%d]", displ, (init_displ + fs.flowsetLen)); #endif for(fieldId=0; fieldIdtemplateInfo.fieldCount; fieldId++) { if(!(displ < (init_displ + fs.flowsetLen))) break; /* Flow too short */ #ifdef DEBUG_FLOWS if(0) traceEvent(CONST_TRACE_INFO, ">>>>> Dissecting flow field " "[displ=%d/%d][template=%d][fieldType=%d][fieldLen=%d][field=%d/%d] [%d...%d][%s]", displ, fs.flowsetLen, fs.templateId, ntohs(fields[fieldId].fieldType), ntohs(fields[fieldId].fieldLen), fieldId, cursor->templateInfo.fieldCount, displ, (init_displ + fs.flowsetLen), nf_hex_dump(&buffer[displ], ntohs(fields[fieldId].fieldLen))); #endif switch(ntohs(fields[fieldId].fieldType)) { case 1: /* IN_BYTES */ memcpy(&record.rcvdOctets, &buffer[displ], 4); break; case 2: /* IN_PKTS */ memcpy(&record.rcvdPkts, &buffer[displ], 4); break; case 4: /* PROT */ memcpy(&record.proto, &buffer[displ], 1); break; case 5: /* TOS */ memcpy(&record.tos, &buffer[displ], 1); break; case 6: /* TCP_FLAGS */ memcpy(&record.tcp_flags, &buffer[displ], 1); break; case 7: /* L4_SRC_PORT */ memcpy(&record.srcport, &buffer[displ], 2); break; case 8: /* IP_SRC_ADDR */ memcpy(&record.srcaddr, &buffer[displ], 4); break; case 9: /* SRC_MASK */ memcpy(&record.src_mask, &buffer[displ], 1); break; case 10: /* INPUT SNMP */ memcpy(&record.input, &buffer[displ], 2); break; case 11: /* L4_DST_PORT */ memcpy(&record.dstport, &buffer[displ], 2); break; case 12: /* IP_DST_ADDR */ memcpy(&record.dstaddr, &buffer[displ], 4); break; case 13: /* DST_MASK */ memcpy(&record.dst_mask, &buffer[displ], 1); break; case 14: /* OUTPUT SNMP */ memcpy(&record.output, &buffer[displ], 2); break; case 15: /* IP_NEXT_HOP */ memcpy(&record.nexthop, &buffer[displ], 4); break; case 17: /* DST_AS */ memcpy(&record.dst_as, &buffer[displ], 2); break; case 21: /* LAST_SWITCHED */ memcpy(&record.last, &buffer[displ], 4); break; case 22: /* FIRST SWITCHED */ memcpy(&record.first, &buffer[displ], 4); break; case 23: /* OUT_BYTES */ memcpy(&record.sentOctets, &buffer[displ], 4); break; case 24: /* OUT_PKTS */ memcpy(&record.sentPkts, &buffer[displ], 4); break; case 58: /* SRC_VLAN */ case 59: /* DST_VLAN */ memcpy(&record.vlanId, &buffer[displ], 2); record.vlanId = ntohs(record.vlanId); break; case 92: /* NW_LATENCY_SEC */ memcpy(&record.nw_latency_sec, &buffer[displ], 4); break; case 93: /* NW_LATENCY_USEC */ memcpy(&record.nw_latency_usec, &buffer[displ], 4); break; /* VoIP Extensions */ case 130: /* SIP_CALL_ID */ memcpy(&record.sip_call_id, &buffer[displ], 50); traceEvent(CONST_TRACE_INFO, "SIP: sip_call_id=%s", record.sip_call_id); break; case 131: /* SIP_CALLING_PARTY */ memcpy(&record.sip_calling_party, &buffer[displ], 50); traceEvent(CONST_TRACE_INFO, "SIP: sip_calling_party=%s", record.sip_calling_party); break; case 132: /* SIP_CALLED_PARTY */ memcpy(&record.sip_called_party, &buffer[displ], 50); traceEvent(CONST_TRACE_INFO, "SIP: sip_called_party=%s", record.sip_called_party); break; } accum_len += ntohs(fields[fieldId].fieldLen); displ += ntohs(fields[fieldId].fieldLen); } /* IMPORTANT NOTE handleGenericFlow handles monodirectional flows, whereas v9 flows and bidirectional. This means that if there's some bidirectional traffic, handleGenericFlow is called twice. */ handleGenericFlow(netflow_device_ip, recordActTime, recordSysUpTime, &record, deviceId, &firstSeen, &lastSeen); #ifdef DEBUG_FLOWS if(0) traceEvent(CONST_TRACE_INFO, ">>>> NETFLOW: Calling insert_flow_record() [accum_len=%d][save=%d]", accum_len, myGlobals.device[deviceId].netflowGlobals->saveFlowsIntoDB); #endif tot_len += accum_len; if(record.rcvdPkts > 0) { u_int32_t tmp; record.sentPkts = record.rcvdPkts; record.sentOctets = record.rcvdOctets; tmp = record.srcaddr; record.srcaddr = record.dstaddr; record.dstaddr = tmp; tmp = record.srcport; record.srcport = record.dstport; record.dstport = tmp; handleGenericFlow(netflow_device_ip, recordActTime, recordSysUpTime, &record, deviceId, &firstSeen, &lastSeen); } myGlobals.device[deviceId].netflowGlobals->numNetFlowsV9Rcvd++; } if(tot_len < fs.flowsetLen) { u_short padding = fs.flowsetLen - tot_len; if(padding >= 4) { traceEvent(CONST_TRACE_WARNING, "Template len mismatch [tot_len=%d][flow_len=%d]", tot_len, fs.flowsetLen); } else { #ifdef DEBUG_FLOWS traceEvent(CONST_TRACE_INFO, ">>>>> %d bytes padding [tot_len=%d][flow_len=%d]", padding, tot_len, fs.flowsetLen); #endif displ += padding; } } } else { #ifdef DEBUG_FLOWS traceEvent(CONST_TRACE_INFO, ">>>>> Rcvd flow with UNKNOWN template %d [displ=%d][len=%d]", fs.templateId, displ, fs.flowsetLen); #endif myGlobals.device[deviceId].netflowGlobals->numNetFlowsV9UnknTemplRcvd++; displ += fs.flowsetLen; } } } } /* for */ } else if(the5Record.flowHeader.version == htons(5)) { int i, numFlows = ntohs(the5Record.flowHeader.count); recordActTime = the5Record.flowHeader.unix_secs; recordSysUpTime = the5Record.flowHeader.sysUptime; if(numFlows > CONST_V5FLOWS_PER_PAK) numFlows = CONST_V5FLOWS_PER_PAK; #ifdef DEBUG_FLOWS if(0) traceEvent(CONST_TRACE_INFO, "dissectFlow(%d flows)", numFlows); #endif /* Lock white/black lists for duration of this flow packet */ accessMutex(&myGlobals.device[deviceId].netflowGlobals->whiteblackListMutex, "flowPacket"); /* Reset the record so that fields that are not contained into v5 records are set to zero */ memset(&record, 0, sizeof(record)); record.vlanId = NO_VLAN; /* No VLAN */ record.nw_latency_sec = record.nw_latency_usec = htonl(0); for(i=0; inumNetFlowsV5Rcvd += numFlows; releaseMutex(&myGlobals.device[deviceId].netflowGlobals->whiteblackListMutex); } else myGlobals.device[deviceId].netflowGlobals->numBadNetFlowsVersionsRcvd++; /* CHANGE */ } /* ****************************** */ #ifdef MAKE_WITH_NETFLOWSIGTRAP RETSIGTYPE netflowcleanup(int signo) { static int msgSent = 0; int i; void *array[20]; size_t size; char **strings; if(msgSent<10) { traceEvent(CONST_TRACE_FATALERROR, "NETFLOW: caught signal %d %s", signo, signo == SIGHUP ? "SIGHUP" : signo == SIGINT ? "SIGINT" : signo == SIGQUIT ? "SIGQUIT" : signo == SIGILL ? "SIGILL" : signo == SIGABRT ? "SIGABRT" : signo == SIGFPE ? "SIGFPE" : signo == SIGKILL ? "SIGKILL" : signo == SIGSEGV ? "SIGSEGV" : signo == SIGPIPE ? "SIGPIPE" : signo == SIGALRM ? "SIGALRM" : signo == SIGTERM ? "SIGTERM" : signo == SIGUSR1 ? "SIGUSR1" : signo == SIGUSR2 ? "SIGUSR2" : signo == SIGCHLD ? "SIGCHLD" : #ifdef SIGCONT signo == SIGCONT ? "SIGCONT" : #endif #ifdef SIGSTOP signo == SIGSTOP ? "SIGSTOP" : #endif #ifdef SIGBUS signo == SIGBUS ? "SIGBUS" : #endif #ifdef SIGSYS signo == SIGSYS ? "SIGSYS" #endif : "other"); msgSent++; } #ifdef HAVE_BACKTRACE /* Don't double fault... */ /* signal(signo, SIG_DFL); */ /* Grab the backtrace before we do much else... */ size = backtrace(array, 20); strings = (char**)backtrace_symbols(array, size); traceEvent(CONST_TRACE_ERROR, "NETFLOW: BACKTRACE: backtrace is:"); if (size < 2) { traceEvent(CONST_TRACE_ERROR, "NETFLOW: BACKTRACE: **unavailable!"); } else { /* Ignore the 0th entry, that's our cleanup() */ for (i=1; iifStatsQueue_len > 0) { InterfaceStats *iface; accessMutex(&myGlobals.device[deviceId].netflowGlobals->ifStatsQueueMutex, "netflowUtilsLoop"); iface = myGlobals.device[deviceId].netflowGlobals->ifStatsQueue[--myGlobals.device[deviceId].netflowGlobals->ifStatsQueue_len]; releaseMutex(&myGlobals.device[deviceId].netflowGlobals->ifStatsQueueMutex); updateInterfaceName(iface); } else { waitCondvar(&myGlobals.device[deviceId].netflowGlobals->ifStatsQueueCondvar); } } } #endif /* ****************************** */ static void* netflowMainLoop(void* _deviceId) { fd_set netflowMask; int rc, len, deviceId; u_char buffer[2048]; struct sockaddr_in fromHost; #ifdef MAX_NETFLOW_PACKET_BUFFER struct timeval netflowStartOfRecordProcessing, netflowEndOfRecordProcessing; float elapsed; #endif deviceId = (int)((long)_deviceId); if(!(myGlobals.device[deviceId].netflowGlobals->netFlowInSocket > 0)) return(NULL); traceEvent(CONST_TRACE_INFO, "THREADMGMT[t%lu]: NETFLOW: thread starting [p%d]", pthread_self(), getpid()); #ifdef MAKE_WITH_NETFLOWSIGTRAP signal(SIGSEGV, netflowcleanup); signal(SIGHUP, netflowcleanup); signal(SIGINT, netflowcleanup); signal(SIGQUIT, netflowcleanup); signal(SIGILL, netflowcleanup); signal(SIGABRT, netflowcleanup); signal(SIGFPE, netflowcleanup); signal(SIGKILL, netflowcleanup); signal(SIGPIPE, netflowcleanup); signal(SIGALRM, netflowcleanup); signal(SIGTERM, netflowcleanup); signal(SIGUSR1, netflowcleanup); signal(SIGUSR2, netflowcleanup); /* signal(SIGCHLD, netflowcleanup); */ #ifdef SIGCONT signal(SIGCONT, netflowcleanup); #endif #ifdef SIGSTOP signal(SIGSTOP, netflowcleanup); #endif #ifdef SIGBUS signal(SIGBUS, netflowcleanup); #endif #ifdef SIGSYS signal(SIGSYS, netflowcleanup); #endif #endif /* MAKE_WITH_NETFLOWSIGTRAP */ myGlobals.device[deviceId].activeDevice = 1; myGlobals.device[deviceId].netflowGlobals->threadActive = 1; ntopSleepUntilStateRUN(); traceEvent(CONST_TRACE_INFO, "THREADMGMT[t%lu]: NETFLOW: (port %d) thread running [p%d]", pthread_self(), myGlobals.device[deviceId].netflowGlobals->netFlowInPort, getpid()); for(;myGlobals.ntopRunState <= FLAG_NTOPSTATE_RUN;) { int maxSock = myGlobals.device[deviceId].netflowGlobals->netFlowInSocket; struct timeval wait_time; FD_ZERO(&netflowMask); FD_SET(myGlobals.device[deviceId].netflowGlobals->netFlowInSocket, &netflowMask); #ifdef HAVE_SCTP if(myGlobals.device[deviceId].netflowGlobals->netFlowInSctpSocket > 0) { FD_SET(myGlobals.device[deviceId].netflowGlobals->netFlowInSctpSocket, &netflowMask); if(myGlobals.device[deviceId].netflowGlobals->netFlowInSctpSocket > maxSock) maxSock = myGlobals.device[deviceId].netflowGlobals->netFlowInSctpSocket; } #endif if(!myGlobals.device[deviceId].activeDevice) break; wait_time.tv_sec = 3, wait_time.tv_usec = 0; rc = select(maxSock+1, &netflowMask, NULL, NULL, &wait_time); if(!myGlobals.device[deviceId].activeDevice) break; if(rc > 0) { if(FD_ISSET(myGlobals.device[deviceId].netflowGlobals->netFlowInSocket, &netflowMask)){ len = sizeof(fromHost); rc = recvfrom(myGlobals.device[deviceId].netflowGlobals->netFlowInSocket, (char*)&buffer, sizeof(buffer), 0, (struct sockaddr*)&fromHost, (socklen_t*)&len); } #ifdef HAVE_SCTP else { struct msghdr msg; struct iovec iov[2]; char controlVector[256]; memset(controlVector, 0, sizeof(controlVector)); iov[0].iov_base = buffer; iov[0].iov_len = sizeof(buffer); iov[1].iov_base = NULL; iov[1].iov_len = 0; msg.msg_name = (caddr_t)&fromHost; msg.msg_namelen = sizeof(fromHost); msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_control = (caddr_t)controlVector; msg.msg_controllen = sizeof(controlVector); rc = recvmsg(myGlobals.device[deviceId].netflowGlobals->netFlowInSctpSocket, &msg, 0); } #endif #ifdef DEBUG_FLOWS traceEvent(CONST_TRACE_INFO, "NETFLOW_DEBUG: Received NetFlow packet(len=%d)(deviceId=%d)", rc, deviceId); #endif if(rc > 0) { int i; #ifdef MAX_NETFLOW_PACKET_BUFFER gettimeofday(&netflowStartOfRecordProcessing, NULL); #endif myGlobals.device[deviceId].netflowGlobals->numNetFlowsPktsRcvd++; NTOHL(fromHost.sin_addr.s_addr); for(i=0; iprobeList[i].probeAddr.s_addr == 0) { myGlobals.device[deviceId].netflowGlobals->probeList[i].probeAddr.s_addr = fromHost.sin_addr.s_addr; myGlobals.device[deviceId].netflowGlobals->probeList[i].pkts = 1; break; } else if(myGlobals.device[deviceId].netflowGlobals->probeList[i].probeAddr.s_addr == fromHost.sin_addr.s_addr) { myGlobals.device[deviceId].netflowGlobals->probeList[i].pkts++; break; } } dissectFlow(fromHost.sin_addr.s_addr, (char*)buffer, rc, deviceId); #ifdef MAX_NETFLOW_PACKET_BUFFER gettimeofday(&netflowEndOfRecordProcessing, NULL); elapsed = timeval_subtract(netflowEndOfRecordProcessing, netflowStartOfRecordProcessing); netflowpacketBuffer[++netflowpacketBufferCount & (MAX_NETFLOW_PACKET_BUFFER - 1)] = elapsed; if(elapsed > netflowpmaxTime) netflowpmaxTime = elapsed; #endif } } else { if((rc < 0) && (myGlobals.ntopRunState <= FLAG_NTOPSTATE_RUN) && (errno != EINTR /* Interrupted system call */)) { traceEvent(CONST_TRACE_ERROR, "NETFLOW: select() failed(%d, %s), terminating netFlow", errno, strerror(errno)); break; } } } if(myGlobals.device[deviceId].netflowGlobals != NULL) { myGlobals.device[deviceId].netflowGlobals->threadActive = 0; myGlobals.device[deviceId].netflowGlobals->netFlowThread = 0; #ifdef HAVE_SNMP myGlobals.device[deviceId].netflowGlobals->netFlowUtilsThread = 0; #endif } myGlobals.device[deviceId].activeDevice = 0; traceEvent(CONST_TRACE_INFO, "THREADMGMT[t%lu]: NETFLOW: thread terminated [p%d]", pthread_self(), getpid()); return(NULL); } /* ****************************** */ static void initNetFlowDevice(int deviceId) { int a, b, c, d, a1, b1, c1, d1, rc; char value[1024], workList[1024]; if(!pluginActive) return; traceEvent(CONST_TRACE_INFO, "NETFLOW: initializing deviceId=%d", deviceId); if(myGlobals.device[deviceId].netflowGlobals == NULL) { traceEvent(CONST_TRACE_ERROR, "NETFLOW: initNetFlowDevice internal error"); return; } allocDeviceMemory(deviceId); setPluginStatus(NULL); myGlobals.device[deviceId].netflowGlobals->threadActive = 0; createMutex(&myGlobals.device[deviceId].netflowGlobals->whiteblackListMutex); createMutex(&myGlobals.device[deviceId].netflowGlobals->ifStatsMutex); #ifdef HAVE_SNMP createMutex(&myGlobals.device[deviceId].netflowGlobals->ifStatsQueueMutex); createCondvar(&myGlobals.device[deviceId].netflowGlobals->ifStatsQueueCondvar); #endif if(fetchPrefsValue(nfValue(deviceId, "netFlowInPort", 1), value, sizeof(value)) == -1) storePrefsValue(nfValue(deviceId, "netFlowInPort", 1), "0"); else myGlobals.device[deviceId].netflowGlobals->netFlowInPort = atoi(value); if((fetchPrefsValue(nfValue(deviceId, "ifNetMask", 1), value, sizeof(value)) == -1) || (((rc = sscanf(value, "%d.%d.%d.%d/%d.%d.%d.%d", &a, &b, &c, &d, &a1, &b1, &c1, &d1)) != 8) && ((rc = sscanf(value, "%d.%d.%d.%d/%d", &a, &b, &c, &d, &a1)) != 5))) { storePrefsValue(nfValue(deviceId, "ifNetMask", 1), "192.168.0.0/255.255.255.0"); myGlobals.device[deviceId].netflowGlobals->netFlowIfAddress.s_addr = 0xC0A80000; myGlobals.device[deviceId].netflowGlobals->netFlowIfMask.s_addr = 0xFFFFFF00; } else { myGlobals.device[deviceId].netflowGlobals->netFlowIfAddress.s_addr = (a << 24) +(b << 16) +(c << 8) + d; if(rc == 8) myGlobals.device[deviceId].netflowGlobals->netFlowIfMask.s_addr = (a1 << 24) +(b1 << 16) +(c1 << 8) + d1; else { myGlobals.device[deviceId].netflowGlobals->netFlowIfMask.s_addr = 0xffffffff >> a1; myGlobals.device[deviceId].netflowGlobals->netFlowIfMask.s_addr =~ myGlobals.device[deviceId].netflowGlobals->netFlowIfMask.s_addr; } } if(fetchPrefsValue(nfValue(deviceId, "whiteList", 1), value, sizeof(value)) == -1) { storePrefsValue(nfValue(deviceId, "whiteList", 1), ""); myGlobals.device[deviceId].netflowGlobals->netFlowWhiteList = strdup(""); } else myGlobals.device[deviceId].netflowGlobals->netFlowWhiteList = strdup(value); accessMutex(&myGlobals.device[deviceId].netflowGlobals->whiteblackListMutex, "initNetFlowDevice"); handleWhiteBlackListAddresses((char*)&value, myGlobals.device[deviceId].netflowGlobals->whiteNetworks, &myGlobals.device[deviceId].netflowGlobals->numWhiteNets, (char*)&workList, sizeof(workList)); if(myGlobals.device[deviceId].netflowGlobals->netFlowWhiteList != NULL) free(myGlobals.device[deviceId].netflowGlobals->netFlowWhiteList); myGlobals.device[deviceId].netflowGlobals->netFlowWhiteList = strdup(workList); releaseMutex(&myGlobals.device[deviceId].netflowGlobals->whiteblackListMutex); traceEvent(CONST_TRACE_INFO, "NETFLOW: White list initialized to '%s'", myGlobals.device[deviceId].netflowGlobals->netFlowWhiteList); if(fetchPrefsValue(nfValue(deviceId, "blackList", 1), value, sizeof(value)) == -1) { storePrefsValue(nfValue(deviceId, "blackList", 1), ""); myGlobals.device[deviceId].netflowGlobals->netFlowBlackList=strdup(""); } else myGlobals.device[deviceId].netflowGlobals->netFlowBlackList=strdup(value); accessMutex(&myGlobals.device[deviceId].netflowGlobals->whiteblackListMutex, "initNetFlowDevice()"); handleWhiteBlackListAddresses((char*)&value, myGlobals.device[deviceId].netflowGlobals->blackNetworks, &myGlobals.device[deviceId].netflowGlobals->numBlackNets, (char*)&workList, sizeof(workList)); if(myGlobals.device[deviceId].netflowGlobals->netFlowBlackList != NULL) free(myGlobals.device[deviceId].netflowGlobals->netFlowBlackList); myGlobals.device[deviceId].netflowGlobals->netFlowBlackList = strdup(workList); releaseMutex(&myGlobals.device[deviceId].netflowGlobals->whiteblackListMutex); traceEvent(CONST_TRACE_INFO, "NETFLOW: Black list initialized to '%s'", myGlobals.device[deviceId].netflowGlobals->netFlowBlackList); if(fetchPrefsValue(nfValue(deviceId, "netFlowAggregation", 1), value, sizeof(value)) == -1) storePrefsValue(nfValue(deviceId, "netFlowAggregation", 1), "0" /* noAggregation */); else myGlobals.device[deviceId].netflowGlobals->netFlowAggregation = atoi(value); if(fetchPrefsValue(nfValue(deviceId, "netFlowAssumeFTP", 1), value, sizeof(value)) == -1) { storePrefsValue(nfValue(deviceId, "netFlowAssumeFTP", 1), "0" /* no */); myGlobals.device[deviceId].netflowGlobals->netFlowAssumeFTP = 0; } else myGlobals.device[deviceId].netflowGlobals->netFlowAssumeFTP = atoi(value); if(fetchPrefsValue(nfValue(deviceId, "enableSessionHandling", 1), value, sizeof(value)) == -1) { storePrefsValue(nfValue(deviceId, "enableSessionHandling", 1), "0" /* no */); myGlobals.device[deviceId].netflowGlobals->enableSessionHandling = 0; } else myGlobals.device[deviceId].netflowGlobals->enableSessionHandling = atoi(value); if(fetchPrefsValue(nfValue(deviceId, "saveFlowsIntoDB", 1), value, sizeof(value)) == -1) { storePrefsValue(nfValue(deviceId, "saveFlowsIntoDB", 1), "0" /* no */); myGlobals.device[deviceId].netflowGlobals->saveFlowsIntoDB = 0; } else myGlobals.device[deviceId].netflowGlobals->saveFlowsIntoDB = atoi(value); if(fetchPrefsValue(nfValue(deviceId, "netFlowDumpInterval", 1), value, sizeof(value)) == -1) { storePrefsValue(nfValue(deviceId, "netFlowDumpInterval", 1), "0" /* no */); myGlobals.device[deviceId].netflowGlobals->dumpInterval = 0; } else myGlobals.device[deviceId].netflowGlobals->dumpInterval = atoi(value); if(fetchPrefsValue(nfValue(deviceId, "netFlowDumpPath", 1), value, sizeof(value)) == -1) { myGlobals.device[deviceId].netflowGlobals->dumpPath = strdup("./netflow-dump"); storePrefsValue(nfValue(deviceId, "netFlowDumpPath", 1), myGlobals.device[deviceId].netflowGlobals->dumpPath); } else myGlobals.device[deviceId].netflowGlobals->dumpPath = strdup(value); if(setNetFlowInSocket(deviceId) != 0) return; if(fetchPrefsValue(nfValue(deviceId, "debug", 1), value, sizeof(value)) == -1) { storePrefsValue(nfValue(deviceId, "debug", 1), "0"); myGlobals.device[deviceId].netflowGlobals->netFlowDebug = 0; } else { myGlobals.device[deviceId].netflowGlobals->netFlowDebug = atoi(value); } /* Allocate a pure dummy for white/black list use */ myGlobals.device[deviceId].netflowGlobals->dummyHost = (HostTraffic*)malloc(sizeof(HostTraffic)); memset(myGlobals.device[deviceId].netflowGlobals->dummyHost, 0, sizeof(HostTraffic)); myGlobals.device[deviceId].netflowGlobals->dummyHost->hostIp4Address.s_addr = 0x00112233; strncpy(myGlobals.device[deviceId].netflowGlobals->dummyHost->hostNumIpAddress, " ", sizeof(myGlobals.device[deviceId].netflowGlobals->dummyHost->hostNumIpAddress)); strncpy(myGlobals.device[deviceId].netflowGlobals->dummyHost->hostResolvedName, "white/black list dummy", sizeof(myGlobals.device[deviceId].netflowGlobals->dummyHost->hostResolvedName)); myGlobals.device[deviceId].netflowGlobals->dummyHost->hostResolvedNameType = FLAG_HOST_SYM_ADDR_TYPE_FAKE; strcpy(myGlobals.device[deviceId].netflowGlobals->dummyHost->ethAddressString, "00:00:00:00:00:00"); setEmptySerial(&myGlobals.device[deviceId].netflowGlobals->dummyHost->hostSerial); myGlobals.device[deviceId].netflowGlobals->dummyHost->portsUsage = NULL; myGlobals.device[deviceId].activeDevice = 1; myGlobals.device[deviceId].samplingRate = 1; myGlobals.device[deviceId].mtuSize = myGlobals.mtuSize[myGlobals.device[deviceId].datalink]; myGlobals.device[deviceId].headerSize = myGlobals.headerSize[myGlobals.device[deviceId].datalink]; initDeviceSemaphores(deviceId); } /* ****************************** */ static int initNetFlowFunct(void) { char value[128]; traceEvent(CONST_TRACE_INFO, "NETFLOW: Welcome to the netFlow plugin"); pluginActive = 1; myGlobals.runningPref.mergeInterfaces = 0; /* Use different devices */ #ifdef MAX_NETFLOW_FLOW_BUFFER memset(&netflowflowBuffer, 0, sizeof(netflowflowBuffer)); netflowflowBufferCount = 0; netflowfmaxTime = 0.0; #endif #ifdef MAX_NETFLOW_PACKET_BUFFER memset(&netflowpacketBuffer, 0, sizeof(netflowpacketBuffer)); netflowpacketBufferCount = 0; netflowpmaxTime = 0.0; #endif if((fetchPrefsValue(nfValue(0, "knownDevices", 0), value, sizeof(value)) != -1) && (strlen(value) > 0)) { char *strtokState, *dev; traceEvent(CONST_TRACE_INFO, "NETFLOW: initializing '%s' devices", value); dev = strtok_r(value, ",", &strtokState); while(dev != NULL) { int deviceId = atoi(dev); if(deviceId > 0) { if((deviceId = createNetFlowDevice(deviceId)) == -1) { pluginActive = 0; return(-1); } } dev = strtok_r(NULL, ",", &strtokState); } } else traceEvent(CONST_TRACE_INFO, "NETFLOW: no devices to initialize"); return(0); } /* ****************************** */ static void printNetFlowDeviceConfiguration(void) { char buf[512], value[128]; int i = 0; sendString("
\n"); sendString("\n"); sendString("
Available NetFlow Devices
\n"); if((fetchPrefsValue(nfValue(0, "knownDevices", 0), value, sizeof(value)) != -1) && (strlen(value) > 0)) { char *strtokState, *dev; sendString("
pluginURLname); sendString("\" METHOD=GET>\n"); dev = strtok_r(value, ",", &strtokState); while(dev != NULL) { int id = mapNetFlowDeviceToNtopDevice(atoi(dev)); if(id == -1) safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s.%s\n", dev, i == 0 ? "CHECKED" : "", NETFLOW_DEVICE_NAME, dev); else safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s\n", dev, i == 0 ? "CHECKED" : "", myGlobals.device[id].humanFriendlyName); sendString(buf); if(pluginActive) { safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "[ Delete ]", netflowPluginInfo->pluginURLname, dev); sendString(buf); } sendString("
\n"); i++; dev = strtok_r(NULL, ",", &strtokState); } if(pluginActive) sendString("

 " "\n

\n"); } /* *********************** */ if(pluginActive) { sendString("

pluginURLname); sendString("\" METHOD=GET>\n"); sendString("

 \n

\n"); } else { sendString("

Please pluginURLname); sendString("=1\">enable the NetFlow plugin first
\n"); } sendString("

"); printHTMLtrailer(); } /* ****************************** */ static void printNetFlowStatistics(void) { char buf[1024]; int i, printedStatistics=0; #ifdef MAX_NETFLOW_PACKET_BUFFER float rminTime=99999.0, rmaxTime=0.0, /*stddev:*/ rM, rT, rQ, rR, rSD, rXBAR; #endif #ifdef MAX_NETFLOW_FLOW_BUFFER float fminTime=99999.0, fmaxTime=0.0, /*stddev:*/ fM, fT, fQ, fR, fSD, fXBAR; #endif memset(&buf, 0, sizeof(buf)); printHTMLheader("NetFlow Statistics", NULL, 0); for(i = 0; inumNetFlowsPktsRcvd > 0)) { if(printedStatistics == 0) { sendString("
\n"); printedStatistics = 1; } safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "\n", i, myGlobals.device[i].humanFriendlyName); sendString(buf); printNetFlowStatisticsRcvd(i); } } if(printedStatistics == 1) { sendString("
Device %d - %s
\n
\n"); } else { printNoDataYet(); } #if defined(MAX_NETFLOW_FLOW_BUFFER) || defined(MAX_NETFLOW_PACKET_BUFFER) printSectionTitle("netFlow Processing times"); sendString("
\n
" "

These numbers are the elapsed time (in seconds) to process each netFlow " "packet and each individual flow. The computations are based only on the most " "recent"); #ifdef MAX_NETFLOW_FLOW_BUFFER sendString(" " xstr(MAX_NETFLOW_FLOW_BUFFER) " flows"); #ifdef MAX_NETFLOW_PACKET_BUFFER sendString(" and"); #endif #endif #ifdef MAX_NETFLOW_PACKET_BUFFER sendString(" " xstr(MAX_NETFLOW_PACKET_BUFFER) " flow packets"); #endif sendString(" processed.

\n" "

Errors may cause processing to be abandoned and those flows (flow packets) " "are not counted in these values.

\n" "

Small averages are good, especially if the standard deviation is small " "(standard deviation is a measurement of the variability of the actual values " "around the average).

\n" "

 

\n" "
\n"); #endif /* MAX_NETFLOW_FLOW_BUFFER || MAX_NETFLOW_PACKET_BUFFER */ #ifdef MAX_NETFLOW_FLOW_BUFFER printSectionTitle("Individual Flows"); if(netflowflowBufferCount >= MAX_NETFLOW_FLOW_BUFFER) { sendString("
\n" "" "\n"); for(i=0; i fmaxTime) fmaxTime = netflowflowBuffer[i]; if(netflowflowBuffer[i] < fminTime) fminTime = netflowflowBuffer[i]; if(i==0) { fM = netflowflowBuffer[0]; fT = 0.0; } else { fQ = netflowflowBuffer[i] - fM; fR = fQ / (float)(i+1); fM += fR; fT = fT + i * fQ * fR; } } fSD = sqrtf(fT / (MAX_NETFLOW_FLOW_BUFFER - 1)); fXBAR /*average*/ = fM; sendString("\n", fminTime); sendString(buf); sendString("\n", fXBAR); sendString(buf); sendString("\n", fmaxTime); sendString(buf); sendString("\n", fSD); sendString(buf); sendString("\n", netflowfmaxTime); sendString(buf); sendString("
ItemTime
Minimum"); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%.6f
Average"); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%.6f
Maximum"); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%.6f
Standard Deviation"); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%.6f
Maximum ever"); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%.6f
\n
\n"); } else { printNoDataYet(); } #ifdef MAX_NETFLOW_PACKET_BUFFER sendString("

 

\n"); #endif #endif /* MAX_NETFLOW_FLOW_BUFFER */ #ifdef MAX_NETFLOW_PACKET_BUFFER printSectionTitle("Flow Packets"); if(netflowpacketBufferCount >= MAX_NETFLOW_PACKET_BUFFER) { sendString("
\n" "" "\n"); for(i=0; i rmaxTime) rmaxTime = netflowpacketBuffer[i]; if(netflowpacketBuffer[i] < rminTime) rminTime = netflowpacketBuffer[i]; if(i==0) { rM = netflowpacketBuffer[0]; rT = 0.0; } else { rQ = netflowpacketBuffer[i] - rM; rR = rQ / (float)(i+1); rM += rR; rT = rT + i * rQ * rR; } } rSD = sqrtf(rT / (MAX_NETFLOW_PACKET_BUFFER - 1)); rXBAR /*average*/ = rM; sendString("\n", rminTime); sendString(buf); sendString("\n", rXBAR); sendString(buf); sendString("\n", rmaxTime); sendString(buf); sendString("\n", rSD); sendString(buf); sendString("\n", netflowpmaxTime); sendString(buf); sendString("
ItemTime
Minimum"); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%.6f
Average"); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%.6f
Maximum"); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%.6f
Standard Deviation"); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%.6f
Maximum ever"); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%.6f
\n
\n"); } else { printNoDataYet(); } #endif /* MAX_NETFLOW_PACKET_BUFFER */ printPluginTrailer(NULL, "NetFlow is a trademark of Cisco Systems"); } /* ****************************** */ static void printNetFlowConfiguration(int deviceId) { char buf[512], buf1[32], buf2[32]; #ifdef HAVE_SCPT #define UDPSLASHSCPT "UDP/SCTP" #else #define UDPSLASHSCPT "UDP" #endif sendString("
\n"); sendString("\n"); sendString(""); sendString("\n"); sendString("\n"); sendString("\n"); sendString("\n"); sendString("\n"); sendString("\n"); sendString("\n"); sendString("\n"); sendString("\n"); sendString("\n"); sendString("\n\n"); sendString("\n"); sendString("\n\n"); sendString("\n"); sendString("\n" "\n"); /* ****************************************************** */ sendString("\n"); sendString("\n\n"); /* ****************************************************** */ #ifdef HAVE_MYSQL_H sendString("\n"); sendString("\n\n"); #endif /* ****************************************************** */ sendString("\n"); sendString("\n\n"); /* *************************************** */ sendString("\n"); sendString("\n"); sendString("\n\n"); sendString("\n"); sendString("\n\n"); sendString("\n"); /* ********************************************* */ sendString("\n"); sendString("\n\n"); sendString("\n"); sendString("
Incoming Flows
NetFlow Device
pluginURLname); sendString("\" method=GET>\n

"); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "", myGlobals.device[deviceId].netflowGlobals->netFlowDeviceId); sendString(buf); sendString(" "); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), " [ List NetFlow Interfaces ]

\n
", netflowPluginInfo->pluginName); sendString(buf); sendString("
Flow
Collection
Local
Collector
" UDPSLASHSCPT "
Port
pluginURLname); sendString("\" method=GET>\n

"); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "", myGlobals.device[deviceId].netflowGlobals->netFlowDeviceId); sendString(buf); sendString("netFlowInPort); sendString(buf); sendString("\"> [ Use a port value of 0 to disable collection ] " "" "

\n
\n\n" "

If you want ntop to display NetFlow data it receives from other " "hosts, i.e. act as a collector, you must specify the " UDPSLASHSCPT " port to listen to. " "The default port used for NetFlow is " DEFAULT_NETFLOW_PORT_STR ".

\n" "

\n"); if(myGlobals.device[deviceId].netflowGlobals->netFlowInPort == 0) sendString("

WARNING: " "The 'Local Collector " UDPSLASHSCPT "Port' is zero (none). " "Even if this plugin is ACTIVE, you must still enter a port number for " "ntop to receive and process NetFlow data.

\n"); sendString("
Virtual
NetFlow
Interface
Network
Address
pluginURLname); sendString("\" method=GET>\n"); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "", myGlobals.device[deviceId].netflowGlobals->netFlowDeviceId); sendString(buf); sendString(" netFlowIfAddress, buf1, sizeof(buf1)), _intoa(myGlobals.device[deviceId].netflowGlobals->netFlowIfMask, buf2, sizeof(buf2))); sendString(buf); sendString("\"> "); sendString("

\n
\n"); sendString("

This value is in the form of a network address and mask on the " "network where the actual NetFlow probe is located. " "ntop uses this value to determine which TCP/IP addresses are " "local and which are remote.

\n" "

You may specify this in either format, <network>/<mask> or " "CIDR (<network>/<bits>). An existing value is displayed " "in <network>/<mask> format.

\n" "

If the NetFlow probe is monitoring only a single network, then " "this is all you need to set. If the NetFlow probe is monitoring " "multiple networks, then pick one of them for this setting and use " "the -m | --local-subnets parameter to specify the others.

\n" "

This interface is called 'virtual' because the ntop host " "is not really connected to the network you specify here.

\n" "
Flow Aggregation
pluginURLname); sendString("\" method=GET>\n"); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "", myGlobals.device[deviceId].netflowGlobals->netFlowDeviceId); sendString(buf); sendString("

"); sendString("

\n" "

ntop can aggregate (combine) NetFlow information based " "on a number of 'policies'. The default is to store all NetFlow " "data (perform no addregation). Other choices are:

\n" "
    \n" "
  • Port Aggregation combines all traffic by port number, " "regardless of source or destination address. For example, web " "traffic to both 192.168.1.1 and 192.168.1.2 would be combined " "into a single 'host'.
  • \n" "
  • Host Aggregation combines all traffic to a host, " "regardless of source or destination port number. For example, " "both web and ftp traffic to 192.168.1.1 would be combined into " "a single 'port'.
  • \n" "
  • Protocol Aggregation combines all traffic by TCP/IP " "protocol (TCP, UDP or ICMP), regardless of source or destination " "address or port. For example, all ICMP traffic would be combined, " "regardless of origin or destination.
  • \n" "
  • AS Aggregation  combines all NetFlow data by AS " "(Autonomous System) number, that is as if the source and destination " "address and port were all zero. For more information on AS Numbers, " "see RFC 1930 and the high level " "assignments at IANA
  • \n" "
\n" "
FilteringWhite List
pluginURLname); sendString("\" method=GET>\n"); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "", myGlobals.device[deviceId].netflowGlobals->netFlowDeviceId); sendString(buf); sendString("netFlowWhiteList == NULL ? " " : myGlobals.device[deviceId].netflowGlobals->netFlowWhiteList); sendString(buf); sendString("\">

\n
\n" "

This is a list of one or more TCP/IP host(s)/network(s) which we will " "store data from when these host(s)/network(s) occur in the NetFlow records.

\n" "
Black List
pluginURLname); sendString("\" method=GET>"); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "", myGlobals.device[deviceId].netflowGlobals->netFlowDeviceId); sendString(buf); sendString("netFlowBlackList == NULL ? " " : myGlobals.device[deviceId].netflowGlobals->netFlowBlackList); sendString(buf); sendString("\">

\n
\n" "

This is a list of one or more TCP/IP host(s)/network(s) which we will " "exclude data from (i.e. not store it) when these host(s)/network(s) occur " "in the NetFlow records.

\n" "
    " "
  • Changes to white / black lists take affect immediately, " "but are NOT retro-active.
  • \n" "
  • Use a space to disable a list.
  • \n" "
  • Use a.b.c.d/32 for a single host in a list.
  • \n" "
  • The white / black lists accept both <network>/<mask> and " "CIDR <network>/<bits> format. Both formats may be used in the " "same list. " "For example, 192.168.1.0/24 means all addresses with 24 bits of network and " "thus 8 bits of host, or the range from 192.168.1.0 to 192.168.1.255. " "Similarly, the list 192.168.1.0/24,192.168.2.0/255.255.255.0 means the range " "from 192.168.1.0 - 192.168.2.255.
  • \n" "
  • The white list and black interact this way:\n" "
    • If present, the black list is processed FIRST. Data from any host " "matching the black list is simply thrown away.
    • \n" "
    • If no black list is specified, no hosts are excluded.
    • \n" "
    • If present, the white list is processed SECOND. Data from any host " "NOT matching the white list is thrown away.
    • \n" "
    • If no white list is specified, the value 0.0.0.0/0 (ALL hosts) is used.
    • \n" "
    \n
  • \n
\n" "
 
General Options
Enable Session Handling
pluginURLname); sendString("\" method=GET>\n

"); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "", myGlobals.device[deviceId].netflowGlobals->netFlowDeviceId); sendString(buf); if(myGlobals.device[deviceId].netflowGlobals->enableSessionHandling) { sendString("Yes\n" "No\n"); } else { sendString("Yes\n" "No\n"); } sendString("

\n
\n" "

If enabled, incoming flows will be added to the list of active TCP/UDP" " sessions. If you have a large network, make sure you enable this option " "only if the ntop host has enough computing resources as this can lead " "to high CPU and memory consumption.

\n" "
Save Flows
into SQL DB
pluginURLname); sendString("\" method=GET>\n

"); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "", myGlobals.device[deviceId].netflowGlobals->netFlowDeviceId); sendString(buf); if(myGlobals.device[deviceId].netflowGlobals->saveFlowsIntoDB) { sendString("Yes\n" "No\n"); } else { sendString("Yes\n" "No\n"); } sendString("

\n
\n" "

This options instruments ntop to save flows into the configured " "SQL database. Note that you can also configure " "DB options and records persistency.

\n" "
Assume FTP
pluginURLname); sendString("\" method=GET>\n

"); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "", myGlobals.device[deviceId].netflowGlobals->netFlowDeviceId); sendString(buf); if(myGlobals.device[deviceId].netflowGlobals->netFlowAssumeFTP) { sendString("Yes\n" "No\n"); } else { sendString("Yes\n" "No\n"); } sendString("

\n"); sendString("
\n" "

ntop handles the FTP protocol differently when using NetFlow data " "vs. the normal full protocol analysis. In the NetFlow data, ntop sees " "only the address and port number and so can not monitor the ftp control channel " "to detect which dynamically assigned ports are being used for ftp data.

\n" "

This option tells ntop to assume that data from an unknown high " "(>1023) port to an unknown high port should be treated as FTP data.

\n" "

Use this only if you understand your data flows.

\n" "

For most situations this is not a good assumption - for example, " "peer-to-peer traffic also is high port to high port. " "However, in limited situations, this option enables you to obtain a more " "correct view of your traffic.

\n" "

This option takes effect IMMEDIATELY

\n" "
Flow DumpDump Interval
pluginURLname); sendString("\" method=GET>\n"); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "", myGlobals.device[deviceId].netflowGlobals->netFlowDeviceId); sendString(buf); sendString("dumpInterval); sendString(buf); sendString("\">

\n
\n" "

Specifies how often data is stored permanently. " "Set it to 0 (zero) to disable dumping

\n
Dump File Path
pluginURLname); sendString("\" method=GET>\n"); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "", myGlobals.device[deviceId].netflowGlobals->netFlowDeviceId); sendString(buf); sendString("dumpPath == NULL ? "./netflow-dump" : myGlobals.device[deviceId].netflowGlobals->dumpPath); sendString("\">

\n
\n" "

Specifies the directory where dump files will be saved.

\n
You can instrument ntop to save incoming flows on disk so" " that you can use them for integration with other applications or for " "historical purposes:
    " "
  • Flows are stored on files whose name is <time of the day>.flow" "
  • The file contents is [<flow length (4 digits 0 padded)><raw flow>]*" "
Debug
pluginURLname); sendString("\" method=GET>\n

"); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "", myGlobals.device[deviceId].netflowGlobals->netFlowDeviceId); sendString(buf); if(myGlobals.device[deviceId].netflowGlobals->netFlowDebug) { sendString("On"); sendString("Off"); } else { sendString("On"); sendString("Off"); } sendString("

\n"); sendString("
\n" "

This option turns on debugging, which dumps a huge quantity of " "noise into the standard ntop log, all about what the NetFlow " "plugin is doing. If you are doing development, this might be helpful, " "otherwise leave it alone!

\n" "
REMEMBER
  • Regardless of settings here, " "the NetFlow plugin must be ACTIVE on the main plugin menu (click " "here to go back) " "for ntop to receive and/or " "process NetFlow data.\n" "
  • Any option not indicated as taking effect immediately will require you " "to recycle (inactivate and then activate) the NetFlow plugin in order " "for the change to take affect.
\n
\n"); } #undef UDPSLASHSCPT /* ****************************** */ static void printNetFlowStatisticsRcvd(int deviceId) { char buf[512], formatBuf[32], formatBuf2[32]; u_int i, totFlows; InterfaceStats *ifStats = myGlobals.device[deviceId].netflowGlobals->ifStats; if(ifStats != NULL) { sendString("\nInterface Statistics\n\n"); while(ifStats != NULL) { struct stat statbuf; int found = 0; struct in_addr addr; safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s/interfaces/%s/NetFlow/%d/ifInOctets.rrd", myGlobals.rrdPath, myGlobals.device[deviceId].uniqueIfName, ifStats->interface_id); revertSlashIfWIN32(buf, 0); if(!stat(buf, &statbuf)) found = 1; else { safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s/interfaces/%s/NetFlow/%d/ifOutOctets.rrd", myGlobals.rrdPath, myGlobals.device[deviceId].uniqueIfName, ifStats->interface_id); revertSlashIfWIN32(buf, 0); if(!stat(buf, &statbuf)) found = 1; } if(!found) { safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "\nInterface %d\n", ifStats->interface_id); sendString(buf); } else { safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "\n" "" " " "" "\n\n", myGlobals.device[deviceId].uniqueIfName, ifStats->interface_id, myGlobals.device[deviceId].uniqueIfName, ifStats->interface_id); sendString(buf); } addr.s_addr = ifStats->netflow_device_ip; safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "NetFlow Device: %s
", _intoa(addr, formatBuf, sizeof(formatBuf))); sendString(buf); if(ifStats->interface_name[0] != '\0') { safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "Interface Name: %s
", ifStats->interface_name); sendString(buf); } safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "Pkts: %s in/%s out
", formatPkts(ifStats->inPkts.value, formatBuf, sizeof(formatBuf)), formatPkts(ifStats->outPkts.value, formatBuf2, sizeof(formatBuf2))); sendString(buf); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "Bytes: %s in/%s out", formatBytes(ifStats->inBytes.value, 1, formatBuf, sizeof(formatBuf)), formatBytes(ifStats->outBytes.value, 1, formatBuf2, sizeof(formatBuf2))); sendString(buf); sendString("\n"); ifStats = ifStats->next; } } /* ***************************************************************** */ sendString("\n" "Received Flows\n" "\n" "\n" "Flow Senders\n" ""); for(i=0; iprobeList[i].probeAddr.s_addr == 0) break; safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s [%s pkts]
\n", _intoa(myGlobals.device[deviceId].netflowGlobals->probeList[i].probeAddr, buf, sizeof(buf)), formatPkts(myGlobals.device[deviceId].netflowGlobals->probeList[i].pkts, formatBuf, sizeof(formatBuf))); sendString(buf); } sendString(" \n\n"); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "\n" "Number of Packets Received\n" "%s\n" "\n", formatPkts(myGlobals.device[deviceId].netflowGlobals->numNetFlowsPktsRcvd, formatBuf, sizeof(formatBuf))); sendString(buf); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "\n" "Number of Packets with Bad Version\n" "%s\n" "\n", formatPkts(myGlobals.device[deviceId].netflowGlobals->numBadNetFlowsVersionsRcvd, formatBuf, sizeof(formatBuf))); sendString(buf); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "\n" "Number of Packets Processed\n" "%s\n" "\n", formatPkts(myGlobals.device[deviceId].netflowGlobals->numNetFlowsPktsRcvd - myGlobals.device[deviceId].netflowGlobals->numBadNetFlowsVersionsRcvd, formatBuf, sizeof(formatBuf))); sendString(buf); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "\n" "Number of Valid Flows Received\n" "%s\n" "\n", formatPkts(myGlobals.device[deviceId].netflowGlobals->numNetFlowsRcvd, formatBuf, sizeof(formatBuf))); sendString(buf); if(myGlobals.device[deviceId].netflowGlobals->numNetFlowsPktsRcvd > 0) { totFlows = myGlobals.device[deviceId].netflowGlobals->numNetFlowsV5Rcvd + myGlobals.device[deviceId].netflowGlobals->numNetFlowsV7Rcvd + myGlobals.device[deviceId].netflowGlobals->numNetFlowsV9Rcvd + myGlobals.device[deviceId].netflowGlobals->numBadFlowPkts + myGlobals.device[deviceId].netflowGlobals->numBadFlowBytes + myGlobals.device[deviceId].netflowGlobals->numBadFlowReality + myGlobals.device[deviceId].netflowGlobals->numNetFlowsV9UnknTemplRcvd; safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "\n" "Average Number of Flows per Packet\n" "%.1f\n" "\n", (float)totFlows/(float)myGlobals.device[deviceId].netflowGlobals->numNetFlowsPktsRcvd); sendString(buf); } safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "\n" "Number of V1 Flows Received\n" "%s\n" "\n", formatPkts(myGlobals.device[deviceId].netflowGlobals->numNetFlowsV1Rcvd, formatBuf, sizeof(formatBuf))); sendString(buf); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "\n" "Number of V5 Flows Received\n" "%s\n" "\n", formatPkts(myGlobals.device[deviceId].netflowGlobals->numNetFlowsV5Rcvd, formatBuf, sizeof(formatBuf))); sendString(buf); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "\n" "Number of V7 Flows Received\n" "%s\n" "\n", formatPkts(myGlobals.device[deviceId].netflowGlobals->numNetFlowsV7Rcvd, formatBuf, sizeof(formatBuf))); sendString(buf); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "\n" "Number of V9 Flows Received\n" "%s\n" "\n", formatPkts(myGlobals.device[deviceId].netflowGlobals->numNetFlowsV9Rcvd, formatBuf, sizeof(formatBuf))); sendString(buf); if(myGlobals.device[deviceId].netflowGlobals->numNetFlowsV9TemplRcvd) { safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "\n" "Total V9 Templates Received\n" "%s\n" "\n", formatPkts(myGlobals.device[deviceId].netflowGlobals->numNetFlowsV9TemplRcvd, formatBuf, sizeof(formatBuf))); sendString(buf); } if(myGlobals.device[deviceId].netflowGlobals->numNetFlowsV9BadTemplRcvd) { safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "\n" "Number of Bad V9 Templates Received\n" "%s\n" "\n", formatPkts(myGlobals.device[deviceId].netflowGlobals->numNetFlowsV9BadTemplRcvd, formatBuf, sizeof(formatBuf))); sendString(buf); } if(myGlobals.device[deviceId].netflowGlobals->numNetFlowsV9UnknTemplRcvd) { safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "\n" "Number of V9 Flows with Unknown Templates Received\n" "%s\n" "\n", formatPkts(myGlobals.device[deviceId].netflowGlobals->numNetFlowsV9UnknTemplRcvd, formatBuf, sizeof(formatBuf))); sendString(buf); } sendString(" \n" "\n" "Discarded Flows\n" "\n"); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "\n" "Number of Flows with Zero Packet Count\n" "%s\n" "\n", formatPkts(myGlobals.device[deviceId].netflowGlobals->numBadFlowPkts, formatBuf, sizeof(formatBuf))); sendString(buf); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "\n" "Number of Flows with Zero Byte Count\n" "%s\n" "\n", formatPkts(myGlobals.device[deviceId].netflowGlobals->numBadFlowBytes, formatBuf, sizeof(formatBuf))); sendString(buf); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "\n" "Number of Flows with Bad Data\n" "%s\n" "\n", formatPkts(myGlobals.device[deviceId].netflowGlobals->numBadFlowReality, formatBuf, sizeof(formatBuf))); sendString(buf); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "\n" "Number of Flows with Unknown Template\n" "%s\n" "\n", formatPkts(myGlobals.device[deviceId].netflowGlobals->numNetFlowsV9UnknTemplRcvd, formatBuf, sizeof(formatBuf))); sendString(buf); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "\n" "Total Number of Flows Processed\n" "%s\n" "\n", formatPkts(myGlobals.device[deviceId].netflowGlobals->numNetFlowsProcessed, formatBuf, sizeof(formatBuf))); sendString(buf); if((myGlobals.device[deviceId].netflowGlobals->numSrcNetFlowsEntryFailedWhiteList + myGlobals.device[deviceId].netflowGlobals->numSrcNetFlowsEntryFailedBlackList + myGlobals.device[deviceId].netflowGlobals->numDstNetFlowsEntryFailedWhiteList + myGlobals.device[deviceId].netflowGlobals->numDstNetFlowsEntryFailedBlackList) > 0) { sendString(" \n" "\n" "Accepted/Rejected Flows\n" "\n" "\n" " \n" "Source / Destination\n" "\n"); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "\n" "Rejected - Black list\n" "%s / %s\n" "\n", formatPkts(myGlobals.device[deviceId].netflowGlobals->numSrcNetFlowsEntryFailedBlackList, formatBuf, sizeof(formatBuf)), formatPkts(myGlobals.device[deviceId].netflowGlobals->numDstNetFlowsEntryFailedBlackList, formatBuf2, sizeof(formatBuf2))); sendString(buf); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "\n" "Rejected - White list\n" "%s / %s\n" "\n", formatPkts(myGlobals.device[deviceId].netflowGlobals->numSrcNetFlowsEntryFailedWhiteList, formatBuf, sizeof(formatBuf)), formatPkts(myGlobals.device[deviceId].netflowGlobals->numDstNetFlowsEntryFailedWhiteList, formatBuf2, sizeof(formatBuf2))); sendString(buf); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "\n" "Accepted\n" "%s / %s\n" "\n", formatPkts(myGlobals.device[deviceId].netflowGlobals->numSrcNetFlowsEntryAccepted, formatBuf, sizeof(formatBuf)), formatPkts(myGlobals.device[deviceId].netflowGlobals->numDstNetFlowsEntryAccepted, formatBuf2, sizeof(formatBuf2))); sendString(buf); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "\n" "Total\n" "%s / %s\n" "\n", formatPkts(myGlobals.device[deviceId].netflowGlobals->numSrcNetFlowsEntryFailedBlackList + myGlobals.device[deviceId].netflowGlobals->numSrcNetFlowsEntryFailedWhiteList + myGlobals.device[deviceId].netflowGlobals->numSrcNetFlowsEntryAccepted, formatBuf, sizeof(formatBuf)), formatPkts(myGlobals.device[deviceId].netflowGlobals->numDstNetFlowsEntryFailedBlackList + myGlobals.device[deviceId].netflowGlobals->numDstNetFlowsEntryFailedWhiteList + myGlobals.device[deviceId].netflowGlobals->numDstNetFlowsEntryAccepted, formatBuf2, sizeof(formatBuf2))); sendString(buf); } #ifdef DEBUG_FLOWS sendString(" \n" "\n" "Debug>\n" "\n" "\n" "White net list\n" ""); if(myGlobals.device[deviceId].netflowGlobals->numWhiteNets == 0) { sendString("none"); } else { sendString("Network     " "Netmask     " "Hostmask
\n"); for(i=0; inumWhiteNets; i++) { safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "
\n%3d. %08x(%3d.%3d.%3d.%3d) " "%08x(%3d.%3d.%3d.%3d) %08x(%3d.%3d.%3d.%3d)", i, myGlobals.device[deviceId].netflowGlobals->whiteNetworks[i][0], ((myGlobals.device[deviceId].netflowGlobals->whiteNetworks[i][0] >> 24) & 0xff), ((myGlobals.device[deviceId].netflowGlobals->whiteNetworks[i][0] >> 16) & 0xff), ((myGlobals.device[deviceId].netflowGlobals->whiteNetworks[i][0] >> 8) & 0xff), ((myGlobals.device[deviceId].netflowGlobals->whiteNetworks[i][0] ) & 0xff), myGlobals.device[deviceId].netflowGlobals->whiteNetworks[i][1], ((myGlobals.device[deviceId].netflowGlobals->whiteNetworks[i][1] >> 24) & 0xff), ((myGlobals.device[deviceId].netflowGlobals->whiteNetworks[i][1] >> 16) & 0xff), ((myGlobals.device[deviceId].netflowGlobals->whiteNetworks[i][1] >> 8) & 0xff), ((myGlobals.device[deviceId].netflowGlobals->whiteNetworks[i][1] ) & 0xff), myGlobals.device[deviceId].netflowGlobals->whiteNetworks[i][2], ((myGlobals.device[deviceId].netflowGlobals->whiteNetworks[i][2] >> 24) & 0xff), ((myGlobals.device[deviceId].netflowGlobals->whiteNetworks[i][2] >> 16) & 0xff), ((myGlobals.device[deviceId].netflowGlobals->whiteNetworks[i][2] >> 8) & 0xff), ((myGlobals.device[deviceId].netflowGlobals->whiteNetworks[i][2] ) & 0xff) ); sendString(buf); if(inumWhiteNets) sendString("
\n"); } } sendString("\n\n"); sendString("\n" "Black net list\n" ""); if(myGlobals.device[deviceId].netflowGlobals->numBlackNets == 0) { sendString("none"); } else { sendString("Network     " "Netmask     " "Hostmask
\n"); for(i=0; inumBlackNets; i++) { safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "
\n%3d. %08x(%3d.%3d.%3d.%3d) " "%08x(%3d.%3d.%3d.%3d) %08x(%3d.%3d.%3d.%3d)", i, myGlobals.device[deviceId].netflowGlobals->blackNetworks[i][0], ((myGlobals.device[deviceId].netflowGlobals->blackNetworks[i][0] >> 24) & 0xff), ((myGlobals.device[deviceId].netflowGlobals->blackNetworks[i][0] >> 16) & 0xff), ((myGlobals.device[deviceId].netflowGlobals->blackNetworks[i][0] >> 8) & 0xff), ((myGlobals.device[deviceId].netflowGlobals->blackNetworks[i][0] ) & 0xff), myGlobals.device[deviceId].netflowGlobals->blackNetworks[i][1], ((myGlobals.device[deviceId].netflowGlobals->blackNetworks[i][1] >> 24) & 0xff), ((myGlobals.device[deviceId].netflowGlobals->blackNetworks[i][1] >> 16) & 0xff), ((myGlobals.device[deviceId].netflowGlobals->blackNetworks[i][1] >> 8) & 0xff), ((myGlobals.device[deviceId].netflowGlobals->blackNetworks[i][1] ) & 0xff), myGlobals.device[deviceId].netflowGlobals->blackNetworks[i][2], ((myGlobals.device[deviceId].netflowGlobals->blackNetworks[i][2] >> 24) & 0xff), ((myGlobals.device[deviceId].netflowGlobals->blackNetworks[i][2] >> 16) & 0xff), ((myGlobals.device[deviceId].netflowGlobals->blackNetworks[i][2] >> 8) & 0xff), ((myGlobals.device[deviceId].netflowGlobals->blackNetworks[i][2] ) & 0xff) ); sendString(buf); if(inumBlackNets) sendString("
\n"); } } sendString("\n\n"); #endif } /* ****************************** */ static int createNetFlowDevice(int netFlowDeviceId) { int deviceId; char buf[32], value[128]; traceEvent(CONST_TRACE_INFO, "NETFLOW: createNetFlowDevice(%d)", netFlowDeviceId); safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s.%d", NETFLOW_DEVICE_NAME, netFlowDeviceId); deviceId = createDummyInterface(buf); if(deviceId != -1) { myGlobals.device[deviceId].netflowGlobals = (NetFlowGlobals*)malloc(sizeof(NetFlowGlobals)); if(myGlobals.device[deviceId].netflowGlobals == NULL) { /* Not enough memory */ traceEvent(CONST_TRACE_ERROR, "NETFLOW: not enough memory (netflowGlobals malloc)"); return(-1); } memset(myGlobals.device[deviceId].netflowGlobals, 0, sizeof(NetFlowGlobals)); myGlobals.device[deviceId].activeDevice = 1; myGlobals.device[deviceId].dummyDevice = 0; myGlobals.device[deviceId].netflowGlobals->netFlowDeviceId = netFlowDeviceId; initNetFlowDevice(deviceId); setNetFlowInterfaceMatrix(deviceId); if(fetchPrefsValue(nfValue(deviceId, "humanFriendlyName", 1), value, sizeof(value)) != -1) { free(myGlobals.device[deviceId].humanFriendlyName); myGlobals.device[deviceId].humanFriendlyName = strdup(value); calculateUniqueInterfaceName(deviceId); } traceEvent(CONST_TRACE_INFO, "NETFLOW: createNetFlowDevice created device %d", deviceId); } else traceEvent(CONST_TRACE_ERROR, "NETFLOW: createDummyInterface failed"); return(deviceId); } /* ****************************** */ /* #define DEBUG_FLOWS */ static int mapNetFlowDeviceToNtopDevice(int netFlowDeviceId) { int i; for(i=0; inetFlowDeviceId == netFlowDeviceId)) { #ifdef DEBUG_FLOWS traceEvent(CONST_TRACE_INFO, "NETFLOW: mapNetFlowDeviceToNtopDevice(%d) = %d", netFlowDeviceId, i); #endif return(i); } else { #ifdef DEBUG_FLOWS traceEvent(CONST_TRACE_INFO, "NETFLOW: mapNetFlowDeviceToNtopDevice (id=%d) <=> (netFlowDeviceId=%d)", i, myGlobals.device[i].netflowGlobals->netFlowDeviceId); #endif } } else { #ifdef DEBUG_FLOWS traceEvent(CONST_TRACE_INFO, "NETFLOW: netflowGlobals(%d) = NULL\n", i); #endif } #ifdef DEBUG_FLOWS traceEvent(CONST_TRACE_INFO, "NETFLOW: mapNetFlowDeviceToNtopDevice(%d) failed\n", netFlowDeviceId); #endif return(-1); /* Not found */ } /* #undef DEBUG_FLOWS */ /* ****************************** */ static void flushDevicePrefs(int deviceId) { if(deviceId >= myGlobals.numDevices) return; delPrefsValue(nfValue(deviceId, "netFlowInPort", 1)); delPrefsValue(nfValue(deviceId, "ifNetMask", 1)); delPrefsValue(nfValue(deviceId, "whiteList", 1)); delPrefsValue(nfValue(deviceId, "netFlowDumpPath", 1)); delPrefsValue(nfValue(deviceId, "netFlowDumpInterval", 1)); delPrefsValue(nfValue(deviceId, "blackList", 1)); delPrefsValue(nfValue(deviceId, "enableSessionHandling", 1)); delPrefsValue(nfValue(deviceId, "saveFlowsIntoDB", 1)); delPrefsValue(nfValue(deviceId, "netFlowAssumeFTP", 1)); delPrefsValue(nfValue(deviceId, "netFlowAggregation", 1)); delPrefsValue(nfValue(deviceId, "debug", 1)); delPrefsValue(nfValue(deviceId, "humanFriendlyName", 1)); } /* ****************************** */ static void handleNetflowHTTPrequest(char* _url) { char workList[1024], *url; int deviceId = -1, originalId = -1; sendHTTPHeader(FLAG_HTTP_TYPE_HTML, 0, 1); /* **************************** * Process URL stuff * ****************************** */ if((_url != NULL) && pluginActive) { char *strtokState; if(strncasecmp(_url, CONST_NETFLOW_STATISTICS_HTML, strlen(CONST_NETFLOW_STATISTICS_HTML)) == 0) { printNetFlowStatistics(); printHTMLtrailer(); return; } url = strtok_r(_url, "&", &strtokState); while(url != NULL) { char *device, *_value = NULL; device = strtok(url, "="); if(device != NULL) _value = strtok(NULL, "="); else _value = NULL; if(_value == NULL) _value = ""; if(_value && device) { char value[256]; unescape(value, sizeof(value), _value); if(strcmp(device, "device") == 0) { originalId = deviceId = atoi(value); if((deviceId > 0) && ((deviceId = mapNetFlowDeviceToNtopDevice(deviceId)) == -1)) { printHTMLheader("NetFlow Configuration Error", NULL, 0); printFlagedWarning("Unable to locate the specified device. Please activate the plugin first."); return; } } else if(strcmp(device, "port") == 0) { if(myGlobals.device[deviceId].netflowGlobals->netFlowInPort != atoi(value)) { if(deviceId > 0) { myGlobals.device[deviceId].netflowGlobals->netFlowInPort = atoi(value); storePrefsValue(nfValue(deviceId, "netFlowInPort", 1), value); setNetFlowInSocket(deviceId); } } } else if(strcmp(device, "name") == 0) { char old_name[256], new_name[256]; int rc; sanitize_rrd_string(value); safe_snprintf(__FILE__, __LINE__, old_name, sizeof(old_name), "%s/interfaces/%s", myGlobals.rrdPath, myGlobals.device[deviceId].uniqueIfName); revertSlashIfWIN32(old_name, 0); free(myGlobals.device[deviceId].humanFriendlyName); myGlobals.device[deviceId].humanFriendlyName = strdup(value); storePrefsValue(nfValue(deviceId, "humanFriendlyName", 1), value); calculateUniqueInterfaceName(deviceId); safe_snprintf(__FILE__, __LINE__, new_name, sizeof(new_name), "%s/interfaces/%s", myGlobals.rrdPath, myGlobals.device[deviceId].uniqueIfName); revertSlashIfWIN32(new_name, 0); rc = rename(old_name, new_name); } else if(strcmp(device, "debug") == 0) { if(deviceId > 0) { myGlobals.device[deviceId].netflowGlobals->netFlowDebug = atoi(value); storePrefsValue(nfValue(deviceId, "debug", 1), value); } } else if(strcmp(device, "netFlowAggregation") == 0) { if(deviceId > 0) { myGlobals.device[deviceId].netflowGlobals->netFlowAggregation = atoi(value); storePrefsValue(nfValue(deviceId, "netFlowAggregation", 1), value); } } else if(strcmp(device, "netFlowAssumeFTP") == 0) { if(deviceId > 0) { myGlobals.device[deviceId].netflowGlobals->netFlowAssumeFTP = atoi(value); storePrefsValue(nfValue(deviceId, "netFlowAssumeFTP", 1), value); } } else if(strcmp(device, "saveFlowsIntoDB") == 0) { if(deviceId > 0) { myGlobals.device[deviceId].netflowGlobals->saveFlowsIntoDB = atoi(value); storePrefsValue(nfValue(deviceId, "saveFlowsIntoDB", 1), value); } } else if(strcmp(device, "enableSessionHandling") == 0) { if(deviceId > 0) { myGlobals.device[deviceId].netflowGlobals->enableSessionHandling = atoi(value); storePrefsValue(nfValue(deviceId, "enableSessionHandling", 1), value); } } else if(strcmp(device, "ifNetMask") == 0) { int a, b, c, d, a1, b1, c1, d1; if(deviceId > 0) { if(sscanf(value, "%d.%d.%d.%d/%d.%d.%d.%d", &a, &b, &c, &d, &a1, &b1, &c1, &d1) == 8) { myGlobals.device[deviceId].netflowGlobals->netFlowIfAddress.s_addr = (a << 24) +(b << 16) +(c << 8) + d; myGlobals.device[deviceId].netflowGlobals->netFlowIfMask.s_addr = (a1 << 24) +(b1 << 16) +(c1 << 8) + d1; storePrefsValue(nfValue(deviceId, "ifNetMask", 1), value); freeNetFlowMatrixMemory(deviceId); setNetFlowInterfaceMatrix(deviceId); } else if(sscanf(value, "%d.%d.%d.%d/%d", &a, &b, &c, &d, &a1) == 5) { myGlobals.device[deviceId].netflowGlobals->netFlowIfAddress.s_addr = (a << 24) +(b << 16) +(c << 8) + d; myGlobals.device[deviceId].netflowGlobals->netFlowIfMask.s_addr = 0xffffffff >> a1; myGlobals.device[deviceId].netflowGlobals->netFlowIfMask.s_addr =~ myGlobals.device[deviceId].netflowGlobals->netFlowIfMask.s_addr; storePrefsValue(nfValue(deviceId, "ifNetMask", 1), value); freeNetFlowMatrixMemory(deviceId); setNetFlowInterfaceMatrix(deviceId); } else traceEvent(CONST_TRACE_ERROR, "NETFLOW: HTTP request netmask parse error (%s)", value); } } else if(strcmp(device, "whiteList") == 0) { /* Cleanup the http control char xform */ char *fPtr=value, *tPtr=value; if(deviceId > 0) { while(fPtr[0] != '\0') { if((fPtr[0] == '%') && (fPtr[1] == '2')) { *tPtr++ = (fPtr[2] == 'C') ? ',' : '/'; fPtr += 3; } else { *tPtr++ = *fPtr++; } } tPtr[0]='\0'; accessMutex(&myGlobals.device[deviceId].netflowGlobals->whiteblackListMutex, "handleNetflowHTTPrequest()w"); handleWhiteBlackListAddresses(value, myGlobals.device[deviceId].netflowGlobals->whiteNetworks, &myGlobals.device[deviceId].netflowGlobals->numWhiteNets, (char*)&workList, sizeof(workList)); if(myGlobals.device[deviceId].netflowGlobals->netFlowWhiteList != NULL) free(myGlobals.device[deviceId].netflowGlobals->netFlowWhiteList); myGlobals.device[deviceId].netflowGlobals->netFlowWhiteList=strdup(workList); releaseMutex(&myGlobals.device[deviceId].netflowGlobals->whiteblackListMutex); storePrefsValue(nfValue(deviceId, "whiteList", 1), myGlobals.device[deviceId].netflowGlobals->netFlowWhiteList); } } else if(strcmp(device, "blackList") == 0) { /* Cleanup the http control char xform */ char *fPtr=value, *tPtr=value; if(deviceId > 0) { while(fPtr[0] != '\0') { if((fPtr[0] == '%') && (fPtr[1] == '2')) { *tPtr++ = (fPtr[2] == 'C') ? ',' : '/'; fPtr += 3; } else { *tPtr++ = *fPtr++; } } tPtr[0]='\0'; accessMutex(&myGlobals.device[deviceId].netflowGlobals->whiteblackListMutex, "handleNetflowHTTPrequest()b"); handleWhiteBlackListAddresses(value, myGlobals.device[deviceId].netflowGlobals->blackNetworks, &myGlobals.device[deviceId].netflowGlobals->numBlackNets, (char*)&workList, sizeof(workList)); if(myGlobals.device[deviceId].netflowGlobals->netFlowBlackList != NULL) free(myGlobals.device[deviceId].netflowGlobals->netFlowBlackList); myGlobals.device[deviceId].netflowGlobals->netFlowBlackList=strdup(workList); releaseMutex(&myGlobals.device[deviceId].netflowGlobals->whiteblackListMutex); storePrefsValue(nfValue(deviceId, "blackList", 1), myGlobals.device[deviceId].netflowGlobals->netFlowBlackList); } } else if(strcmp(device, "netFlowDumpInterval") == 0) { if(deviceId > 0) { myGlobals.device[deviceId].netflowGlobals->dumpInterval = atoi(value); storePrefsValue(nfValue(deviceId, "netFlowDumpInterval", 1), value); } } else if(strcmp(device, "netFlowDumpPath") == 0) { if(deviceId > 0) { myGlobals.device[deviceId].netflowGlobals->dumpPath = strdup(value); storePrefsValue(nfValue(deviceId, "netFlowDumpPath", 1), value); } } } url = strtok_r(NULL, "&", &strtokState); } } #ifdef DEBUG_FLOWS traceEvent(CONST_TRACE_INFO, "NETFLOW: deviceId=%d", deviceId); #endif if(deviceId == -1) { printHTMLheader("NetFlow Device Configuration", NULL, 0); printNetFlowDeviceConfiguration(); return; } else if(deviceId < 0) { /* Delete an existing device */ char value[128]; int readDeviceId; deviceId = -deviceId; if((deviceId < 0) || ((readDeviceId = mapNetFlowDeviceToNtopDevice(deviceId)) == -1)) { printHTMLheader("NetFlow Configuration Error", NULL, 0); printFlagedWarning("Unable to locate the specified device. Please activate the plugin first."); return; } traceEvent(CONST_TRACE_INFO, "NETFLOW: Attempting to delete [deviceId=%d][NetFlow device=%d]", deviceId, readDeviceId); if(fetchPrefsValue(nfValue(deviceId, "knownDevices", 0), value, sizeof(value)) != -1) { char *strtokState, *dev, value1[128]; value1[0] = '\0'; dev = strtok_r(value, ",", &strtokState); while(dev != NULL) { int _dev = atoi(dev); if(_dev != deviceId) { if(value1[0] != '\0') strcat(value1, ","); strcat(value1, dev); } dev = strtok_r(NULL, ",", &strtokState); } storePrefsValue(nfValue(deviceId, "knownDevices", 0), value1); } myGlobals.device[readDeviceId].activeDevice = 0; // Terminate thread flushDevicePrefs(readDeviceId); traceEvent(CONST_TRACE_INFO, "NETFLOW: Device [deviceId=%d][active=%d]", readDeviceId, myGlobals.device[readDeviceId].activeDevice); // termNetflowDevice(readDeviceId); checkReportDevice(); printHTMLheader("NetFlow Device Configuration", NULL, 0); printNetFlowDeviceConfiguration(); return; } else if(deviceId == 0) { /* Add new device */ char value[128]; if((fetchPrefsValue(nfValue(deviceId, "knownDevices", 0), value, sizeof(value)) != -1) && (strlen(value) > 0)) { char *strtokState, *dev, value1[128], buf[256]; traceEvent(CONST_TRACE_INFO, "NETFLOW: knownDevices=%s", value); value1[0] = '\0'; dev = strtok_r(value, ",", &strtokState); while(dev != NULL) { int _dev; if(strlen(dev) > 0) { _dev = atoi(dev); strcat(value1, ","); strcat(value1, dev); if(_dev >= deviceId) deviceId = _dev+1; } dev = strtok_r(NULL, ",", &strtokState); } if(deviceId == 0) deviceId = 2; safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s,%d", value1, deviceId); traceEvent(CONST_TRACE_INFO, "NETFLOW: knownDevices=%s", buf); storePrefsValue(nfValue(deviceId, "knownDevices", 0), buf); } else { deviceId = 2; /* 1 is reserved */ traceEvent(CONST_TRACE_INFO, "NETFLOW: knownDevices=2"); storePrefsValue(nfValue(deviceId, "knownDevices", 0), "2"); } if((deviceId = createNetFlowDevice(deviceId)) <= 0) { printHTMLheader("NetFlow Configuration Error", NULL, 0); printFlagedWarning("Unable to create a new NetFlow device"); return; } } else { /* Existing device */ } if(deviceId > 0) { /* **************************** * Print Configuration stuff * ****************************** */ printHTMLheader("NetFlow Configuration", NULL, 0); printNetFlowConfiguration(deviceId); sendString("

\n"); if(myGlobals.device[deviceId].netflowGlobals->numNetFlowsPktsRcvd > 0) { /* **************************** * Print statistics * ****************************** */ printSectionTitle("Flow Statistics"); sendString("

\n"); if(myGlobals.device[deviceId].netflowGlobals->numNetFlowsPktsRcvd > 0) printNetFlowStatisticsRcvd(deviceId); sendString("
\n
\n"); sendString("

\n" "\n" "\n\n
" "NOTES:
    " "
  • The virtual NIC, '" NETFLOW_DEVICE_NAME "' is activated only when incoming " "flow capture is enabled.
  • \n" "
  • Once the virtual NIC is activated, it will remain available for the " "duration of the ntop run, even if you disable incoming flows.
  • \n" "
  • NetFlow packets are associated with this separate, virtual device and are " "not mixed with captured packets.
  • \n" "
  • Activating incoming flows will override the command line -M | " "--no-interface-merge parameter for the duration of the ntop run.
  • \n" "
  • NetFlow activation may (rarely) require ntop restart.
  • \n" "
  • You can switch the reporting device using Admin | Switch NIC, or this " "link.
  • \n" "
 
\n"); if(myGlobals.device[deviceId].netflowGlobals->whiteblackListMutex.isLocked) { sendString("\n" "\n" "\n" "\n"); sendString("\n" "\n
 
Mutexes
List Mutex"); printMutexStatus(FALSE, &myGlobals.device[deviceId].netflowGlobals->whiteblackListMutex, "White/Black list mutex"); printMutexStatus(FALSE, &myGlobals.device[deviceId].netflowGlobals->ifStatsMutex, "Interface statistics mutex"); sendString("
\n"); } } /* ****************************** * Print closing * ****************************** */ sendString("\n" "\n" "\n\n
 

Please be aware that if you need a fast, light, memory savvy, " "highly configurable NetFlow probe, you better give " "nProbe " "a try.

\n" "

If you are looking for a cheap, dedicated hardware NetFlow probe you " "should look into " "nBox86 " "\"nBox.

\n" "
 
\n"); } safe_snprintf(__FILE__, __LINE__, workList, sizeof(workList), "%s?device=%d", netflowPluginInfo->pluginURLname, originalId); printPluginTrailer((myGlobals.device[deviceId].netflowGlobals->numNetFlowsPktsRcvd > 0) ? workList : NULL, "NetFlow is a trademark of Cisco Systems"); printHTMLtrailer(); } /* ****************************** */ static void termNetflowDevice(int deviceId) { traceEvent(CONST_TRACE_INFO, "NETFLOW: terminating device %s", myGlobals.device[deviceId].humanFriendlyName); if(!pluginActive) return; if(myGlobals.device[deviceId].activeDevice == 0) { /* traceEvent(CONST_TRACE_WARNING, "NETFLOW: deviceId=%d terminated already", deviceId); */ return; } if(myGlobals.device[deviceId].netflowGlobals == NULL) { traceEvent(CONST_TRACE_WARNING, "NETFLOW: deviceId=%d terminating a non-NetFlow device", deviceId); return; } if((deviceId >= 0) && (deviceId < myGlobals.numDevices)) { if(myGlobals.device[deviceId].netflowGlobals->threadActive) { killThread(&myGlobals.device[deviceId].netflowGlobals->netFlowThread); #ifdef HAVE_SNMP killThread(&myGlobals.device[deviceId].netflowGlobals->netFlowUtilsThread); #endif myGlobals.device[deviceId].netflowGlobals->threadActive = 0; } tryLockMutex(&myGlobals.device[deviceId].netflowGlobals->whiteblackListMutex, "termNetflow"); deleteMutex(&myGlobals.device[deviceId].netflowGlobals->whiteblackListMutex); if(myGlobals.device[deviceId].netflowGlobals->netFlowInSocket > 0) { closeNwSocket(&myGlobals.device[deviceId].netflowGlobals->netFlowInSocket); #ifdef HAVE_SCTP if(myGlobals.device[deviceId].netflowGlobals->netFlowInSctpSocket > 0) closeNwSocket(&myGlobals.device[deviceId].netflowGlobals->netFlowInSctpSocket); #endif } while(myGlobals.device[deviceId].netflowGlobals->templates != NULL) { FlowSetV9 *temp = myGlobals.device[deviceId].netflowGlobals->templates->next; free(myGlobals.device[deviceId].netflowGlobals->templates->fields); free(myGlobals.device[deviceId].netflowGlobals->templates); myGlobals.device[deviceId].netflowGlobals->templates = temp; } free(myGlobals.device[deviceId].netflowGlobals); myGlobals.device[deviceId].activeDevice = 0; } else traceEvent(CONST_TRACE_WARNING, "NETFLOW: requested invalid termination of deviceId=%d", deviceId); } /* **************************************** */ static void termNetflowFunct(u_char termNtop /* 0=term plugin, 1=term ntop */) { char value[128]; traceEvent(CONST_TRACE_ALWAYSDISPLAY, "NETFLOW: Terminating NetFlow"); if((fetchPrefsValue(nfValue(0, "knownDevices", 0), value, sizeof(value)) != -1) && (strlen(value) > 0)) { char *strtokState, *dev; dev = strtok_r(value, ",", &strtokState); while(dev != NULL) { int deviceId = atoi(dev); if((deviceId > 0) && ((deviceId = mapNetFlowDeviceToNtopDevice(deviceId)) > 0)) { termNetflowDevice(deviceId); } else traceEvent(CONST_TRACE_WARNING, "NETFLOW: requested invalid termination of deviceId=%d", deviceId); dev = strtok_r(NULL, ",", &strtokState); } } else traceEvent(CONST_TRACE_INFO, "NETFLOW: no devices to terminate (%s)", value); traceEvent(CONST_TRACE_INFO, "NETFLOW: Thanks for using ntop NetFlow"); traceEvent(CONST_TRACE_ALWAYSDISPLAY, "NETFLOW: Done"); fflush(stdout); pluginActive = 0; } /* **************************************** */ #ifdef DEBUG_FLOWS static void handleNetFlowPacket(u_char *_deviceId, const struct pcap_pkthdr *h, const u_char *p) { int sampledPacketSize; int deviceId, rc; if(myGlobals.runningPref.rFileName != NULL) { /* ntop is reading packets from a file */ struct ether_header ehdr; u_int caplen = h->caplen; u_int length = h->len; unsigned short eth_type; u_int8_t flags = 0; struct ip ip; deviceId = 1; /* Dummy value */ #ifdef DEBUG_FLOWS if(0) traceEvent(CONST_TRACE_INFO, "Rcvd packet to dissect [caplen=%d][len=%d]", caplen, length); #endif if(caplen >= sizeof(struct ether_header)) { memcpy(&ehdr, p, sizeof(struct ether_header)); eth_type = ntohs(ehdr.ether_type); if(eth_type == ETHERTYPE_IP) { u_int plen, hlen; u_short sport, dport; #ifdef DEBUG_FLOWS if(0) traceEvent(CONST_TRACE_INFO, "Rcvd IP packet to dissect"); #endif memcpy(&ip, p+sizeof(struct ether_header), sizeof(struct ip)); hlen =(u_int)ip.ip_hl * 4; NTOHL(ip.ip_dst.s_addr); NTOHL(ip.ip_src.s_addr); plen = length-sizeof(struct ether_header); #ifdef DEBUG_FLOWS if(0) traceEvent(CONST_TRACE_INFO, "Rcvd IP packet to dissect " "[deviceId=%d][sender=%s][proto=%d][len=%d][hlen=%d]", deviceId, intoa(ip.ip_src), ip.ip_p, plen, hlen); #endif if(ip.ip_p == IPPROTO_UDP) { if(plen >(hlen+sizeof(struct udphdr))) { char* rawSample =(void*)(p+sizeof(struct ether_header)+hlen+sizeof(struct udphdr)); int rawSampleLen = h->caplen-(sizeof(struct ether_header)+hlen+sizeof(struct udphdr)); #ifdef DEBUG_FLOWS if(0) traceEvent(CONST_TRACE_INFO, "Rcvd from from %s [netflowGlobals=%x]", intoa(ip.ip_src), myGlobals.device[deviceId].netflowGlobals); #endif myGlobals.device[deviceId].netflowGlobals->numNetFlowsPktsRcvd++; dissectFlow(rawSample, rawSampleLen, deviceId); } } } else { #ifdef DEBUG_FLOWS if(0) traceEvent(CONST_TRACE_INFO, "Rcvd non-IP [0x%04X] packet to dissect", eth_type); #endif } } } } #endif /* ***************************************** */ /* Plugin entry fctn */ #ifdef MAKE_STATIC_PLUGIN PluginInfo* netflowPluginEntryFctn(void) #else PluginInfo* PluginEntryFctn(void) #endif { traceEvent(CONST_TRACE_ALWAYSDISPLAY, "NETFLOW: Welcome to %s.(C) 2002-07 by Luca Deri", netflowPluginInfo->pluginName); return(netflowPluginInfo); } /* This must be here so it can access the struct PluginInfo, above */ static void setPluginStatus(char * status) { if(netflowPluginInfo->pluginStatusMessage != NULL) free(netflowPluginInfo->pluginStatusMessage); if(status == NULL) { netflowPluginInfo->pluginStatusMessage = NULL; } else { netflowPluginInfo->pluginStatusMessage = strdup(status); } }