.. _mpls:

MPLS howto
----------

Short introduction into Linux MPLS. Requirements:

* kernel >= 4.4
* modules: `mpls_router`, `mpls_iptunnel`
* `$ sudo sysctl net.mpls.platform_labels=$x`, where `$x` -- number of labels
* `pyroute2` >= 0.4.0

MPLS labels
===========

Possible label formats::

    # int
    "newdst": 20

    # list of ints
    "newdst": [20]
    "newdst": [20, 30]

    # string
    "newdst": "20/30"

    # dict
    "newdst": {"label": 20}

    # list of dicts
    "newdst": [{"label": 20, "tc": 0, "bos": 0, "ttl": 16},
               {"label": 30, "tc": 0, "bos": 1, "ttl": 16}]


IPRoute
=======

MPLS routes
~~~~~~~~~~~

Label swap::

    from pyroute2 import IPRoute
    from pyroute2.common import AF_MPLS

    ipr = IPRoute()
    # get the `eth0` interface's index:
    idx = ipr.link_lookup(ifname="eth0")[0]
    # create the request
    req = {"family": AF_MPLS,
           "oif": idx,
           "dst": 20,
           "newdst": [30]}
    # set up the route
    ipr.route("add", **req)

Please notice that "dst" can specify only one label, even being a list.
Label push::

    req = {"family": AF_MPLS,
           "oif": idx,
           "dst": 20,
           "newdst": [20, 30]}
    ipr.route("add", **req)

One can set up also the `via` field::

    from socket import AF_INET

    req = {"family": AF_MPLS,
           "oif": idx,
           "dst": 20,
           "newdst": [30],
           "via": {"family": AF_INET,
                   "addr": "1.2.3.4"}}
    ipr.route("add", **req)

MPLS lwtunnel
~~~~~~~~~~~~~

To inject IP packets into MPLS::

    req = {"dst": "1.2.3.0/24",
           "oif": idx,
           "encap": {"type": "mpls",
                     "labels": [202, 303]}}
    ipr.route("add", **req)

NDB
===

.. note:: basic MPLS routes management in NDB since version 0.5.11

List MPLS routes::

    >>> from pyroute2.common import AF_MPLS
    >>> ndb.routes.dump().filter(family=AF_MPLS)
    ('localhost', 0, 28, 20, 0, 0, 254, 4, 0, 1, 0, ...
    ('localhost', 0, 28, 20, 0, 0, 254, 4, 0, 1, 0, ...

    >>> ndb.routes.dump().filter(family=AF_MPLS).select('oif', 'dst', 'newdst')
    (40627, '[{"label": 16, "tc": 0, "bos": 1, "ttl": 0}]', '[{"label": 500, ...
    (40627, '[{"label": 40, "tc": 0, "bos": 1, "ttl": 0}]', '[{"label": 40, ...

List lwtunnel routes::

    >>> ndb.routes.dump().filter(lambda x: x.encap is not None)
    ('localhost', 0, 2, 24, 0, 0, 254, 4, 0, 1, 16, '10.255.145.0', ...
    ('localhost', 0, 2, 24, 0, 0, 254, 4, 0, 1, 0, '192.168.142.0', ...

    >>> ndb.routes.dump().filter(lambda x: x.encap is not None).select('dst', 'encap') 
    ('10.255.145.0', '[{"label": 20, "tc": 0, "bos": 0, "ttl": 0}, ...
    ('192.168.142.0', '[{"label": 20, "tc": 0, "bos": 0, "ttl": 0}, ...

Create MPLS routes::

    >>> from pyroute2.common import AF_MPLS
    >>> ndb.routes.create(family=AF_MPLS,
                          dst=128,                       # label
                          oif=1,                         # output interface
                          newdst=[128, 132]).commit()    # label stack

Create lwtunnel::

    >>> ndb.routes.create(dst='192.168.145.0/24',
                          gateway='192.168.140.5', 
                          encap={'type': 'mpls',
                                 'labels': [128, 132]}).commit()

