Skip to content

Instantly share code, notes, and snippets.

@jochumdev
Last active April 27, 2018 22:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jochumdev/9b9d1589289b649d8207 to your computer and use it in GitHub Desktop.
Save jochumdev/9b9d1589289b649d8207 to your computer and use it in GitHub Desktop.
Hetzner Mininet.
#!/usr/bin/env python
"""
Helper to create a Hetzner root server style Network.
At Hetzner the gateway blocks alls MAC's except the
one of the route server, this means you need to proxy_arp
your virtual hosts.
Known bugs:
- pingAll doesn't seem to work right with this.
"""
import string
from mininet.net import Mininet
from mininet.node import Node, Controller
from mininet.cli import CLI
from mininet.link import TCLink
from mininet.log import setLogLevel, info, warn, debug
class MultiIfaceHost(Node):
def __init__(self, name, interfaces, **params):
Node.__init__(self, name, **params)
self.interfaces = interfaces
def config(self, mac=None, ip=None,
defaultRoute=None, lo='up', **_params):
r = Node.config(self, mac, None, None, **_params)
for idx, interface in enumerate(self.interfaces):
info('*** Setting up interface: %d\n' % idx)
if idx >= len(self.intfNames()):
msg = '**** Can\'t setup interface number %d, ' +\
'i only have %d interfaces.\n'
warn(msg % (idx, len(self.intfNames())))
break
iface = self.intfNames()[idx]
# Set the interface mac.
self.setMAC(interface['mac'], iface)
# Remove the auto configured address
self.cmd('ip addr 0 dev %s' % iface)
# Set the mtu if given (by adding a cmd)
if 'mtu' in interface:
interface['cmds'].append(
'ifconfig $iface mtu %d' % interface['mtu']
)
# Execute all the cmds.
for cmd in interface['cmds']:
cmdToRun = string.Template(cmd).substitute(iface=iface)
debug("**** %s: %s\n" % (iface, cmdToRun))
self.cmd(cmdToRun)
return r
def rootNetwork(net, topo):
result = {'switches': {},
'gw': None,
'root': None,
'guests': {}}
info('*** Add switches\n')
for sw in 'sint1', 'spub1':
sw = '%s%s' % (topo['prefix'], sw)
result['switches'][sw] = net.addSwitch(sw)
info('*** Add root gateway\n')
gw_config = [
{
'mac': topo['gw']['mac'],
'mtu': 1500 if 'mtu' not in topo['gw'] else topo['gw']['mtu'],
'cmds': [
'ip addr add %s/%s brd %s dev $iface' %
(topo['gw']['ipv4'],
topo['gw']['ipv4sub'],
topo['gw']['ipv4brd']),
'sysctl -w net.ipv4.ip_forward=1',
'iptables -A INPUT -i $iface -m mac ! --mac-source %s -j DROP' %
topo['root']['mac']
]
}
]
# Add a route from the the gw to the guests floating ips
for guest in topo['guests']:
gw_config[0]['cmds'].append(
'ip route add %s dev $iface scope link' % guest['ipv4']
)
# Configure IPv6 ?
if 'ipv6' in topo['gw']:
gw_config[0]['cmds'].extend([
# Of course we need ipv6 forwarding.
'sysctl -w net.ipv6.conf.all.forwarding=1',
# Filter all macs other than the roots.
'ip6tables -A INPUT -i $iface -m mac ! --mac-source %s -j DROP' %
topo['root']['mac'],
# The Public internet needs to know howto reach this /64 subnet.
# Think its this address where its routed to.
'ip -6 addr add %s/%s dev $iface' %
(topo['gw']['ipv6'], topo['gw']['ipv6prefix']),
# The root will reach this gw trough fe80::1
'ip -6 addr add fe80::1 dev $iface scope link',
# A /128 route to root (so the gw knows where the root gw is.)
'ip -6 route add %s/128 dev $iface' %
topo['root']['ipv6'],
# And a /64 for the roots subnet.
'ip -6 route add %s/%s via %s dev $iface' %
(topo['root']['eth1ipv6'],
topo['root']['eth1ipv6prefix'],
topo['root']['ipv6']),
# A "public" IPv6 to tests ping to.
'ip -6 addr add %s dev $iface' % topo['gw']['ipv6testaddress']
])
root_config = [
{
'mac': topo['root']['mac'],
'mtu': 1500 if 'mtu' not in topo['root']
else topo['root']['mtu'],
'cmds': [
'ip addr add %s/%s brd %s dev $iface' %
(topo['root']['ipv4'],
topo['root']['ipv4sub'],
topo['root']['ipv4brd']),
'ip route add %s/%s gw %s' %
(topo['root']['ipv4'],
topo['root']['ipv4sub'],
topo['gw']['ipv4']),
'sysctl -w net.ipv4.ip_forward=1',
'sysctl -w net.ipv4.conf.all.proxy_arp=1',
'sysctl -w net.ipv4.conf.$iface.proxy_arp=1',
'sysctl -w net.ipv4.conf.default.proxy_arp=1',
]
},
{
'mac': topo['root']['eth1mac'],
'mtu': 1500 if 'eth1mtu' not in topo['root']
else topo['root']['eth1mtu'],
'cmds': [
'ip addr add %s/32 dev $iface' % topo['root']['ipv4'],
]
}
]
if 'ipv6' in topo['root']:
root_config[0]['cmds'].extend([
'sysctl -w net.ipv6.conf.all.forwarding=1',
'ip -6 addr add %s/%s dev $iface' %
(topo['root']['ipv6'], topo['root']['ipv6prefix']),
'ip -6 route add default via %s dev $iface' %
(topo['root']['ipv6defaultgw'])
])
if 'eth1ipv6' in topo['root']:
root_config[1]['cmds'].append(
'ip -6 addr add %s/%s dev $iface' %
(topo['root']['eth1ipv6'], topo['root']['eth1ipv6prefix'])
)
# Add a link local route for each guest floating ip.
for guest in topo['guests']:
root_config[1]['cmds'].append(
'ip route add %s dev $iface scope link' % guest['ipv4']
)
result['gw'] = net.addHost(
'%sgw' % topo['prefix'],
cls=MultiIfaceHost,
interfaces=gw_config
)
result['root'] = net.addHost(
'%s%s' % (topo['prefix'], topo['root']['name']),
cls=MultiIfaceHost,
interfaces=root_config
)
for guest in topo['guests']:
guest_config = [
{
'mac': guest['mac'],
'mtu': 1500 if 'mtu' not in guest else guest['mtu'],
'cmds': [
'ip addr add %s peer %s/32 brd %s dev $iface' %
(guest['ipv4'],
topo['root']['ipv4'],
guest['ipv4brd']),
'ip route add default via %s dev $iface' %
topo['root']['ipv4']
]
},
]
if 'ipv6' in guest:
guest_config[0]['cmds'].extend([
'ip -6 addr add %s/%s dev $iface' %
(guest['ipv6'], guest['ipv6prefix']),
'ip -6 route add default via %s dev $iface' %
(topo['root']['eth1ipv6'])
])
result['guests']['%s%s' % (topo['prefix'], guest['name'])] = \
net.addHost(
'%s%s' % (topo['prefix'], guest['name']),
cls=MultiIfaceHost,
interfaces=guest_config
)
info('*** Add links\n')
# Gw to spub1
if 'bw' in topo['gw']:
net.addLink(
result['gw'],
result['switches']['%sspub1' % topo['prefix']],
bw=topo['gw']['bw'])
else:
net.addLink(
result['gw'],
result['switches']['%sspub1' % topo['prefix']])
# Root server to the gw (over spub1)
if 'bw' in topo['root']:
net.addLink(
result['root'],
result['switches']['%sspub1' % topo['prefix']],
bw=topo['root']['bw'])
else:
net.addLink(
result['root'],
result['switches']['%sspub1' % topo['prefix']])
# Root server to sint1 (our guests)
net.addLink(result['root'], result['switches']['%ssint1' % topo['prefix']])
for k, guest in result['guests'].iteritems():
info('***** Linking %s with sint1\n' % guest)
net.addLink(guest, result['switches']['%ssint1' % topo['prefix']])
return result
if __name__ == '__main__':
setLogLevel('info')
root_network = {
'prefix': '', # Prefix all names with this
'gw':
{'mac': '78:fe:3d:43:ff:ff', # Mac (use "arp -n" to get)
'ipv4': '5.9.223.233', # IPv4 address
'ipv4sub': '29', # IPv4 netmask in CIDR
'ipv4brd': '5.9.223.239', # IPv4 broadcast
'ipv6': '2001:db8:1:2::1', # IPv6 Addrss of the gw ::1
'ipv6prefix': 128, # IPv6 netmask
'ipv6testaddress': '2001:db8:f:f::1/64', # IPv6 Address to test pings to.
'bw': 1000}, # Limit bandwith to 1Gbit
'root':
{'name': 'root', # Name of the root server
'mtu': 1500, # MTU of eth0
'mac': '44:8a:5b:2c:ff:ff', # Mac of eth0
'ipv4': '5.9.223.235', # IPv4 address of eth0
'ipv4sub': '29', # IPv4 netmask of eth0
'ipv4brd': '5.9.223.239', # IPv4 Broadcast
'ipv6': '2001:db8:1:2::2', # IPv6 address of eth0
'ipv6prefix': 128, # IPv6 netmask of eth0
'ipv6defaultgw': 'fe80::1', # IPv6 default eth0 gateway
'bw': 1000, # Limit bandwith to 1Gbit
'eth1mtu': 1450, # MTU of eth1 set to change
'eth1mac': '3e:d8:dc:cd:58:18', # Mac of eth1
'eth1ipv6': '2001:db8:1:2::2', # IPv6 address of eth1
'eth1ipv6prefix': 64}, # IPv6 netmask of eth1
'guests': [ # Infinite guests allowed
{'name': 'guest1',
'mac': '00:50:56:00:00:01',
'ipv4': '5.9.224.182',
'ipv4sub': '32',
'ipv4brd': '5.9.224.191',
'ipv6': '2001:db8:1:2:f1ee::2',
'ipv6prefix': 64,
'mtu': 1450},
{'name': 'guest2',
'mac': '00:50:56:00:00:02',
'ipv4': '5.9.224.19',
'ipv4sub': '32',
'ipv4brd': '5.9.224.31',
'ipv6': '2001:db8:1:2:f1ee::5',
'ipv6prefix': 64,
'mtu': 1450}]
}
net = Mininet(controller=Controller, link=TCLink)
info('*** Adding controller\n')
net.addController('c0')
rootNetwork(net, root_network)
info('*** Starting network\n')
net.start()
info('*** Running CLI\n')
CLI(net)
info('*** Stopping network')
net.stop()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment