--- /dev/null
+One connection with two CHILD_SAs between the hosts and subnet behind the
+gateways <b>moon</b> and <b>sun</b>, respectively, are set up using XFRM
+interfaces.
+<p/>
+The gateways use <b>route-based forwarding</b> with <b>XFRM interfaces</b>, with
+firewall rules to allow traffic to pass. Both peers use connection-defined
+interface IDs so all CHILD_SAs share the same XFRM interface. The IKE daemon
+does not install routes for CHILD_SAs with outbound interface ID, so routes for
+the target subnets are installed statically or via updown events.
+<p/>
+Both gateways use separate interfaces for in- and outbound traffic (which is
+completely optional and mainly for testing purposes, a single interface will
+usually be enough). Gateway <b>moon</b> creates them before initiating the
+connection, while gateway <b>sun</b> dynamically creates the interfaces via
+ike-updown event using the passed unique generated interface IDs.
+<p/>
+Clients <b>alice</b> and <b>venus</b> behind gateway <b>moon</b> ping client
+<b>bob</b> located behind gateway <b>sun</b>.
--- /dev/null
+#!/usr/bin/env python
+
+import vici
+import daemon
+import logging
+from logging.handlers import SysLogHandler
+import subprocess
+
+
+logger = logging.getLogger('updownLogger')
+handler = SysLogHandler(address='/dev/log', facility=SysLogHandler.LOG_DAEMON)
+handler.setFormatter(logging.Formatter('charon-updown: %(message)s'))
+logger.addHandler(handler)
+logger.setLevel(logging.INFO)
+
+
+def handle_interfaces(ike_sa, up):
+ if_id_in = int(ike_sa['if-id-in'], 16)
+ if_id_out = int(ike_sa['if-id-out'], 16)
+ ifname_in = "xfrm-{}-in".format(if_id_in)
+ ifname_out = "xfrm-{}-out".format(if_id_out)
+
+ if up:
+ logger.info("add XFRM interfaces %s and %s", ifname_in, ifname_out)
+ subprocess.call(["/usr/local/libexec/ipsec/xfrmi", "-n", ifname_out,
+ "-i", str(if_id_out), "-d", "eth0"])
+ subprocess.call(["/usr/local/libexec/ipsec/xfrmi", "-n", ifname_in,
+ "-i", str(if_id_in), "-d", "eth0"])
+ subprocess.call(["ip", "link", "set", ifname_out, "up"])
+ subprocess.call(["ip", "link", "set", ifname_in, "up"])
+ subprocess.call(["iptables", "-A", "FORWARD", "-o", ifname_out,
+ "-j", "ACCEPT"])
+ subprocess.call(["iptables", "-A", "FORWARD", "-i", ifname_in,
+ "-j", "ACCEPT"])
+
+ else:
+ logger.info("delete XFRM interfaces %s and %s", ifname_in, ifname_out)
+ subprocess.call(["iptables", "-D", "FORWARD", "-o", ifname_out,
+ "-j", "ACCEPT"])
+ subprocess.call(["iptables", "-D", "FORWARD", "-i", ifname_in,
+ "-j", "ACCEPT"])
+ subprocess.call(["ip", "link", "del", ifname_out])
+ subprocess.call(["ip", "link", "del", ifname_in])
+
+
+def install_routes(ike_sa):
+ if_id_out = int(ike_sa['if-id-out'], 16)
+ ifname_out = "xfrm-{}-out".format(if_id_out)
+ child_sa = next(ike_sa["child-sas"].itervalues())
+
+ for ts in child_sa['remote-ts']:
+ logger.info("add route to %s via %s", ts, ifname_out)
+ subprocess.call(["ip", "route", "add", ts, "dev", ifname_out])
+
+
+# daemonize and run parallel to the IKE daemon
+with daemon.DaemonContext():
+ logger.debug("starting Python updown listener")
+ try:
+ session = vici.Session()
+ ver = session.version()
+ logger.info("connected to {daemon} {version} ({sysname}, {release}, "
+ "{machine})".format(**ver))
+ except:
+ logger.error("failed to get status via vici")
+ sys.exit(1)
+
+ try:
+ for label, event in session.listen(["ike-updown", "child-updown"]):
+ logger.debug("received event: %s %s", label, repr(event))
+
+ name = next((x for x in iter(event) if x != "up"))
+ up = event.get("up", "") == "yes"
+ ike_sa = event[name]
+
+ if label == "ike-updown":
+ handle_interfaces(ike_sa, up)
+
+ elif label == "child-updown" and up:
+ install_routes(ike_sa)
+
+ except IOError:
+ logger.error("daemon disconnected")
+ except:
+ logger.error("exception while listening for events " +
+ repr(sys.exc_info()[1]))