root/trunk/wifidog/src/fw_iptables.c @ 458

Revision 458, 15.8 KB (checked in by minaguib, 8 years ago)

* Bugfix previous commit of re-doing firewall - was not respecting gw_interface when it should have
* Got rid of TABLE_WIFIDOG_WIFI_TO_GW completely since it's unneeded and screwed up bytecount

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/********************************************************************\
2 * This program is free software; you can redistribute it and/or    *
3 * modify it under the terms of the GNU General Public License as   *
4 * published by the Free Software Foundation; either version 2 of   *
5 * the License, or (at your option) any later version.              *
6 *                                                                  *
7 * This program is distributed in the hope that it will be useful,  *
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
10 * GNU General Public License for more details.                     *
11 *                                                                  *
12 * You should have received a copy of the GNU General Public License*
13 * along with this program; if not, contact:                        *
14 *                                                                  *
15 * Free Software Foundation           Voice:  +1-617-542-5942       *
16 * 59 Temple Place - Suite 330        Fax:    +1-617-542-2652       *
17 * Boston, MA  02111-1307,  USA       gnu@gnu.org                   *
18 *                                                                  *
19 \********************************************************************/
20
21/* $Header$ */
22/** @internal
23  @file fw_iptables.c
24  @brief Firewall iptables functions
25  @author Copyright (C) 2004 Philippe April <papril777@yahoo.com>
26 */
27
28#define _GNU_SOURCE
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <stdarg.h>
33#include <syslog.h>
34#include <errno.h>
35#include <string.h>
36#include <pthread.h>
37#include <sys/socket.h>
38#include <netinet/in.h>
39#include <arpa/inet.h>
40
41#include "common.h"
42
43#include "conf.h"
44#include "fw_iptables.h"
45#include "firewall.h"
46#include "debug.h"
47#include "util.h"
48#include "client_list.h"
49
50static int iptables_do_command(char *format, ...);
51static char *iptables_compile(char *, t_firewall_rule *);
52static void iptables_load_ruleset(char *, char *);
53
54extern pthread_mutex_t  client_list_mutex;
55extern pthread_mutex_t  config_mutex;
56
57/**
58Used to supress the error output of the firewall during destruction */ 
59static int fw_quiet = 0;
60
61/** @internal */
62static int
63iptables_do_command(char *format, ...)
64{
65    va_list vlist;
66    char *fmt_cmd,
67        *cmd;
68    int rc;
69
70    va_start(vlist, format);
71    vasprintf(&fmt_cmd, format, vlist);
72    asprintf(&cmd, "iptables %s", fmt_cmd);
73    debug(LOG_DEBUG, "Executing command: %s", cmd);
74       
75    rc = execute(cmd, fw_quiet);
76
77    free(fmt_cmd);
78    free(cmd);
79
80    return rc;
81}
82
83/**
84 * @internal
85 * Compiles a struct definition of a firewall rule into a valid iptables
86 * command.
87 * @arg chain Chain that the command will be (-A)ppended to.
88 * @arg rule Definition of a rule into a struct, from conf.c.
89 */
90static char *
91iptables_compile(char *chain, t_firewall_rule *rule)
92{
93    char        command[MAX_BUF],
94                *mode;
95   
96    memset(command, 0, MAX_BUF);
97   
98    if (rule->block_allow == 1) {
99        mode = strdup("ACCEPT");
100    } else {
101        mode = strdup("REJECT");
102    }
103   
104    snprintf(command, sizeof(command),  "-t filter -A %s ", chain);
105    if (rule->mask != NULL) {
106        snprintf((command + strlen(command)), (sizeof(command) - 
107                strlen(command)), "-d %s ", rule->mask);
108    }
109    if (rule->protocol != NULL) {
110        snprintf((command + strlen(command)), (sizeof(command) -
111                strlen(command)), "-p %s ", rule->protocol);
112    }
113    if (rule->port != NULL) {
114        snprintf((command + strlen(command)), (sizeof(command) -
115                strlen(command)), "--dport %s ", rule->port);
116    }
117    snprintf((command + strlen(command)), (sizeof(command) - 
118            strlen(command)), "-j %s", mode);
119   
120    free(mode);
121
122    /* XXX The buffer command, an automatic variable, will get cleaned
123     * off of the stack when we return, so we strdup() it. */
124    return(strdup(command));
125}
126
127/**
128 * @internal
129 * Load all the rules in a rule set.
130 * @arg ruleset Name of the ruleset
131 * @arg chain IPTables chain the rules go into
132 */
133static void
134iptables_load_ruleset(char *ruleset, char *chain)
135{
136        t_firewall_rule         *rules;
137        char                    *cmd;
138
139        debug(LOG_DEBUG, "Load ruleset %s into chain %s", ruleset, chain);
140       
141        for (rules = get_ruleset(ruleset); rules != NULL; rules = rules->next) {
142                cmd = iptables_compile(chain, rules);
143                debug(LOG_DEBUG, "Loading rule \"%s\" into %s", cmd, chain);
144                iptables_do_command(cmd);
145                free(cmd);
146        }
147
148        debug(LOG_DEBUG, "Ruleset %s loaded into %s", ruleset, chain);
149}
150
151void
152iptables_fw_clear_authservers(void)
153{
154    iptables_do_command("-t filter -F " TABLE_WIFIDOG_AUTHSERVERS);
155}
156
157void
158iptables_fw_set_authservers(void)
159{
160    s_config *config;
161    t_auth_serv *auth_server;
162   
163    config = config_get_config();
164   
165    LOCK_CONFIG();
166   
167    for (auth_server = config->auth_servers; auth_server != NULL; auth_server = auth_server->next) {
168            if (auth_server->last_ip == NULL || strcmp(auth_server->last_ip, "0.0.0.0") == 0) {
169                iptables_do_command("-t filter -A " TABLE_WIFIDOG_AUTHSERVERS " -d %s -j ACCEPT", auth_server->authserv_hostname);
170            } else {
171                iptables_do_command("-t filter -A " TABLE_WIFIDOG_AUTHSERVERS " -d %s -j ACCEPT", auth_server->last_ip);
172            }
173    }
174
175    UNLOCK_CONFIG();
176}
177
178/** Initialize the firewall rules
179 */
180int
181iptables_fw_init(void)
182{
183    s_config *config;
184         char * gw_interface = NULL;
185         int gw_port = 0;
186   
187    fw_quiet = 0;
188
189    config = config_get_config();
190         LOCK_CONFIG();
191         gw_interface = strdup(config->gw_interface);
192         gw_port = config->gw_port;
193         UNLOCK_CONFIG();
194   
195         /*
196          *
197          * Everything in the MANGLE table
198          *
199          */
200
201                        /* Create new chains */
202                        iptables_do_command("-t mangle -N " TABLE_WIFIDOG_OUTGOING);
203                        iptables_do_command("-t mangle -N " TABLE_WIFIDOG_INCOMING);
204
205                        /* Assign links and rules to these new chains */
206                        iptables_do_command("-t mangle -I PREROUTING 1 -i %s -j " TABLE_WIFIDOG_OUTGOING, gw_interface);
207                        iptables_do_command("-t mangle -I POSTROUTING 1 -o %s -j " TABLE_WIFIDOG_INCOMING, gw_interface);
208
209
210         /*
211          *
212          * Everything in the NAT table
213          *
214          */
215
216                        /* Create new chains */
217                        iptables_do_command("-t nat -N " TABLE_WIFIDOG_WIFI_TO_INTERNET);
218                        iptables_do_command("-t nat -N " TABLE_WIFIDOG_UNKNOWN);
219
220                        /* Assign links and rules to these new chains */
221                        iptables_do_command("-t nat -I PREROUTING 1 -i %s -j " TABLE_WIFIDOG_WIFI_TO_INTERNET, gw_interface);
222                        iptables_do_command("-t nat -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -m mark --mark 0x%u -j RETURN", FW_MARK_KNOWN);
223                        iptables_do_command("-t nat -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -m mark --mark 0x%u -j RETURN", FW_MARK_PROBATION);
224                        iptables_do_command("-t nat -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -j " TABLE_WIFIDOG_UNKNOWN);
225
226                        iptables_do_command("-t nat -A " TABLE_WIFIDOG_UNKNOWN " -p tcp --dport 80 -j REDIRECT --to-ports %d", gw_port);
227
228
229         /*
230          *
231          * Everything in the FILTER table
232          *
233          */
234
235                        /* Create new chains */
236                        iptables_do_command("-t filter -N " TABLE_WIFIDOG_WIFI_TO_INTERNET);
237                        iptables_do_command("-t filter -N " TABLE_WIFIDOG_AUTHSERVERS);
238                        iptables_do_command("-t filter -N " TABLE_WIFIDOG_LOCKED);
239                        iptables_do_command("-t filter -N " TABLE_WIFIDOG_GLOBAL);
240                        iptables_do_command("-t filter -N " TABLE_WIFIDOG_VALIDATE);
241                        iptables_do_command("-t filter -N " TABLE_WIFIDOG_KNOWN);
242                        iptables_do_command("-t filter -N " TABLE_WIFIDOG_UNKNOWN);
243
244                        /* Assign links and rules to these new chains */
245                        iptables_do_command("-t filter -I FORWARD 1 -i %s -j " TABLE_WIFIDOG_WIFI_TO_INTERNET, gw_interface);
246                        iptables_do_command("-t filter -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -j " TABLE_WIFIDOG_AUTHSERVERS);
247                        iptables_fw_set_authservers();
248
249                        iptables_do_command("-t filter -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -m mark --mark 0x%u -j " TABLE_WIFIDOG_LOCKED, FW_MARK_LOCKED);
250                        iptables_load_ruleset("locked-users", TABLE_WIFIDOG_LOCKED);
251
252                        iptables_do_command("-t filter -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -j " TABLE_WIFIDOG_GLOBAL);
253                        iptables_load_ruleset("global", TABLE_WIFIDOG_GLOBAL);
254
255                        iptables_do_command("-t filter -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -m mark --mark 0x%u -j " TABLE_WIFIDOG_VALIDATE, FW_MARK_PROBATION);
256                        iptables_load_ruleset("validating-users", TABLE_WIFIDOG_VALIDATE);
257
258                        iptables_do_command("-t filter -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -m mark --mark 0x%u -j " TABLE_WIFIDOG_KNOWN, FW_MARK_KNOWN);
259                        iptables_load_ruleset("known-users", TABLE_WIFIDOG_KNOWN);
260   
261                        iptables_do_command("-t filter -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -j " TABLE_WIFIDOG_UNKNOWN);
262                        iptables_load_ruleset("unknown-users", TABLE_WIFIDOG_UNKNOWN);
263                        iptables_do_command("-t filter -A " TABLE_WIFIDOG_UNKNOWN " -j REJECT --reject-with icmp-port-unreachable");
264
265        free(gw_interface);
266
267    return 1;
268}
269
270/** Remove the firewall rules
271 * This is used when we do a clean shutdown of WiFiDog and when it starts to make
272 * sure there are no rules left over
273 */
274int
275iptables_fw_destroy(void)
276{
277    fw_quiet = 1;
278
279         /*
280          *
281          * Everything in the MANGLE table
282          *
283          */
284         iptables_fw_destroy_mention("mangle", "PREROUTING", TABLE_WIFIDOG_OUTGOING);
285         iptables_fw_destroy_mention("mangle", "POSTROUTING", TABLE_WIFIDOG_INCOMING);
286    iptables_do_command("-t mangle -F " TABLE_WIFIDOG_OUTGOING);
287    iptables_do_command("-t mangle -F " TABLE_WIFIDOG_INCOMING);
288    iptables_do_command("-t mangle -X " TABLE_WIFIDOG_OUTGOING);
289    iptables_do_command("-t mangle -X " TABLE_WIFIDOG_INCOMING);
290
291         /*
292          *
293          * Everything in the NAT table
294          *
295          */
296         iptables_fw_destroy_mention("nat", "PREROUTING", TABLE_WIFIDOG_WIFI_TO_INTERNET);
297    iptables_do_command("-t nat -F " TABLE_WIFIDOG_WIFI_TO_INTERNET);
298    iptables_do_command("-t nat -F " TABLE_WIFIDOG_UNKNOWN);
299    iptables_do_command("-t nat -X " TABLE_WIFIDOG_WIFI_TO_INTERNET);
300    iptables_do_command("-t nat -X " TABLE_WIFIDOG_UNKNOWN);
301
302         /*
303          *
304          * Everything in the FILTER table
305          *
306          */
307         iptables_fw_destroy_mention("filter", "FORWARD", TABLE_WIFIDOG_WIFI_TO_INTERNET);
308         iptables_do_command("-t filter -F " TABLE_WIFIDOG_WIFI_TO_INTERNET);
309         iptables_do_command("-t filter -F " TABLE_WIFIDOG_AUTHSERVERS);
310         iptables_do_command("-t filter -F " TABLE_WIFIDOG_LOCKED);
311         iptables_do_command("-t filter -F " TABLE_WIFIDOG_GLOBAL);
312         iptables_do_command("-t filter -F " TABLE_WIFIDOG_VALIDATE);
313         iptables_do_command("-t filter -F " TABLE_WIFIDOG_KNOWN);
314         iptables_do_command("-t filter -F " TABLE_WIFIDOG_UNKNOWN);
315         iptables_do_command("-t filter -X " TABLE_WIFIDOG_WIFI_TO_INTERNET);
316         iptables_do_command("-t filter -X " TABLE_WIFIDOG_AUTHSERVERS);
317         iptables_do_command("-t filter -X " TABLE_WIFIDOG_LOCKED);
318         iptables_do_command("-t filter -X " TABLE_WIFIDOG_GLOBAL);
319         iptables_do_command("-t filter -X " TABLE_WIFIDOG_VALIDATE);
320         iptables_do_command("-t filter -X " TABLE_WIFIDOG_KNOWN);
321         iptables_do_command("-t filter -X " TABLE_WIFIDOG_UNKNOWN);
322
323    return 1;
324}
325
326/*
327 * Helper for iptables_fw_destroy
328 * @param table The table to search
329 * @param chain The chain in that table to search
330 * @param mention A word to find and delete in rules in the given table+chain
331 */
332int
333iptables_fw_destroy_mention(
334                char * table,
335                char * chain,
336                char * mention
337) {
338        FILE *p = NULL;
339        char *command = NULL;
340        char *command2 = NULL;
341        char line[MAX_BUF];
342        char rulenum[10];
343        int deleted = 0;
344
345        asprintf(&command, "iptables -t %s -L %s -n --line-numbers -v", table, chain);
346
347        if ((p = popen(command, "r"))) {
348                /* Skip first 2 lines */
349                while (!feof(p) && fgetc(p) != '\n');
350                while (!feof(p) && fgetc(p) != '\n');
351                /* Loop over entries */
352                while (fgets(line, sizeof(line), p)) {
353                        /* Look for mention */
354                        if (strstr(line, mention)) {
355                                /* Found mention - Get the rule number into rulenum*/
356                                if (sscanf(line, "%9[0-9]", rulenum) == 1) {
357                                        /* Delete the rule: */
358                                        asprintf(&command2, "-t %s -D %s %s", table, chain, rulenum);
359                                        iptables_do_command(command2);
360                                        free(command2);
361                                        deleted = 1;
362                                        /* Do not keep looping - the captures rulenums will no longer be accurate */
363                                        break;
364                                }
365                        }
366                }
367                pclose(p);
368        }
369
370        free(command);
371
372        if (deleted) {
373                /* Recurse just in case there are more in the same table+chain */
374                iptables_fw_destroy_mention(table, chain, mention);
375        }
376
377        return (deleted);
378}
379
380/** Set the firewall access for a specific client */
381int
382iptables_fw_access(fw_access_t type, char *ip, char *mac, int tag)
383{
384    int rc;
385
386    fw_quiet = 0;
387
388    switch(type) {
389        case FW_ACCESS_ALLOW:
390            iptables_do_command("-t mangle -A " TABLE_WIFIDOG_OUTGOING " -s %s -m mac --mac-source %s -j MARK --set-mark %d", ip, mac, tag);
391            rc = iptables_do_command("-t mangle -A " TABLE_WIFIDOG_INCOMING " -d %s -j ACCEPT", ip);
392            break;
393        case FW_ACCESS_DENY:
394            iptables_do_command("-t mangle -D " TABLE_WIFIDOG_OUTGOING " -s %s -m mac --mac-source %s -j MARK --set-mark %d", ip, mac, tag);
395            rc = iptables_do_command("-t mangle -D " TABLE_WIFIDOG_INCOMING " -d %s -j ACCEPT", ip);
396            break;
397        default:
398            rc = -1;
399            break;
400    }
401
402    return rc;
403}
404
405/** Update the counters of all the clients in the client list */
406int
407iptables_fw_counters_update(void)
408{
409    FILE *output;
410    char *script,
411        ip[16],
412        rc;
413    unsigned long int counter;
414    t_client *p1;
415         struct in_addr tempaddr;
416
417    /* Look for outgoing traffic */
418    asprintf(&script, "%s %s", "iptables", "-v -n -x -t mangle -L " TABLE_WIFIDOG_OUTGOING);
419    if (!(output = popen(script, "r"))) {
420        debug(LOG_ERR, "popen(): %s", strerror(errno));
421        return -1;
422    }
423    free(script);
424
425    /* skip the first two lines */
426    while (('\n' != fgetc(output)) && !feof(output))
427        ;
428    while (('\n' != fgetc(output)) && !feof(output))
429        ;
430    while (output && !(feof(output))) {
431        rc = fscanf(output, "%*s %lu %*s %*s %*s %*s %*s %15[0-9.] %*s %*s %*s %*s %*s 0x%*u", &counter, ip);
432        if (2 == rc && EOF != rc) {
433                          /* Sanity*/
434                          if (!inet_aton(ip, &tempaddr)) {
435                                  debug(LOG_WARNING, "I was supposed to read an IP address but instead got [%s] - ignoring it", ip);
436                                  continue;
437                          }
438            debug(LOG_DEBUG, "Outgoing %s Bytes=%ld", ip, counter);
439            LOCK_CLIENT_LIST();
440            if ((p1 = client_list_find_by_ip(ip))) {
441                if (p1->counters.outgoing < counter) {
442                    p1->counters.outgoing = counter;
443                    p1->counters.last_updated = time(NULL);
444                    debug(LOG_DEBUG, "%s - Updated outgoing counter to %ld bytes from outgoing chain", ip, counter);
445                }
446            } else {
447                debug(LOG_ERR, "Could not find %s in client list", ip);
448            }
449            UNLOCK_CLIENT_LIST();
450        }
451    }
452    pclose(output);
453
454    /* Look for incoming traffic */
455    asprintf(&script, "%s %s", "iptables", "-v -n -x -t mangle -L " TABLE_WIFIDOG_INCOMING);
456    if (!(output = popen(script, "r"))) {
457        debug(LOG_ERR, "popen(): %s", strerror(errno));
458        return -1;
459    }
460    free(script);
461
462    /* skip the first two lines */
463    while (('\n' != fgetc(output)) && !feof(output))
464        ;
465    while (('\n' != fgetc(output)) && !feof(output))
466        ;
467    while (output && !(feof(output))) {
468        rc = fscanf(output, "%*s %lu %*s %*s %*s %*s %*s %*s %15[0-9.]", &counter, ip);
469        if (2 == rc && EOF != rc) {
470                          /* Sanity*/
471                          if (!inet_aton(ip, &tempaddr)) {
472                                  debug(LOG_WARNING, "I was supposed to read an IP address but instead got [%s] - ignoring it", ip);
473                                  continue;
474                          }
475            debug(LOG_DEBUG, "Incoming %s Bytes=%ld", ip, counter);
476            LOCK_CLIENT_LIST();
477            if ((p1 = client_list_find_by_ip(ip))) {
478                if (p1->counters.incoming < counter) {
479                    p1->counters.incoming = counter;
480                    p1->counters.last_updated = time(NULL);
481                    debug(LOG_DEBUG, "%s - Updated counter to %ld bytes", ip, counter);
482                }
483            } else {
484                debug(LOG_ERR, "Could not find %s in client list", ip);
485            }
486            UNLOCK_CLIENT_LIST();
487        }
488    }
489    pclose(output);
490
491    return 1;
492}
Note: See TracBrowser for help on using the browser.