-
Notifications
You must be signed in to change notification settings - Fork 51
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Olivier Tilmans <olivier.tilmans@uclouvain.be>
- Loading branch information
0 parents
commit 44b661f
Showing
12 changed files
with
671 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
.autoenv* | ||
*.swp | ||
*.pyc | ||
build | ||
dist | ||
*.egg-info | ||
*.egg | ||
*.cache |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
recursive-include fibbingnode/res * |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# IPMininet | ||
|
||
This is a python library, extending [Mininet](http://mininet.org), in order | ||
to support emulation of (complex) IP networks. As such it provides new classes, | ||
such as Routers, auto-configures all properties not set by the user, such as | ||
IP addresses or router configuration files, ... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
from mininet.cli import CLI | ||
from mininet.log import lg | ||
|
||
|
||
class IPCLI(CLI): | ||
def do_route(self, line=""): | ||
"""route destination: Print all the routes towards that destination | ||
for every router in the network""" | ||
for r in self.mn.routers: | ||
self.default('[%s] ip route get %s' % (r.name, line)) | ||
|
||
def do_ip(self, line): | ||
"""ip IP1 IP2 ...: return the node associated to the given IP""" | ||
for ip in line.split(' '): | ||
try: | ||
n = self.mn.node_for_ip(ip) | ||
except KeyError: | ||
n = 'unknown IP' | ||
finally: | ||
lg.info(ip, '|', n) | ||
|
||
def do_ips(self, line): | ||
"""ips n1 n2 ...: return the ips associated to the given node name""" | ||
for n in line.split(' '): | ||
try: | ||
l = [itf.ip for itf in self.mn[n].intfList()] | ||
except KeyError: | ||
l = 'unknown node' | ||
finally: | ||
lg.info(n, '|', l) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
"""This module defines topology class that supports adding L3 routers""" | ||
import ipmininet | ||
|
||
from mininet.topo import Topo | ||
|
||
|
||
class IPTopo(Topo): | ||
"""A topology that supports L3 routers""" | ||
|
||
def isNodeType(self, n, x): | ||
"""Return wether node n has a key x set to True | ||
:param n: node name | ||
:param x: the key to check""" | ||
try: | ||
return self.g.node[n].get(x, False) | ||
except KeyError: # node not found | ||
return False | ||
|
||
def addRouter(self, name, **kwargs): | ||
"""Add a router to the topology | ||
:param name: the name of the node""" | ||
return self.addNode(name, isRouter=True, **kwargs) | ||
|
||
def isRouter(self, n): | ||
"""Check whether the given node is a router | ||
:param n: node name""" | ||
return self.isNodeType(n, 'isRouter') | ||
|
||
def hosts(self, sort=True): | ||
# The list si already sorted, simply filter out the routers | ||
return [h for h in super(IPTopo, self).hosts(sort) | ||
if not self.isRouter(h)] | ||
|
||
def routers(self, sort=True): | ||
"""Return a list of router node names""" | ||
return filter(self.isRouter, self.nodes(sort)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
hostname ${node.hostname} | ||
password ${node.password} | ||
% if node.ospf.logfile: | ||
log file ${node.ospf.logfile} | ||
% endif | ||
% for section in node.ospf.debug: | ||
debug ospf section | ||
% endfor | ||
! | ||
% for intf in node.ospf.interfaces: | ||
interface ${intf.name} | ||
# ${intf.description} | ||
# Highiest priority routers will be DR | ||
ip ospf priority ${intf.ospf.priority} | ||
ip ospf cost ${intf.ospf.cost} | ||
# dead/hello intervals must be consistent across a broadcast domain | ||
ip ospf dead-interval ${intf.ospf.dead_int} | ||
ip ospf hello-interval ${intf.ospf.hello_int} | ||
! | ||
% endfor | ||
router ospf | ||
router-id ${node.ospf.router_id} | ||
% for type, prop in node.ospf.redistribute: | ||
redistribute ${type} metric-type ${prop.metric_type} metric ${prop.metric} | ||
% endfor | ||
% for net in node.ospf.networks: | ||
network ${net.domain.with_prefixlen} area ${net.area} | ||
% endfor | ||
! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
hostname ${node.hostname} | ||
password ${node.password} | ||
% if node.zebra.logfile: | ||
log file ${node.zebra.logfile} | ||
% endif | ||
% for section in node.zebra.debug: | ||
debug zebra section | ||
% endfor | ||
% for pl in node.zebra.prefixlists: | ||
ip prefix-list ${pl.name} ${pl.action} ${pl.prefix} ${pl.condition} | ||
% endfor | ||
! | ||
% for rm in node.zebra.routemaps: | ||
route-map ${rm.name} ${rm.action} ${rm.prio} | ||
% for prefix in rm.prefix: | ||
match ip address prefix-list ${prefix} | ||
% endfor | ||
! | ||
% for proto in rm.proto: | ||
ip protocol ${proto} route-map ${rm.name} | ||
% endfor | ||
% endfor | ||
! | ||
% for prefix, via in node.zebra.static_routes: | ||
ip route ${prefix} via ${via} | ||
% endfor |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
"""This module defines a data-store to help dealing wtih all (possibly) | ||
auto-allocated properties of a topology: ip addresses, router ids, ...""" | ||
import json | ||
from ipaddress import ip_interface | ||
|
||
from ipmininet import otherIntf, realIntfList | ||
|
||
from mininet.log import lg | ||
|
||
|
||
class TopologyDB(object): | ||
"""A convenience store for auto-allocated mininet properties. | ||
This is *NOT* to be used as IGP graph""" | ||
def __init__(self, db=None, net=None, *args, **kwargs): | ||
"""Either extract properties from a network or load a save file | ||
:param db: a path towards a saved version of this class which will | ||
be loaded | ||
:param net: an IPNet instance which will be parsed in order to extract | ||
useful properties | ||
""" | ||
super(TopologyDB, self).__init__(*args, **kwargs) | ||
"""dict keyed by node name -> | ||
dict keyed by - properties -> val | ||
- neighbor -> interface properties""" | ||
self._network = {} | ||
if db: | ||
self.load(db) | ||
if net: | ||
self.parse_net(net) | ||
if not db and not net: | ||
lg.warning('TopologyDB instantiated without any data') | ||
|
||
def load(self, fpath): | ||
"""Load a topology database | ||
:param fpath: path towards the file to load""" | ||
with open(fpath, 'r') as f: | ||
self._network = json.load(f) | ||
|
||
def save(self, fpath): | ||
"""Save the topology database | ||
:param fpath: the save file name""" | ||
with open(fpath, 'w') as f: | ||
json.dump(self._network, f) | ||
|
||
def _node(self, x): | ||
try: | ||
return self._network[x] | ||
except KeyError: | ||
raise ValueError('No node named %s in the network' % x) | ||
|
||
def _interface(self, x, y): | ||
try: | ||
return self._node(x)[y] | ||
except KeyError: | ||
raise ValueError('The link %s-%s does not exist' % (x, y)) | ||
|
||
def interface(self, x, y): | ||
"""Return the ip address of the interface of x facing y | ||
:param x: the node from which we want an IP address | ||
:param y: the node on the other side of the link | ||
:return: ip_interface-like object""" | ||
return ip_interface(self._interface(x, y)['ip']) | ||
|
||
def interface_bandwidth(self, x, y): | ||
"""Return the bandwidth capacity of the interface on node x | ||
facing node y. | ||
:param x: node name | ||
:param y: node name | ||
:return: The bandwidth of link x-y, -1 if unlimited""" | ||
try: | ||
return self._interface(x, y)['bw'] | ||
except KeyError: | ||
return -1 | ||
|
||
def subnet(self, x, y): | ||
"""Return the subnet linking node x and y | ||
:param x: node name | ||
:param y: node name | ||
:return: ip_network-like object""" | ||
return self.interface(x, y).network | ||
|
||
def routerid(self, x): | ||
"""Return the router id of a node | ||
:param x: router name | ||
:return: the routerid""" | ||
n = self._node(x) | ||
if n['type'] != 'router': | ||
raise TypeError('%s is not a router' % x) | ||
return n['routerid'] | ||
|
||
def parse_net(self, net): | ||
"""Stores the content of the given network | ||
:param net: IPNet instance""" | ||
for h in net.hosts: | ||
self.add_host(h) | ||
for s in net.switches: | ||
self.add_switch(s) | ||
for r in net.routers: | ||
self.add_router(r) | ||
|
||
def _add_node(self, n, props): | ||
for itf in realIntfList(n): | ||
nh = otherIntf(itf) | ||
props[nh.node.name] = { | ||
'ip': '%s/%s' % (itf.ip, itf.prefixLen), | ||
'name': itf.name, | ||
'bw': itf.params.get('bw', -1) | ||
} | ||
self._network[n.name] = props | ||
|
||
def add_host(self, n): | ||
"""Register an host | ||
:param n: Host instance""" | ||
self._add_node(n, {'type': 'host'}) | ||
|
||
def add_switch(self, n): | ||
"""Register an switch | ||
:param n: Switch instance""" | ||
self._add_node(n, {'type': 'switch'}) | ||
|
||
def add_router(self, n): | ||
"""Register an router | ||
:param n: Router instance""" | ||
self._add_node(n, {'type': 'router', | ||
'routerid': n.id}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[aliases] | ||
test=pytest |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
#!/usr/bin/env python | ||
|
||
"Setuptools params" | ||
|
||
from setuptools import setup, find_packages | ||
|
||
VERSION = '0.1a' | ||
|
||
modname = distname = 'ipmininet' | ||
|
||
setup( | ||
name=distname, | ||
version=VERSION, | ||
description='A mininet extension providing components to emulate IP networks', | ||
author='Olivier Tilmans', | ||
author_email='olivier.tilmans@uclouvain.be', | ||
packages=find_packages(), | ||
include_package_data = True, | ||
classifiers=[ | ||
"License :: OSI Approved :: BSD License", | ||
"Programming Language :: Python", | ||
"Development Status :: 2 - Pre-Alpha", | ||
"Intended Audience :: Developers", | ||
"Topic :: System :: Networking", | ||
], | ||
keywords='networking OSPF IP mininet', | ||
license='GPLv2', | ||
install_requires=[ | ||
'setuptools', | ||
'mako', | ||
'py2-ipaddress' | ||
], | ||
tests_require=['pytest'], | ||
setup_requires=['pytest-runner'] | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import pytest | ||
import ipaddress | ||
|
||
|
||
def test_nested_ip_networks(): | ||
"""This test ensures that we can build an IPvXNetwork from another one. | ||
If this breaks, need to grep through for ip_network calls as I removed the | ||
checks when instantiating these ... | ||
Test passing with py2-ipaddress (3.4.1)""" | ||
_N = ipaddress.ip_network | ||
for p in ('::/0', | ||
'0.0.0.0/0', | ||
'1.2.3.0/24', | ||
'2001:db8:1234::/48'): | ||
n1 = _N(p) # Build an IPvXNetwork | ||
n2 = _N(n1) # Build a new one from the previous one | ||
assert (n1 == n2 and | ||
n1.with_prefixlen == p and | ||
n2.with_prefixlen == p and | ||
n1.max_prefixlen == n2.max_prefixlen) |