blob: 561eecba173911d871b32dda119460f27faf2b7b [file] [log] [blame]
/*
*
* 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);
}