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