Using IPv6 Segment Routing

IPMininet enables you to use the Linux implementation of IPv6 Segment Routing (SRv6).

Using this part of IPMininet requires a recent version of the linux kernel (see https://segment-routing.org/index.php/Implementation/Installation) for more details.

Note that all the abstractions presented here have to be used once the network is setup. We leverage the following method to do it:

IPTopo.post_build(net: ipmininet.ipnet.IPNet)

A method that will be invoked once the topology has been fully built and before it is started.

Parameters:net – The freshly built (Mininet) network

Activation

By default, routers and hosts will drop packets with IPv6 Segment Routing Header. You can enable IPv6 Segment Routing on a per-node basis with the following function.

ipmininet.srv6.enable_srv6(node: ipmininet.router.__router.IPNode)

Enable IPv6 Segment Routing parsing on all interfaces of the node

Here is an example of topology where IPv6 Segment Routing is enabled on all hosts and routers:

from ipmininet.iptopo import IPTopo
from ipmininet.srv6 import enable_srv6


class MyTopology(IPTopo):

    def build(self, *args, **kwargs):
        r1 = self.addRouter("r1")
        r2 = self.addRouter("r2")
        h1 = self.addHost("h1")
        h2 = self.addHost("h2")

        self.addLink(r1, r2)
        self.addLink(r1, h1)
        self.addLink(r2, h2)

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

    def post_build(self, net):
        for n in net.hosts + net.routers:
            enable_srv6(n)
        super().post_build(net)

Using any of the following SRv6 abstractions also enables SRv6 on all hosts and routers.

Insertion and encapsulation

The following abstraction enables you to insert or encapsulate SRH in your packets based on the destination.

SRv6Encap.__init__(net: ipmininet.ipnet.IPNet, node: Union[ipmininet.router.__router.IPNode, str], to: Union[str, ipaddress.IPv6Network, ipmininet.router.__router.IPNode, ipmininet.link.IPIntf] = '::/0', through: List[Union[str, ipaddress.IPv6Address, ipmininet.router.__router.IPNode, ipmininet.link.IPIntf]] = (), mode='encap', cost=1)
Parameters:
  • net – The IPNet instance
  • node – The IPNode object on which the route has to be installed or the name of this node
  • to – Either directly the prefix, an IPNode, a name of an IPNode or an IPIntf object so that it matches all its addresses.
  • through – A list of nexthops to set in the IPv6 Segment Routing Header. Each element of the list can either be an IPv6 address, an IPIntf or an IPNode. In both later cases, the default IPv6 address is selected.
  • mode – Either SRv6Encap.ENCAP or SRv6Encap.INLINE whether the route should encapsulate packets in an outer IPv6 packet with the SRH or insert the SRH directly inside the packet.
  • cost – The cost of using the route: routes with lower cost is preferred if the destination prefix is the same.
from ipmininet.iptopo import IPTopo
from ipmininet.srv6 import SRv6Encap


class MyTopology(IPTopo):

    def build(self, *args, **kwargs):
        r1 = self.addRouter("r1")
        r2 = self.addRouter("r2")
        h1 = self.addHost("h1")
        h2 = self.addHost("h2")

        self.addLink(r1, r2)
        self.addLink(r1, h1)
        self.addLink(r2, h2)

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

    def post_build(self, net):
        # No need to enable SRv6 because the call to the abstraction
        # triggers it

        # This adds an SRH encapsulation route on h1 for packets to h2
        SRv6Encap(net=net, node="h1", to="h2",
                  # You can specify the intermediate point with any
                  # of the host, its name, an interface or the address
                  # itself
                  through=[net["r1"], "r1", net["r1"].intf("lo"),
                           net["r1"].intf("lo").ip6],
                  # Either insertion (INLINE) or encapsulation (ENCAP)
                  mode=SRv6Encap.INLINE)
        super().post_build(net)

Advanced configuration

The advanced processing of SRv6-enabled packets detailed in https://segment-routing.org/index.php/Implementation/AdvancedConf is also supported by IPMininet with the following abstractions:

SRv6EndFunction.__init__(net: ipmininet.ipnet.IPNet, node: Union[ipmininet.router.__router.IPNode, str], to: Union[str, ipaddress.IPv6Network, ipmininet.router.__router.IPNode, ipmininet.link.IPIntf] = '::/0', cost=1, table: Optional[ipmininet.srv6.LocalSIDTable] = None)
Parameters:
  • net – The IPNet instance
  • node – The IPNode object on which the route has to be installed or the name of this node
  • to – Either directly the prefix, an IPNode, a name of an IPNode or an IPIntf object so that it matches all its addresses.
  • cost – The cost of using the route: routes with lower cost is preferred if the destination prefix is the same.
  • table – Install the route into the specified table instead of the main one.
SRv6EndXFunction.__init__(nexthop: Union[str, ipaddress.IPv6Address, ipmininet.link.IPIntf, ipmininet.router.__router.IPNode], *args, **kwargs)
Parameters:
  • net – The IPNet instance
  • node – The IPNode object on which the route has to be installed
  • to – Either directly the prefix, an IPNode or an IPIntf object so that it matches all its addresses.
  • cost – The cost of using the route: routes with lower cost is preferred if the destination prefix is the same.
  • table – Install the route into the specified table instead of the main one.
  • nexthop – The nexthop to consider when forwarding the packet. It can be an IPv6 address, an IPIntf or an IPNode. In both later cases, the default IPv6 address is selected.
SRv6EndTFunction.__init__(lookup_table: str, *args, **kwargs)
Parameters:
  • net – The IPNet instance
  • node – The IPNode object on which the route has to be installed
  • to – Either directly the prefix, an IPNode or an IPIntf object so that it matches all its addresses.
  • cost – The cost of using the route: routes with lower cost is preferred if the destination prefix is the same.
  • table – Install the route into the specified table instead of the main one.
  • lookup_table – The packet is forwarded to the nexthop looked up in this specified routing table
SRv6EndDX2Function.__init__(interface: Union[str, ipmininet.link.IPIntf], *args, **kwargs)
Parameters:
  • net – The IPNet instance
  • node – The IPNode object on which the route has to be installed
  • to – Either directly the prefix, an IPNode or an IPIntf object so that it matches all its addresses.
  • cost – The cost of using the route: routes with lower cost is preferred if the destination prefix is the same.
  • table – Install the route into the specified table instead of the main one.
  • interface – The packet is forwarded to this specific interface
SRv6EndDX6Function.__init__(nexthop: Union[str, ipaddress.IPv6Address, ipmininet.link.IPIntf, ipmininet.router.__router.IPNode], *args, **kwargs)
Parameters:
  • net – The IPNet instance
  • node – The IPNode object on which the route has to be installed
  • to – Either directly the prefix, an IPNode or an IPIntf object so that it matches all its addresses.
  • cost – The cost of using the route: routes with lower cost is preferred if the destination prefix is the same.
  • table – Install the route into the specified table instead of the main one.
  • nexthop – The nexthop to consider when forwarding the packet. It can be an IPv6 address, an IPIntf or an IPNode. In both later cases, the default IPv6 address is selected.
SRv6EndDX4Function.__init__(nexthop: Union[str, ipaddress.IPv4Address, ipmininet.link.IPIntf, ipmininet.router.__router.IPNode], *args, **kwargs)
Parameters:
  • net – The IPNet instance
  • node – The IPNode object on which the route has to be installed
  • to – Either directly the prefix, an IPNode or an IPIntf object so that it matches all its addresses.
  • cost – The cost of using the route: routes with lower cost is preferred if the destination prefix is the same.
  • table – Install the route into the specified table instead of the main one.
  • nexthop – The nexthop to consider when forwarding the packet. It can be an IPv4 address, an IPIntf or an IPNode. In both later cases, the default IPv4 address is selected.
SRv6EndDT6Function.__init__(lookup_table: str, *args, **kwargs)
Parameters:
  • net – The IPNet instance
  • node – The IPNode object on which the route has to be installed
  • to – Either directly the prefix, an IPNode or an IPIntf object so that it matches all its addresses.
  • cost – The cost of using the route: routes with lower cost is preferred if the destination prefix is the same.
  • table – Install the route into the specified table instead of the main one.
  • lookup_table – The packet is forwarded to the nexthop looked up in this specified routing table
SRv6EndB6Function.__init__(segments: List[Union[str, ipaddress.IPv6Address, ipmininet.router.__router.IPNode, ipmininet.link.IPIntf]], *args, **kwargs)
Parameters:
  • net – The IPNet instance
  • node – The IPNode object on which the route has to be installed
  • to – Either directly the prefix, an IPNode or an IPIntf object so that it matches all its addresses.
  • cost – The cost of using the route: routes with lower cost is preferred if the destination prefix is the same.
  • table – Install the route into the specified table instead of the main one.
  • segments – A list of segments to set in the IPv6 Segment Routing Header. Each element of the list can either be an IPv6 address, an IPIntf or an IPNode. In both later cases, the default IPv6 address is selected.
SRv6EndB6EncapsFunction.__init__(segments: List[Union[str, ipaddress.IPv6Address, ipmininet.router.__router.IPNode, ipmininet.link.IPIntf]], *args, **kwargs)
Parameters:
  • net – The IPNet instance
  • node – The IPNode object on which the route has to be installed
  • to – Either directly the prefix, an IPNode or an IPIntf object so that it matches all its addresses.
  • cost – The cost of using the route: routes with lower cost is preferred if the destination prefix is the same.
  • table – Install the route into the specified table instead of the main one.
  • segments – A list of segments to set in the IPv6 Segment Routing Header. Each element of the list can either be an IPv6 address, an IPIntf or an IPNode. In both later cases, the default IPv6 address is selected.

They are used in the same way as SRv6Encap in the previous section. You can instantiate any of these classes to make add the corresponding route to your network.

However, these special routes are supposed to be in a separate tables, called a Local SID table so that we can split plain routing and SRH management. To create this new table in a router, you can instantiate the following class:

LocalSIDTable.__init__(node: ipmininet.router.__router.IPNode, matching: Iterable[Union[str, ipaddress.IPv6Network, ipmininet.router.__router.IPNode, ipmininet.link.IPIntf]] = ('::/0', ))
Parameters:
  • node – The node on which the table is added
  • matching – The list of destinations whose processing is delegated to the table; destinations can be raw prefixes, interfaces (implying all networks on the interface) or nodes (implying all networks on its loopback interface)

This LocalSIDTable instance can then be used to instantiate the previous classes to insert the routes in this table instead of the default one.

from ipmininet.iptopo import IPTopo
from ipmininet.srv6 import SRv6Encap, SRv6EndFunction, LocalSIDTable


class MyTopology(IPTopo):

    def build(self, *args, **kwargs):
        r1 = self.addRouter("r1")
        r2 = self.addRouter("r2", lo_addresses="2042:2:2::1/64")
        h1 = self.addHost("h1")
        h2 = self.addHost("h2")

        self.addLink(r1, r2)
        self.addLink(r1, h1)
        self.addLink(r2, h2)

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

    def post_build(self, net):
        # No need to enable SRv6 because the call to the abstraction
        # triggers it

        # This adds an SRH encapsulation route on h1 for packets to h2
        SRv6Encap(net=net, node="h1", to="h2",
                  # You can specify the intermediate point with any
                  # of the host, its name, an interface or the address
                  # itself
                  through=[net["r1"], "r1", net["r1"].intf("lo"),
                           net["r1"].intf("lo").ip6, "2042:2:2::200"],
                  # Either insertion (INLINE) or encapsulation (ENCAP)
                  mode=SRv6Encap.INLINE)

        # Creates a LocalSIDTable table handling all traffic for
        # 2042:2:2::/64 (except 2042:2:2::1 which is the loopback address)
        sid_table = LocalSIDTable(net["r2"], matching=["2042:2:2::/64"])

        # Receiving a packet on r2 with "2042:2:2::200" as active segment
        # will trigger the function 'End' which is regular SRH processing
        SRv6EndFunction(net=net, node="r2", to="2042:2:2::200",
                        table=sid_table)

        super().post_build(net)