Commit 2fb63515 authored by Julien Muchembled's avatar Julien Muchembled

Add support for ipv4 payload

There is no plan for a default ipv4 route.
parent f128ba9d
#!/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'
...@@ -50,6 +50,8 @@ for name in """internet=I registry=R ...@@ -50,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(
...@@ -237,7 +239,12 @@ def node_by_ll(addr): ...@@ -237,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,
...@@ -247,12 +254,13 @@ def node_by_ll(addr): ...@@ -247,12 +254,13 @@ def node_by_ll(addr):
_ll[a] = n, t _ll[a] = n, t
return _ll[addr] return _ll[addr]
def route_svg(z = 4, default = type('', (), {'short': None})): 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 is None or 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( g[node_by_ll(r.nexthop)].append(
node_by_ll(r.prefix)[0] if r.prefix else default) node_by_ll(r.prefix)[0] if r.prefix else default)
...@@ -297,13 +305,25 @@ if len(sys.argv) > 1: ...@@ -297,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
...@@ -335,21 +355,16 @@ if len(sys.argv) > 1: ...@@ -335,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')
......
...@@ -29,16 +29,17 @@ def _get_all_route_data(): ...@@ -29,16 +29,17 @@ def _get_all_route_data():
if line == "": if line == "":
continue continue
# PATCH: parse 'from' # PATCH: parse 'from'
# PATCH: 'dev' is missing on 'unreachable' ipv4 routes
match = re.match('(?:(unicast|local|broadcast|multicast|throw|' match = re.match('(?:(unicast|local|broadcast|multicast|throw|'
r'unreachable|prohibit|blackhole|nat) )?(\S+)(?: from (\S+))?' r'unreachable|prohibit|blackhole|nat) )?(\S+)(?: from (\S+))?'
r'(?: via (\S+))? dev (\S+).*(?: metric (\d+))?', line) 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)
#src = match.group(3) #src = match.group(3)
nexthop = match.group(4) nexthop = match.group(4)
interface = ifdata[match.group(5)] interface = ifdata[match.group(5) or "lo"]
metric = match.group(6) 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
......
...@@ -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
...@@ -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:
......
...@@ -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,9 +59,11 @@ def client(iface, address_list, encrypt, *args, **kw): ...@@ -59,9 +59,11 @@ def client(iface, address_list, encrypt, *args, **kw):
return openvpn(iface, encrypt, *remote, **kw) return openvpn(iface, encrypt, *remote, **kw)
def router(ip, src, hello_interval, log_path, state_path, def router(ip, ip4, src, hello_interval, log_path, state_path,
pidfile, control_socket, default, *args, **kw): pidfile, control_socket, default, *args, **kw):
ip, n = ip ip, n = ip
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,12 +74,16 @@ def router(ip, src, hello_interval, log_path, state_path, ...@@ -72,12 +74,16 @@ def router(ip, src, hello_interval, log_path, state_path,
'-C', 'default ' + default, '-C', 'default ' + default,
'-C', 'redistribute local deny', '-C', 'redistribute local deny',
'-C', 'redistribute ip %s/%s eq %s' % (ip, n, n)] '-C', 'redistribute ip %s/%s eq %s' % (ip, n, n)]
if ip4:
cmd += '-C', 'redistribute ip %s/%s eq %s' % (ip4, n4, n4)
if src: if src:
cmd += '-C', 'install ip ::/0 eq 0 src-prefix ' + src cmd += '-C', 'install ip ::/0 eq 0 src-prefix ' + src
elif src is None: elif src is None:
cmd += '-C', 'redistribute ip ::/0 eq 0' cmd += '-C', 'redistribute ip ::/0 eq 0'
cmd += ('-C', 'redistribute deny', cmd += ('-C', 'redistribute deny',
'-C', 'install pref-src ' + ip) '-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
cmd += args cmd += args
......
...@@ -120,6 +120,8 @@ class RegistryServer(object): ...@@ -120,6 +120,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)
......
...@@ -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
......
...@@ -229,6 +229,11 @@ def main(): ...@@ -229,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)
...@@ -265,6 +270,31 @@ def main(): ...@@ -265,6 +270,31 @@ def main():
try: try:
exit.acquire() exit.acquire()
ipv4 = getattr(cache, 'ipv4', None)
if ipv4:
serial = int(cert.cert.get_subject().serialNumber)
if serial.bit_length() <= cache.ipv4_sublen <= 16:
dot4 = lambda x: socket.inet_ntoa(struct.pack('!I', x))
ip4('route', 'unreachable', ipv4, 'proto', 'static')
ipv4, n = ipv4.split('/')
ipv4, = struct.unpack('!I', socket.inet_aton(ipv4))
n = int(n) + cache.ipv4_sublen
x = ipv4 | serial << 32 - n
ipv4 = dot4(x | (n < 31))
config.openvpn_args += '--ifconfig', \
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 if os.uname()[2] < '2.6.40': # BBB
logging.warning("Fallback to ip-addrlabel because Linux < 3.0" logging.warning("Fallback to ip-addrlabel because Linux < 3.0"
" does not support RTA_PREFSRC for ipv6. Note however that" " does not support RTA_PREFSRC for ipv6. Note however that"
...@@ -335,7 +365,7 @@ def main(): ...@@ -335,7 +365,7 @@ def main():
ip('route', 'unreachable', my_network) ip('route', 'unreachable', my_network)
config.babel_args += config.iface_list config.babel_args += config.iface_list
cleanup.append(plib.router((my_ip, len(subnet)), cleanup.append(plib.router((my_ip, len(subnet)), ipv4,
None if config.gateway else None if config.gateway else
'' if config.default else '' if config.default else
my_network, cache.hello, my_network, cache.hello,
......
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