Main Page | Data Structures | Directories | File List | Data Fields

src/fw_iptables.c

00001 /********************************************************************\
00002  * This program is free software; you can redistribute it and/or    *
00003  * modify it under the terms of the GNU General Public License as   *
00004  * published by the Free Software Foundation; either version 2 of   *
00005  * the License, or (at your option) any later version.              *
00006  *                                                                  *
00007  * This program is distributed in the hope that it will be useful,  *
00008  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00009  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00010  * GNU General Public License for more details.                     *
00011  *                                                                  *
00012  * You should have received a copy of the GNU General Public License*
00013  * along with this program; if not, contact:                        *
00014  *                                                                  *
00015  * Free Software Foundation           Voice:  +1-617-542-5942       *
00016  * 59 Temple Place - Suite 330        Fax:    +1-617-542-2652       *
00017  * Boston, MA  02111-1307,  USA       gnu@gnu.org                   *
00018  *                                                                  *
00019  \********************************************************************/
00020 
00021 /* $Header: /cvsroot/wifidog/wifidog/src/fw_iptables.c,v 1.32 2005/04/01 23:25:32 aprilp Exp $ */
00028 #define _GNU_SOURCE
00029 
00030 #include <stdio.h>
00031 #include <stdlib.h>
00032 #include <stdarg.h>
00033 #include <syslog.h>
00034 #include <errno.h>
00035 #include <string.h>
00036 #include <pthread.h>
00037 #include <sys/socket.h>
00038 #include <netinet/in.h>
00039 #include <arpa/inet.h>
00040 
00041 #include "common.h"
00042 
00043 #include "safe.h"
00044 #include "conf.h"
00045 #include "fw_iptables.h"
00046 #include "firewall.h"
00047 #include "debug.h"
00048 #include "util.h"
00049 #include "client_list.h"
00050 
00051 static int iptables_do_command(char *format, ...);
00052 static char *iptables_compile(char *, t_firewall_rule *);
00053 static void iptables_load_ruleset(char *, char *);
00054 
00055 extern pthread_mutex_t  client_list_mutex;
00056 extern pthread_mutex_t  config_mutex;
00057 
00060 static int fw_quiet = 0;
00061 
00063 static int
00064 iptables_do_command(char *format, ...)
00065 {
00066     va_list vlist;
00067     char *fmt_cmd,
00068         *cmd;
00069     int rc;
00070 
00071     va_start(vlist, format);
00072     safe_vasprintf(&fmt_cmd, format, vlist);
00073          va_end(vlist);
00074 
00075     safe_asprintf(&cmd, "iptables %s", fmt_cmd);
00076 
00077     free(fmt_cmd);
00078 
00079     debug(LOG_DEBUG, "Executing command: %s", cmd);
00080         
00081     rc = execute(cmd, fw_quiet);
00082 
00083     free(cmd);
00084 
00085     return rc;
00086 }
00087 
00095 static char *
00096 iptables_compile(char *chain, t_firewall_rule *rule)
00097 {
00098     char        command[MAX_BUF],
00099                 *mode;
00100     
00101     memset(command, 0, MAX_BUF);
00102     
00103     if (rule->block_allow == 1) {
00104         mode = safe_strdup("ACCEPT");
00105     } else {
00106         mode = safe_strdup("REJECT");
00107     }
00108     
00109     snprintf(command, sizeof(command),  "-t filter -A %s ", chain);
00110     if (rule->mask != NULL) {
00111         snprintf((command + strlen(command)), (sizeof(command) - 
00112                 strlen(command)), "-d %s ", rule->mask);
00113     }
00114     if (rule->protocol != NULL) {
00115         snprintf((command + strlen(command)), (sizeof(command) -
00116                 strlen(command)), "-p %s ", rule->protocol);
00117     }
00118     if (rule->port != NULL) {
00119         snprintf((command + strlen(command)), (sizeof(command) -
00120                 strlen(command)), "--dport %s ", rule->port);
00121     }
00122     snprintf((command + strlen(command)), (sizeof(command) - 
00123             strlen(command)), "-j %s", mode);
00124     
00125     free(mode);
00126 
00127     /* XXX The buffer command, an automatic variable, will get cleaned
00128      * off of the stack when we return, so we strdup() it. */
00129     return(safe_strdup(command));
00130 }
00131 
00138 static void
00139 iptables_load_ruleset(char *ruleset, char *chain)
00140 {
00141         t_firewall_rule         *rules;
00142         char                    *cmd;
00143 
00144         debug(LOG_DEBUG, "Load ruleset %s into chain %s", ruleset, chain);
00145         
00146         for (rules = get_ruleset(ruleset); rules != NULL; rules = rules->next) {
00147                 cmd = iptables_compile(chain, rules);
00148                 debug(LOG_DEBUG, "Loading rule \"%s\" into %s", cmd, chain);
00149                 iptables_do_command(cmd);
00150                 free(cmd);
00151         }
00152 
00153         debug(LOG_DEBUG, "Ruleset %s loaded into %s", ruleset, chain);
00154 }
00155 
00156 void
00157 iptables_fw_clear_authservers(void)
00158 {
00159     iptables_do_command("-t filter -F " TABLE_WIFIDOG_AUTHSERVERS);
00160     iptables_do_command("-t nat -F " TABLE_WIFIDOG_AUTHSERVERS);
00161 }
00162 
00163 void
00164 iptables_fw_set_authservers(void)
00165 {
00166     s_config *config;
00167     t_auth_serv *auth_server;
00168    
00169     config = config_get_config();
00170     
00171     for (auth_server = config->auth_servers; auth_server != NULL; auth_server = auth_server->next) {
00172             if (auth_server->last_ip && strcmp(auth_server->last_ip, "0.0.0.0") != 0) {
00173                 iptables_do_command("-t filter -A " TABLE_WIFIDOG_AUTHSERVERS " -d %s -j ACCEPT", auth_server->last_ip);
00174                 iptables_do_command("-t nat -A " TABLE_WIFIDOG_AUTHSERVERS " -d %s -j ACCEPT", auth_server->last_ip);
00175             }
00176     }
00177 
00178 }
00179 
00182 int
00183 iptables_fw_init(void)
00184 {
00185     s_config *config;
00186          char * gw_interface = NULL;
00187          char * gw_address = NULL;
00188          int gw_port = 0;
00189    
00190     fw_quiet = 0;
00191 
00192          LOCK_CONFIG();
00193     config = config_get_config();
00194          gw_interface = safe_strdup(config->gw_interface);
00195          gw_address = safe_strdup(config->gw_address);
00196          gw_port = config->gw_port;
00197          UNLOCK_CONFIG();
00198     
00199          /*
00200           *
00201           * Everything in the MANGLE table
00202           *
00203           */
00204 
00205                         /* Create new chains */
00206                         iptables_do_command("-t mangle -N " TABLE_WIFIDOG_OUTGOING);
00207                         iptables_do_command("-t mangle -N " TABLE_WIFIDOG_INCOMING);
00208 
00209                         /* Assign links and rules to these new chains */
00210                         iptables_do_command("-t mangle -I PREROUTING 1 -i %s -j " TABLE_WIFIDOG_OUTGOING, gw_interface);
00211                         iptables_do_command("-t mangle -I POSTROUTING 1 -o %s -j " TABLE_WIFIDOG_INCOMING, gw_interface);
00212 
00213 
00214          /*
00215           *
00216           * Everything in the NAT table
00217           *
00218           */
00219 
00220                         /* Create new chains */
00221                         iptables_do_command("-t nat -N " TABLE_WIFIDOG_OUTGOING);
00222                         iptables_do_command("-t nat -N " TABLE_WIFIDOG_WIFI_TO_ROUTER);
00223                         iptables_do_command("-t nat -N " TABLE_WIFIDOG_WIFI_TO_INTERNET);
00224                         iptables_do_command("-t nat -N " TABLE_WIFIDOG_UNKNOWN);
00225                         iptables_do_command("-t nat -N " TABLE_WIFIDOG_AUTHSERVERS);
00226 
00227                         /* Assign links and rules to these new chains */
00228                         iptables_do_command("-t nat -I PREROUTING 1 -i %s -j " TABLE_WIFIDOG_OUTGOING, gw_interface);
00229 
00230                         iptables_do_command("-t nat -A " TABLE_WIFIDOG_OUTGOING " -d %s -j " TABLE_WIFIDOG_WIFI_TO_ROUTER, gw_address);
00231                         iptables_do_command("-t nat -A " TABLE_WIFIDOG_WIFI_TO_ROUTER " -j ACCEPT");
00232 
00233                         iptables_do_command("-t nat -A " TABLE_WIFIDOG_OUTGOING " -j " TABLE_WIFIDOG_WIFI_TO_INTERNET);
00234                         iptables_do_command("-t nat -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -m mark --mark 0x%u -j ACCEPT", FW_MARK_KNOWN);
00235                         iptables_do_command("-t nat -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -m mark --mark 0x%u -j ACCEPT", FW_MARK_PROBATION);
00236                         iptables_do_command("-t nat -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -j " TABLE_WIFIDOG_UNKNOWN);
00237 
00238                         iptables_do_command("-t nat -A " TABLE_WIFIDOG_UNKNOWN " -j " TABLE_WIFIDOG_AUTHSERVERS);
00239                         iptables_do_command("-t nat -A " TABLE_WIFIDOG_UNKNOWN " -p tcp --dport 80 -j REDIRECT --to-ports %d", gw_port);
00240 
00241 
00242          /*
00243           *
00244           * Everything in the FILTER table
00245           *
00246           */
00247 
00248                         /* Create new chains */
00249                         iptables_do_command("-t filter -N " TABLE_WIFIDOG_WIFI_TO_INTERNET);
00250                         iptables_do_command("-t filter -N " TABLE_WIFIDOG_AUTHSERVERS);
00251                         iptables_do_command("-t filter -N " TABLE_WIFIDOG_LOCKED);
00252                         iptables_do_command("-t filter -N " TABLE_WIFIDOG_GLOBAL);
00253                         iptables_do_command("-t filter -N " TABLE_WIFIDOG_VALIDATE);
00254                         iptables_do_command("-t filter -N " TABLE_WIFIDOG_KNOWN);
00255                         iptables_do_command("-t filter -N " TABLE_WIFIDOG_UNKNOWN);
00256 
00257                         /* Assign links and rules to these new chains */
00258                         iptables_do_command("-t filter -I FORWARD 1 -i %s -j " TABLE_WIFIDOG_WIFI_TO_INTERNET, gw_interface);
00259                         iptables_do_command("-t filter -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -j " TABLE_WIFIDOG_AUTHSERVERS);
00260                         iptables_fw_set_authservers();
00261 
00262                         iptables_do_command("-t filter -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -m mark --mark 0x%u -j " TABLE_WIFIDOG_LOCKED, FW_MARK_LOCKED);
00263                         iptables_load_ruleset("locked-users", TABLE_WIFIDOG_LOCKED);
00264 
00265                         iptables_do_command("-t filter -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -j " TABLE_WIFIDOG_GLOBAL);
00266                         iptables_load_ruleset("global", TABLE_WIFIDOG_GLOBAL);
00267 
00268                         iptables_do_command("-t filter -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -m mark --mark 0x%u -j " TABLE_WIFIDOG_VALIDATE, FW_MARK_PROBATION);
00269                         iptables_load_ruleset("validating-users", TABLE_WIFIDOG_VALIDATE);
00270 
00271                         iptables_do_command("-t filter -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -m mark --mark 0x%u -j " TABLE_WIFIDOG_KNOWN, FW_MARK_KNOWN);
00272                         iptables_load_ruleset("known-users", TABLE_WIFIDOG_KNOWN);
00273     
00274                         iptables_do_command("-t filter -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -j " TABLE_WIFIDOG_UNKNOWN);
00275                         iptables_load_ruleset("unknown-users", TABLE_WIFIDOG_UNKNOWN);
00276                         iptables_do_command("-t filter -A " TABLE_WIFIDOG_UNKNOWN " -j REJECT --reject-with icmp-port-unreachable");
00277 
00278         free(gw_interface);
00279         free(gw_address);
00280 
00281     return 1;
00282 }
00283 
00288 int
00289 iptables_fw_destroy(void)
00290 {
00291     fw_quiet = 1;
00292 
00293          debug(LOG_DEBUG, "Destroying our iptables entries");
00294 
00295          /*
00296           *
00297           * Everything in the MANGLE table
00298           *
00299           */
00300          debug(LOG_DEBUG, "Destroying chains in the MANGLE table");
00301          iptables_fw_destroy_mention("mangle", "PREROUTING", TABLE_WIFIDOG_OUTGOING);
00302          iptables_fw_destroy_mention("mangle", "POSTROUTING", TABLE_WIFIDOG_INCOMING);
00303     iptables_do_command("-t mangle -F " TABLE_WIFIDOG_OUTGOING);
00304     iptables_do_command("-t mangle -F " TABLE_WIFIDOG_INCOMING);
00305     iptables_do_command("-t mangle -X " TABLE_WIFIDOG_OUTGOING);
00306     iptables_do_command("-t mangle -X " TABLE_WIFIDOG_INCOMING);
00307 
00308         /*
00309          *
00310          * Everything in the NAT table
00311          *
00312          */
00313         debug(LOG_DEBUG, "Destroying chains in the NAT table");
00314         iptables_fw_destroy_mention("nat", "PREROUTING", TABLE_WIFIDOG_OUTGOING);
00315         iptables_do_command("-t nat -F " TABLE_WIFIDOG_AUTHSERVERS);
00316     iptables_do_command("-t nat -F " TABLE_WIFIDOG_OUTGOING);
00317     iptables_do_command("-t nat -F " TABLE_WIFIDOG_WIFI_TO_ROUTER);
00318     iptables_do_command("-t nat -F " TABLE_WIFIDOG_WIFI_TO_INTERNET);
00319     iptables_do_command("-t nat -F " TABLE_WIFIDOG_UNKNOWN);
00320         iptables_do_command("-t nat -X " TABLE_WIFIDOG_AUTHSERVERS);
00321     iptables_do_command("-t nat -X " TABLE_WIFIDOG_OUTGOING);
00322     iptables_do_command("-t nat -X " TABLE_WIFIDOG_WIFI_TO_ROUTER);
00323     iptables_do_command("-t nat -X " TABLE_WIFIDOG_WIFI_TO_INTERNET);
00324     iptables_do_command("-t nat -X " TABLE_WIFIDOG_UNKNOWN);
00325 
00326          /*
00327           *
00328           * Everything in the FILTER table
00329           *
00330           */
00331          debug(LOG_DEBUG, "Destroying chains in the FILTER table");
00332          iptables_fw_destroy_mention("filter", "FORWARD", TABLE_WIFIDOG_WIFI_TO_INTERNET);
00333          iptables_do_command("-t filter -F " TABLE_WIFIDOG_WIFI_TO_INTERNET);
00334          iptables_do_command("-t filter -F " TABLE_WIFIDOG_AUTHSERVERS);
00335          iptables_do_command("-t filter -F " TABLE_WIFIDOG_LOCKED);
00336          iptables_do_command("-t filter -F " TABLE_WIFIDOG_GLOBAL);
00337          iptables_do_command("-t filter -F " TABLE_WIFIDOG_VALIDATE);
00338          iptables_do_command("-t filter -F " TABLE_WIFIDOG_KNOWN);
00339          iptables_do_command("-t filter -F " TABLE_WIFIDOG_UNKNOWN);
00340          iptables_do_command("-t filter -X " TABLE_WIFIDOG_WIFI_TO_INTERNET);
00341          iptables_do_command("-t filter -X " TABLE_WIFIDOG_AUTHSERVERS);
00342          iptables_do_command("-t filter -X " TABLE_WIFIDOG_LOCKED);
00343          iptables_do_command("-t filter -X " TABLE_WIFIDOG_GLOBAL);
00344          iptables_do_command("-t filter -X " TABLE_WIFIDOG_VALIDATE);
00345          iptables_do_command("-t filter -X " TABLE_WIFIDOG_KNOWN);
00346          iptables_do_command("-t filter -X " TABLE_WIFIDOG_UNKNOWN);
00347 
00348     return 1;
00349 }
00350 
00351 /*
00352  * Helper for iptables_fw_destroy
00353  * @param table The table to search
00354  * @param chain The chain in that table to search
00355  * @param mention A word to find and delete in rules in the given table+chain
00356  */
00357 int
00358 iptables_fw_destroy_mention(
00359                 char * table,
00360                 char * chain,
00361                 char * mention
00362 ) {
00363         FILE *p = NULL;
00364         char *command = NULL;
00365         char *command2 = NULL;
00366         char line[MAX_BUF];
00367         char rulenum[10];
00368         int deleted = 0;
00369 
00370         debug(LOG_DEBUG, "Attempting to destroy all mention of %s from %s.%s", mention, table, chain);
00371 
00372         safe_asprintf(&command, "iptables -t %s -L %s -n --line-numbers -v", table, chain);
00373 
00374         if ((p = popen(command, "r"))) {
00375                 /* Skip first 2 lines */
00376                 while (!feof(p) && fgetc(p) != '\n');
00377                 while (!feof(p) && fgetc(p) != '\n');
00378                 /* Loop over entries */
00379                 while (fgets(line, sizeof(line), p)) {
00380                         /* Look for mention */
00381                         if (strstr(line, mention)) {
00382                                 /* Found mention - Get the rule number into rulenum*/
00383                                 if (sscanf(line, "%9[0-9]", rulenum) == 1) {
00384                                         /* Delete the rule: */
00385                                         debug(LOG_DEBUG, "Deleting rule %s from %s.%s because it mentions %s", rulenum, table, chain, mention);
00386                                         safe_asprintf(&command2, "-t %s -D %s %s", table, chain, rulenum);
00387                                         iptables_do_command(command2);
00388                                         free(command2);
00389                                         deleted = 1;
00390                                         /* Do not keep looping - the captured rulenums will no longer be accurate */
00391                                         break;
00392                                 }
00393                         }
00394                 }
00395                 pclose(p);
00396         }
00397 
00398         free(command);
00399 
00400         if (deleted) {
00401                 /* Recurse just in case there are more in the same table+chain */
00402                 iptables_fw_destroy_mention(table, chain, mention);
00403         }
00404 
00405         return (deleted);
00406 }
00407 
00409 int
00410 iptables_fw_access(fw_access_t type, char *ip, char *mac, int tag)
00411 {
00412     int rc;
00413 
00414     fw_quiet = 0;
00415 
00416     switch(type) {
00417         case FW_ACCESS_ALLOW:
00418             iptables_do_command("-t mangle -A " TABLE_WIFIDOG_OUTGOING " -s %s -m mac --mac-source %s -j MARK --set-mark %d", ip, mac, tag);
00419             rc = iptables_do_command("-t mangle -A " TABLE_WIFIDOG_INCOMING " -d %s -j ACCEPT", ip);
00420             break;
00421         case FW_ACCESS_DENY:
00422             iptables_do_command("-t mangle -D " TABLE_WIFIDOG_OUTGOING " -s %s -m mac --mac-source %s -j MARK --set-mark %d", ip, mac, tag);
00423             rc = iptables_do_command("-t mangle -D " TABLE_WIFIDOG_INCOMING " -d %s -j ACCEPT", ip);
00424             break;
00425         default:
00426             rc = -1;
00427             break;
00428     }
00429 
00430     return rc;
00431 }
00432 
00434 int
00435 iptables_fw_counters_update(void)
00436 {
00437     FILE *output;
00438     char *script,
00439         ip[16],
00440         rc;
00441     unsigned long long int counter;
00442     t_client *p1;
00443          struct in_addr tempaddr;
00444 
00445     /* Look for outgoing traffic */
00446     safe_asprintf(&script, "%s %s", "iptables", "-v -n -x -t mangle -L " TABLE_WIFIDOG_OUTGOING);
00447     output = popen(script, "r");
00448     free(script);
00449     if (!output) {
00450         debug(LOG_ERR, "popen(): %s", strerror(errno));
00451         return -1;
00452     }
00453 
00454     /* skip the first two lines */
00455     while (('\n' != fgetc(output)) && !feof(output))
00456         ;
00457     while (('\n' != fgetc(output)) && !feof(output))
00458         ;
00459     while (output && !(feof(output))) {
00460         rc = fscanf(output, "%*s %llu %*s %*s %*s %*s %*s %15[0-9.] %*s %*s %*s %*s %*s 0x%*u", &counter, ip);
00461         if (2 == rc && EOF != rc) {
00462                           /* Sanity*/
00463                           if (!inet_aton(ip, &tempaddr)) {
00464                                   debug(LOG_WARNING, "I was supposed to read an IP address but instead got [%s] - ignoring it", ip);
00465                                   continue;
00466                           }
00467             debug(LOG_DEBUG, "Outgoing %s Bytes=%llu", ip, counter);
00468             LOCK_CLIENT_LIST();
00469             if ((p1 = client_list_find_by_ip(ip))) {
00470                 if (p1->counters.outgoing < counter) {
00471                     p1->counters.outgoing = counter;
00472                     p1->counters.last_updated = time(NULL);
00473                     debug(LOG_DEBUG, "%s - Updated counter.outgoing to %llu bytes", ip, counter);
00474                 }
00475             } else {
00476                 debug(LOG_ERR, "Could not find %s in client list", ip);
00477             }
00478             UNLOCK_CLIENT_LIST();
00479         }
00480     }
00481     pclose(output);
00482 
00483     /* Look for incoming traffic */
00484     safe_asprintf(&script, "%s %s", "iptables", "-v -n -x -t mangle -L " TABLE_WIFIDOG_INCOMING);
00485     output = popen(script, "r");
00486     free(script);
00487     if (!output) {
00488         debug(LOG_ERR, "popen(): %s", strerror(errno));
00489         return -1;
00490     }
00491 
00492     /* skip the first two lines */
00493     while (('\n' != fgetc(output)) && !feof(output))
00494         ;
00495     while (('\n' != fgetc(output)) && !feof(output))
00496         ;
00497     while (output && !(feof(output))) {
00498         rc = fscanf(output, "%*s %llu %*s %*s %*s %*s %*s %*s %15[0-9.]", &counter, ip);
00499         if (2 == rc && EOF != rc) {
00500                           /* Sanity*/
00501                           if (!inet_aton(ip, &tempaddr)) {
00502                                   debug(LOG_WARNING, "I was supposed to read an IP address but instead got [%s] - ignoring it", ip);
00503                                   continue;
00504                           }
00505             debug(LOG_DEBUG, "Incoming %s Bytes=%llu", ip, counter);
00506             LOCK_CLIENT_LIST();
00507             if ((p1 = client_list_find_by_ip(ip))) {
00508                 if (p1->counters.incoming < counter) {
00509                     p1->counters.incoming = counter;
00510                     debug(LOG_DEBUG, "%s - Updated counter.incoming to %llu bytes", ip, counter);
00511                 }
00512             } else {
00513                 debug(LOG_ERR, "Could not find %s in client list", ip);
00514             }
00515             UNLOCK_CLIENT_LIST();
00516         }
00517     }
00518     pclose(output);
00519 
00520     return 1;
00521 }

Generated on Sun Apr 3 20:04:46 2005 for WifiDog by  doxygen 1.4.1