/* * -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- * http://www.ntop.org * * Copyright (C) 1998-2007 Luca Deri * * -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- * * 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. */ #include "ntop.h" #ifdef HAVE_BACKTRACE extern size_t backtrace(void* thearray[], int thearraylen); extern char** backtrace_symbols(void* thearray[], int thearraylen); #endif static int *servicesMapper = NULL; /* temporary value */ /* *************************** */ static void printMutexInfo(PthreadMutex *mutexId, char *mutexName) { traceEvent(CONST_TRACE_INFO, "%s is %s (last lock %s:%d) [max lock time %s:%d (%.6f sec)]", mutexName, mutexId->isLocked ? "*locked*" : "unlocked", mutexId->lock.file, mutexId->lock.line, mutexId->max.file, mutexId->max.line, mutexId->maxLockedDuration); } #ifndef WIN32 void handleSigHup(int signalId _UNUSED_) { int i; printMutexInfo(&myGlobals.gdbmMutex, "myGlobals.gdbmMutex"); for(i=0; i FLAG_NTOPSTATE_RUN) break; if(rc == -1) { if(myGlobals.device[i].name != NULL) /* This is not a shutdown */ traceEvent(CONST_TRACE_ERROR, "Reading packets on device %d (%s): '%s'", i, myGlobals.device[i].humanFriendlyName, pcap_geterr(myGlobals.device[i].pcapPtr)); break; } else if(rc == 0) { if(myGlobals.runningPref.rFileName != NULL) { traceEvent(CONST_TRACE_INFO, "pcap_loop (%s) returned %d [No more packets to read]", myGlobals.device[i].humanFriendlyName, rc); break; /* No more packets to read */ } } } myGlobals.device[i].pcapDispatchThreadId = 0; traceEvent(CONST_TRACE_INFO, "THREADMGMT[t%lu]: NPS(%s): pcapDispatch thread terminated [p%d]", pthread_self(), myGlobals.device[i].humanFriendlyName, getpid()); return(NULL); } /* **************************************** */ #ifndef WIN32 RETSIGTYPE handleDiedChild(int sig _UNUSED_) { int status; pid_t pidId; while((pidId = waitpid(-1, &status, WNOHANG)) > 0) { #ifdef DEBUG if(status == 0) { myGlobals.numChildren--; traceEvent(CONST_TRACE_INFO, "A child has terminated [pid=%d status=%d children=%d]", pidId, status, myGlobals.numChildren); } #endif } signal(SIGCHLD, handleDiedChild); } #endif /* **************************************** */ void daemonizeUnderUnix(void) { #ifndef WIN32 int childpid; signal(SIGHUP, SIG_IGN); #ifdef HANDLE_DIED_CHILD signal(SIGCHLD, handleDiedChild); #else signal(SIGCHLD, SIG_IGN); #endif signal(SIGQUIT, SIG_IGN); if((childpid=fork()) < 0) traceEvent(CONST_TRACE_ERROR, "INIT: Occurred while daemonizing (errno=%d)", errno); else { #ifdef DEBUG traceEvent(CONST_TRACE_INFO, "DEBUG: after fork() in %s (%d)", childpid ? "parent" : "child", childpid); #endif if(!childpid) { /* child */ traceEvent(CONST_TRACE_INFO, "INIT: Bye bye: I'm becoming a daemon..."); detachFromTerminalUnderUnix(1); } else { /* father */ traceEvent(CONST_TRACE_INFO, "INIT: Parent process is exiting (this is normal)"); exit(0); } } myGlobals.mainThreadId = pthread_self(); traceEvent(CONST_TRACE_ALWAYSDISPLAY, "THREADMGMT[t%lu]: Now running as a daemon", myGlobals.mainThreadId); #endif } /* **************************************** */ void detachFromTerminalUnderUnix(int doChdir) { #ifndef WIN32 #ifdef MAKE_WITH_SYSLOG /* Child processes must log to syslog. * If no facility was set through -L | --use-syslog=facility * then force the default */ if(myGlobals.runningPref.useSyslog == FLAG_SYSLOG_NONE) myGlobals.runningPref.useSyslog = DEFAULT_SYSLOG_FACILITY; #endif /* MAKE_WITH_SYSLOG */ if(doChdir) (void)chdir("/"); setsid(); /* detach from the terminal */ fclose(stdin); fclose(stdout); /* fclose(stderr); */ /* * clear any inherited file mode creation mask */ umask (0); /* * Use line buffered stdout */ /* setlinebuf (stdout); */ #if SETVBUF_REVERSED setvbuf(stdout, _IOLBF, (char *)NULL, 0); #else setvbuf(stdout, (char *)NULL, _IOLBF, 0); #endif #endif /* WIN32 */ } /* **************************************** */ static short handleProtocol(char* protoName, char *protocol) { int i, idx, lowProtoPort, highProtoPort; short printWarnings = 0; u_char dummyEntry = 0; if(protocol[0] == '\0') return(-1); else if(isdigit(protocol[0]) || (protocol[0] == '-')) { /* numeric protocol port handling */ lowProtoPort = highProtoPort = 0; sscanf(protocol, "%d-%d", &lowProtoPort, &highProtoPort); if(highProtoPort < lowProtoPort) highProtoPort = lowProtoPort; if(lowProtoPort < 0) { lowProtoPort = 0; dummyEntry = 1; /* Dummy entry */ } if(highProtoPort >= MAX_IP_PORT) highProtoPort = MAX_IP_PORT-1; for(idx=lowProtoPort; idx<= highProtoPort; idx++) { if(servicesMapper[idx] == -1) { myGlobals.ipPortMapper.numElements++; #ifdef DEBUG printf("[%d] '%s' [port=%d]\n", myGlobals.numIpProtosToMonitor, protoName, idx); #endif if(dummyEntry) servicesMapper[idx] = -myGlobals.numIpProtosToMonitor; else servicesMapper[idx] = myGlobals.numIpProtosToMonitor; } else if(printWarnings) traceEvent(CONST_TRACE_WARNING, "INIT: IP port %d (%s) has been discarded (multiple instances)", idx, protoName); } return(idx); } for(i=1; iname, protocol) == 0)) idx = myGlobals.udpSvc[i]->port; else if((myGlobals.tcpSvc[i] != NULL) && (strcmp(myGlobals.tcpSvc[i]->name, protocol) == 0)) idx = myGlobals.tcpSvc[i]->port; if(idx != -1) { if(servicesMapper[idx] == -1) { myGlobals.ipPortMapper.numElements++; #ifdef DEBUG printf("[%d] '%s' [%s:%d]\n", myGlobals.numIpProtosToMonitor, protoName, protocol, idx); #endif servicesMapper[idx] = myGlobals.numIpProtosToMonitor; } else if(printWarnings) traceEvent(CONST_TRACE_WARNING, "INIT: Protocol '%s' has been discarded (multiple instances)", protocol); return(idx); } } if(printWarnings) traceEvent(CONST_TRACE_WARNING, "INIT: Unknown protocol '%s' - it has been ignored", protocol); return(-1); } /* **************************************** */ static int handleProtocolList(char* protoName, char *protocolList) { char tmpStr[255]; char* lastEntry, *protoEntry; int increment=0, rc=0; if(servicesMapper == NULL) { servicesMapper = (int*)malloc(sizeof(int)*MAX_IP_PORT); memset(servicesMapper, -1, sizeof(int)*MAX_IP_PORT); } #ifdef DEBUG traceEvent(CONST_TRACE_INFO, "%s - %s", protoName, protocolList); #endif /* The trick below is used to avoid to modify static memory like in the case where this function is called by addDefaultProtocols() */ lastEntry = strncpy(tmpStr, protocolList, sizeof(tmpStr)); while((protoEntry = strchr(lastEntry, '|')) != NULL) { protoEntry[0] = '\0'; rc = handleProtocol(protoName, lastEntry); if(rc != -1) increment = 1; lastEntry = &protoEntry[1]; } if(increment == 1) { if(myGlobals.numIpProtosToMonitor == 0) myGlobals.ipTrafficProtosNames = (char**)malloc(sizeof(char*)); else myGlobals.ipTrafficProtosNames = (char**)realloc(myGlobals.ipTrafficProtosNames, sizeof(char*)*(myGlobals.numIpProtosToMonitor+1)); rc = myGlobals.numIpProtosToMonitor; myGlobals.ipTrafficProtosNames[myGlobals.numIpProtosToMonitor] = strdup(protoName); myGlobals.numIpProtosToMonitor++; #ifdef DEBUG traceEvent(CONST_TRACE_INFO, "%d) %s - %s", myGlobals.numIpProtosToMonitor, protoName, protocolList); #endif } #ifdef DEBUG traceEvent(CONST_TRACE_INFO, "handleProtocolList(%s) = %d", protoName, rc); #endif return(rc); } /* **************************************** */ void addNewIpProtocolToHandle(char* name, u_int16_t id, u_int16_t idAlias) { ProtocolsList *proto = myGlobals.ipProtosList; int i; while(proto != NULL) { if(proto->protocolId == id) return; /* Already there */ proto = proto->next; } proto = calloc(1, sizeof(ProtocolsList)); proto->next = myGlobals.ipProtosList; proto->protocolName = strdup(name); proto->protocolId = id, proto->protocolIdAlias = idAlias; myGlobals.ipProtosList = proto; myGlobals.numIpProtosList++; for(i=0; i= MAX_IP_PORT)) return(-1); else { int j, found, slotId = (3*port) % myGlobals.ipPortMapper.numSlots; for(j=0, found=0; j FLAG_NTOPSTATE_RUN) break; if(myGlobals.runningPref.rFileName == NULL) myGlobals.actTime = time(NULL); for(i=0; i FLAG_NTOPSTATE_RUN) break; if(myGlobals.runningPref.rFileName == NULL) myGlobals.actTime = time(NULL); countCycle++; #ifdef FINGERPRINT_DEBUG traceEvent(CONST_TRACE_NOISY, "FINGERPRINT_DEBUG: starting cycle %d", countCycle); #endif for(deviceId=0; deviceIdfingerprint == NULL) continue; if(el->fingerprint[0] == ':') continue; if(addrnull(&el->hostIpAddress)) continue; if(el->hostNumIpAddress[0] == '\0') continue; countScan++; setHostFingerprint(el); if((el->fingerprint[0] == ':') && (el->fingerprint[0] != '\0')) countResolved++; } ntop_conditional_sched_yield(); /* Allow other threads to run */ } if(countScan > 0) traceEvent(CONST_TRACE_NOISY, "SFP: Ending fingerprint scan cycle %d - checked %d, resolved %d", countCycle, countScan, countResolved); } myGlobals.nextFingerprintScan = 0; myGlobals.scanFingerprintsThreadId = 0; traceEvent(CONST_TRACE_INFO, "THREADMGMT[t%lu]: SFP: Fingerprint scan thread terminated [p%d]", pthread_self(), getpid()); return(NULL); } /* **************************************** */ static void cleanupThreadIs(char *buf, int sizeofbuf) { int i; buf[0]='\0'; if(buf[0] != '\0') { if(pthread_self() == myGlobals.mainThreadId) strncpy(buf, "MAIN", sizeofbuf); else if(pthread_self() == myGlobals.scanFingerprintsThreadId) strncpy(buf, "SFP", sizeofbuf); else if(pthread_self() == myGlobals.scanIdleThreadId) strncpy(buf, "SIH", sizeofbuf); else if(pthread_self() == myGlobals.handleWebConnectionsThreadId) strncpy(buf, "WEB", sizeofbuf); #if defined(HAVE_OPENSSL) && defined(MAKE_WITH_SSLWATCHDOG) else if(pthread_self() == myGlobals.sslwatchdogChildThreadId) strncpy(buf, "SSL", sizeofbuf); #endif else for(i=0; inetFlowThread)) { safe_snprintf(__FILE__, __LINE__, buf, sizeofbuf, "NF(%s)", myGlobals.device[i].humanFriendlyName); break; } } } if(buf[0] == '\0') { for(i=0; isflowThread)) { safe_snprintf(__FILE__, __LINE__, buf, sizeofbuf, "SF(%s)", myGlobals.device[i].humanFriendlyName); break; } } } if(buf[0] == '\0') { strncpy(buf, "unknown", sizeofbuf); } } /* *************************** */ void runningThreads(char *buf, int sizeofbuf, int do_join) { char buf2[LEN_MEDIUM_WORK_BUFFER]; int i; if(!do_join) { memset(&buf2, 0, sizeof(buf2)); #if defined(HAVE_OPENSSL) && defined(MAKE_WITH_SSLWATCHDOG) safe_snprintf(__FILE__, __LINE__, buf, sizeofbuf, "%s%s%s%s", myGlobals.scanFingerprintsThreadId != 0 ? " SFP" : "", myGlobals.scanIdleThreadId != 0 ? " SIH" : "", myGlobals.handleWebConnectionsThreadId != 0 ? " WEB" : "", myGlobals.sslwatchdogChildThreadId != 0 ? " SSL" : ""); #else safe_snprintf(__FILE__, __LINE__, buf, sizeofbuf, "%s%s%s", myGlobals.scanFingerprintsThreadId != 0 ? " SFP" : "", myGlobals.scanIdleThreadId != 0 ? " SIH" : "", myGlobals.handleWebConnectionsThreadId != 0 ? " WEB" : ""); #endif } for(i=0; i= 0) { traceEvent(CONST_TRACE_INFO, "STATS: %s packets received by filter on %s", formatPkts((Counter)pcapStats.ps_recv, buf2, sizeof(buf2)), myGlobals.device[i].name); traceEvent(CONST_TRACE_INFO, "STATS: %s packets dropped (according to libpcap)", formatPkts((Counter)pcapStats.ps_drop, buf2, sizeof(buf2))); } traceEvent(CONST_TRACE_INFO, "STATS: %s packets dropped (by ntop)", formatPkts(myGlobals.device[i].droppedPkts.value, buf2, sizeof(buf2))); /* signalCondvar(&myGlobals.device[i].queueCondvar); */ pcap_close(myGlobals.device[i].pcapPtr); traceEvent(CONST_TRACE_INFO, "Joining thread NPS(%s)", myGlobals.device[i].humanFriendlyName); if(joinThread(&myGlobals.device[i].pcapDispatchThreadId) != 0) traceEvent(CONST_TRACE_INFO, "joinThread() returned: %s", strerror(errno)); } } } for(i=0; inetFlowThread != 0)) { if(!do_join) { safe_snprintf(__FILE__, __LINE__, buf2, sizeof(buf2), " NF%d", i); safe_strncat(buf, sizeofbuf, buf2); } else { traceEvent(CONST_TRACE_INFO, "Joining thread NF%d [%u]", i, (unsigned int)myGlobals.device[i].netflowGlobals->netFlowThread); close(myGlobals.device[i].netflowGlobals->netFlowInSocket); if(joinThread(&myGlobals.device[i].netflowGlobals->netFlowThread) != 0) traceEvent(CONST_TRACE_INFO, "joinThread() returned %s", strerror(errno)); } } } for(i=0; isflowThread != 0)) { if(!do_join) { safe_snprintf(__FILE__, __LINE__, buf2, sizeof(buf2), " SF%d", i); safe_strncat(buf, sizeofbuf, buf2); } else { traceEvent(CONST_TRACE_INFO, "Joining thread SF%d", i); if(joinThread(&myGlobals.device[i].sflowGlobals->sflowThread) != 0) traceEvent(CONST_TRACE_INFO, "joinThread() returned %s", strerror(errno)); } } } for(i=0; i= FLAG_NTOPSTATE_SHUTDOWN) { return; } if(myGlobals.ntopRunState != FLAG_NTOPSTATE_SHUTDOWNREQ) { /* TODO * If the web server didn't start this, how do we kill it? * It's sitting there in select() waiting for input! */ } setRunState(FLAG_NTOPSTATE_SHUTDOWN); if(cleanup_called) { exit(0); } cleanup_called = 1; cleanupThreadIs(buf, sizeof(buf)); traceEvent(CONST_TRACE_INFO, "CLEANUP[t%lu] catching thread is %s", pthread_self(), buf); runningThreads(buf, sizeof(buf), 0); traceEvent(CONST_TRACE_INFO, "CLEANUP: Running threads%s", buf); runningThreads(buf, sizeof(buf), 1); #ifndef WIN32 #ifdef MAKE_WITH_SSLWATCHDOG if(myGlobals.sslwatchdogChildThreadId != 0) { killThread(&myGlobals.sslwatchdogChildThreadId); } #ifdef MAKE_WITH_SSLWATCHDOG_RUNTIME if(myGlobals.runningPref.useSSLwatchdog == 1) #endif { deleteCondvar(&myGlobals.sslwatchdogCondvar); } #endif #endif /* #ifndef WIN32 */ killThread(&myGlobals.handleWebConnectionsThreadId); killThread(&myGlobals.scanIdleThreadId); killThread(&myGlobals.scanFingerprintsThreadId); /* Prevents the web interface from running */ traceEvent(CONST_TRACE_ALWAYSDISPLAY, "CLEANUP: Locking purge mutex (may block for a little while)"); accessMutex(&myGlobals.purgeMutex, "cleanup"); traceEvent(CONST_TRACE_ALWAYSDISPLAY, "CLEANUP: Locked purge mutex, continuing shutdown"); runningThreads(buf, sizeof(buf), 0); traceEvent(CONST_TRACE_INFO, "CLEANUP: Continues%s%s%s", buf[0] == '\0' ? "" : " (still running", buf, buf[0] == '\0' ? "" : ")"); for(i=0; inext; free(myGlobals.device[i].fragmentList); myGlobals.device[i].fragmentList = fragment; } } for(i=0; i */ for(j=0; j<(myGlobals.device[i].numHosts*myGlobals.device[i].numHosts); j++) if(myGlobals.device[i].ipTrafficMatrix[j] != NULL) free(myGlobals.device[i].ipTrafficMatrix[j]); free(myGlobals.device[i].ipTrafficMatrix); } /* FIX if(myGlobals.device[i].ipTrafficMatrixHosts != NULL) free(myGlobals.device[i].ipTrafficMatrixHosts); */ if(myGlobals.device[i].ipProtoStats != NULL) free(myGlobals.device[i].ipProtoStats); if(myGlobals.device[i].ipProtosList != NULL) free(myGlobals.device[i].ipProtosList); if(myGlobals.device[i].hash_hostTraffic != NULL) free(myGlobals.device[i].hash_hostTraffic); if(myGlobals.device[i].ipPorts != NULL) { int port; for(port=0; portnext; free(tmp); } } #endif while(myGlobals.device[i].asStats) { AsStats *next = myGlobals.device[i].asStats->next; free(myGlobals.device[i].asStats); myGlobals.device[i].asStats = next; } } if(myGlobals.device) free(myGlobals.device); if(myGlobals.broadcastEntry != NULL) free(myGlobals.broadcastEntry); if(myGlobals.otherHostEntry != NULL) { if(myGlobals.otherHostEntry->portsUsage != NULL) freePortsUsage(myGlobals.otherHostEntry); myGlobals.otherHostEntry->portsUsage = NULL; free(myGlobals.otherHostEntry); } if(myGlobals.startedAs != NULL) free(myGlobals.startedAs); tryLockMutex(&myGlobals.tcpSessionsMutex, "cleanup"); deleteMutex(&myGlobals.tcpSessionsMutex); tryLockMutex(&myGlobals.purgePortsMutex, "cleanup"); deleteMutex(&myGlobals.purgePortsMutex); tryLockMutex(&myGlobals.securityItemsMutex, "cleanup"); deleteMutex(&myGlobals.securityItemsMutex); /* DO NOT DO deleteMutex(&myGlobals.logViewMutex); - need it for the last traceEvent()s */ if(myGlobals.logView != NULL) { for(i=0; i