efw-iptables.c

Go to the documentation of this file.
00001 /* efw_iptables.c  --  iptables implementation - updates Linux iptables
00002  *
00003  *  GPLv2 only - Copyright (C) 2008 - 2010
00004  *               David Sommerseth <dazo@users.sourceforge.net>
00005  *
00006  *  This program is free software; you can redistribute it and/or
00007  *  modify it under the terms of the GNU General Public License
00008  *  as published by the Free Software Foundation; version 2
00009  *  of the License.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00019  *
00020  */
00021 
00032 #include <stdio.h>
00033 #include <stdlib.h>
00034 #include <string.h>
00035 #include <errno.h>
00036 #include <unistd.h>
00037 #include <pthread.h>
00038 #include <sys/wait.h>
00039 
00040 #define EUREPHIA_FWINTF         
00041 #include <eurephiafw_struct.h>
00042 #include <eurephia_context.h>
00043 #include <eurephia_nullsafe.h>
00044 #include <eurephia_log.h>
00045 #include <eurephiafw_helpers.h>
00046 
00047 #define INTERFACEVER "1.0"      
00048 #define INTERFACEAPIVER 1       
00055 const char *eFWinterfaceVersion() {
00056         return "eFW-iptables (v"INTERFACEVER")  David Sommerseth 2008 (C) GPLv2";
00057 }
00058 
00059 
00065 int eFWinterfaceAPIversion() {
00066         return INTERFACEAPIVER;
00067 }
00068 
00069 
00070 int process_input(eurephiaCTX *ctx, const char *fwcmd, const char *msg);
00071 int call_iptables(eurephiaCTX *ctx, const char *fwcmd, char **ipt_args);
00072 
00073 
00079 void eFW_RunFirewall(void *fwargs) {
00080         efw_threaddata *cfg = (efw_threaddata *) fwargs;
00081         eurephiaCTX *ctx = (eurephiaCTX *) cfg->ctx;
00082         int quit = 0;
00083         unsigned int prio;
00084         char buf[EFW_MSG_SIZE+2];
00085         struct timespec tsp;
00086 
00087         DEBUG(ctx, 28, "eFW_RunFirewall:  Waiting for eFW master to get ready");
00088         sem_wait(cfg->semp_master);
00089         DEBUG(ctx, 28, "eFW_RunFirewall:  Telling eFW master that the worker process is ready");
00090         sem_post(cfg->semp_worker);
00091 
00092         if( cfg->fw_command == NULL ) {
00093                 eurephia_log(ctx, LOG_FATAL, 0,
00094                              "eFW_RunFirewall: firewall_command is not configured.  "
00095                              "iptables will not be updated.");
00096                 exit(3);
00097         }
00098 
00099         eurephia_log(ctx, LOG_INFO, 1, "efw_iptables: Firewall interface started");
00100 
00101         // Main loop ... grab messages of the messague queue until shutdown command is sent, or a failure happens
00102         while( quit == 0 ) {
00103                 memset(buf, 0, EFW_MSG_SIZE+2);
00104                 if( mq_receive(cfg->msgq, &buf[0], EFW_MSG_SIZE, &prio) == -1 ) {
00105                         eurephia_log(ctx, LOG_FATAL, 0,
00106                                      "eFW_RunFirewall: Error while reading messages from queue: %s",
00107                                      strerror(errno));
00108                         exit(2);
00109                 }
00110                 quit = (strncmp(buf, "FWSHUTDOWN", 10) == 0 );
00111                 if( !quit ) {
00112                         int res = 0;
00113 
00114                         DEBUG(ctx, 20, "eFW_RunFirewall:  Received '%s'", buf);
00115 
00116                         res = process_input(ctx, cfg->fw_command, buf);
00117                         if( ! res ) {
00118                                 quit = 1;
00119                                 eurephia_log(ctx, LOG_FATAL, 0,
00120                                              "eFW_RunFirewall: Failed updating iptables");
00121                         }
00122                 }
00123         }
00124 
00125 
00126         efwRemoveMessageQueue(ctx, fwargs);
00127 
00128         DEBUG(ctx, 28, "eFW_RunFirewall:  Telling eFW master that the worker process is about to shut down");
00129         sem_post(cfg->semp_worker);
00130 
00131         DEBUG(ctx, 28, "eFW_RunFirewall:  Waiting for eFW master to acknowledge");
00132         // Prepare a timeout
00133         if( clock_gettime(CLOCK_REALTIME, &tsp) == -1 ) {
00134                 eurephia_log(ctx, LOG_FATAL, 0, "eFW_RunFirewall: Could not prepare timeout for shutdown ack: %s",
00135                              strerror(errno));
00136                 sleep(10);
00137         } else {
00138                 tsp.tv_sec += 30;  // Wait up to 30 seconds for shutdown ack.
00139 
00140                 // Wait for acknowledge
00141                 if( sem_timedwait(cfg->semp_master, &tsp) == -1 ) {
00142                         eurephia_log(ctx, LOG_PANIC, 0, "eFW_RunFirewall: Did not receive any shutdown ack: %s",
00143                                      strerror(errno));
00144                 } else {
00145                         eurephia_log(ctx, LOG_INFO, 1, "efw_iptables: Firewall interface is shut down");
00146                 }
00147         }
00148         efwRemoveSemaphores(ctx, fwargs);
00149         exit(0);
00150 }
00151 
00152 
00163 int process_input(eurephiaCTX *ctx, const char *fwcmd, const char *input) {
00164         char mode[3], *addr = NULL, *destchain = NULL, *jump = NULL;
00165         char *msg = NULL, *orig_msg = NULL;
00166         char *iptables_args[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
00167         int ret = 0;
00168 
00169         orig_msg = strdup_nullsafe(input);
00170         msg = orig_msg;
00171         DEBUG(ctx, 36, "eFW_RunFirewall::process_input(ctx, '%s')", msg);
00172 
00173         //
00174         // Simple parsing of the input string
00175         //
00176         mode[0] = '-';
00177         mode[1] = *msg;
00178         mode[2] = 0;
00179         msg += 2;
00180 
00181         iptables_args[0] = (char *)fwcmd;
00182 
00183         switch( mode[1] ) {
00184         case 'A':
00185         case 'D':
00186                 iptables_args[1] = mode;
00187                 addr = msg;   // start of string for macaddr
00188 
00189                 // Search for end of macaddr and NULL terminate it
00190                 destchain = addr+1;
00191                 while( (*destchain != 0x20) || (*destchain == 0) ) {
00192                         destchain++;
00193                 }
00194                 if( *destchain == 0 ) {
00195                         return 0;
00196                 }
00197                 *destchain = 0; // end of string for macaddr
00198                 destchain++;  // start of string for destchain
00199                 // Search for end of destchain and NULL terminate it
00200                 jump = destchain+1;
00201                 while( (*jump != 0x20) || (*jump == 0) ) {
00202                         jump++;
00203                 }
00204                 *jump = 0; // end of string for destchain
00205                 jump++;  // start of string for jump
00206 
00207                 // Prepare iptables arguments
00208                 iptables_args[2] = destchain;
00209                 iptables_args[3] = "-m\0";
00210                 iptables_args[4] = "mac\0";
00211                 iptables_args[5] = "--mac-source\0";
00212                 iptables_args[6] = addr;
00213                 iptables_args[7] = "-m\0";
00214                 iptables_args[8] = "state\0";
00215                 iptables_args[9] = "--state\0";
00216                 iptables_args[10] = "NEW\0";
00217                 iptables_args[11] = "-j\0";
00218                 iptables_args[12] = jump;
00219                 iptables_args[13] = NULL;
00220 
00221                 eurephia_log(ctx, LOG_INFO, 3, "eFW_RunFirewall - updating iptables rules "
00222                              "==> mode: %s  macaddr: '%s'  destchain: '%s'  jump: '%s'",
00223                              (mode[1] == 'A' ? "ADD":"DELETE"), addr, destchain, jump);
00224                 ret = call_iptables(ctx, fwcmd, iptables_args);
00225                 break;
00226 
00227         case 'B':
00228                 addr = msg; // start of string for IP address to block
00229 
00230                 // Search for end of IP address and NULL terminate it
00231                 destchain = addr+1;
00232                 while( (*destchain != 0x20) || (*destchain == 0) ) {
00233                         destchain++;
00234                 }
00235                 if( *destchain == 0 ) {
00236                         return 0;
00237                 }
00238                 *destchain = 0; // end of string for IP address
00239                 destchain++;    // start of string for destchain
00240 
00241                 // Search for end of destchain and NULL terminate it
00242                 jump = destchain+1;
00243                 while( (*jump != 0x20) || (*jump == 0) ) {
00244                         jump++;
00245                 }
00246                 *jump = 0; // end of string for destchain
00247                 jump++;  // start of string for jump
00248                 if( *jump == 0 ) {
00249                         return 0;
00250                 }
00251 
00252                 iptables_args[1] = "-A\0";
00253                 iptables_args[2] = destchain;
00254                 iptables_args[3] = "-s\0";
00255                 iptables_args[4] = addr;
00256                 iptables_args[5] = "-j\0";
00257                 iptables_args[6] = jump;
00258                 iptables_args[7] = NULL;
00259 
00260                 eurephia_log(ctx, LOG_INFO, 3, "eFW_RunFirewall - updating iptables rules "
00261                              "==> mode: BLACKLIST  destchain: '%s'  IP address: %s  Send to: '%s'",
00262                              destchain, addr, jump);
00263                 ret = call_iptables(ctx, fwcmd, iptables_args);
00264                 break;
00265 
00266         case 'F':
00267                 iptables_args[1] = mode;
00268                 destchain = msg;
00269                 iptables_args[2] = destchain;
00270                 iptables_args[3] = NULL;
00271                 eurephia_log(ctx, LOG_INFO, 3, "eFW_RunFirewall - updating iptables rules "
00272                              "==> mode: FLUSH  destchain: '%s'", destchain);
00273                 ret = call_iptables(ctx, fwcmd, iptables_args);
00274                 break;
00275 
00276         case 'I':
00277                 // Init chain - flush it and then add needed rule for stateful inspection
00278                 destchain = msg;
00279 
00280                 eurephia_log(ctx, LOG_INFO, 3, "eFW_RunFirewall - Initialising iptables chain '%s'",
00281                              destchain);
00282 
00283                 // Flush phase
00284                 iptables_args[1] = "-F";
00285                 destchain = msg;
00286                 iptables_args[2] = destchain;
00287                 iptables_args[3] = NULL;
00288                 ret = call_iptables(ctx, fwcmd, iptables_args);
00289 
00290                 // Add stateful inspection
00291                 iptables_args[1] = "-I\0";
00292                 iptables_args[2] = destchain;
00293                 iptables_args[3] = "-m\0";
00294                 iptables_args[4] = "state\0";
00295                 iptables_args[5] = "--state\0";
00296                 iptables_args[6] = "ESTABLISHED,RELATED\0";
00297                 iptables_args[7] = "-j\0";
00298                 iptables_args[8] = "ACCEPT\0";
00299                 ret &= call_iptables(ctx, fwcmd, iptables_args);
00300                 break;
00301 
00302         default:
00303                 eurephia_log(ctx, LOG_CRITICAL, 0, "eFW_RunFirewall::process_input:  Malformed update request");
00304                 ret = 1;
00305         }
00306         free_nullsafe(ctx, orig_msg);
00307         return ret;
00308 }
00309 
00310 
00322 int call_iptables(eurephiaCTX *ctx, const char *fwcmd, char **ipt_args) {
00323         pid_t pid;
00324         int cmdret = -1;
00325 
00326         // Fork out a child process which will run the iptables command.  Since the execve replaces 
00327         // the current process, we need to do the forking first.
00328         if( (pid = fork()) < 0) {
00329                 eurephia_log(ctx, LOG_FATAL, 0,
00330                              "eFW_RunFirewall::process_input: Failed to fork process for %s", fwcmd);
00331                 return 0;
00332         }
00333 
00334         switch( pid ) {
00335         case 0: // child process - execute the program and exit
00336                 execve(fwcmd, ipt_args, NULL);
00337                 exit(1); // execve should replace the process, but if it fails to do so, make sure we exit
00338 
00339         default: // parent process
00340                 if( waitpid(pid, &cmdret, 0) != pid ) {
00341                         eurephia_log(ctx, LOG_WARNING, 0,
00342                                      "eFW_RunFirewall::process_input: Failed to wait for process for %s"
00343                                      " to complete (%s)", fwcmd, strerror(errno));
00344                 }
00345                 eurephia_log(ctx, LOG_INFO, 4, "eFW_RunFirewall - iptables exited with code: %i ", cmdret);
00346         }
00347         return 1;
00348 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines