doc/developer/WiFiDog_V2

Version 9 (modified by papril, 14 years ago)

--

Problems in V1

  • Too many connections to the authentication server (one per connected client every x minutes)
  • Remove ClientTimeout. All decisions regarding a client timeout should be decided by the auth server
  • wdctl restart adds a lot of dirty code for it to work. (if we download the configuration periodically from the auth server, we don't need the wdctl restart anymore)
  • Firewall abstraction was done quickly
  • No QoS support
  • Doesn't verify that the FW modules work (unless you use the shell script wifidog-init provided, Linux only)
  • Protocol is too simplistic, not flexible, proprietary
  • Wifidog doesn't open internet connection when auth server is not available (needs a policy when that happens.... allow all or deny all)

Proposed V2 features

  • Configuration downloaded from auth server on startup (and periodically) including "who should be connected" (so it gets back to where it was on a restart)
  • QoS support
  • Versioned and more standard protocol
  • Better URLs (RESTful philosophy?)

Proposed architecture and ideas

Threads

Main program

  • Launches a "status" or "authserv" thread
  • It waits for the status thread to download the configuration succesfully before initializing networking and denying clients
  • Once initialized, it waits for connections on its port (redirected from firewall) ** Handles traffic the same way as before (forward to login page... gets back a token later, tests the token with the auth server, applies firewall rules)

Status thread

  • Contacts the auth server and sends status every 5 minutes (default)
    • Wifidog uptime
    • System uptime
    • System free memory
    • System load average
    • Other stats...
    • Currently connected clients
      • Incoming bandwidth statistics
      • Outgoing bandwidth statistics
      • QoS settings
      • Specific firewall rules
  • If auth server can't be contacted for some reason, retry faster (30 seconds maybe)
  • If it failed contacting the auth server twice, apply the "what to do when the internet is down" policy (either "allow" or "reject but explain what's going on")
  • When it receives the status from the auth server, it will also receive a list of "who's supposed to be connected". It will do a DIFF of this with what it has to remove/add who should be there

Options on command line

The required values to set: --auth <authserver hostname> Authserver's hostname --lan <interface> LAN interface

The node ID will be (default) the mac address associated to the interface that the default route is using... (parsing netstat -rn). It should sleep and retry to parse in case the networking (pppoe for example) is not up yet.

When wifidog initiates its config it should get options like "this is the url to contact the login page"...: Ideas:

  • global bandwidth settings (max incoming and outgoing)
  • login page url
  • portal url
  • timers (push status, etc.)

Things tested (proof of concept, code, etc.)

JSON for protocol

XML or YAML would have been great, but I tried to use Syck ( http://whytheluckystiff.net/syck/) and it didn't seem trivial to use, it looks like it supports a stream parser, we need something more like a DOM to find values returned.

JSON (with json-c-0.7  http://oss.metaparadigm.com/json-c/) gives us that in C and is quite elegant.

Here's how we can generate JSON:

struct json_object *status_object = json_object_new_object();
json_object_object_add(status_object, "wifidog_version", json_object_new_string(VERSION));
json_object_object_add(status_object, "protocol_version", json_object_new_double(2.0));
json_object_object_add(status_object, "node_id", json_object_new_string(node_id));
json_object_object_add(status_object, "fetch_config", json_object_new_boolean(1));

struct json_object *node_status_object = json_object_new_object();
json_object_object_add(node_status_object, "wifidog_uptime", json_object_new_int(25));
json_object_object_add(node_status_object, "sys_uptime", json_object_new_int(get_sys_uptime()));
json_object_object_add(node_status_object, "sys_loadavg", json_object_new_double(get_sys_loadavg()));
json_object_object_add(node_status_object, "sys_memfree", json_object_new_int(get_sys_memfree()));

char * json = json_object_to_json_string(status_object);

It returns the JSON string.

To parse:

struct json_object * json_object = json_tokener_parse(the_string);
struct json_object * value_json_object = json_object_object_get(json_object, "node_id");
printf("%s\n", json_object_get_string(value_json_object));

This will retrieve the string value of "node_id" at the first level in the tree.

Tests that the required firewall rules work (Linux)

These lines should test all the modules we need:

iptables -A INPUT -m mac --mac-source 00:00:00:00:00:00 -j ACCEPT
iptables -D INPUT -m mac --mac-source 00:00:00:00:00:00 -j ACCEPT
iptables -t nat -A PREROUTING -p tcp --dport 9999 -j REDIRECT --to-ports 2060
iptables -t nat -D PREROUTING -p tcp --dport 9999 -j REDIRECT --to-ports 2060

If this works, we should be good to go. I'm sure there are more to try (MARK if we still use it), but it's a start.

Firewall rules (Linux)

We used to use MARKs to tag what traffic is known, unknown or in validation.

Now, traffic should be simply known or unknown, specific deny rules (for example, nothing but 80) should be sent as "deny acl rules".

We should try to stop using MARKs, as we only have 255 (it wouldn't be trivial or good I think to track "who has which mark", and have a limite of 255).

Here's what I experienced, it's not complete but I'm posting anyway:

/* Everything from LAN to WAN interface */
iptables -t filter -N wd_lan2wan
/* Default rules (example, deny TCP 25 always) */
iptables -t filter -N wd_lan2wan_fromauth
/* Allowed clients (we can track outgoing traffic here */
iptables -t filter -N wd_lan2wan_clients
/* The last REJECT rule (unknown trafic) */
iptables -t filter -N wd_lan2wan_defaults
/* For incoming stats */
iptables -t filter -N wd_incoming_stats

/* Insert the "catch everything from lan to wan" */
iptables -t filter -I FORWARD 1 -i LAN_INTERFACE -o WAN_INTERFACE -j wd_lan2wan
/*
  * Insert the incoming stats rule _ON TOP_ (we'll RETURN in this chain so we still go to the next chains
  *
  * We do not seem to be able to track incoming traffic by mac address, needs the IP
  */
iptables -t filter -I FORWARD 1 -j wd_incoming_stats
/* 1. Global settings (drop going to port 25 for everyone)
iptables -t filter -A wd_lan2wan -j wd_lan2wan_fromauth
/* 2. Allowed clients and specific client rules (and outgoing bandwidth) */
iptables -t filter -A wd_lan2wan -j wd_lan2wan_clients
/* 3. Deny rule */
iptables -t filter -A wd_lan2wan -j wd_lan2wan_defaults

/* We can only do the redirect to local port 2060 in the PREROUTING or OUTPUT chains in the "nat" table */
iptables -t nat -N wd_redirect
iptables -t nat -I PREROUTING 1 -j wd_redirect

/* For pppoe to work properly..... make sure we have this, even though the router will probably have it, we just have to insert ourselves at the right places? */
iptables -t filter -A wd_lan2wan -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

/* Allow DNS please */
iptables -t filter -A wd_lan2wan_defaults -p tcp --dport 53 -j ACCEPT
iptables -t filter -A wd_lan2wan_defaults -p udp --dport 53 -j ACCEPT

/* Allow auth server */
iptables -t filter -A wd_lan2wan_defaults -d AUTHSERV_HOSTNAME -j ACCEPT

/* Base "reject" rule */
iptables -t filter -A wd_lan2wan_defaults -j REJECT

/* Redirect to local wifidog port 2060
iptables -t nat -A wd_redirect -p tcp --dport 80 -j REDIRECT --to-ports 2060

Almost all of this works, but we have to find the right recipe.

When allowing a client:

/* Track incoming stats (BY IP) */
iptables -t filter -A wd_incoming_stats -d 192.168.1.10 -j RETURN

/* Allow MAC address and count outgoing stats (by MAC and IP) */
iptables -t filter -A wd_lan2wan_clients -s 192.168.1.10 --match mac --mac-source 01:02:03:04:05:06 -j ACCEPT

/* Don't redirect anymore please (inserted on TOP) */
iptables -t nat -I wd_redirect 1 -s 192.168.1.10 --match mac --mac-source 01:02:03:04:05 -j ACCEPT

I'd much rather have only 1 rule to add to allow a client, but we need to count the stats. If we could only have TWO rules, that'd be neat, but then we'd have to MARK the traffic at some point I think if we want to avoid that REDIRECT rule. We could do it, but... that assumes (like before) that a specific "magic number" MARK is available (not in use).