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

Revision 424, 15.6 KB (checked in by minaguib, 8 years ago)

Keep track of whether we're online or not, show user apology if we're not
Fix building ipkg's - didn't work on my openwrt build
Fix memory leak
Misc

  • 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
38#include "common.h"
39
40#include "conf.h"
41#include "fw_iptables.h"
42#include "firewall.h"
43#include "debug.h"
44#include "util.h"
45#include "client_list.h"
46
47static int iptables_do_command(char *format, ...);
48static char *iptables_compile(char *, t_firewall_rule *);
49static void iptables_load_ruleset(char *, char *);
50
51extern pthread_mutex_t  client_list_mutex;
52extern pthread_mutex_t  config_mutex;
53
54/**
55Used to supress the error output of the firewall during destruction */ 
56static int fw_quiet = 0;
57
58/** @internal */
59static int
60iptables_do_command(char *format, ...)
61{
62    va_list vlist;
63    char *fmt_cmd,
64        *cmd;
65    int rc;
66
67    va_start(vlist, format);
68    vasprintf(&fmt_cmd, format, vlist);
69    asprintf(&cmd, "iptables %s", fmt_cmd);
70    debug(LOG_DEBUG, "Executing command: %s", cmd);
71       
72    rc = execute(cmd, fw_quiet);
73
74    free(fmt_cmd);
75    free(cmd);
76
77    return rc;
78}
79
80/**
81 * @internal
82 * Compiles a struct definition of a firewall rule into a valid iptables
83 * command.
84 * @arg chain Chain that the command will be (-A)ppended to.
85 * @arg rule Definition of a rule into a struct, from conf.c.
86 */
87static char *
88iptables_compile(char *chain, t_firewall_rule *rule)
89{
90    char        command[MAX_BUF],
91                *mode;
92   
93    memset(command, 0, MAX_BUF);
94   
95    if (rule->block_allow == 1) {
96        mode = strdup("ACCEPT");
97    } else {
98        mode = strdup("DROP");
99    }
100   
101    snprintf(command, sizeof(command),  "-t nat -A %s ", chain);
102    if (rule->mask != NULL) {
103        snprintf((command + strlen(command)), (sizeof(command) - 
104                strlen(command)), "-d %s ", rule->mask);
105    }
106    if (rule->protocol != NULL) {
107        snprintf((command + strlen(command)), (sizeof(command) -
108                strlen(command)), "-p %s ", rule->protocol);
109    }
110    if (rule->port != NULL) {
111        snprintf((command + strlen(command)), (sizeof(command) -
112                strlen(command)), "--dport %s ", rule->port);
113    }
114    snprintf((command + strlen(command)), (sizeof(command) - 
115            strlen(command)), "-j %s", mode);
116   
117    free(mode);
118
119    /* XXX The buffer command, an automatic variable, will get cleaned
120     * off of the stack when we return, so we strdup() it. */
121    return(strdup(command));
122}
123
124/**
125 * @internal
126 * Load all the rules in a rule set.
127 * @arg ruleset Name of the ruleset
128 * @arg chain IPTables chain the rules go into
129 */
130static void
131iptables_load_ruleset(char *ruleset, char *chain)
132{
133        t_firewall_rule         *rules;
134        char                    *cmd;
135
136        debug(LOG_DEBUG, "Load ruleset %s into chain %s", ruleset, chain);
137       
138        for (rules = get_ruleset(ruleset); rules != NULL; rules = rules->next) {
139                cmd = iptables_compile(chain, rules);
140                debug(LOG_DEBUG, "Loading rule \"%s\" into %s", cmd, chain);
141                iptables_do_command(cmd);
142                free(cmd);
143        }
144
145        debug(LOG_DEBUG, "Ruleset %s loaded into %s", ruleset, chain);
146}
147
148void
149iptables_fw_clear_authservers(void)
150{
151    iptables_do_command("-t nat -F " TABLE_WIFIDOG_AUTHSERVERS);
152}
153
154void
155iptables_fw_set_authservers(void)
156{
157    s_config *config;
158    t_auth_serv *auth_server;
159   
160    config = config_get_config();
161   
162    LOCK_CONFIG();
163   
164    for (auth_server = config->auth_servers; auth_server != NULL; auth_server = auth_server->next) {
165            if (auth_server->last_ip == NULL || strcmp(auth_server->last_ip, "0.0.0.0") == 0) {
166                iptables_do_command("-t nat -A " TABLE_WIFIDOG_AUTHSERVERS " -d %s -j ACCEPT", auth_server->authserv_hostname);
167            } else {
168                iptables_do_command("-t nat -A " TABLE_WIFIDOG_AUTHSERVERS " -d %s -j ACCEPT", auth_server->last_ip);
169            }
170    }
171
172    UNLOCK_CONFIG();
173}
174
175/** Initialize the firewall rules
176 */
177int
178iptables_fw_init(void)
179{
180    s_config *config;
181   
182    config = config_get_config();
183    fw_quiet = 0;
184   
185    /* Create authservers table here instead of in iptables_fw_set_authservers
186     * so we only have to flush it and not destroy/create every time */
187    iptables_do_command("-t nat -N " TABLE_WIFIDOG_AUTHSERVERS);
188
189    iptables_fw_set_authservers();
190
191    LOCK_CONFIG();
192   
193    iptables_do_command("-t nat -N " TABLE_WIFIDOG_VALIDATE);
194    iptables_do_command("-t nat -A " TABLE_WIFIDOG_VALIDATE " -j " TABLE_WIFIDOG_AUTHSERVERS);
195    iptables_do_command("-t nat -A " TABLE_WIFIDOG_VALIDATE " -d %s -j ACCEPT", config->gw_address);
196
197    UNLOCK_CONFIG();
198
199    /** Insert global rules BEFORE the "defaults" */
200    iptables_load_ruleset("global", TABLE_WIFIDOG_VALIDATE);
201    iptables_load_ruleset("validating-users", TABLE_WIFIDOG_VALIDATE);
202
203    LOCK_CONFIG();
204   
205    iptables_do_command("-t nat -N " TABLE_WIFIDOG_UNKNOWN);
206    iptables_do_command("-t nat -A " TABLE_WIFIDOG_UNKNOWN " -j " TABLE_WIFIDOG_AUTHSERVERS);
207    iptables_do_command("-t nat -A " TABLE_WIFIDOG_UNKNOWN " -d %s -j ACCEPT", config->gw_address);
208
209    UNLOCK_CONFIG();
210   
211    /** Insert global rules BEFORE the "defaults" */
212    iptables_load_ruleset("global", TABLE_WIFIDOG_UNKNOWN);
213    iptables_load_ruleset("unknown-users", TABLE_WIFIDOG_UNKNOWN);
214    LOCK_CONFIG();
215
216    /* XXX If there's a rule in global for port 80, it overrides this. */
217    iptables_do_command("-t nat -A " TABLE_WIFIDOG_UNKNOWN " -p tcp --dport 80 -j REDIRECT --to-ports %d", config->gw_port);
218    UNLOCK_CONFIG();
219    iptables_do_command("-t nat -A " TABLE_WIFIDOG_UNKNOWN " -j DROP");
220
221    iptables_do_command("-t nat -N " TABLE_WIFIDOG_KNOWN);
222    /* Insert global rules BEFORE the "defaults" */
223    iptables_load_ruleset("global", TABLE_WIFIDOG_KNOWN);
224    iptables_load_ruleset("known-users", TABLE_WIFIDOG_KNOWN);
225
226    iptables_do_command("-t nat -N " TABLE_WIFIDOG_LOCKED);
227    iptables_load_ruleset("locked-users", TABLE_WIFIDOG_KNOWN);
228   
229    iptables_do_command("-t nat -N " TABLE_WIFIDOG_CLASS);
230    LOCK_CONFIG();
231    iptables_do_command("-t nat -A " TABLE_WIFIDOG_CLASS " -i %s -m mark --mark 0x%u -j " TABLE_WIFIDOG_VALIDATE, config->gw_interface, FW_MARK_PROBATION);
232    iptables_do_command("-t nat -A " TABLE_WIFIDOG_CLASS " -i %s -m mark --mark 0x%u -j " TABLE_WIFIDOG_KNOWN, config->gw_interface, FW_MARK_KNOWN);
233    iptables_do_command("-t nat -A " TABLE_WIFIDOG_CLASS " -i %s -m mark --mark 0x%u -j " TABLE_WIFIDOG_LOCKED, config->gw_interface, FW_MARK_LOCKED);
234    iptables_do_command("-t nat -A " TABLE_WIFIDOG_CLASS " -i %s -j " TABLE_WIFIDOG_UNKNOWN, config->gw_interface);
235    iptables_do_command("-t nat -I PREROUTING 1 -i %s -j " TABLE_WIFIDOG_CLASS, config->gw_interface);
236
237    iptables_do_command("-t mangle -N " TABLE_WIFIDOG_OUTGOING);
238    iptables_do_command("-t mangle -I PREROUTING 1 -i %s -j " TABLE_WIFIDOG_OUTGOING, config->gw_interface);
239
240    iptables_do_command("-t mangle -N " TABLE_WIFIDOG_INCOMING);
241    if (config->external_interface) {
242        iptables_do_command("-t mangle -I FORWARD 1 -i %s -j " TABLE_WIFIDOG_INCOMING, config->external_interface);
243    } else {
244        iptables_do_command("-t mangle -I FORWARD 1 -j " TABLE_WIFIDOG_INCOMING);
245    }
246
247    iptables_do_command("-t filter -N " TABLE_WIFIDOG_WIFI_TO_GW);
248    iptables_do_command("-t filter -I INPUT 1 -i %s -j " TABLE_WIFIDOG_WIFI_TO_GW, config->gw_interface);
249
250    UNLOCK_CONFIG();
251   
252    return 1;
253}
254
255/** Remove the firewall rules
256 * This is used when we do a clean shutdown of WiFiDog and when it starts to make
257 * sure there are no rules left over
258 */
259int
260iptables_fw_destroy(void)
261{
262    int rc;
263    s_config *config = config_get_config();
264
265    fw_quiet = 1;
266    iptables_do_command("-t filter -F " TABLE_WIFIDOG_WIFI_TO_GW);
267    iptables_do_command("-t nat -F " TABLE_WIFIDOG_CLASS);
268    iptables_do_command("-t mangle -F " TABLE_WIFIDOG_OUTGOING);
269    iptables_do_command("-t mangle -F " TABLE_WIFIDOG_INCOMING);
270
271    iptables_do_command("-t nat -F " TABLE_WIFIDOG_AUTHSERVERS);
272    iptables_do_command("-t nat -F " TABLE_WIFIDOG_VALIDATE);
273    iptables_do_command("-t nat -F " TABLE_WIFIDOG_UNKNOWN);
274    iptables_do_command("-t nat -F " TABLE_WIFIDOG_KNOWN);
275    iptables_do_command("-t nat -F " TABLE_WIFIDOG_LOCKED);
276    iptables_do_command("-t nat -X " TABLE_WIFIDOG_AUTHSERVERS);
277    iptables_do_command("-t nat -X " TABLE_WIFIDOG_VALIDATE);
278    iptables_do_command("-t nat -X " TABLE_WIFIDOG_UNKNOWN);
279    iptables_do_command("-t nat -X " TABLE_WIFIDOG_KNOWN);
280    iptables_do_command("-t nat -X " TABLE_WIFIDOG_LOCKED);
281
282    /* We loop in case wifidog has crashed and left some unwanted rules,
283     * maybe we shouldn't loop forever, we'll try anyway
284     */
285    rc = 0;
286    while (rc == 0) {
287        rc = iptables_do_command("-t nat -D PREROUTING -i %s -j " TABLE_WIFIDOG_CLASS, config->gw_interface);
288    }
289    iptables_do_command("-t nat -X " TABLE_WIFIDOG_CLASS);
290
291    rc = 0;
292    while (rc == 0) {
293        rc = iptables_do_command("-t filter -D INPUT -i %s -j " TABLE_WIFIDOG_WIFI_TO_GW, config->gw_interface);
294    }
295    iptables_do_command("-t filter -X " TABLE_WIFIDOG_WIFI_TO_GW);
296
297    rc = 0;
298    while (rc == 0) {
299        rc = iptables_do_command("-t mangle -D PREROUTING -i %s -j " TABLE_WIFIDOG_OUTGOING, config->gw_interface);
300    }
301    iptables_do_command("-t mangle -X " TABLE_WIFIDOG_OUTGOING);
302
303    rc = 0;
304    while (rc == 0) {
305        if (config->external_interface) {
306            rc = iptables_do_command("-t mangle -D FORWARD -i %s -j " TABLE_WIFIDOG_INCOMING, config->external_interface);
307        } else {
308            rc = iptables_do_command("-t mangle -D FORWARD -j " TABLE_WIFIDOG_INCOMING);
309        }
310    }
311    iptables_do_command("-t mangle -X " TABLE_WIFIDOG_INCOMING);
312
313    return 1;
314}
315
316/** Set the firewall access for a specific client */
317int
318iptables_fw_access(fw_access_t type, char *ip, char *mac, int tag)
319{
320    int rc;
321
322    fw_quiet = 0;
323
324    switch(type) {
325        case FW_ACCESS_ALLOW:
326            iptables_do_command("-t filter -A " TABLE_WIFIDOG_WIFI_TO_GW " -s %s -j ACCEPT", ip);
327            iptables_do_command("-t mangle -A " TABLE_WIFIDOG_OUTGOING " -s %s -m mac --mac-source %s -j MARK --set-mark %d", ip, mac, tag);
328            rc = iptables_do_command("-t mangle -A " TABLE_WIFIDOG_INCOMING " -d %s -j ACCEPT", ip);
329            break;
330        case FW_ACCESS_DENY:
331            iptables_do_command("-t filter -D " TABLE_WIFIDOG_WIFI_TO_GW " -s %s -j ACCEPT", ip);
332            iptables_do_command("-t mangle -D " TABLE_WIFIDOG_OUTGOING " -s %s -m mac --mac-source %s -j MARK --set-mark %d", ip, mac, tag);
333            rc = iptables_do_command("-t mangle -D " TABLE_WIFIDOG_INCOMING " -d %s -j ACCEPT", ip);
334            break;
335        default:
336            rc = -1;
337            break;
338    }
339
340    return rc;
341}
342
343/** Update the counters of all the clients in the client list */
344int
345iptables_fw_counters_update(void)
346{
347    FILE *output;
348    char *script,
349        ip[16],
350        rc;
351    unsigned long int counter;
352    t_client *p1;
353
354    /* Look for outgoing traffic */
355    asprintf(&script, "%s %s", "iptables", "-v -x -t mangle -L " TABLE_WIFIDOG_OUTGOING);
356    if (!(output = popen(script, "r"))) {
357        debug(LOG_ERR, "popen(): %s", strerror(errno));
358        return -1;
359    }
360    free(script);
361
362    /* skip the first two lines */
363    while (('\n' != fgetc(output)) && !feof(output))
364        ;
365    while (('\n' != fgetc(output)) && !feof(output))
366        ;
367    while (output && !(feof(output))) {
368        rc = fscanf(output, "%*s %lu %*s %*s %*s %*s %*s %s %*s %*s %*s %*s %*s 0x%*u", &counter, ip);
369        if (2 == rc && EOF != rc) {
370            debug(LOG_DEBUG, "Outgoing %s Bytes=%ld", ip, counter);
371            LOCK_CLIENT_LIST();
372            if ((p1 = client_list_find_by_ip(ip))) {
373                if (p1->counters.outgoing < counter) {
374                    p1->counters.outgoing = counter;
375                    p1->counters.last_updated = time(NULL);
376                    debug(LOG_DEBUG, "%s - Updated outgoing counter to %ld bytes from outgoing chain", ip, counter);
377                }
378            } else {
379                debug(LOG_ERR, "Could not find %s in client list", ip);
380            }
381            UNLOCK_CLIENT_LIST();
382        }
383    }
384    pclose(output);
385
386    /* Look for wifi-to-firewall traffic */
387    asprintf(&script, "%s %s", "iptables", "-v -x -t filter -L " TABLE_WIFIDOG_WIFI_TO_GW);
388    if (!(output = popen(script, "r"))) {
389        debug(LOG_ERR, "popen(): %s", strerror(errno));
390        return -1;
391    }
392    free(script);
393
394    /* skip the first two lines */
395    while (('\n' != fgetc(output)) && !feof(output))
396        ;
397    while (('\n' != fgetc(output)) && !feof(output))
398        ;
399    while (output && !(feof(output))) {
400        rc = fscanf(output, "%*s %lu %*s %*s %*s %*s %*s %s %*s %*s %*s %*s %*s 0x%*u", &counter, ip);
401        if (2 == rc && EOF != rc) {
402            debug(LOG_DEBUG, "WIFI2FW %s Bytes=%ld", ip, counter);
403            LOCK_CLIENT_LIST();
404            if ((p1 = client_list_find_by_ip(ip))) {
405                if (p1->counters.togateway < counter) {
406                    p1->counters.togateway = counter;
407                    p1->counters.last_updated = time(NULL);
408                    debug(LOG_DEBUG, "%s - Updated togateway counter to %ld bytes from wifi2fw chain", ip, counter);
409                }
410            } else {
411                debug(LOG_ERR, "Could not find %s in client list", ip);
412            }
413            UNLOCK_CLIENT_LIST();
414        }
415    }
416    pclose(output);
417
418    /* Look for incoming traffic */
419    asprintf(&script, "%s %s", "iptables", "-v -x -t mangle -L " TABLE_WIFIDOG_INCOMING);
420    if (!(output = popen(script, "r"))) {
421        debug(LOG_ERR, "popen(): %s", strerror(errno));
422        return -1;
423    }
424    free(script);
425
426    /* skip the first two lines */
427    while (('\n' != fgetc(output)) && !feof(output))
428        ;
429    while (('\n' != fgetc(output)) && !feof(output))
430        ;
431    while (output && !(feof(output))) {
432        rc = fscanf(output, "%*s %lu %*s %*s %*s %*s %*s %*s %s", &counter, ip);
433        if (2 == rc && EOF != rc) {
434            debug(LOG_DEBUG, "Incoming %s Bytes=%ld", ip, counter);
435            LOCK_CLIENT_LIST();
436            if ((p1 = client_list_find_by_ip(ip))) {
437                if (p1->counters.incoming < counter) {
438                    p1->counters.incoming = counter;
439                    p1->counters.last_updated = time(NULL);
440                    debug(LOG_DEBUG, "%s - Updated counter to %ld bytes", ip, counter);
441                }
442            } else {
443                debug(LOG_ERR, "Could not find %s in client list", ip);
444            }
445            UNLOCK_CLIENT_LIST();
446        }
447    }
448    pclose(output);
449
450    return 1;
451}
Note: See TracBrowser for help on using the browser.