Permalink
Browse files

frontend: added frontend code to support arp_listener management task…

…. this listens for arp messages from backend and updates the firewall automatically when a host comes up, using partial updates
  • Loading branch information...
1 parent 21dfd89 commit 1e43062925a0e8ae2f9917a6fa8fc9633f9014ae @micolous committed Mar 10, 2013
@@ -421,7 +421,16 @@ def add_host(self, uid, mac, ip):
# add ip ipset entry
ipset('add', ip_set_name(uid), ip)
-
+
+ @dbus.service.method(dbus_interface=DBUS_INTERFACE, in_signature='sss', out_signature='')
+ def del_host(self, uid, mac, ip):
+ """De-registers a host as belonging to a certain user id."""
+ # add ip+mac ipset entry
+ ipset('del', ipmac_set_name(uid), ','.join([ip, mac]))
+
+ # add ip ipset entry
+ ipset('del', ip_set_name(uid), ip)
+
@dbus.service.method(dbus_interface=DBUS_INTERFACE, in_signature='s', out_signature='')
def flush_hosts(self, uid):
"""Removes all hosts for a user."""
@@ -0,0 +1,93 @@
+"""
+tollgate management command: listen arp
+Copyright 2008-2013 Michael Farrell
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""
+
+from django.core.management.base import BaseCommand, CommandError
+from tollgate.frontend.tollgate_controller_api import TollgateController
+from tollgate.frontend.models import apply_ip4portforwards, utcnow, NetworkHost
+import dbus.glib, glib
+from django.core.exceptions import ObjectDoesNotExist
+
+class Command(BaseCommand):
+ args = ''
+ help = """\
+Listens for ARP events from the tollgate backend and synchronises iptables
+appropriately.
+"""
+
+ def arp_handler(self, mac, ip):
+ mac = mac.replace(':', '')
+
+ assert len(mac) == 12, 'MAC address length must be 12'
+ assert 7 <= len(ip) <= 15, 'IP address length must be between 7 and 15'
+
+ #print 'arp: %s, %s' % (mac, ip)
+
+ # check to see if this host exists
+ try:
+ host = NetworkHost.objects.get(mac_address__iexact=mac)
+
+ if not host.online or host.ip_address != ip:
+ # host was not marked as online, make it so.
+ old_ip = host.ip_address
+
+ # does exist, online the host
+ host.online = True
+ host.ip_address = ip
+ host.save()
+
+ if old_ip != ip:
+ # ip change
+ self.portal.disconnect(host.user_profile.user_id, mac, ip)
+
+ # connect it in API
+ self.portal.connect(host.user_profile.user_id, mac, ip)
+
+ # connected!
+ # FIXME: does not apply port forwards straight away.
+ except ObjectDoesNotExist:
+ host = NetworkHost.objects.create(
+ mac_address=mac,
+ computer_name='',
+ first_connection=utcnow(),
+ online=True,
+ ip_address=ip
+ )
+
+ # check that nothing else is on that ip actively
+ recycled_ip_hosts = NetworkHost.objects.filter(ip_address__exact=ip, online=True).exclude(mac_address__iexact=mac)
+
+ # immediately mark as offline so nothing else touches these
+ recycled_ip_hosts.update(online=False)
+
+ # find user/macaddress combos to mark as offline
+ users_to_refresh = set(recycled_ip_hosts.values_list('user_profile__user_id', 'mac_address'))
+
+ # offline the hosts in the firewall
+ for user_id, mac_address in users_to_refresh:
+ self.portal.disconnect(user_id, mac_address, ip)
+
+ # FIXME: does not remove port forwards for old hosts
+
+ def handle(self, *args, **options):
+ print 'ARP listener running...'
+ self.portal = TollgateController(mainloop=True)
+ self.portal.register_arp_packet(self.arp_handler)
+
+ loop = glib.MainLoop()
+ loop.run()
+
@@ -30,7 +30,7 @@ def handle(self, *args, **options):
# refresh information about networkhosts and quota for crontab.
portal = get_portalapi()
try:
- refresh_networkhost(portal)
+ refresh_networkhost_quick(portal)
except Exception, ex:
print "Failed refreshing network hosts"
print ex
@@ -46,12 +46,28 @@ class NotAConsoleException(Exception):
class TollgateController:
- def __init__(self):
+ def __init__(self, mainloop=False):
if not dbus:
return
+
+ if mainloop:
+ # mainloop requested, register with glib
+ from dbus.mainloop.glib import DBusGMainLoop
+ DBusGMainLoop(set_as_default=True)
+
bus = dbus.SystemBus()
remote_object = bus.get_object(DBUS_SERVICE, DBUS_PATH)
self.__interface = dbus.Interface(remote_object, DBUS_INTERFACE)
+
+
+ def register_arp_packet(self, callback):
+ """
+ Registers an event listener for ARP packets recieved by the backend on
+ the internal network interface.
+ """
+ if not dbus:
+ return
+ self.__interface.connect_to_signal(signal_name='on_arp_packet', handler_function=callback)
def connect(self, user_id, mac_address, ip):
if not dbus:

0 comments on commit 1e43062

Please sign in to comment.