chiark / gitweb /
wip
[hippotat.git] / server
diff --git a/server b/server
index 6c229a92bcd930680a209438a6c31678304fb6af..a139e6e240d3d19e24847c54ff77c1698bc52459 100755 (executable)
--- a/server
+++ b/server
@@ -1,11 +1,15 @@
 #!/usr/bin/python3
 
+import twisted
+
 import twisted.web.server import Site
 from twisted.web.resource import Resource
 from twisted.web.server import NOT_DONE_YET
 from twisted.internet import reactor
 
-import configparser
+from optparse import OptionParser
+from configparser import ConfigParser
+from configparser import NoOptionError
 import ipaddress
 
 import collections
@@ -28,27 +32,59 @@ def ipnetwork(input):
     r = ipaddress.IPv6Network(input)
   return r
 
-defcfg = u'''
+defcfg = '''
 [DEFAULT]
 max_batch_down = 65536
 max_queue_time = 10
 max_request_time = 54
 
+[virtual]
+mtu = 1500
+# network
+# [host]
+# [relay]
+
+[server]
+ipif = userv root ipif %(host),%(relay),%(mtu),slip %(network)
+
 [limits]
 max_batch_down = 262144
 max_queue_time = 121
 max_request_time = 121
 '''
 
+class IpifProcessProtocol(twisted.internet.protocol.ProcessProtocol):
+  def __init__(self):
+    self._buffer = b''
+  def connectionMade(self): pass
+  def outReceived(self, data):
+    buffer += data
+    packets = slip_decode(buffer)
+    buffer = packets.pop()
+    for packet in packets:
+      (saddr, daddr) = packet_addrs(packet)
+      route(packet, daddr)
+
+def start_ipif():
+  reactor.spawnProcess(IpifProcessProtocol(),
+                       '/bin/sh',['-c', ipif_command],
+                       childFDs={0:'w', 1:'r', 2:2})
+
+def log_discard(packet, saddr, daddr, why):
+  syslog.syslog(syslog.LOG_DEBUG,
+                'discarded packet %s -> %s (%s)' % (saddr, daddr, why))
+
 def route(packet. daddr):
   try: client = clients[daddr]
   except KeyError: dclient = None
   if dclient is not None:
     dclient.queue_outbound(packet)
-  else if daddr = server or daddr not in network:
+  else if daddr = host or daddr not in network:
     queue_inbound(packet)
+  else if daddr = relay:
+    log_discard(packet, saddr, daddr, 'relay')
   else:
-    syslog.syslog(syslog.LOG_DEBUG, 'no client for %s' % daddr)
+    log_discard(packet, saddr, daddr, 'no client')
 
 class Client():
   def __init__(self, ip, cs):
@@ -69,7 +105,7 @@ class Client():
 
     def process_arriving_data(self, d):
       for packet in slip_decode(d):
-        (saddr, daddr) = ip_64_addrs(packet)
+        (saddr, daddr) = packet_addrs(packet)
         if saddr != self._ip:
           raise ValueError('wrong source address %s' % saddr)
         route(packet, daddr)
@@ -136,13 +172,26 @@ class Client():
 
 def process_cfg():
   global network
-  global ourself
+  global host
+  global relay
+  global ipif_command
 
   network = ipnetwork(cfg.get('virtual','network'))
+  if network.num_addresses < 3 + 2:
+    raise ValueError('network needs at least 2^3 addresses')
+
   try:
-    ourself = cfg.get('virtual','server')
-  except ConfigParser.NoOptionError:
-    ourself = network.hosts().next()
+    host = cfg.get('virtual','host')
+  except NoOptionError:
+    host = network.hosts().next()
+
+  try:
+    relay = cfg.get('virtual','relay')
+  except OptionError:
+    for search in network.hosts():
+      if search = host: continue
+      relay = search
+      break
 
   for cs in cfg.sections():
     if not (':' in cs or '.' in cs): continue
@@ -153,6 +202,12 @@ def process_cfg():
       raise ValueError('multiple client cfg sections for %s' % ci)
     clients[ci] = Client(ci, cs)
 
+  iic_vars = { }
+  for k in ('host','relay','mtu','network'):
+    iic_vars[k] = globals()[k]
+
+  ipif_command = cfg.get('server','ipif', vars=iic_vars)
+
 class FormPage(Resource):
   def render_POST(self, request):
     # find client, update config, etc.
@@ -175,3 +230,19 @@ class FormPage(Resource):
 
     c.process_arriving_data(d)
     c.new_request(request)
+
+def startup():
+  op = OptionParser()
+  op.add_option('-c', '--config', dest='configfile',
+                default='/etc/hippottd/server.conf')
+  global opts
+  (opts, args) = op.parse_args()
+  if len(args): op.error('no non-option arguments please')
+
+  cfg = ConfigParser()
+  cfg.read_string(defcfg)
+  cfg.read_file(opts['configfile'])
+  process_cfg()
+
+  start_ipif()
+  start_http()