/* * This firewall monitor is based on scripts created by Linuxconf:inetdconf * module. * * It will run as a daemon and monitor all network devices. * * If a network device has been brought down the script will be started with * stop as argument. * * If the ip address has changed the script will run with start device * and ip address as arguments. * * Torbjörn Gard, tgard@netg.se */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netdevice.h" static int daemon_proc = 0; static const char *pname = NULL; static char pidfile[50]; static NETDEVICE *netdevice = NULL; #define SCRIPT_LENGTH 128 typedef struct OPTIONS { char *config; /* Config file */ char script[SCRIPT_LENGTH]; /* Firewall script */ int daemon; /* Run as daemon */ int verbose; /* 0,1 */ int frequency; /* Poll frequency (times/sec) */ } _OPTIONS; static struct OPTIONS options; #if 0 void ntoa( unsigned int address, char buffer[] ) { sprintf(buffer, "%d.%d.%d.%d", (unsigned int)(address&0xff000000)>>24, (unsigned int)(address&0x00ff0000)>>16, (unsigned int)(address&0x0000ff00)>>8, (unsigned int)(address&0x000000ff) ); } #endif static void err_doit(int errnoflag, int level, const char *fmt, va_list ap) { int errno_save, n; char buf[512]; errno_save = errno; /* value caller might want printed */ #ifdef HAVE_VSNPRINTF vsnprintf(buf, sizeof(buf), fmt, ap); /* this is safe */ #else vsprintf(buf, fmt, ap); /* this is not safe */ #endif n = strlen(buf); if (errnoflag) snprintf(buf+n, sizeof(buf)-n, ": %s", strerror(errno_save)); strcat(buf, "\n"); if (daemon_proc) { syslog(level, buf); } else { fprintf(stderr, "%s: ", pname); fflush(stdout); /* in case stdout & stderr are the same */ fputs(buf, stderr); fflush(stderr); } return; } void message(const char *fmt, ...) { va_list ap; if ( options.verbose == K_DETAILS_NONE ) return; va_start(ap, fmt); err_doit(0, LOG_INFO, fmt, ap); va_end(ap); return; } static void err_sys(const char *fmt, ...) { va_list ap; va_start(ap, fmt); err_doit(1, LOG_ERR, fmt, ap); va_end(ap); exit(1); } static set_nanosleep( int frequency, struct timespec *time_req ) { if ( frequency < 2 ) { time_req->tv_sec = 1; time_req->tv_nsec = 0; } else { time_req->tv_sec = 0; time_req->tv_nsec = 1000000000/frequency; } } static char * next_word( char *d, char *s, int size ) { while ( *s ) { switch ( *s ) { case ' ': case '\t': s++; continue; default: break; } break; } for ( size--; *s && size; size-- ) { switch ( *s ) { case ' ': case '\t': case '\n': *d = '\0'; return( s ); case '#': *d++ = *s++; *d = '\0'; return( s ); default: *d++ = *s++; break; } } *d = '\0'; return( s ); } static int parse_config_file( const char *config_file ) { FILE *fopen(), *fp; char line[512], word[64], *p; fp = fopen( config_file, "r" ); if ( fp == NULL ) { syslog( LOG_ERR, "parse_config_file: %s: %s", config_file, strerror(errno) ); return( 0 ); } while ( fgets( line, sizeof( line ), fp ) ) { if ( line[0] == '#' ) continue; p = next_word( word, line, sizeof( word ) ); while ( strlen( word ) ) { if ( strcmp( word, K_SCRIPT_FILE ) == 0 ) { p = next_word( options.script, p, sizeof(options.script) ); } else if ( strcmp( word, K_FREQUENCY ) == 0 ) { p = next_word( word, p, sizeof(word) ); options.frequency = atoi( word ); } else if ( strcmp( word, K_VERBOSE ) == 0 ) { p = next_word( word, p, sizeof(word) ); options.verbose = atoi( word ); } p = next_word( word, p, sizeof( word ) ); } } fclose( fp ); if ( strlen(options.script) == 0 ) { strncpy( options.script,FIREWALL_SCRIPT,sizeof(options.script)); } if ( options.frequency == 0 ) { options.frequency = 10; } //fprintf(stderr,"parse_config_file: %s script=%s frequency=%d verbose=%d\n", config_file, options.script, options.frequency, options.verbose); return ( 1 ); } static void firewall( NETDEVICE *netdevice, const char *script ) { NETDEVICE ifn; char command[256]; for (ifn=netdevice[0]; ifn; ifn=ifn->next) { if ( ifn->status_fw != STATUS_FW_DO ) { continue; } switch ( ifn->status_if ) { case STATUS_IF_UP: snprintf(command, sizeof(command), "%s %s %s %s", script, "start", ifn->name, ifn->ip_alfa ); break; case STATUS_IF_DOWN: snprintf(command, sizeof(command), "%s %s %s", script, "stop", ifn->name ); break; default: //fprintf(stderr,"firewall: status_if=%d\n", (int)ifn->status_if); return; } message( "Executing %s", command ); { /* fork & wait block */ pid_t child, pid; int status = 0; if ( (child = fork()) == 0 ) { execl( "/bin/sh", "sh", "-c", command, NULL ); perror( "exec" ); exit( -1 ); } while ( 1 ) { if ( (pid = waitpid( child, &status, 0 )) == -1 ) { syslog(LOG_ERR,"wait: %s: %s", command, strerror( errno )); continue; } if ( pid != child ) continue; if ( WIFEXITED( status ) ) { if ( WEXITSTATUS( status ) ) { message("Returned non zero exit: %d", WEXITSTATUS( status ) ); } } break; } } /* end fork & wait block */ switch ( ifn->status_if ) { case STATUS_IF_DOWN: ifn->status_fw = STATUS_FW_INIT; ifn->status_if = STATUS_IF_INIT; break; case STATUS_IF_UP: ifn->status_fw = STATUS_FW_DONE; break; } } return; } static void set_firewall( NETDEVICE *netdevice, const char *script ) { NETDEVICE ifn; if ( options.verbose == K_DETAILS_MANY ) message( "Setting firewall" ); for (ifn=netdevice[0]; ifn; ifn=ifn->next) { ifn->status_fw = STATUS_FW_DO; ifn->status_if = STATUS_IF_UP; } firewall( netdevice, script ); } static void reset_firewall( NETDEVICE *netdevice, char *script ) { NETDEVICE ifn; if ( options.verbose == K_DETAILS_MANY ) message( "Resetting firewall" ); for (ifn=netdevice[0]; ifn; ifn=ifn->next) { ifn->status_fw = STATUS_FW_DO; ifn->status_if = STATUS_IF_DOWN; } firewall( netdevice, script ); } static void config_file_modified( NETDEVICE *netdevice, const char *config_file, struct timespec *time_req ) { static time_t mtime = 0; struct stat config_stat; char script_file[SCRIPT_LENGTH]; if ( stat( config_file, &config_stat ) == -1 ) { syslog(LOG_ERR,"stat: %s: %s", config_file, strerror( errno )); exit( 1 ); } if ( mtime == 0 ) { mtime = config_stat.st_mtime; return; } if ( mtime == config_stat.st_mtime ) { return; } mtime = config_stat.st_mtime; if ( options.verbose == K_DETAILS_MANY ) message( "%s modified", config_file ); strncpy( script_file, options.script, sizeof( script_file ) ); if ( parse_config_file( config_file ) == 0 ) { return; } if ( strcmp( script_file, options.script ) != 0 ) { if ( options.verbose == K_DETAILS_MANY ) message( "%s changed to %s in %s", script_file, options.script, config_file ); set_firewall( netdevice, options.script ); } set_nanosleep( options.frequency, time_req ); } static void script_file_modified( NETDEVICE *netdevice, const char *script ) { static time_t mtime = 0; struct stat script_stat; if ( stat( script, &script_stat ) == -1 ) { syslog(LOG_ERR,"stat: %s: %s", script, strerror( errno )); exit( 1 ); } if ( mtime == 0 ) { mtime = script_stat.st_mtime; return; } if ( mtime == script_stat.st_mtime ) { return; } mtime = script_stat.st_mtime; if ( options.verbose == K_DETAILS_MANY ) message( "%s modified", script ); set_firewall( netdevice, script ); } /* * Function prototypes. */ typedef void Sigfunc(int); Sigfunc *signal(int, Sigfunc*); Sigfunc *Signal(int, Sigfunc*); static pid_t Fork(void); Sigfunc * signal(int signo, Sigfunc *func) { struct sigaction act, oact; act.sa_handler = func; sigemptyset(&act.sa_mask); act.sa_flags = 0; if (sigaction(signo, &act, &oact) < 0) return(SIG_ERR); return(oact.sa_handler); } Sigfunc * Signal(int signo, Sigfunc *func) { Sigfunc *sigfunc; if ( (sigfunc = signal(signo, func)) == SIG_ERR) err_sys("signal error"); return(sigfunc); } static pid_t Fork(void) { pid_t pid; if ((pid = fork()) == -1) err_sys("fork error"); return (pid); } static void check_command( const char *command) { struct stat statbuf; if (stat(command, &statbuf) < 0) { (void)printf("%s: no such file or directory.\n", command); exit (0); } if (S_ISDIR(statbuf.st_mode)) { (void)printf ("%s: is a directory.\n", command); exit (0); } } static void usage() { (void)printf ( "usage: %s [-c ] [-p