doc/developer/WiFiDog_V2

Version 5 (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

Things tested

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.

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