Configuring daemons

We can add daemons to the routers or hosts and pass options to them. In the following code snippet, we add BGP daemon to r1.

from ipmininet.iptopo import IPTopo
from ipmininet.router.config import OSPF, OSPF6, RouterConfig

class MyTopology(IPTopo):

    def build(self, *args, **kwargs):

        r1 = self.addRouter("r1", config=RouterConfig)
        r1.addDaemon(OSPF, hello_int=1)
        r1.addDaemon(OSPF6, hello_int=1)

        # [...]

        super().build(*args, **kwargs)

This page presents how to configure each daemon.

BGP

When adding BGP to a router with router.addDaemon(BGP, **kargs), we change the following default parameters:

BGP.set_defaults(defaults)
Parameters:
  • debug – the set of debug events that should be logged
  • address_families – The set of AddressFamily to use

We can declare a set of routers in the same AS by using the overlay AS:

The overlay iBGPFullMesh extends the AS class and allows us to establish iBGP sessions in full mesh between BGP routers.

There are also some helper functions:

BGPConfig.set_local_pref(local_pref: int, from_peer: str, matching: Sequence[Union[ipmininet.router.config.zebra.AccessList, ipmininet.router.config.zebra.CommunityList]] = ()) → ipmininet.router.config.bgp.BGPConfig
Set local pref on a peering with ‘from_peer’ on routes
matching all of the access and community lists in ‘matching’
Parameters:
  • local_pref – The local pref value to set
  • from_peer – The peer on which the local pref is applied
  • matching – A list of AccessList and/or CommunityList
Returns:

self

BGPConfig.set_med(med: int, to_peer: str, matching: Sequence[Union[ipmininet.router.config.zebra.AccessList, ipmininet.router.config.zebra.CommunityList]] = ()) → ipmininet.router.config.bgp.BGPConfig
Set MED on a peering with ‘to_peer’ on routes
matching all of the access and community lists in ‘matching’
Parameters:
  • med – The local pref value to set
  • to_peer – The peer to which the med is applied
  • matching – A list of AccessList and/or CommunityList
Returns:

self

BGPConfig.set_community(community: Union[str, int], from_peer: Optional[str] = None, to_peer: Optional[str] = None, matching: Sequence[Union[ipmininet.router.config.zebra.AccessList, ipmininet.router.config.zebra.CommunityList]] = ()) → ipmininet.router.config.bgp.BGPConfig
Set community on a routes received from ‘from_peer’
and routes sent to ‘to_peer’ on routes matching all of the access and community lists in ‘matching’
Parameters:
  • community – The community value to set
  • from_peer – The peer on which received routes have to have the community
  • to_peer – The peer on which sent routes have to have the community
  • matching – A list of AccessList and/or CommunityList
Returns:

self

BGPConfig.deny(name: Optional[str] = None, from_peer: Optional[str] = None, to_peer: Optional[str] = None, matching: Sequence[Union[ipmininet.router.config.zebra.AccessList, ipmininet.router.config.zebra.CommunityList]] = (), order=10) → ipmininet.router.config.bgp.BGPConfig
Deny all routes received from ‘from_peer’
and routes sent to ‘to_peer’ matching all of the access and community lists in ‘matching’
Parameters:
  • name – The name of the route-map
  • from_peer – The peer on which received routes have to have the community
  • to_peer – The peer on which sent routes have to have the community
  • matching – A list of AccessList and/or CommunityList
  • order – The order in which route-maps are applied, i.e., lower order means applied before
Returns:

self

BGPConfig.permit(name: Optional[str] = None, from_peer: Optional[str] = None, to_peer: Optional[str] = None, matching: Sequence[Union[ipmininet.router.config.zebra.AccessList, ipmininet.router.config.zebra.CommunityList]] = (), order=10) → ipmininet.router.config.bgp.BGPConfig
Accept all routes received from ‘from_peer’
and routes sent to ‘to_peer’ matching all of the access and community lists in ‘matching’
Parameters:
  • name – The name of the route-map
  • from_peer – The peer on which received routes have to have the community
  • to_peer – The peer on which sent routes have to have the community
  • matching – A list of AccessList and/or CommunityList
  • order – The order in which route-maps are applied, i.e., lower order means applied before
Returns:

self

bgp.bgp_fullmesh(routers: Sequence[str])

Establish a full-mesh set of BGP peerings between routers

Parameters:
  • topo – The current topology
  • routers – The set of routers peering within each other
bgp.bgp_peering(a: str, b: str)

Register a BGP peering between two nodes

bgp.ebgp_session(a: RouterDescription, b: RouterDescription, link_type: Optional[str] = None)

Register an eBGP peering between two nodes, and disable IGP adjacencies between them.

Parameters:
  • topo – The current topology
  • a – Local router
  • b – Peer router
  • link_type – Can be set to SHARE or CLIENT_PROVIDER. In this case ebgp_session will create import and export filter and set local pref based on the link type
bgp.set_rr(rr: str, peers: Sequence[str] = ())

Set rr as route reflector for all router r

Parameters:
  • topo – The current topology
  • rr – The route reflector
  • peers – Clients of the route reflector

The following code shows how to use all these abstractions:

from ipmininet.iptopo import IPTopo
from ipmininet.router.config import BGP, bgp_fullmesh, bgp_peering, \
    ebgp_session, RouterConfig, AccessList, CommunityList

class MyTopology(IPTopo):

    def build(self, *args, **kwargs):

        # AS1 routers
        as1r1, as1r2, as1r3 = self.addRouters("as1r1", "as1r2", "as1r3",
                                              config=RouterConfig)
        as1r1.addDaemon(BGP)
        as1r2.addDaemon(BGP)
        as1r3.addDaemon(BGP)

        self.addLinks((as1r1, as1r2), (as1r1, as1r3), (as1r2, as1r3))

        # AS2 routers
        as2r1, as2r2, as2r3 = self.addRouters("as2r1", "as2r2", "as2r3",
                                              config=RouterConfig)
        as2r1.addDaemon(BGP)
        as2r2.addDaemon(BGP)
        as2r3.addDaemon(BGP)

        self.addLinks((as2r1, as2r2), (as2r1, as2r3), (as2r2, as2r3))

        # AS3 routers
        as3r1, as3r2, as3r3 = self.addRouters("as3r1", "as3r2", "as3r3",
                                              config=RouterConfig)
        as3r1.addDaemon(BGP)
        as3r2.addDaemon(BGP)
        as3r3.addDaemon(BGP)

        self.addLinks((as3r1, as3r2), (as3r1, as3r3), (as3r2, as3r3))

        # Inter-AS links
        self.addLinks((as1r1, as2r1), (as2r3, as3r1))

        # Add an access list to 'any'
        # This can be an IP prefix or address instead
        all_al = AccessList('all', ('any',))

        # Add a community list to as2r1
        loc_pref = CommunityList('loc-pref', '2:80')

        # as2r1 set the local pref of all the route coming from as1r1 and matching the community list community to 80
        as2r1.get_config(BGP).set_local_pref(80, from_peer=as1r1, matching=(loc_pref,))

        # as1r1 set the community of all the route sent to as2r1 and matching the access list all_al to 2:80
        as1r1.get_config(BGP).set_community('2:80', to_peer=as2r1, matching=(all_al,))

        #  as3r1 set the med of all the route coming from as2r3 and matching the access list all_al to 50
        as3r1.get_config(BGP).set_med(50, to_peer=as2r3, matching=(all_al,))

        # AS1 is composed of 3 routers that have a full-mesh set of iBGP peering between them
        self.addiBGPFullMesh(1, routers=[as1r1, as1r2, as1r3])

        # AS2 only has one iBGP session between its routers
        self.addAS(2, routers=[as2r1, as2r2, as2r3])
        bgp_peering(self, as2r1, as2r3)

        # AS3 is also composed of 3 routers that have a full-mesh set of iBGP peering between them
        self.addAS(3, routers=[as3r1, as3r2, as3r3])
        bgp_fullmesh(self, [as3r1, as3r2, as3r3])

        # Establish eBGP sessions between ASes
        ebgp_session(self, as1r1, as2r1)
        ebgp_session(self, as2r3, as3r1)

        super().build(*args, **kwargs)

IPTables

This is currently mainly a proxy class to generate a list of static rules to pass to iptables.

It takes one parameter:

IPTables.set_defaults(defaults)
Parameters:rules – The (ordered) list of iptables Rules that should be executed or the list of Chain objects each containing rules. If a rule is an iterable of strings, these will be joined using a space.

These rules can be Rule objects with raw iptables command As such, see man iptables and man iptables-extensions to see the various table names, commands, pre-existing chains, …

class ipmininet.router.config.iptables.Rule(*args, **kw)

A Simple wrapper to represent an IPTable rule

Parameters:
  • args – the rule members, which will joined by a whitespace
  • table – Specify the table in which the rule should be installed. Defaults to filter.

In this example, only ICMP traffic will be allowed between the routers over IPv4 as well as non-privileged TCP ports:

from ipmininet.iptopo import IPTopo
from ipmininet.router.config import IPTables, Rule

class MyTopology(IPTopo):

    def build(self, *args, **kwargs):
        r1 = self.addRouter('r1')
        r2 = self.addRouter('r2')
        self.addLink(r1, r2)

        ip_rules = [Rule("-P INPUT DROP"),
                    Rule("-A INPUT -p tcp -m multiport --ports 80:1024 -j "
                         "DROP"),
                    Rule("-A INPUT -p tcp -m multiport ! --ports 1480 -j "
                         "ACCEPT"),
                    Rule("-A INPUT -p icmp -j ACCEPT")]
        r1.addDaemon(IPTables, rules=ip_rules)
        r2.addDaemon(IPTables, rules=ip_rules)

        super().build(*args, **kwargs)

You can use other classes for better abstraction. You can use the Chain class or one of its subclasses:

class ipmininet.router.config.iptables.Chain(table='filter', name='INPUT', default='DROP', rules=())

Chains are the hooks location for the respective tables. Tables support a limited subset of the available chains, see man iptables.

Build a chain description. For convenience, most parameters have more intuitive aliases than their one-letter CLI params.

Params table:The table on which the chain applies.
Params name:The chain name
Params default:The default verdict if nothing matches
Params rules:The ordered list of ChainRule to apply
class ipmininet.router.config.iptables.Filter(**kwargs)

The filter table acts as inbound, outbound, and forwarding firewall.

class ipmininet.router.config.iptables.InputFilter(**kwargs)

The inbound firewall.

class ipmininet.router.config.iptables.OutputFilter(**kwargs)

The outbound firewall.

class ipmininet.router.config.iptables.TransitFilter(**kwargs)

The forward firewall.

Each rule in the Chain instance is a ChainRule that you can use directly or use one of its subclasses:

class ipmininet.router.config.iptables.ChainRule(action='DROP', **kwargs)

Describe one set of matching criteria and the corresponding action when embedded in a chain.

Params action:The action to perform on matching packets.
Params oif:match in the output interface (optional)
Params iif:match on the input interface (optional)
Params src:match on the source address/network (optional)
Params dst:match on the destination address/network (optional)
Params proto:match on the protocol name/number (optional)
Params match:additional matching clauses, per man iptables (optional)
Params port:match on the source or destination port number/range (optional)
Params sport:match on the source port number/range/name (optional)
Params dport:match on the destination port number/range/name (optional)
class ipmininet.router.config.iptables.Allow(**kwargs)

Shorthand for ChainRule(action=’ACCEPT’, …). Expresses a whitelisting rule.

class ipmininet.router.config.iptables.Deny(**kwargs)

Shorthand for ChainRule(action=’DROP’, …). Expresses a blacklisting rule.

Each input value used for matching in ChainRule constructor can be negated with the NOT class:

class ipmininet.router.config.iptables.NOT(clause)

Negates the match clause :param clause: The value of the match clause to negate

This example implements the same properties as the previous one with the API.

from ipmininet.iptopo import IPTopo
from ipmininet.router.config import IPTables, InputFilter, NOT, \
    Deny, Allow

class MyTopology(IPTopo):

    def build(self, *args, **kwargs):
        r1 = self.addRouter('r1')
        r2 = self.addRouter('r2')
        self.addLink(r1, r2)

        ip_rules = [InputFilter(default="DROP", rules=[
                        Deny(proto='tcp', port='80:1024'),
                        Allow(proto='tcp', port=NOT(1480)),
                        Allow(proto='icmp'),
                    ])]
        r1.addDaemon(IPTables, rules=ip_rules)
        r2.addDaemon(IPTables, rules=ip_rules)

        super().build(*args, **kwargs)

IP6Tables

This class is the IPv6 equivalent to IPTables.

It also takes the same parameter (see previous section for details):

IP6Tables.set_defaults(defaults)
Parameters:rules – The (ordered) list of iptables Rules that should be executed or the list of Chain objects each containing rules. If a rule is an iterable of strings, these will be joined using a space.

OpenR

The OpenR daemon can be tuned by adding keyword arguments to router.addDaemon(OpenR, **kargs). Here is a list of the parameters:

OpenrDaemon._defaults(**kwargs)

Default parameters of the OpenR daemon. The template file openr.mako sets the default parameters listed here. See: https://github.com/facebook/openr/blob/master/openr/docs/Runbook.md.

Parameters:
  • alloc_prefix_len – Block size of allocated prefix in terms of it’s prefix length. In this case ‘/80’ prefix will be elected for a node. e.g. ‘face:b00c:0:0:1234::/80’. Default: 128.
  • assume_drained – Default: False.
  • config_store_filepath – Default: /tmp/aq_persistent_config_store.bin
  • decision_debounce_max_ms – Knobs to control how often to run Decision. On receipt of first even debounce is created with MIN time which grows exponentially up to max if there are more events before debounce is executed. This helps us to react to single network failures quickly enough (with min duration) while avoid high CPU utilization under heavy network churn. Default: 250.
  • decision_debounce_min_ms – Knobs to control how often to run Decision. On receipt of first even debounce is created with MIN time which grows exponentially up to max if there are more events before debounce is executed. This helps us to react to single network failures quickly enough (with min duration) while avoid high CPU utilization under heavy network churn. Default: 10.
  • decision_rep_port – Default: 60004.
  • domain – Name of domain this node is part of. OpenR will ‘only’ form adjacencies to OpenR instances within it’s own domain. This option becomes very useful if you want to run OpenR on two nodes adjacent to each other but belonging to different domains, e.g. Data Center and Wide Area Network. Usually it should depict the Network. Default: openr.
  • dryrun – OpenR will not try to program routes in it’s default configuration. You should explicitly set this option to false to proceed with route programming. Default: False.
  • enable_subnet_validation – OpenR supports subnet validation to avoid mis-cabling of v4 addresses on different subnets on each end of the link. Need to enable v4 and this flag at the same time to turn on validation. Default: True.
  • enable_fib_sync – Default: False.
  • enable_health_checker – OpenR can measure network health internally by pinging other nodes in the network and exports this information as counters or via breeze APIs. By default health checker is disabled. The expectation is that each node must have at least one v6 loopback addressed announced into the network for the reachability check. Default: False.
  • enable_legacy_flooding – Default: True.
  • enable_lfa – With this option, additional Loop-Free Alternate (LFA) routes can be computed, per RFC 5286, for fast failure recovery. Under the failure of all primary nexthops for a prefix, because of link failure, next best precomputed LFA will be used without need of an SPF run. Default: False.
  • enable_netlink_fib_handler – Knob to enable/disable default implementation of ‘FibService’ that comes along with OpenR for Linux platform. If you want to run your own FIB service then disable this option. Default: True.
  • enable_netlink_system_handler – Knob to enable/disable default implementation of ‘SystemService’ and ‘PlatformPublisher’ that comes along with OpenR for Linux platform. If you want to run your own SystemService then disable this option. Default: True.
  • enable_perf_measurement – Experimental feature to measure convergence performance. Performance information can be viewed via breeze API ‘breeze perf fib’. Default: True.
  • enable_prefix_alloc – Enable prefix allocator to elect and assign a unique prefix for the node. You will need to specify other configuration parameters below. Default: False.
  • enable_rtt_metric – Default mechanism for cost of a link is ‘1’ and hence cost of path is hop count. With this option you can ask OpenR to compute and use RTT of a link as a metric value. You should only use this for networks where links have significant delay, on the order of a couple of milliseconds. Using this for point-to-point links will cause lot of churn in metric updates as measured RTT will fluctuate a lot because of packet processing overhead. RTT is measured at application level and hence the fluctuation for point-to-point links. Default: True.
  • enable_secure_thrift_server – Flag to enable TLS for our thrift server. Disable this for plaintext thrift. Default: False.
  • enable_segment_routing – Experimental and partially implemented segment routing feature. As of now it only elects node/adjacency labels. In future we will extend it to compute and program FIB routes. Default: False.
  • enable_spark – Default: True.
  • enable_v4 – OpenR supports v4 as well but it needs to be turned on explicitly. It is expected that each interface will have v4 address configured for link local transport and v4/v6 topologies are congruent. Default: False.
  • enable_watchdog – Default: True.
  • fib_handler_port – TCP port on which ‘FibService’ will be listening. Default: 60100.
  • fib_rep_port – Default: 60009.
  • health_checker_ping_interval_s – Configure ping interval of the health checker. The below option configures it to ping all other nodes every 3 seconds. Default: 3.
  • health_checker_rep_port – Default: 60012.
  • ifname_prefix – Interface prefixes to perform neighbor discovery on. All interfaces whose names start with these are used for neighbor discovery. Default: “”
  • iface_regex_exclude – Default:”“.
  • iface_regex_include – Default: “”.
  • ip_tos – Set type of service (TOS) value with which every control plane packet from Open/R will be marked with. This marking can be used to prioritize control plane traffic (as compared to data plane) so that congestion in network doesn’t affect operations of Open/R. Default: 192
  • key_prefix_filters – This comma separated string is used to set the key prefixes when key prefix filter is enabled (See SET_LEAF_NODE). It is also set when requesting KEY_DUMP from peer to request keys that match one of these prefixes. Default: “”.
  • kvstore_flood_msg_per_sec – Default: 0.
  • kvstore_flood_msg_burst_size – Default: 0.
  • kvstore_flood_msg_per_sec – Default: 0.
  • kvstore_ttl_decrement_ms – Default: 1.
  • kvstore_zmq_hwm – Set buffering size for KvStore socket communication. Updates to neighbor node during flooding can be buffered upto this number. For larger networks where burst of updates can be high having high value makes sense. For smaller networks where burst of updates are low, having low value makes more sense. Default: 65536.
  • link_flap_initial_backoff_ms – Default: 1000.
  • link_flap_max_backoff_ms – Default: 60000.
  • link_monitor_cmd_port – Default: 60006.
  • loopback_iface – Indicates loopback address to which auto elected prefix will be assigned if enabled. Default: “lo”.
  • memory_limit_mb – Enforce upper limit on amount of memory in mega-bytes that open/r process can use. Above this limit watchdog thread will trigger crash. Service can be auto-restarted via system or some kind of service manager. This is very useful to guarantee protocol doesn’t cause trouble to other services on device where it runs and takes care of slow memory leak kind of issues. Default: 300.
  • minloglevel – Log messages at or above this level. Again, the numbers of severity levels INFO, WARNING, ERROR, and FATAL are 0, 1, 2, and 3, respectively. Default: 0.
  • node_name – Name of the OpenR node. Crucial setting if you run multiple nodes. Default: “”.
  • override_loopback_addr – Whenever new address is elected for a node, before assigning it to interface all previously allocated prefixes or other global prefixes will be overridden with the new one. Use it with care! Default: False.
  • prefix_manager_cmd_port – Default: 60011.
  • prefixes – Static list of comma separate prefixes to announce from the current node. Can’t be changed while running. Default: “”.
  • redistribute_ifaces – Comma separated list of interface names whose ‘/32’ (for v4) and ‘/128’ (for v6) should be announced. OpenR will monitor address add/remove activity on this interface and announce it to rest of the network. Default: “lo1”.
  • seed_prefix – In order to elect a prefix for the node a super prefix to elect from is required. This is only applicable when ‘ENABLE_PREFIX_ALLOC’ is set to true. Default: “”.
  • set_leaf_node – Sometimes a node maybe a leaf node and have only one path in to network. This node does not require to keep track of the entire topology. In this case, it may be useful to optimize memory by reducing the amount of key/vals tracked by the node. Setting this flag enables key prefix filters defined by KEY_PREFIX_FILTERS. A node only tracks keys in kvstore that matches one of the prefixes in KEY_PREFIX_FILTERS. Default: False.
  • set_loopback_address – If set to true along with ‘ENABLE_PREFIX_ALLOC’ then second valid IP address of the block will be assigned onto ‘LOOPBACK_IFACE’ interface. e.g. in this case ‘face:b00c:0:0:1234::1/80’ will be assigned on ‘lo’ interface. Default: False.
  • spark_fastinit_keepalive_time_ms – When interface is detected UP, OpenR can perform fast initial neighbor discovery as opposed to slower keep alive packets. Default value is 100 which means neighbor will be discovered within 200ms on a link. Default: 100.
  • spark_hold_time_s – Hold time indicating time in seconds from it’s last hello after which neighbor will be declared as down. Default: 30.
  • spark_keepalive_time_s – How often to send spark hello messages to neighbors. Default: 3.
  • static_prefix_alloc – Default: False.
  • tls_acceptable_peers – A comma separated list of strings. Strings are x509 common names to accept SSL connections from. Default: “”
  • tls_ecc_curve_name – If we are running an SSL thrift server, this option specifies the eccCurveName for the associated wangle::SSLContextConfig. Default: “prime256v1”.
  • tls_ticket_seed_path – If we are running an SSL thrift server, this option specifies the TLS ticket seed file path to use for client session resumption. Default: “”.
  • x509_ca_path – If we are running an SSL thrift server, this option specifies the certificate authority path for verifying peers. Default: “”.
  • x509_cert_path – If we are running an SSL thrift server, this option specifies the certificate path for the associated wangle::SSLContextConfig. Default: “”.
  • x509_key_path – If we are running an SSL thrift server, this option specifies the key path for the associated wangle::SSLContextConfig. Default: “”.
  • logbufsecs – Default: 0
  • log_dir – Directory to store log files at. The folder must exist. Default: /var/log.
  • max_log_size – Default: 1.
  • v – Show all verbose ‘VLOG(m)’ messages for m less or equal the value of this flag. Use higher value for more verbose logging. Default: 1.

OSPF

You can add keyword arguments to router.addDaemon(OSPF, **kargs) to change the following parameters:

OSPF.set_defaults(defaults)
Parameters:
  • debug – the set of debug events that should be logged
  • dead_int – Dead interval timer
  • hello_int – Hello interval timer
  • priority – priority for the interface, used for DR election
  • redistribute – set of OSPFRedistributedRoute sources

This daemon also uses the following interface parameters:

  • igp_passive: Whether the interface is passive (default value: False)
  • ospf_dead_int: Dead interval timer specific to this interface (default value: dead_int parameter)
  • ospf_hello_int: Hello interval timer specific to this interface (default value: hello_int parameter)
  • ospf_priority: Priority for this specific to this interface (default value: priority parameter)

OSPF uses two link parameters:

  • igp_cost: The IGP cost of the link (default value: 1)
  • igp_area: The OSPF area of the link (default value: ‘0.0.0.0’)

We can pass parameters to links and interfaces when calling addLink():

from ipmininet.iptopo import IPTopo

class MyTopology(IPTopo):

    def build(self, *args, **kwargs):

        # Add routers (OSPF daemon is added by default with the default config)
        router1, router2 = self.addRouters("router1", "router2")

        # Add link
        l = self.addLink(router1, router2,
                         igp_cost=5, igp_area="0.0.0.1")  # Link parameters
        l[router1].addParams(ospf_dead_int=1)             # Router1 interface parameters
        l[router2].addParams(ospf_priority=1)             # Router2 interface parameters

        super().build(*args, **kwargs)

OSPF can use an overlay to declare with routers or links are completely in a given OSPF area. The following code adds all the interfaces of router r1 to ‘0.0.0.1’ while the link between r2 and r3 is in area ‘0.0.0.5’:

from ipmininet.iptopo import IPTopo

class MyTopology(IPTopo):

    def build(self, *args, **kwargs):

        # Add routers (OSPF daemon is added by default with the default config)
        r1, r2, r3 = self.addRouters("r1", "r2", "r3")

        # Add links
        self.addLinks((r1, r2), (r1, r3), (r2, r3))

        # Define OSPF areas
        self.addOSPFArea('0.0.0.1', routers=[r1], links=[])
        self.addOSPFArea('0.0.0.5', routers=[], links=[(r2, r3)])

        super().build(*args, **kwargs)

OSPF6

OSPF6 supports the same parameters as OSPF. It supports the following parameter:

OSPF6.set_defaults(defaults)
Parameters:
  • debug – the set of debug events that should be logged
  • dead_int – Dead interval timer
  • hello_int – Hello interval timer
  • priority – priority for the interface, used for DR election
  • redistribute – set of OSPFRedistributedRoute sources
  • instance_id – the number of the attached OSPF instance

OSPF6 uses one link parameter:

  • igp_cost: The IGP cost of the link (default value: 1)

It uses the following interface parameters:

  • igp_passive: Whether the interface is passive (default value: False)
  • instance_id: The number of the attached OSPF6 instance (default value: 0)
  • ospf6_dead_int: Dead interval timer specific to this interface (default value: ospf_dead_int parameter)
  • ospf6_hello_int: Hello interval timer specific to this interface (default value: ospf_hello_int parameter)
  • ospf6_priority: Priority for this specific to this interface (default value: ospf_priority parameter)
from ipmininet.iptopo import IPTopo

class MyTopology(IPTopo):

    def build(self, *args, **kwargs):

        # Add routers (OSPF daemon is added by default with the default config)
        router1, router2 = self.addRouters("router1", "router2")

        # Add link
        l = self.addLink(router1, router2,
                         igp_cost=5)            # Link parameters
        l[router1].addParams(ospf6_dead_int=1)  # Router1 interface parameters
        l[router2].addParams(ospf6_priority=1)  # Router2 interface parameters

        super().build(*args, **kwargs)

PIMD

When adding PIMD to a router with router.addDaemon(PIMD, **kargs), we can give the following parameters:

PIMD.set_defaults(defaults)
Parameters:
  • debug – the set of debug events that should be logged
  • multicast_ssm – Enable pim ssm mode by default or not
  • multicast_igmp – Enable igmp by default or not

Named

When adding Named to a host with host.addDaemon(Named, **kargs), we can give the following parameters:

Named.set_defaults(defaults)
Parameters:
  • log_severity – It controls the logging levels and may take the values defined. Logging will occur for any message equal to or higher than the level specified (=>) lower levels will not be logged. These levels are ‘critical’, ‘error’, ‘warning’, ‘notice’, ‘info’, ‘debug’ and ‘dynamic’.
  • dns_server_port – The port number of the dns server
  • hint_root_zone – Add hints to root dns servers if this is not the root server

Named uses an overlay to declare DNS zones:

DNSZone.__init__(name: str, dns_master: str, dns_slaves: Sequence[str] = (), records: Sequence[ipmininet.host.config.named.DNSRecord] = (), nodes: Sequence[str] = (), refresh_time=86400, retry_time=7200, expire_time=3600000, min_ttl=172800, ns_domain_name: Optional[str] = None, subdomain_delegation=True, delegated_zones: Sequence[DNSZone] = ())
Parameters:
  • name – The domain name of the zone
  • dns_master – The name of the master DNS server
  • dns_slaves – The list of names of DNS slaves
  • records – The list of DNS Records to be included in the zone
  • nodes – The list of nodes for which one A/AAAA record has to be created for each of their IPv4/IPv6 addresses
  • refresh_time – The number of seconds before the zone should be refreshed
  • retry_time – The number of seconds before a failed refresh should be retried
  • expire_time – The upper limit in seconds before a zone is considered no longer authoritative
  • min_ttl – The negative result TTL
  • ns_domain_name – If it is defined, it is the suffix of the domain of the name servers, otherwise, parameter ‘name’ is used.
  • subdomain_delegation – If set, additional records for subdomain name servers are added to guarantee correct delegation
  • delegated_zones – Additional delegated zones

The following code will create a DNS server in dns_master and dns_slave with one DNS zone: ‘mydomain.org’. This will also create one reverse DNS zones for both IPv4 and IPv6.

from ipmininet.iptopo import IPTopo
from ipmininet.host.config import Named

class MyTopology(IPTopo):

    def build(self, *args, **kwargs):

        # Add router
        r = self.addRouter("r")

        # Add hosts
        h1 = self.addHost("h1")
        h2 = self.addHost("h2")
        h3 = self.addHost("h3")
        dns_master = self.addHost("dns_master")
        dns_master.addDaemon(Named)
        dns_slave = self.addHost("dns_slave")
        dns_slave.addDaemon(Named)

        # Add links
        for h in self.hosts():
            self.addLink(r, h)

        # Define a DNS Zone
        self.addDNSZone(name="mydomain.org",
                        dns_master=dns_master,
                        dns_slaves=[dns_slave],
                        nodes=self.hosts())

        super().build(*args, **kwargs)

By default, the DNSZone will create all the NS, A and AAAA records. If you need to change the TTL of one of these record, you can define it explicitly.

from ipmininet.iptopo import IPTopo
from ipmininet.host.config import Named, NSRecord

class MyTopology(IPTopo):

    def build(self, *args, **kwargs):

        # Add router
        r = self.addRouter("r")

        # Add hosts
        h1 = self.addHost("h1")
        h2 = self.addHost("h2")
        h3 = self.addHost("h3")
        dns_master = self.addHost("dns_master")
        dns_master.addDaemon(Named)
        dns_slave = self.addHost("dns_slave")
        dns_slave.addDaemon(Named)

        # Add links
        for h in self.hosts():
            self.addLink(r, h)

        # Define a DNS Zone
        records = [NSRecord("mydomain.org", dns_master, ttl=120), NSRecord("mydomain.org", dns_slave, ttl=120)]
        self.addDNSZone(name="mydomain.org",
                        dns_master=dns_master,
                        dns_slaves=[dns_slave],
                        records=records,
                        nodes=self.hosts())

        super().build(*args, **kwargs)

By default, one reverse DNS zone are created for all A records and another for all AAAA records. However, you may want to split the PTR records more than two different zones. You may also want to change the default values of the zones or their PTR records. To change this, you can declare the reverse DNS zone yourself. No need to add the PTR records that you don’t want to modify, they will be created for you and placed in the zone that you declared if they fit in its domain name. Otherwise, another zone will be created.

from ipmininet.iptopo import IPTopo
from ipmininet.host.config import Named, PTRRecord
from ipaddress import ip_address

class MyTopology(IPTopo):

    def build(self, *args, **kwargs):

        # Add router
        r = self.addRouter("r")

        # Add hosts
        h1 = self.addHost("h1")
        h2 = self.addHost("h2")
        h3 = self.addHost("h3")
        dns_master = self.addHost("dns_master")
        dns_master.addDaemon(Named)
        dns_slave = self.addHost("dns_slave")
        dns_slave.addDaemon(Named)

        # Add links
        for h in [h1, h2, dns_master, dns_slave]:
            self.addLink(r, h)
        lrh3 = self.addLink(r, h3)
        self.addSubnet(links=[lrh3], subnets=["192.168.0.0/24", "fc00::/64"])

        # Define a DNS Zone
        self.addDNSZone(name="mydomain.org",
                        dns_master=dns_master,
                        dns_slaves=[dns_slave],
                        nodes=self.hosts())

        # Change the TTL of one PTR record and the retry_time of its zone
        ptr_record = PTRRecord("fc00::2", h3 + ".mydomain.org", ttl=120)
        reverse_domain_name = ip_address("fc00::").reverse_pointer[-10:]  # keeps "f.ip6.arpa"
        self.addDNSZone(name=reverse_domain_name, dns_master=dns_master, dns_slaves=[dns_slave],
                        records=[ptr_record], ns_domain_name="mydomain.org", retry_time=8200)

        super().build(*args, **kwargs)

By default, subdomains authority is delegated to the direct child zone name servers by copying the NS records of the child zone to the parent zone. You can remove this behavior, by zone, by setting the parameter subdomain_delegation to False. You can also delegate more zones by using the delegated_zones parameter.

By default, all DNS servers that are not root DNS servers have hints to the root DNS servers (if the root zone is added to the topology). This behavior can be disabled by setting the parameter hint_root_zone of Named to False.

RADVD

When adding RADVD to a router with router.addDaemon(RADVD, **kargs), we can give the following parameters:

RADVD.set_defaults(defaults)
Parameters:debuglevel – Turn on debugging information. Takes an integer between 0 and 5, where 0 completely turns off debugging, and 5 is extremely verbose. (see radvd(8) for more details)

This daemon also uses the following interface parameters:

  • ra: A list of AdvPrefix objects that describes the prefixes to advertise
  • rdnss: A list of AdvRDNSS objects that describes the DNS servers to advertise
from ipmininet.iptopo import IPTopo
from ipmininet.router.config import RADVD, AdvPrefix, AdvRDNSS

class MyTopology(IPTopo):

    def build(self, *args, **kwargs):

        r = self.addRouter('r')
        r.addDaemon(RADVD, debug=0)

        h = self.addHost('h')
        dns = self.addHost('dns')

        lrh = self.addLink(r, h)
        lrh[r].addParams(ip=("2001:1341::1/64", "2001:2141::1/64"),
                         ra=[AdvPrefix("2001:1341::/64", valid_lifetime=86400, preferred_lifetime=14400),
                             AdvPrefix("2001:2141::/64")],
                         rdnss=[AdvRDNSS("2001:89ab::d", max_lifetime=25),
                                AdvRDNSS("2001:cdef::d", max_lifetime=25)])
        lrdns = self.addLink(r, dns)
        lrdns[r].addParams(ip=("2001:89ab::1/64", "2001:cdef::1/64"))    # Static IP addresses
        lrdns[dns].addParams(ip=("2001:89ab::d/64", "2001:cdef::d/64"))  # Static IP addresses

        super().build(*args, **kwargs)

Instead of giving all addresses explicitly, you can use AdvConnectedPrefix() to advertise all the prefixes of the interface. You can also give the name of the DNS server (instead of an IP address) in the AdvRDNSS constructor.

from ipmininet.iptopo import IPTopo
from ipmininet.router.config import RouterConfig, RADVD, AdvConnectedPrefix, AdvRDNSS

class MyTopology(IPTopo):

    def build(self, *args, **kwargs):

        r = self.addRouter('r')
        r.addDaemon(RADVD, debug=0)

        h = self.addHost('h')
        dns = self.addHost('dns')

        lrh = self.addLink(r, h)
        lrh[r].addParams(ip=("2001:1341::1/64", "2001:2141::1/64"),
                         ra=[AdvConnectedPrefix(valid_lifetime=86400, preferred_lifetime=14400)],
                         rdnss=[AdvRDNSS(dns, max_lifetime=25)])
        lrdns = self.addLink(r, dns)
        lrdns[r].addParams(ip=("2001:89ab::1/64", "2001:cdef::1/64"))    # Static IP addresses
        lrdns[dns].addParams(ip=("2001:89ab::d/64", "2001:cdef::d/64"))  # Static IP addresses

        super().build(*args, **kwargs)

RIPng

When adding RIPng to a router with router.addDaemon(RIPng, **kargs), we can give the following parameters:

RIPng.set_defaults(defaults)
Parameters:
  • debug – the set of debug events that should be logged (default: []).
  • redistribute – set of RIPngRedistributedRoute sources (default: []).
  • split_horizon – the daemon uses the split-horizon method (default: False).
  • split_horizon_with_poison – the daemon uses the split-horizon. with reversed poison method. If both split_horizon_with_poison and split_horizon are set to True, RIPng will use the split-horizon with reversed poison method (default: True).
  • update_timer – routing table timer value in second (default value:30).
  • timeout_timer – routing information timeout timer (default value:180).
  • garbage_timer – garbage collection timer (default value:120).

RIPng uses one link parameter:

  • igp_metric: the metric of the link (default value: 1)

We can pass parameters to links when calling addLink():

from ipmininet.iptopo import IPTopo
from ipmininet.router.config import RIPng, RouterConfig

class MyTopology(IPTopo):

    def build(self, *args, **kwargs):
        # We use RouterConfig to prevent OSPF6 to be run
        r1, r2 = self.addRouters("r1", "r2", config=RouterConfig)
        h1 = self.addHost("h1")
        h2 = self.addHost("h2")

        self.addLink(r1, r2, igp_metric=10)  # The IGP metric is set to 10
        self.addLinks((r1, h1), (r2, h2))

        r1.addDaemon(RIPng)
        r2.addDaemon(RIPng)

        super().build(*args, **kwargs)

SSHd

The SSHd daemon does not take any parameter. The SSH private and public keys are randomly generated but you can retrieve their paths with the following line:

from ipmininet.router.config.sshd import KEYFILE, PUBKEY

Zebra

FRRouting daemons (i.e., OSPF, OSPF6, BGP and PIMD) require this daemon and automatically trigger it. So we only need to explicitly add it through router.addDaemon(Zebra, **kargs) if we want to change one of its parameters:

Zebra.set_defaults(defaults)
Parameters:
  • debug – the set of debug events that should be logged
  • access_lists – The set of AccessList to create, independently from the ones already included by route_maps
  • route_maps – The set of RouteMap to create