Commit 9dc1707e authored by Julien Muchembled's avatar Julien Muchembled

Our fork of Babeld can now override RTA_(PREF)SRC locally

This simplify network configuration a lot, and on recent kernels, this fixes
wrong source address for extra interfaces that already have a public IP.
parent cfb2c159
......@@ -7,7 +7,7 @@ Standards-Version: 3.9.1
Package: re6stnet
Architecture: all
Depends: ${misc:Depends}, python (>= 2.6.6-3), python (<< 2.8), python-argparse, python-openssl (>= 0.13), openvpn (>= 2.1.3), babeld (= 1.5.1-nxd2), iproute2 | iproute, openssl
Depends: ${misc:Depends}, python (>= 2.6.6-3), python (<< 2.8), python-argparse, python-openssl (>= 0.13), openvpn (>= 2.1.3), babeld (= 1.6.0~1.g8950d3b-nxd1), iproute2 | iproute, openssl
Recommends: ${python:Recommends}, logrotate
Suggests: ndisc6
Description: resilient, scalable, IPv6 network application
......@@ -28,16 +28,18 @@ def _get_all_route_data():
for line in ipdata.split("\n"):
if line == "":
match = re.match(r'(?:(unicast|local|broadcast|multicast|throw|' +
r'unreachable|prohibit|blackhole|nat) )?' +
r'(\S+)(?: via (\S+))? dev (\S+).*(?: metric (\d+))?', line)
# PATCH: parse 'from'
match = re.match('(?:(unicast|local|broadcast|multicast|throw|'
r'unreachable|prohibit|blackhole|nat) )?(\S+)(?: from (\S+))?'
r'(?: via (\S+))? dev (\S+).*(?: metric (\d+))?', line)
if not match:
raise RuntimeError("Invalid output from `ip route': `%s'" % line)
tipe = or "unicast"
prefix =
nexthop =
interface = ifdata[]
metric =
#src =
nexthop =
interface = ifdata[]
metric =
if prefix == "default" or'/0$', prefix):
prefix = None
prefix_len = 0
......@@ -5,5 +5,5 @@ pp 1194 tcp
ca ca.crt
cert m1/cert.crt
key m1/cert.key
table 0
neighbour 6/16
......@@ -5,4 +5,4 @@ pp 1194 tcp
ca ca.crt
cert m2/cert.crt
key m2/cert.key
table 0
......@@ -5,4 +5,3 @@ pp 1194 tcp
ca ca.crt
cert m3/cert.crt
key m3/cert.key
table 0
......@@ -5,4 +5,4 @@ pp 1194 tcp
ca ca.crt
cert m4/cert.crt
key m4/cert.key
table 0
......@@ -4,6 +4,6 @@ state m5/
ca ca.crt
cert m5/cert.crt
key m5/cert.key
table 0
client-count 0
max-clients 0
......@@ -5,7 +5,7 @@ pp 1194 tcp
ca ca.crt
cert m6/cert.crt
key m6/cert.key
table 0
# TODO: Run a DHCPv4 client on machine9. Unfortunately, isc-dhcp-client 4.2.4
# fails with "Bind socket to interface: No such device"
daemon "exec dnsmasq -d8 - -i $re6stnet_iface -F,,,infinite -F ${re6stnet_subnet%/*},ra-only,${re6stnet_subnet#*/},1d -O option:router, -l m6/dnsmasq.leases"
......@@ -5,4 +5,4 @@ pp 1194 tcp
ca ca.crt
cert m7/cert.crt
key m7/cert.key
table 0
......@@ -4,5 +4,5 @@ state m8/
ca ca.crt
cert m8/cert.crt
key m8/cert.key
table 0
......@@ -31,7 +31,7 @@ If you already have IPv6 connectivity by autoconfiguration and still want to
use it for communications that are unrelated to this network, then:
- your kernel must support source address based routing (because you can't
use ``--table 0`` option).
use ``--default`` option).
- you must set ``net.ipv6.conf.<iface>.accept_ra`` sysctl to value 2 and
trigger SLAAC with ``rdisc6 <iface>`` to restore the default route if the
kernel removed while enabling forwarding.
......@@ -70,7 +70,7 @@ Important note about NetworkManager
It is required to configure properly every connection defined in NetworkManager
because default settings are wrong and conflict with re6st:
- If re6st routes all your IPv6 traffic, using ``--table 0`` option, then make
- If re6st routes all your IPv6 traffic, using ``--default`` option, then make
sure to disable IPv6 in NetworkManager.
- Otherwise, the following options must be set in [ipv6] section::
......@@ -59,10 +59,9 @@ def client(iface, address_list, encrypt, *args, **kw):
return openvpn(iface, encrypt, *remote, **kw)
def router(subnet, hello_interval, table, log_path, state_path, pidfile,
control_socket, default, *args, **kw):
s = utils.ipFromBin(subnet)
n = len(subnet)
def router(ip, src, hello_interval, log_path, state_path,
pidfile, control_socket, default, *args, **kw):
ip, n = ip
cmd = ['babeld',
'-h', str(hello_interval),
'-H', str(hello_interval),
......@@ -72,12 +71,13 @@ def router(subnet, hello_interval, table, log_path, state_path, pidfile,
'-C', 'default ' + default,
'-C', 'redistribute local deny',
'-C', 'redistribute ip %s/%u eq %u' % (s, n, n),
'-C', 'redistribute deny']
if table:
cmd += '-t%u' % table, '-T%u' % table
cmd[-2:-2] = '-C', 'redistribute ip ::/0 eq 0'
'-C', 'redistribute ip %s/%s eq %s' % (ip, n, n)]
if src:
cmd += '-C', 'install ip ::/0 eq 0 src-prefix ' + src
elif src is None:
cmd += '-C', 'redistribute ip ::/0 eq 0'
cmd += ('-C', 'redistribute deny',
'-C', 'install pref-src ' + ip)
if control_socket:
cmd += '-R', '%s' % control_socket
cmd += args
import atexit, errno, logging, os, shutil, signal
import socket, subprocess, sys, time, threading
import socket, struct, subprocess, sys, time, threading
from collections import deque
from functools import partial
from re6st import plib, tunnel, utils, version, x509
......@@ -58,11 +58,13 @@ def getConfig():
_ = parser.add_argument_group('routing').add_argument
_('-B', dest='babel_args', metavar='ARG', action='append', default=[],
help="Extra arguments to forward to Babel.")
_('--table', type=int, default=42,
help="Use given table id. Set 0 to use the main table, if you want to"
" access internet via this network (in this case, make sure you"
" don't already have a default route). Don't use this option with"
" --gateway (main table is automatically used).")
_('-D', '--default', action='store_true',
help="Access internet via this network (in this case, make sure you"
" don't already have a default route), or if your kernel was"
" compiled without support for source address based routing"
" (CONFIG_IPV6_SUBTREES). Meaningless with --gateway.")
_('--table', type=int, choices=(0,),
help="DEPRECATED: Use --default instead of --table=0")
_('--gateway', action='store_true',
help="Act as a gateway for this network (the default route will be"
" exported). Do never use it if you don't know what it means.")
......@@ -140,9 +142,15 @@ def main():
if config.max_clients is None:
config.max_clients = cache.max_clients
if config.table is not None:
logging.warning("--table option is deprecated: use --default instead")
config.default = True
if config.default and config.gateway:
sys.exit("error: conflicting options --default and --gateway")
if 'none' in config.disable_proto:
config.disable_proto = ()
if not config.table:
if config.default:
# Make sure we won't tunnel over re6st.
config.disable_proto = tuple(set(('tcp6', 'udp6')).union(
......@@ -236,7 +244,6 @@ def main():
os.environ['re6stnet_iface'] = config.main_interface
os.environ['re6stnet_subnet'] = my_subnet
os.environ['re6stnet_network'] = my_network
my_ip += '/%s' % len(subnet)
# Init db and tunnels
config.babel_args += server_tunnels
......@@ -257,22 +264,16 @@ def main():
# Source address selection is defined by RFC 6724, and in most
# applications, it usually works thanks to rule 5 (prefer outgoing
# interface). But here, it rarely applies because we use several
# interfaces to connect to a re6st network.
# Rule 7 is little strange because it prefers temporary addresses
# over IP with a longer matching prefix (rule 8, which is not even
# mandatory).
# So only rule 6 can make the difference, i.e. prefer same label.
# The value of the label does not matter, except that it must be
# different from ::/0's (normally equal to 1).
# XXX: This does not work with extra interfaces that already have
# an public IP so Babel must be changed to set a source
# address on routes it installs.
ip('addrlabel', 'prefix', my_network, 'label', '99')
if os.uname()[2] < '2.6.40': # BBB
logging.warning("Fallback to ip-addrlabel because Linux < 3.0"
" does not support RTA_PREFSRC for ipv6. Note however that"
" this workaround does not work with extra interfaces that"
" already have a public IP")
ip('addrlabel', 'prefix', my_network, 'label', '99')
# No need to tell babeld not to set a preferred source IP in
# installed routes. The kernel will silently discard the option.
R = {}
# prepare persistent interfaces
if config.client:
address_list = [x for x in utils.parse_address(config.client)
if x[2] not in config.disable_proto]
......@@ -296,7 +297,8 @@ def main():
R[r] = partial(tunnel_manager.handleServerEvent, r)
ip('addr', my_ip, 'dev', config.main_interface)
ip('addr', my_ip + '/%s' % len(subnet),
'dev', config.main_interface)
if_rt = ['ip', '-6', 'route', 'del',
'fe80::/64', 'dev', config.main_interface]
if config.main_interface == 'lo':
......@@ -305,30 +307,13 @@ def main():
if_rt[4] = my_subnet
x = [my_network]
if config.gateway:
config.table = 0
elif config.table:
x += 'table', str(config.table)
ip('rule', 'from', *x)
except EnvironmentError:
logging.error("It seems that your kernel was compiled"
" without support for source address based routing"
" (CONFIG_IPV6_SUBTREES). Consider using --table=0"
" option if you can't change your kernel.")
ip('rule', 'to', *x)
if_rt += x[1:]
call(if_rt[:3] + ['add', 'proto', 'static'] + if_rt[4:])
if config.default:
def check_no_default_route():
for route in call(('ip', '-6', 'route', 'show',
if ' proto 42 ' not in route:
sys.exit("Detected default route (%s)"
" whereas you specified --table=0."
" whereas you specified --default."
" Fix your configuration." % route)
def check_no_default_route_thread():
......@@ -347,10 +332,13 @@ def main():
t = threading.Thread(target=check_no_default_route_thread)
t.daemon = True
ip('route', 'unreachable', *x)
ip('route', 'unreachable', my_network)
config.babel_args += config.iface_list
cleanup.append(plib.router(subnet, cache.hello, config.table,
cleanup.append(plib.router((my_ip, len(subnet)),
None if config.gateway else
'' if config.default else
my_network, cache.hello,
os.path.join(config.log, 'babeld.log'),
os.path.join(config.state, 'babeld.state'),
os.path.join(, ''),
......@@ -8,7 +8,7 @@ Release: %(set %ver; echo ${1#*-})
License: GPLv2+
Group: Applications/Internet
BuildArch: noarch
Requires: babeld = 1.5.1-nxd2
Requires: babeld = 1.6-git0.8950d3b.nxd1
Requires: iproute
Requires: openssl
Requires: openvpn
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment