1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
|
.. 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
"dst": 20
# list of ints
"newdst": [20]
"newdst": [20, 30]
# string
"labels": "20/30"
Any of these notations should be accepted by `pyroute2`, if not -- try
another format and submit an issue to the project github page. The code
is quite new, some issues are possible.
Refer also to the test cases, there are many usage samples:
* `tests/general/test_ipr.py`
* `tests/general/test_ipdb.py`
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)
Notice, that `dst` is a single label, while `newdst` is a stack. 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)
IPDB
====
MPLS routes
~~~~~~~~~~~
The `IPDB` database also supports MPLS routes, they are reflected in the
`ipdb.routes.tables["mpls"]`::
>>> (ipdb
... .routes
... .add({"family": AF_MPLS,
... "oif": ipdb.interfaces["eth0"]["index"],
... "dst": 20,
... "newdst": [30]})
... .commit())
<skip>
>>> (ipdb
... .routes
... .add({"family": AF_MPLS,
... "oif": ipdb.interfaces["eth0"]["index"],
... "dst": 22,
... "newdst": [22, 42]})
... .commit())
<skip>
>>> ipdb.routes.tables["mpls"].keys()
[20, 22]
Pls notice, that there is only one MPLS routing table.
Multipath MPLS::
with IDPB() as ipdb:
(ipdb
.routes
.add({"family": AF_MPLS,
"dst": 20,
"multipath": [{"via": {"family": AF_INET,
"addr": "10.0.0.2"},
"oif": ipdb.interfaces["eth0"]["index"],
"newdst": [30]},
{"via": {"family": AF_INET,
"addr": "10.0.0.3"},
"oif": ipdb.interfaces["eth0"]["index"],
"newdst": [40]}]})
.commit())
MPLS lwtunnel
~~~~~~~~~~~~~
LWtunnel routes reside in common route tables::
with IPDB() as ipdb:
(ipdb
.routes
.add({"dst": "1.2.3.0/24",
"oif": ipdb.interfaces["eth0"]["index"],
"encap": {"type": "mpls",
"labels": [22]}})
.commit())
print(ipdb.routes["1.2.3.0/24"])
Multipath MPLS lwtunnel::
with IPDB() as ipdb:
(ipdb
.routes
.add({"dst": "1.2.3.0/24",
"table": 200,
"multipath": [{"oif": ipdb.interfaces["eth0"]["index"],
"gateway": "10.0.0.2",
"encap": {"type": "mpls",
"labels": [200, 300]}},
{"oif": ipdb.interfaces["eth1"]["index"],
"gateway": "172.16.0.2",
"encap": {"type": "mpls",
"labels": [200, 300]}}]})
.commit())
print(ipdb.routes.tables[200]["1.2.3.0/24"])
|