| /* |
| * |
| * Copyright (C) 2007 Mindspeed Technologies, Inc. |
| * |
| * 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 "cmm.h" |
| #include "forward_engine.h" |
| #include "keytrack.h" |
| #include "itf.h" |
| |
| #include <net/if.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <sched.h> |
| #include <signal.h> |
| #include <stdarg.h> |
| #if !defined(__UCLIBC__) |
| #include <execinfo.h> |
| #endif |
| |
| struct cmm_global globalConf; |
| unsigned int nf_conntrack_max = CONNTRACK_MAX; |
| |
| struct kernel_ucontext { |
| unsigned long uc_flags; |
| struct kernel_ucontext *uc_link; |
| stack_t uc_stack; |
| struct sigcontext uc_mcontext; |
| sigset_t uc_sigmask; |
| /* Allow for uc_sigmask growth. Glibc uses a 1024-bit sigset_t. */ |
| int __unused[32 - (sizeof (sigset_t) / sizeof (int))]; |
| /* Last for extensibility. Eight byte aligned because some |
| coprocessors require eight byte alignment. */ |
| unsigned long uc_regspace[128] __attribute__((__aligned__(8))); |
| }; |
| |
| static void cmm_crit_err_hdlr(int sig_num, siginfo_t *info, void *ucontext) |
| { |
| struct sigcontext *sigcontext; |
| #if !defined(__UCLIBC__) |
| void *array[50]; |
| char **messages; |
| int size, i; |
| #endif |
| |
| sigcontext = &((struct kernel_ucontext *)ucontext)->uc_mcontext; |
| |
| fprintf(stderr, "\n%s: signal %d (%s), fault address is %p at %p PID %d\n", |
| __func__, sig_num, strsignal(sig_num), info->si_addr, |
| (void *)sigcontext->arm_pc, getpid()); |
| |
| fprintf(stderr, "register dump:\nr0:%08lx r1:%08lx r2:%08lx r3:%08lx r4:%08lx r5:%08lx r6:%08lx r7:%08lx\n", |
| sigcontext->arm_r0, sigcontext->arm_r1, sigcontext->arm_r2, sigcontext->arm_r3, |
| sigcontext->arm_r4, sigcontext->arm_r5, sigcontext->arm_r6, sigcontext->arm_r7); |
| |
| fprintf(stderr, "r8:%08lx r9:%08lx r10:%08lx fp:%08lx ip:%08lx sp:%08lx lr:%08lx pc:%08lx\n", |
| sigcontext->arm_r8, sigcontext->arm_r9, sigcontext->arm_r10, sigcontext->arm_fp, |
| sigcontext->arm_ip, sigcontext->arm_sp, sigcontext->arm_lr, sigcontext->arm_pc); |
| |
| fprintf(stderr, "cpsr:%08lx fault_address:%08lx\n", sigcontext->arm_cpsr, sigcontext->fault_address); |
| |
| #if !defined(__UCLIBC__) |
| size = backtrace(array, 50); |
| |
| /* overwrite sigaction with caller's address */ |
| array[1] = (void *)sigcontext->arm_pc; |
| |
| messages = backtrace_symbols(array, size); |
| |
| if (messages) |
| { |
| for (i = 1; i < size; ++i) |
| { |
| fprintf(stderr, "[bt]: (%d) %s\n", i, messages[i]); |
| } |
| |
| free(messages); |
| } |
| else |
| { |
| for (i = 1; i < size; ++i) |
| { |
| fprintf(stderr, "[bt]: (%d) %p\n", i, array[i]); |
| } |
| } |
| #endif |
| exit(EXIT_FAILURE); |
| } |
| |
| |
| void cmm_print_func(int level, const char *format, ...) |
| { |
| va_list args; |
| |
| va_start(args, format); |
| |
| if (level & DEBUG_CRIT) |
| { |
| if (globalConf.cli.handle) |
| cli_vabufprint(globalConf.cli.handle, (char *)format, args); |
| else |
| vfprintf(stderr, format, args); |
| } |
| else if (level & DEBUG_STDOUT) |
| { |
| if (globalConf.cli.handle) |
| cli_vabufprint(globalConf.cli.handle, (char *)format, args); |
| else |
| vfprintf(stdout, format, args); |
| } |
| else if (globalConf.cli.handle && (level & globalConf.debug_level)) |
| cli_vabufprint(globalConf.cli.handle, (char *)format, args); |
| |
| if (globalConf.logFile && (level & globalConf.log_level)) |
| { |
| pthread_mutex_lock(&globalConf.logMutex); |
| if (!(level & DEBUG_NOTIMESTAMP)) |
| { |
| time_t now; |
| struct tm now_tm; |
| char date[24]; |
| |
| time(&now); |
| localtime_r(&now, &now_tm); |
| strftime(date, 24, "%F %T", &now_tm); |
| fprintf(globalConf.logFile, "%s: ", date); |
| } |
| |
| vfprintf(globalConf.logFile, format, args); |
| pthread_mutex_unlock(&globalConf.logMutex); |
| } |
| |
| va_end(args); |
| } |
| |
| /***************************************************************** |
| * mac_ntop - converts a mac address in numerical format to presentation format |
| * |
| * |
| *****************************************************************/ |
| const char *mac_ntop(const void *mac, char *buf, size_t len) |
| { |
| snprintf(buf, len, "%02x:%02x:%02x:%02x:%02x:%02x", ((unsigned char *)mac)[0], |
| ((unsigned char *)mac)[1], |
| ((unsigned char *)mac)[2], |
| ((unsigned char *)mac)[3], |
| ((unsigned char *)mac)[4], |
| ((unsigned char *)mac)[5]); |
| |
| return buf; |
| } |
| |
| /*Print CMM help*/ |
| void cmmHelp() |
| { |
| cmm_print(DEBUG_STDOUT, cmm_help); |
| } |
| |
| /***************************************************************** |
| * cmmVersion() |
| * |
| * Prints cmm version |
| * |
| *****************************************************************/ |
| void cmmVersion() |
| { |
| cmm_print(DEBUG_STDOUT, "Cmm version %s\n", CMM_VERSION); |
| cmm_print(DEBUG_STDOUT, "Developped by %s\n", developpers); |
| } |
| |
| /***************************************************************** |
| * cmmIsDaemonRunning() |
| * |
| * Check if cmm daemon is running. If so, it returns the pid |
| * |
| *****************************************************************/ |
| int cmmIsDaemonRunning() |
| { |
| FILE*fd; |
| char buf[10]; |
| |
| fd = fopen(CMM_PID_FILE_PATH, "r"); |
| if(fd > 0) |
| { |
| // Read the pid written in the pid file |
| fgets(buf, 10, fd); |
| fclose(fd); |
| |
| // Check the daemon is really running |
| if (getpgid(atoi(buf)) != -1) |
| return atoi(buf); |
| } |
| |
| // No daemon is running |
| return 0; |
| } |
| |
| /***************************************************************** |
| * cmmCreateDaemonPidFile() |
| * |
| * Create a pid file. |
| * This function supposes that no cmm daemon is running and |
| * then removes the old pid file if there is. |
| * |
| *****************************************************************/ |
| int cmmCreateDaemonPidFile() |
| { |
| int fp; |
| char buf[10+1+1]; /* int can have up to 10 chars + 1 for sign + 1 for trailing \0 */ |
| |
| fp = open(CMM_PID_FILE_PATH, O_WRONLY | O_CREAT | O_EXCL); |
| if(fp < 0) |
| { |
| if(errno == EEXIST) |
| { |
| // A file already exists, delete it and create a new one |
| if (remove(CMM_PID_FILE_PATH)) |
| { |
| cmm_print(DEBUG_CRIT, "Unable to delete old %s\n", CMM_PID_FILE_PATH); |
| return -1; |
| } |
| // Now the old file is deleted, we can create a new one |
| fp = open(CMM_PID_FILE_PATH, O_WRONLY | O_CREAT | O_EXCL); |
| } |
| else |
| { |
| cmm_print(DEBUG_CRIT, "Error opening %s\n", CMM_PID_FILE_PATH); |
| return -1; |
| } |
| } |
| |
| snprintf(buf, sizeof(buf), "%d\n", getpid()); |
| write(fp, buf, strlen(buf)); |
| close(fp); |
| |
| globalConf.cmmPid = getpid(); |
| |
| return 0; |
| } |
| |
| |
| /***************************************************************** |
| * sig_term_hdlr |
| * |
| * |
| ******************************************************************/ |
| static void sig_term_hdlr(int signum) |
| { |
| cmm_print(DEBUG_INFO, "%s: entered\n", __func__); |
| |
| cmm_third_part_exit(globalConf.third_part_data); |
| |
| cmmCliExit(&globalConf.cli); |
| |
| cmmDaemonExit(&globalConf.daemon); |
| |
| cmmCtExit(&globalConf.ct); |
| |
| nfct_close(globalConf.nf_conntrack_handle); |
| |
| remove(CMM_PID_FILE_PATH); |
| |
| cmm_print(DEBUG_INFO, "%s: exiting\n", __func__); |
| |
| if (globalConf.logFile) |
| fclose(globalConf.logFile); |
| |
| /* Killing ...*/ |
| exit(EXIT_SUCCESS); |
| } |
| |
| |
| int main (int argc, char ** argv) |
| { |
| sigset_t block_mask; |
| extern char *optarg; |
| extern int optind; |
| char confFilePath[512+1]; |
| //struct sched_param schedParams; |
| struct sigaction action; |
| int option; |
| int daemonize = 1; |
| |
| // Forward engine programmation is enabled by default |
| globalConf.enable = 1; |
| globalConf.debug_level = DEBUG_ERROR; |
| globalConf.vlan_policy = ALLOW; |
| globalConf.ff_enable = 1; |
| #ifdef C2000_DPI |
| globalConf.dpi_enable = 0; |
| #endif |
| globalConf.asymff_enable = 0; |
| globalConf.logFile = NULL; |
| globalConf.log_level = 0; |
| |
| globalConf.tun_proto = IPPROTO_IPIP; /* Current default handling of TUN interface is an 4o6 tunnel*/ |
| globalConf.tun_family = AF_INET6; |
| |
| #ifdef MUTEX_DEBUG |
| mutexes = 0; |
| #endif |
| |
| action.sa_sigaction = cmm_crit_err_hdlr; |
| sigemptyset (&action.sa_mask); |
| action.sa_flags = SA_RESTART | SA_SIGINFO; |
| |
| if (sigaction(SIGSEGV, &action, NULL) < 0) |
| { |
| fprintf(stderr, "sigaction((%s)) failed, %s\n", strsignal(SIGSEGV), strerror(errno)); |
| |
| exit(EXIT_FAILURE); |
| } |
| |
| if (sigaction(SIGBUS, &action, NULL) < 0) |
| { |
| fprintf(stderr, "sigaction((%s)) failed, %s\n", strsignal(SIGBUS), strerror(errno)); |
| |
| exit(EXIT_FAILURE); |
| } |
| |
| if (sigaction(SIGFPE, &action, NULL) < 0) |
| { |
| fprintf(stderr, "sigaction((%s)) failed, %s\n", strsignal(SIGFPE), strerror(errno)); |
| |
| exit(EXIT_FAILURE); |
| } |
| |
| if (sigaction(SIGILL, &action, NULL) < 0) |
| { |
| fprintf(stderr, "sigaction((%s)) failed, %s\n", strsignal(SIGILL), strerror(errno)); |
| |
| exit(EXIT_FAILURE); |
| } |
| |
| if (sigaction(SIGABRT, &action, NULL) < 0) |
| { |
| fprintf(stderr, "sigaction((%s)) failed, %s\n", strsignal(SIGABRT), strerror(errno)); |
| |
| exit(EXIT_FAILURE); |
| } |
| |
| // Analyse the command line |
| while ((option = getopt(argc, argv, "c:f:n:Fhv")) != -1) |
| { |
| switch (option) |
| { |
| case 'c': // Launch cmm as a client communicating with the cmm daemon |
| cmmClient(optarg, argc-optind, &argv[optind]); |
| return 0; |
| |
| case 'f': // Specify configuration file |
| //Get the argument |
| strncpy(confFilePath, optarg, 512); |
| confFilePath[512] = '\0'; |
| break; |
| |
| case 'n': // Get the max conntrack connections |
| sscanf(optarg, "%u", &nf_conntrack_max); |
| break; |
| |
| case 'F': // Don't daemonize |
| daemonize = 0; |
| break; |
| case 'h': // Print help |
| cmmHelp(); |
| return 0; |
| case 'v': // Print version and exit |
| cmmVersion(); |
| return 0; |
| default: |
| break; |
| } |
| } |
| /* cmm_print(DEBUG_STDOUT, "nf_conntrack_max %u\n", nf_conntrack_max);*/ |
| |
| // If cmm daemon is already running, return |
| if (cmmIsDaemonRunning()) |
| { |
| cmm_print(DEBUG_CRIT, "cmm daemon is already running\n"); |
| goto err0; |
| } |
| |
| // Daemonize the application |
| if (daemonize) daemon(0, 1); |
| |
| //Ensure clean termination |
| action.sa_handler = sig_term_hdlr; |
| sigemptyset(&action.sa_mask); |
| sigaddset(&action.sa_mask, SIGTERM); |
| action.sa_flags = 0; |
| |
| if (sigaction(SIGTERM, &action, NULL) < 0) |
| { |
| cmm_print(DEBUG_ERROR, "%s: sigaction() failed %s\n", __func__, strerror(errno)); |
| goto err0; |
| } |
| |
| // Need to daemonize before creating a Pid File |
| if (cmmCreateDaemonPidFile()) |
| goto err0; |
| |
| // Parse the configuration file |
| if (cmmFcParser(strlen(confFilePath) ? confFilePath : CONF_FILE_PATH)) |
| goto err1; |
| |
| // Change priority of the process (we need to have a highest priority) |
| //memset(&schedParams, 0 , sizeof(schedParams)); |
| //schedParams.sched_priority = 99; |
| //sched_setscheduler(0, SCHED_FIFO, &schedParams); |
| |
| sigemptyset(&block_mask); |
| sigaddset(&block_mask, SIGTERM); |
| |
| sigprocmask(SIG_BLOCK, &block_mask, NULL); |
| |
| // Open a Netfilter socket |
| /* Use of the following handle must be protected by ctMutex */ |
| globalConf.nf_conntrack_handle = nfct_open(CONNTRACK, 0); |
| if (!globalConf.nf_conntrack_handle) |
| { |
| cmm_print(DEBUG_CRIT, "%s: nfct_open()failed, %s\n", __func__, strerror(errno)); |
| goto err1; |
| } |
| |
| if (cmmCtInit(&globalConf.ct) < 0) |
| goto err1a; |
| |
| if (cmmDaemonInit(&globalConf.daemon) < 0) |
| goto err2; |
| |
| if (cmmCliInit(&globalConf.cli) < 0) |
| goto err3; |
| |
| /* If callback support is enabled, then CMM calls 3rd Party initialization function */ |
| globalConf.third_part_data = cmm_third_part_init(); |
| |
| sigprocmask(SIG_UNBLOCK, &block_mask, NULL); |
| |
| /* Loop until sigterm is received */ |
| while (1) |
| pause(); |
| |
| cmm_print(DEBUG_INFO, "%s: exiting\n", __func__); |
| |
| return 0; |
| |
| err3: |
| cmmDaemonExit(&globalConf.daemon); |
| |
| err2: |
| cmmCtExit(&globalConf.ct); |
| |
| err1a: |
| nfct_close(globalConf.nf_conntrack_handle); |
| |
| err1: |
| remove(CMM_PID_FILE_PATH); |
| |
| err0: |
| cmm_print(DEBUG_INFO, "%s: exiting\n", __func__); |
| |
| if (globalConf.logFile) |
| fclose(globalConf.logFile); |
| |
| exit(EXIT_FAILURE); |
| } |
| |
| |
| |