Skip to content

Instantly share code, notes, and snippets.

@gizmoguy
Last active September 28, 2017 20:18
Show Gist options
  • Save gizmoguy/085a13bbdb4aa61675c409c294385f74 to your computer and use it in GitHub Desktop.
Save gizmoguy/085a13bbdb4aa61675c409c294385f74 to your computer and use it in GitHub Desktop.
Reconfigure FAUCET configuration based on hostapd events, more information here: https://faucet-sdn.blogspot.co.nz/2016/07/8021x-authentication-on-faucet-nfv.html
version: 2
acls:
eapoll_offload:
# Send EAP over LAN packets towards hostapd port
- rule:
dl_type: 0x888e # EAP over LAN
actions:
output:
vlan_vid: 10
port: 7
# Allow other protocols
- rule:
actions:
allow: 1
vlans:
untrusted:
name: "untrusted"
description: "client untrusted network"
vid: 10
wan:
name: "wan"
description: "wan vlan"
vid: 20
dps:
at-x230-1:
description: at-x230-1
dp_id: 115621608155
hardware: Allied-Telesis
interfaces:
1:
acl_in: eapoll_offload
max_hosts: 1
name: port1.0.1
native_vlan: untrusted
permanent_learn: true
2:
acl_in: eapoll_offload
max_hosts: 1
name: port1.0.2
native_vlan: untrusted
permanent_learn: true
3:
acl_in: eapoll_offload
max_hosts: 1
name: port1.0.3
native_vlan: untrusted
permanent_learn: true
4:
acl_in: eapoll_offload
max_hosts: 1
name: port1.0.4
native_vlan: untrusted
permanent_learn: true
5:
acl_in: eapoll_offload
max_hosts: 1
name: port1.0.5
native_vlan: untrusted
permanent_learn: true
6:
name: wan
native_vlan: wan
permanent_learn: true
7:
name: nfv
native_vlan: wan
permanent_learn: true
tagged_vlans:
- untrusted
#!/usr/bin/python
import fcntl
import logging
import os
import re
import requests
import subprocess
import time
import yaml
# which vlans shall we configure when we authenticate/deauthenticate clients?
UNTRUSTED_VLAN = 'untrusted'
TRUSTED_VLAN = 'wan'
faucet_config = '/etc/ryu/faucet/faucet.yaml'
prometheus_host = 'http://localhost:9090'
log_file = '/var/log/hostapd-trigger.log'
hostapd_cli = subprocess.Popen(
['/usr/sbin/hostapd_cli', '-p', '/var/run/hostapd'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
shell=True)
fcntl.fcntl(hostapd_cli.stdout, fcntl.F_SETFL, os.O_NONBLOCK)
logging.basicConfig(filename=log_file,level=logging.DEBUG)
def change_port_vlan(dpid, port, vlan):
try:
with open(faucet_config, 'r') as stream:
conf = yaml.safe_load(stream)
except yaml.YAMLError as ex:
logging.error('Error in file %s (%s)', config_file, str(ex))
return None
config_changed = False
for key, dp_config in conf['dps'].iteritems():
if dp_config['dp_id'] != dpid:
continue
if dp_config['interfaces'][port]['native_vlan'] == vlan:
continue
dp_config['interfaces'][port]['native_vlan'] = vlan
config_changed = True
if config_changed:
with open(faucet_config, 'w') as stream:
yaml.dump(conf, stream, default_flow_style=False)
os.system("pkill -HUP ryu-manager")
else:
logging.error('no config changes required in faucet.yaml')
def promote_port(dpid, port):
change_port_vlan(dpid, port, TRUSTED_VLAN)
def demote_port(dpid, port):
change_port_vlan(dpid, port, UNTRUSTED_VLAN)
def mac_lookup(mac):
mac_int = int(mac.replace(':', ''), 16)
api_query = {"query": "learned_macs == %s" % mac_int}
r = requests.get('%s/api/v1/query' % prometheus_host, params=api_query)
interfaces = []
if r.status_code == requests.codes.ok:
response = r.json()
for metric in response['data']['result']:
interface = metric['metric']
interfaces.append({
'dpid': int(interface['dpid'], 16),
'port': int(interface['port']),
'vlan': interface['vlan']
})
return interfaces
while True:
hostapi_log = ''
try:
hostapi_log = hostapd_cli.stdout.readline()
except IOError:
pass
status_change = False
# was a MAC authenticated or de-authenticated
if hostapi_log:
connected_m = re.search('AP-STA-CONNECTED (\S+)', hostapi_log)
disconnected_m = re.search('AP-STA-DISCONNECTED (\S+)', hostapi_log)
if connected_m:
mac = connected_m.group(1)
logging.info('mac %s authenticated', mac)
for interface in mac_lookup(mac):
logging.info('promoting port %s on dpid %s from vlan %s to %s',
interface['port'], interface['dpid'], interface['vlan'], TRUSTED_VLAN)
promote_port(interface['dpid'], interface['port'])
elif disconnected_m:
mac = disconnected_m.group(1)
logging.info('mac %s not authenticated', mac)
for interface in mac_lookup(mac):
logging.info('demoting port %s on dpid %s from vlan %s to %s',
interface['port'], interface['dpid'], interface['vlan'], UNTRUSTED_VLAN)
demote_port(interface['dpid'], interface['port'])
else:
time.sleep(0.5)
hostapd_cli.stdin.write('\n')
interface=enp1s0.10
driver=wired
logger_stdout=-1
logger_stdout_level=2
ieee8021x=1
skip_inactivity_poll=1
ap_max_inactivity=3600
eap_reauth_period=3600
eap_server=0
eapol_version=1
own_ip_addr=127.0.0.1
auth_server_addr=127.0.0.1
auth_server_port=1812
auth_server_shared_secret=testing123
ctrl_interface=/var/run/hostapd
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment