Commit abf8211e authored by Alain Takoudjou's avatar Alain Takoudjou

Merge branch 'proto' into re6st-slapos

parents 47d1bb85 40d4e496
...@@ -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
#!/usr/bin/python #!/usr/bin/python
import math, nemu, os, signal, socket, subprocess, sys, time, weakref import math, nemu, os, re, signal, socket, subprocess, sys, time, weakref
from collections import defaultdict from collections import defaultdict
IPTABLES = 'iptables' IPTABLES = 'iptables'
SCREEN = 'screen' SCREEN = 'screen'
...@@ -38,6 +38,8 @@ def _add_interface(node, iface): ...@@ -38,6 +38,8 @@ def _add_interface(node, iface):
return Node__add_interface(node, iface) return Node__add_interface(node, iface)
nemu.Node._add_interface = _add_interface nemu.Node._add_interface = _add_interface
execfile("fixnemu.py")
# create nodes # create nodes
for name in """internet=I registry=R for name in """internet=I registry=R
gateway1=g1 machine1=1 machine2=2 gateway1=g1 machine1=1 machine2=2
...@@ -48,6 +50,8 @@ for name in """internet=I registry=R ...@@ -48,6 +50,8 @@ for name in """internet=I registry=R
globals()[name] = node = nemu.Node() globals()[name] = node = nemu.Node()
node.name = name node.name = name
node.short = short node.short = short
node.Popen(('sysctl', '-q',
'net.ipv4.icmp_echo_ignore_broadcasts=0')).wait()
node._screen = node.Popen((SCREEN, '-DmS', name)) node._screen = node.Popen((SCREEN, '-DmS', name))
node.screen = (lambda name: lambda *cmd: node.screen = (lambda name: lambda *cmd:
subprocess.call([SCREEN, '-r', name, '-X', 'eval'] + map( subprocess.call([SCREEN, '-r', name, '-X', 'eval'] + map(
...@@ -235,7 +239,12 @@ def node_by_ll(addr): ...@@ -235,7 +239,12 @@ def node_by_ll(addr):
for a in a: for a in a:
p = a['prefix_len'] p = a['prefix_len']
a = a['address'] a = a['address']
if a.startswith('2001:db8:'): if a.startswith('10.'):
if a.startswith('10.42.'):
assert not p % 8
_ll[socket.inet_ntoa(socket.inet_aton(
a)[:p/8].ljust(4, '\0'))] = n, t
elif a.startswith('2001:db8:'):
assert not p % 8 assert not p % 8
a = socket.inet_ntop(socket.AF_INET6, a = socket.inet_ntop(socket.AF_INET6,
socket.inet_pton(socket.AF_INET6, socket.inet_pton(socket.AF_INET6,
...@@ -245,17 +254,19 @@ def node_by_ll(addr): ...@@ -245,17 +254,19 @@ def node_by_ll(addr):
_ll[a] = n, t _ll[a] = n, t
return _ll[addr] return _ll[addr]
def route_svg(z=4): def route_svg(ipv4, z = 4, default = type('', (), {'short': None})):
graph = {} graph = {}
for n in nodes: for n in nodes:
g = graph[n] = defaultdict(list) g = graph[n] = defaultdict(list)
for r in n.get_routes(): for r in n.get_routes():
if r.prefix and r.prefix.startswith('2001:db8:'): if (r.prefix and r.prefix.startswith('10.42.') if ipv4 else
r.prefix is None or r.prefix.startswith('2001:db8:')):
try: try:
g[node_by_ll(r.nexthop)].append(node_by_ll(r.prefix)[0]) g[node_by_ll(r.nexthop)].append(
node_by_ll(r.prefix)[0] if r.prefix else default)
except KeyError: except KeyError:
pass pass
gv = ["digraph { splines = true; edge[color=grey, labelangle=0, arrowhead=dot];"] gv = ["digraph { splines = true; edge[color=grey, labelangle=0];"]
N = len(nodes) N = len(nodes)
a = 2 * math.pi / N a = 2 * math.pi / N
edges = set() edges = set()
...@@ -268,17 +279,23 @@ def route_svg(z=4): ...@@ -268,17 +279,23 @@ def route_svg(z=4):
l.append((min(j, N - j), p, r)) l.append((min(j, N - j), p, r))
for j, (l, (p, t), r) in enumerate(sorted(l)): for j, (l, (p, t), r) in enumerate(sorted(l)):
l = [] l = []
arrowhead = 'odot'
for r in sorted(r.short for r in r): for r in sorted(r.short for r in r):
if r == p.short: if r:
r = '<font color="grey">%s</font>' % r if r == p.short:
l.append(r) r = '<font color="grey">%s</font>' % r
l.append(r)
else:
arrowhead = 'dot'
if (n.name, p.name) in edges: if (n.name, p.name) in edges:
r = 'penwidth=0' r = 'penwidth=0'
else: else:
edges.add((p.name, n.name)) edges.add((p.name, n.name))
r = 'style=solid' if t else 'style=dashed' r = 'style=solid' if t else 'style=dashed'
gv.append('%s -> %s [labeldistance=%u, headlabel=<%s>, %s];' gv.append(
% (p.name, n.name, 1.5 * math.sqrt(j) + 2, ','.join(l), r)) '%s -> %s [labeldistance=%u, headlabel=<%s>, arrowhead=%s, %s];'
% (p.name, n.name, 1.5 * math.sqrt(j) + 2, ','.join(l),
arrowhead, r))
gv.append('}\n') gv.append('}\n')
return subprocess.Popen(('neato', '-Tsvg'), return subprocess.Popen(('neato', '-Tsvg'),
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
...@@ -288,13 +305,25 @@ if len(sys.argv) > 1: ...@@ -288,13 +305,25 @@ if len(sys.argv) > 1:
import SimpleHTTPServer, SocketServer import SimpleHTTPServer, SocketServer
class Handler(SimpleHTTPServer.SimpleHTTPRequestHandler): class Handler(SimpleHTTPServer.SimpleHTTPRequestHandler):
_path_match = re.compile('/(.+)\.html$').match
pages = 'ipv6', 'ipv4', 'tunnels'
def do_GET(self): def do_GET(self):
svg = None svg = None
if self.path == '/route.html': try:
other = 'tunnel' page = self.pages.index(self._path_match(self.path).group(1))
svg = route_svg() except AttributeError, ValueError:
elif self.path == '/tunnel.html': if self.path == '/':
other = 'route' self.send_response(302)
self.send_header('Location', self.pages[0] + '.html')
self.end_headers()
else:
self.send_error(404)
return
if page < 2:
svg = route_svg(page)
else:
gv = registry.Popen(('python', '-c', r"""if 1: gv = registry.Popen(('python', '-c', r"""if 1:
import math, json import math, json
from re6st.registry import RegistryClient from re6st.registry import RegistryClient
...@@ -326,21 +355,16 @@ if len(sys.argv) > 1: ...@@ -326,21 +355,16 @@ if len(sys.argv) > 1:
if not svg: if not svg:
self.send_error(500) self.send_error(500)
return return
else:
if self.path == '/':
self.send_response(302)
self.send_header('Location', 'route.html')
self.end_headers()
else:
self.send_error(404)
return
mt = 'text/html' mt = 'text/html'
body = """<html> body = """<html>
<head><meta http-equiv="refresh" content="10"/></head> <head><meta http-equiv="refresh" content="10"/></head>
<body><a style="position: absolute" href="%s.html">%ss</a> <body><span style="position: absolute">%s</span>
%s %s
</body> </body>
</html>""" % (other, other, svg[svg.find('<svg'):]) </html>""" % (' '.join(x if i == page else
'<a href="%s.html">%s</a>' % (x, x)
for i, x in enumerate(self.pages)),
svg[svg.find('<svg'):])
self.send_response(200) self.send_response(200)
self.send_header('Content-Length', len(body)) self.send_header('Content-Length', len(body))
self.send_header('Content-type', mt + '; charset=utf-8') self.send_header('Content-type', mt + '; charset=utf-8')
......
# -*- coding: utf-8 -*-
# Copyright 2010, 2011 INRIA
# Copyright 2011 Martín Ferrari <martin.ferrari@gmail.com>
#
# This file is contains patches to Nemu.
#
# Nemu is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License version 2, as published by the Free
# Software Foundation.
#
# Nemu 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# Nemu. If not, see <http://www.gnu.org/licenses/>.
import re
from nemu.iproute import backticks, get_if_data, get_all_route_data, route
def _get_all_route_data():
ipdata = backticks([IP_PATH, "-o", "route", "list"]) # "table", "all"
ipdata += backticks([IP_PATH, "-o", "-f", "inet6", "route", "list"])
ifdata = get_if_data()[1]
ret = []
for line in ipdata.split("\n"):
if line == "":
continue
# PATCH: parse 'from'
# PATCH: 'dev' is missing on 'unreachable' ipv4 routes
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 = match.group(1) or "unicast"
prefix = match.group(2)
#src = match.group(3)
nexthop = match.group(4)
interface = ifdata[match.group(5) or "lo"]
metric = match.group(6)
if prefix == "default" or re.search(r'/0$', prefix):
prefix = None
prefix_len = 0
else:
match = re.match(r'([0-9a-f:.]+)(?:/(\d+))?$', prefix)
prefix = match.group(1)
prefix_len = int(match.group(2) or 32)
ret.append(route(tipe, prefix, prefix_len, nexthop, interface.index,
metric))
return ret
get_all_route_data.func_code = _get_all_route_data.func_code
...@@ -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
...@@ -6,3 +6,4 @@ run registry/run ...@@ -6,3 +6,4 @@ run registry/run
hello 4 hello 4
client-count 2 client-count 2
tunnel-refresh 100 tunnel-refresh 100
ipv4 10.42.0.0/16 8
...@@ -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::
......
...@@ -83,6 +83,9 @@ def main(): ...@@ -83,6 +83,9 @@ def main():
_('--anonymous-prefix-length', type=int, _('--anonymous-prefix-length', type=int,
help="Length of allocated anonymous prefixes." help="Length of allocated anonymous prefixes."
" If 0 or unset, registration by email is required") " If 0 or unset, registration by email is required")
_('--ipv4', nargs=2, metavar=("IP/N", "PLEN"),
help="Enable ipv4. Each node is assigned a subnet of length PLEN"
" inside network IP/N.")
_('-l', '--logfile', default='/var/log/re6stnet/registry.log', _('-l', '--logfile', default='/var/log/re6stnet/registry.log',
help="Path to logging file.") help="Path to logging file.")
_('-r', '--run', default='/var/run/re6stnet', _('-r', '--run', default='/var/run/re6stnet',
...@@ -122,6 +125,15 @@ def main(): ...@@ -122,6 +125,15 @@ def main():
parser.error("--min-protocol: value must between %s and %s (included)" parser.error("--min-protocol: value must between %s and %s (included)"
% (version.min_protocol, version.protocol)) % (version.min_protocol, version.protocol))
if config.ipv4:
ipv4, plen = config.ipv4
try:
ip, n = ipv4.split('/')
config.ipv4 = "%s/%s" % (socket.inet_ntoa(socket.inet_aton(ip)),
int(n)), int(plen)
except (socket.error, ValueError):
parser.error("invalid argument --ipv4")
utils.setupLog(config.verbose, config.logfile) utils.setupLog(config.verbose, config.logfile)
if config.max_clients is None: if config.max_clients is None:
......
...@@ -27,7 +27,7 @@ class Cache(object): ...@@ -27,7 +27,7 @@ class Cache(object):
q("CREATE INDEX volatile.stat_try ON stat(try)") q("CREATE INDEX volatile.stat_try ON stat(try)")
q("INSERT INTO volatile.stat (peer) SELECT prefix FROM peer") q("INSERT INTO volatile.stat (peer) SELECT prefix FROM peer")
self._db.commit() self._db.commit()
self._loadConfig(q("SELECT * FROM config")) self._loadConfig(self._selectConfig(q))
try: try:
cert.verifyVersion(self.version) cert.verifyVersion(self.version)
except (AttributeError, x509.VerifyError): except (AttributeError, x509.VerifyError):
...@@ -63,6 +63,11 @@ class Cache(object): ...@@ -63,6 +63,11 @@ class Cache(object):
"value") "value")
return db return db
@staticmethod
def _selectConfig(execute): # BBB: blob
return ((k, str(v) if type(v) is buffer else v)
for k, v in execute("SELECT * FROM config"))
def _loadConfig(self, config): def _loadConfig(self, config):
cls = self.__class__ cls = self.__class__
logging.debug("Loading network parameters:") logging.debug("Loading network parameters:")
...@@ -98,7 +103,7 @@ class Cache(object): ...@@ -98,7 +103,7 @@ class Cache(object):
old = {} old = {}
with self._db as db: with self._db as db:
remove = [] remove = []
for k, v in db.execute("SELECT * FROM config"): for k, v in self._selectConfig(db.execute):
if k in config: if k in config:
old[k] = v old[k] = v
continue continue
...@@ -109,8 +114,11 @@ class Cache(object): ...@@ -109,8 +114,11 @@ class Cache(object):
remove.append(k) remove.append(k)
db.execute("DELETE FROM config WHERE name in ('%s')" db.execute("DELETE FROM config WHERE name in ('%s')"
% "','".join(remove)) % "','".join(remove))
# BBB: Use buffer because of http://bugs.python.org/issue13676
# on Python 2.6
db.executemany("INSERT OR REPLACE INTO config VALUES(?,?)", db.executemany("INSERT OR REPLACE INTO config VALUES(?,?)",
config.iteritems()) ((k, buffer(v) if k in base64 else v)
for k, v in config.iteritems()))
self._loadConfig(config.iteritems()) self._loadConfig(config.iteritems())
return [k for k, v in config.iteritems() return [k for k, v in config.iteritems()
if k not in old or old[k] != v] if k not in old or old[k] != v]
......
...@@ -266,6 +266,8 @@ class Babel(object): ...@@ -266,6 +266,8 @@ class Babel(object):
a = len(self.network) a = len(self.network)
for route in routes: for route in routes:
assert route.flags & 1, route # installed assert route.flags & 1, route # installed
if route.prefix.startswith('\0\0\0\0\0\0\0\0\0\0\xff\xff'):
continue
assert route.neigh_address == route.nexthop, route assert route.neigh_address == route.nexthop, route
address = route.neigh_address, route.ifindex address = route.neigh_address, route.ifindex
neigh_routes = n[address] neigh_routes = n[address]
......
...@@ -59,10 +59,11 @@ def client(iface, address_list, encrypt, *args, **kw): ...@@ -59,10 +59,11 @@ 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, ip4, src, hello_interval, log_path, state_path,
tunnel_interfaces, control_socket, default, *args, **kw): pidfile, control_socket, default, *args, **kw):
s = utils.ipFromBin(subnet) ip, n = ip
n = len(subnet) if ip4:
ip4, n4 = ip4
cmd = ['babeld', cmd = ['babeld',
'-h', str(hello_interval), '-h', str(hello_interval),
'-H', str(hello_interval), '-H', str(hello_interval),
...@@ -72,16 +73,19 @@ def router(subnet, hello_interval, table, log_path, state_path, pidfile, ...@@ -72,16 +73,19 @@ 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 ip4:
if table: cmd += '-C', 'redistribute ip %s/%s eq %s' % (ip4, n4, n4)
cmd += '-t%u' % table, '-T%u' % table if src:
else: cmd += '-C', 'install ip ::/0 eq 0 src-prefix ' + src
cmd[-2:-2] = '-C', 'redistribute ip ::/0 eq 0' elif src is None:
cmd += '-C', 'redistribute ip ::/0 eq 0'
cmd += ('-C', 'redistribute deny',
'-C', 'install pref-src ' + ip)
if ip4:
cmd += '-C', 'install pref-src ' + ip4
if control_socket: if control_socket:
cmd += '-R', '%s' % control_socket cmd += '-R', '%s' % control_socket
for iface in tunnel_interfaces:
cmd += '-C', 'interface %s legacy-rxcost 5120' % iface
cmd += args cmd += args
# WKRD: babeld fails to start if pidfile already exists # WKRD: babeld fails to start if pidfile already exists
try: try:
......
...@@ -60,7 +60,7 @@ class RegistryServer(object): ...@@ -60,7 +60,7 @@ class RegistryServer(object):
"name TEXT PRIMARY KEY NOT NULL", "name TEXT PRIMARY KEY NOT NULL",
"value") "value")
self.prefix = self.getConfig("prefix", None) self.prefix = self.getConfig("prefix", None)
self.version = self.getConfig("version", "\0") self.version = str(self.getConfig("version", "\0")) # BBB: blob
utils.sqliteCreateTable(self.db, "token", utils.sqliteCreateTable(self.db, "token",
"token TEXT PRIMARY KEY NOT NULL", "token TEXT PRIMARY KEY NOT NULL",
"email TEXT NOT NULL", "email TEXT NOT NULL",
...@@ -119,6 +119,8 @@ class RegistryServer(object): ...@@ -119,6 +119,8 @@ class RegistryServer(object):
'protocol': version.protocol, 'protocol': version.protocol,
'registry_prefix': self.prefix, 'registry_prefix': self.prefix,
} }
if self.config.ipv4:
kw['ipv4'], kw['ipv4_sublen'] = self.config.ipv4
for x in ('client_count', 'encrypt', 'hello', for x in ('client_count', 'encrypt', 'hello',
'max_clients', 'min_protocol', 'tunnel_refresh'): 'max_clients', 'min_protocol', 'tunnel_refresh'):
kw[x] = getattr(self.config, x) kw[x] = getattr(self.config, x)
...@@ -126,7 +128,9 @@ class RegistryServer(object): ...@@ -126,7 +128,9 @@ class RegistryServer(object):
if config != self.getConfig('last_config', None): if config != self.getConfig('last_config', None):
self.version = self.encodeVersion( self.version = self.encodeVersion(
1 + self.decodeVersion(self.version)) 1 + self.decodeVersion(self.version))
self.setConfig('version', self.version) # BBB: Use buffer because of http://bugs.python.org/issue13676
# on Python 2.6
self.setConfig('version', buffer(self.version))
self.setConfig('last_config', config) self.setConfig('last_config', config)
self.sendto(self.prefix, 0) self.sendto(self.prefix, 0)
kw[''] = 'version', kw[''] = 'version',
......
...@@ -168,7 +168,8 @@ class BaseTunnelManager(object): ...@@ -168,7 +168,8 @@ class BaseTunnelManager(object):
# TODO: To minimize downtime when network parameters change, we should do # TODO: To minimize downtime when network parameters change, we should do
# our best to not restart any process. Ideally, this list should be # our best to not restart any process. Ideally, this list should be
# empty and the affected subprocesses reloaded. # empty and the affected subprocesses reloaded.
NEED_RESTART = frozenset(('babel_default', 'encrypt', 'hello')) NEED_RESTART = frozenset(('babel_default', 'encrypt', 'hello',
'ipv4', 'ipv4_sublen'))
_forward = None _forward = None
......
#!/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))
...@@ -221,6 +229,11 @@ def main(): ...@@ -221,6 +229,11 @@ def main():
raise EnvironmentError("%r failed with error %u\n%s" raise EnvironmentError("%r failed with error %u\n%s"
% (' '.join(cmd), p.returncode, stderr)) % (' '.join(cmd), p.returncode, stderr))
return stdout return stdout
def ip4(object, *args):
args = ['ip', '-4', object, 'add'] + list(args)
call(args)
args[3] = 'del'
cleanup.append(lambda: subprocess.call(args))
def ip(object, *args): def ip(object, *args):
args = ['ip', '-6', object, 'add'] + list(args) args = ['ip', '-6', object, 'add'] + list(args)
call(args) call(args)
...@@ -236,10 +249,9 @@ def main(): ...@@ -236,10 +249,9 @@ 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
tunnel_interfaces = server_tunnels.keys() config.babel_args += server_tunnels
timeout = 4 * cache.hello timeout = 4 * cache.hello
cleanup = [lambda: cache.cacheMinimize(config.client_count), cleanup = [lambda: cache.cacheMinimize(config.client_count),
lambda: shutil.rmtree(config.run, True)] lambda: shutil.rmtree(config.run, True)]
...@@ -250,29 +262,48 @@ def main(): ...@@ -250,29 +262,48 @@ def main():
cache, cert, config.openvpn_args, timeout, cache, cert, config.openvpn_args, timeout,
config.client_count, config.iface_list, address, ip_changed, config.client_count, config.iface_list, address, ip_changed,
remote_gateway, config.disable_proto, config.neighbour) remote_gateway, config.disable_proto, config.neighbour)
tunnel_interfaces += tunnel_manager.new_iface_list config.babel_args += tunnel_manager.new_iface_list
else: else:
tunnel_manager = tunnel.BaseTunnelManager(cache, cert) tunnel_manager = tunnel.BaseTunnelManager(cache, cert)
cleanup.append(tunnel_manager.sock.close) cleanup.append(tunnel_manager.sock.close)
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 ipv4 = getattr(cache, 'ipv4', None)
# interface). But here, it rarely applies because we use several if ipv4:
# interfaces to connect to a re6st network. serial = int(cert.cert.get_subject().serialNumber)
# Rule 7 is little strange because it prefers temporary addresses if cache.ipv4_sublen <= 16 and serial < 1 << cache.ipv4_sublen:
# over IP with a longer matching prefix (rule 8, which is not even dot4 = lambda x: socket.inet_ntoa(struct.pack('!I', x))
# mandatory). ip4('route', 'unreachable', ipv4, 'proto', 'static')
# So only rule 6 can make the difference, i.e. prefer same label. ipv4, n = ipv4.split('/')
# The value of the label does not matter, except that it must be ipv4, = struct.unpack('!I', socket.inet_aton(ipv4))
# different from ::/0's (normally equal to 1). n = int(n) + cache.ipv4_sublen
# XXX: This does not work with extra interfaces that already have x = ipv4 | serial << 32 - n
# an public IP so Babel must be changed to set a source ipv4 = dot4(x | (n < 31))
# address on routes it installs. config.openvpn_args += '--ifconfig', \
ip('addrlabel', 'prefix', my_network, 'label', '99') ipv4, dot4((1<<32) - (1<<32-n))
ipv4 = ipv4, n
if not isinstance(tunnel_manager, tunnel.TunnelManager):
ip4('addr', "%s/%s" % ipv4,
'dev', config.main_interface)
if config.main_interface == "lo":
ip4('route', 'unreachable', "%s/%s" % (dot4(x), n),
'proto', 'static')
else:
logging.warning(
"IPv4 payload disabled due to wrong network parameters")
ipv4 = None
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 = {} 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 +327,8 @@ def main(): ...@@ -296,7 +327,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 +337,13 @@ def main(): ...@@ -305,30 +337,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,14 +362,17 @@ def main(): ...@@ -347,14 +362,17 @@ 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)), ipv4,
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'),
tunnel_interfaces, control_socket, cache.babel_default, control_socket, cache.babel_default,
*config.babel_args).stop) *config.babel_args).stop)
if config.up: if config.up:
exit.release() exit.release()
......
...@@ -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