Skip to content

Commit ff90b70

Browse files
committed
Merge branch 'master' into refactor-auth
Conflicts: src/firewall.c
2 parents 088fa72 + f4fd9d3 commit ff90b70

File tree

12 files changed

+315
-4
lines changed

12 files changed

+315
-4
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ WiFiDog 1.2.1:
1212
* Enhance safe_malloc to always zero memory (#155)
1313
* Fix segfault related pstr_t's buffer (#149)
1414
* Add open the firewall if no auth servers can be reached (optional, #90)
15+
* Add configurable ARP table location (-a switch). Useful for mock ARP tables during
16+
load tests (#166)
1517
* Various other fixes and minor improvements, see
1618
https://github.com/wifidog/wifidog-gateway/compare/1.2.0...1.2.1
1719

contrib/load-tester/README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
## Wifidog Load Tester ##
2+
3+
* generate\_interfaces.sh: Sets up pseudo interfaces and creates a fake ARP
4+
table in /tmp/arp. Make sure that the IP addresses used do not collide with
5+
your real network
6+
* mock\_auth.py: A mock auth server. Randomly grants or denies access
7+
* fire\_requests.py: Hammers Wifidog with requests. talks to wifidog, never
8+
to the auth server.
9+
* run.sh: Ties it all together. Make sure to run as root.
10+
11+
Once you think the script has run long enough, kill run.sh and look at
12+
valgrind.log. You have to clean up after the script yourself, e.g. kill
13+
mock\_auth.py separately and run **./generate_interfaces.sh stop**
14+
15+
### Note on ARP tables ###
16+
17+
Although generate\_interfaces.sh will add random MAC addresses
18+
to the virtual interfaces, these will not show up in the local
19+
ARP table. The ARP table only lists remote systems. Even with the
20+
**publish** flag set, the ARP table entry will list an all-zero MAC
21+
address. For this reason, the script generates a fake ARP table
22+
and passes it to wifidog with the **-a** switch.
23+
24+
The macvlan type virtual interface used by generate\_interfaces.sh
25+
is still useful if you load-test a remote wifidog instance. In this case,
26+
the remote ARP table will (hopefully) be populated correctly.
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#!/usr/bin/env python2
2+
# -*- coding: utf -*-
3+
4+
5+
import socket
6+
import fcntl
7+
import struct
8+
9+
import uuid
10+
import sys
11+
import random
12+
13+
import argparse
14+
import functools
15+
16+
17+
from multiprocessing import Pool
18+
19+
from httplib import HTTPConnection
20+
21+
PORT = "2060"
22+
23+
24+
def main(targetIF, prefix, maxI):
25+
target = get_ip_address(targetIF)
26+
for i in xrange(int(maxI)):
27+
main_single(target, prefix, i)
28+
29+
def main_single(target, prefix, i):
30+
source = get_ip_address(prefix + str(i))
31+
# source_address requires python 2.7
32+
# urllib2 does not nicely expose source_address, so use
33+
# lower-level API
34+
conn = HTTPConnection(target, PORT, timeout=10, source_address=(source, 0))
35+
conn.connect()
36+
conn.request("GET", "/")
37+
try:
38+
resp = conn.getresponse()
39+
resp.read()
40+
except:
41+
pass
42+
conn = HTTPConnection(target, PORT, timeout=10, source_address=(source, 0))
43+
conn.connect()
44+
token = str(uuid.uuid4())
45+
conn.request("GET", "/wifidog/auth?token=" + token )
46+
try:
47+
resp = conn.getresponse()
48+
# this causes wifidog to ask our mock auth server if the token is
49+
# correct
50+
resp.read()
51+
except:
52+
pass
53+
# log out sometimes
54+
if random.choice([True, False, False]):
55+
conn = HTTPConnection(target, PORT, timeout=10, source_address=(source, 0))
56+
conn.connect()
57+
conn.request("GET", "/wifidog/auth?logout=1&token=" + token)
58+
try:
59+
resp = conn.getresponse()
60+
resp.read()
61+
except:
62+
pass
63+
64+
65+
# http://code.activestate.com/recipes/439094-get-the-ip-address-associated-with-a-network-inter/
66+
def get_ip_address(ifname):
67+
print "ifname: %s" % ifname
68+
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
69+
return socket.inet_ntoa(fcntl.ioctl(
70+
s.fileno(),
71+
0x8915, # SIOCGIFADDR
72+
struct.pack('256s', ifname[:15])
73+
)[20:24])
74+
75+
if __name__ == "__main__":
76+
77+
parser = argparse.ArgumentParser(description='Hammer a wifidog instance with requests')
78+
parser.add_argument('--target-interface', required=True,
79+
help='Interface where Wifidog is listening')
80+
parser.add_argument('--source-interface-prefix', required=True,
81+
help='Prefix of the virtual interfaces from which Wifidog is exercised.')
82+
parser.add_argument('--source-interface-count', required=True,
83+
help='Number of virtual interfaces, where interface is prefix+index')
84+
parser.add_argument('--process-count', required=True,
85+
help='How many processes to run')
86+
87+
args = parser.parse_args()
88+
89+
target = get_ip_address(args.target_interface)
90+
p = Pool(int(args.process_count))
91+
partial = functools.partial(main_single, target, args.source_interface_prefix)
92+
while True:
93+
p.map(partial, list(xrange(int(args.source_interface_count))))
94+
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#/bin/bash
2+
3+
IF="eth0"
4+
# for whatever reason, if you use eth0.x as your virtual interface name,
5+
# dhclient will lose the carrier
6+
PREFIX="mac"
7+
# don't touch resolv.conf
8+
DHCP="dhcpcd --waitip -C resolv.conf"
9+
#DHCP="dhclient -v"
10+
NET="10.0.10."
11+
ARPTABLE="/tmp/arp"
12+
13+
COUNT=$2
14+
15+
function start() {
16+
echo "IP address HW type Flags HW address Mask Device" > $ARPTABLE
17+
# Add internal address for GW
18+
sudo ip link add internal0 link $IF type macvlan mode bridge || exit 1
19+
sudo ip addr add ${NET}254 dev internal0
20+
sudo ip link set internal0 up || exit 2
21+
for i in `seq 0 $COUNT`; do
22+
echo "Add link $i"
23+
# sudo ip link add virtual0 link eth0 type macvlan mode bridge
24+
sudo ip link add ${PREFIX}${i} link $IF type macvlan mode bridge || exit 1
25+
echo "Assigning temporary IP address"
26+
# use link-local address. Ideally, we would check if the
27+
# address is already aissgned
28+
sudo ip addr add ${NET}$(($i + 1)) dev ${PREFIX}${i}
29+
MAC=`ip link show ${PREFIX}${i} | grep ether | sed -e 's,.*link/ether ,,' -e 's, brd.*,,'`
30+
echo "Marking link $i up"
31+
sudo ip link set ${PREFIX}${i} up || exit 2
32+
#echo "Acquiring IP for link $i"
33+
# when using DHCP, the interface would immediately
34+
# go down?
35+
#sudo $DHCP ${PREFIX}${i} || exit 3
36+
echo " ${NET}$(($i + 1)) 0x1 0x2 $MAC * ${PREFIX}${i}" >> $ARPTABLE
37+
done
38+
}
39+
40+
function stop() {
41+
sudo ip link del internal0 2>/dev/null || true
42+
for i in `seq 0 $COUNT`; do
43+
echo "Deleting link $i"
44+
sudo ip link del ${PREFIX}${i} 2>/dev/null || true
45+
done
46+
}
47+
48+
49+
50+
if [[ x$1 == x"start" ]]; then
51+
stop
52+
start
53+
elif [[ x$1 == x"stop" ]]; then
54+
stop
55+
else
56+
echo "Unknown command"
57+
fi

contrib/load-tester/mock_auth.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf -*-
3+
4+
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
5+
6+
import random
7+
8+
def main():
9+
server = HTTPServer(('', 8080), AuthHandler)
10+
server.serve_forever()
11+
12+
13+
class AuthHandler(BaseHTTPRequestHandler):
14+
def do_GET(self):
15+
self.send_response(200)
16+
self.send_header('Content-type', 'text/html')
17+
self.end_headers()
18+
if "ping" in self.path:
19+
self.wfile.write("Pong\n")
20+
else:
21+
self.wfile.write("Auth: %s\n" % random.choice([0,1]))
22+
return
23+
24+
if __name__ == "__main__":
25+
main()

contrib/load-tester/run.sh

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/bin/bash
2+
3+
# On Ubuntu, you may want this:
4+
# echo "core.%e.%p" > /proc/sys/kernel/core_pattern
5+
# http://stackoverflow.com/a/18368068
6+
7+
echo "core.%e.%p" > /proc/sys/kernel/core_pattern
8+
ulimit -c unlimited
9+
10+
COUNT=40
11+
echo "Make sure to configure GatewayInterface in wifidog_mock.conf"
12+
13+
./generate_interfaces.sh start $COUNT || exit 1
14+
15+
./mock_auth.py &
16+
MA_PID="$!"
17+
18+
# trace-children is necessary because of the libtool wrapper -.-
19+
#sudo valgrind --leak-check=full --trace-children=yes --trace-children-skip=/bin/sh \
20+
# --log-file=valgrind.log ../../src/wifidog -d 7 -f -c wifidog-mock.conf 2> wifidog.log &
21+
22+
# for -fsanitize=address
23+
export ASAN_OPTIONS=check_initialization_order=1
24+
export ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer-3.5
25+
26+
../../src/wifidog -d 7 -f -c wifidog-mock.conf -a /tmp/arp 2> wifidog.log &
27+
WD_PID="$!"
28+
29+
IF=`grep GatewayInterface wifidog-mock.conf | cut -f 2 -d ' '`
30+
31+
echo "Waiting for wifidog to come up"
32+
33+
sleep 10
34+
35+
./fire_requests.py \
36+
--target-interface $IF \
37+
--source-interface-prefix mac \
38+
--source-interface-count $COUNT \
39+
--process-count 2
40+
41+
#./generate_interfaces.sh stop
42+
43+
function cleanup() {
44+
45+
kill $MA_PID
46+
kill $WD_PID
47+
./generate_interfaces.sh stop $COUNT
48+
49+
}
50+
51+
trap cleanup EXIT

contrib/load-tester/wdctl.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/bin/bash
2+
3+
while true; do
4+
../../src/wdctl status
5+
sleep 5
6+
done
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
ExternalInterface eth0
2+
GatewayInterface internal0
3+
4+
HtmlMessageFile ../../wifidog-msg.html
5+
6+
7+
AuthServer {
8+
Hostname localhost
9+
HTTPPort 8080
10+
Path /
11+
}
12+
13+
ClientTimeout 5
14+
15+
FirewallRuleSet global {
16+
}
17+
18+
FirewallRuleSet validating-users {
19+
FirewallRule allow to 0.0.0.0/0
20+
}
21+
22+
FirewallRuleSet known-users {
23+
FirewallRule allow to 0.0.0.0/0
24+
}
25+
26+
FirewallRuleSet unknown-users {
27+
FirewallRule allow udp port 53
28+
FirewallRule allow tcp port 53
29+
FirewallRule allow udp port 67
30+
FirewallRule allow tcp port 67
31+
}
32+
33+
FirewallRuleSet locked-users {
34+
FirewallRule block to 0.0.0.0/0
35+
}

src/commandline.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ parse_commandline(int argc, char **argv)
9191
i = 0;
9292
restartargv[i++] = safe_strdup(argv[0]);
9393

94-
while (-1 != (c = getopt(argc, argv, "c:hfd:sw:vx:i:"))) {
94+
while (-1 != (c = getopt(argc, argv, "c:hfd:sw:vx:i:a:"))) {
95+
9596
skiponrestart = 0;
9697

9798
switch (c) {
@@ -152,6 +153,15 @@ parse_commandline(int argc, char **argv)
152153
}
153154
break;
154155

156+
case 'a':
157+
if (optarg) {
158+
free(config->arp_table_path);
159+
config->arp_table_path = safe_strdup(optarg);
160+
} else {
161+
fprintf(stdout, "You must supply the path to the ARP table with -a!");
162+
exit(1);
163+
}
164+
break;
155165
default:
156166
usage();
157167
exit(1);

src/conf.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ config_init(void)
196196
config.ssl_certs = safe_strdup(DEFAULT_AUTHSERVSSLCERTPATH);
197197
config.ssl_verify = DEFAULT_AUTHSERVSSLPEERVER;
198198
config.ssl_cipher_list = NULL;
199+
config.arp_table_path = safe_strdup(DEFAULT_ARPTABLE);
199200
}
200201

201202
/**

0 commit comments

Comments
 (0)