IP-Punt-redirect: allow the use of a FIB path to describe how to
[vpp.git] / test / vpp_ip_route.py
1 """
2   IP Routes
3
4   object abstractions for representing IP routes in VPP
5 """
6
7 from vpp_object import VppObject
8 from socket import inet_pton, inet_ntop, AF_INET, AF_INET6
9 from vpp_ip import DpoProto, VppIpPrefix
10
11 # from vnet/vnet/mpls/mpls_types.h
12 MPLS_IETF_MAX_LABEL = 0xfffff
13 MPLS_LABEL_INVALID = MPLS_IETF_MAX_LABEL + 1
14
15
16 class MRouteItfFlags:
17     MFIB_ITF_FLAG_NONE = 0
18     MFIB_ITF_FLAG_NEGATE_SIGNAL = 1
19     MFIB_ITF_FLAG_ACCEPT = 2
20     MFIB_ITF_FLAG_FORWARD = 4
21     MFIB_ITF_FLAG_SIGNAL_PRESENT = 8
22     MFIB_ITF_FLAG_INTERNAL_COPY = 16
23
24
25 class MRouteEntryFlags:
26     MFIB_ENTRY_FLAG_NONE = 0
27     MFIB_ENTRY_FLAG_SIGNAL = 1
28     MFIB_ENTRY_FLAG_DROP = 2
29     MFIB_ENTRY_FLAG_CONNECTED = 4
30     MFIB_ENTRY_FLAG_INHERIT_ACCEPT = 8
31
32
33 class MplsLspMode:
34     PIPE = 0
35     UNIFORM = 1
36
37
38 def ip_to_dpo_proto(addr):
39     if addr.version == 6:
40         return DpoProto.DPO_PROTO_IP6
41     else:
42         return DpoProto.DPO_PROTO_IP4
43
44
45 def find_route(test, ip_addr, len, table_id=0, inet=AF_INET):
46     if inet == AF_INET:
47         s = 4
48         routes = test.vapi.ip_fib_dump()
49     else:
50         s = 16
51         routes = test.vapi.ip6_fib_dump()
52
53     route_addr = inet_pton(inet, ip_addr)
54     for e in routes:
55         if route_addr == e.address[:s] \
56                 and len == e.address_length \
57                 and table_id == e.table_id:
58             return True
59     return False
60
61
62 def find_mroute(test, grp_addr, src_addr, grp_addr_len,
63                 table_id=0, inet=AF_INET):
64     if inet == AF_INET:
65         s = 4
66         routes = test.vapi.ip_mfib_dump()
67     else:
68         s = 16
69         routes = test.vapi.ip6_mfib_dump()
70     gaddr = inet_pton(inet, grp_addr)
71     saddr = inet_pton(inet, src_addr)
72     for e in routes:
73         if gaddr == e.grp_address[:s] \
74                 and grp_addr_len == e.address_length \
75                 and saddr == e.src_address[:s] \
76                 and table_id == e.table_id:
77             return True
78     return False
79
80
81 def find_mpls_route(test, table_id, label, eos_bit, paths=None):
82     dump = test.vapi.mpls_fib_dump()
83     for e in dump:
84         if label == e.label \
85                 and eos_bit == e.eos_bit \
86                 and table_id == e.table_id:
87             if not paths:
88                 return True
89             else:
90                 if (len(paths) != len(e.path)):
91                     return False
92                 for i in range(len(paths)):
93                     if (paths[i] != e.path[i]):
94                         return False
95                 return True
96     return False
97
98
99 def fib_interface_ip_prefix(test, address, length, sw_if_index):
100     vp = VppIpPrefix(address, length)
101     addrs = test.vapi.ip_address_dump(sw_if_index, is_ipv6=vp.is_ip6)
102
103     if vp.is_ip6:
104         n = 16
105     else:
106         n = 4
107
108     for a in addrs:
109         if a.prefix_length == length and \
110                 a.sw_if_index == sw_if_index and \
111                 a.ip[:n] == vp.bytes:
112             return True
113     return False
114
115
116 class VppIpTable(VppObject):
117
118     def __init__(self,
119                  test,
120                  table_id,
121                  is_ip6=0):
122         self._test = test
123         self.table_id = table_id
124         self.is_ip6 = is_ip6
125
126     def add_vpp_config(self):
127         self._test.vapi.ip_table_add_del(is_ipv6=self.is_ip6, is_add=1,
128                                          table_id=self.table_id)
129         self._test.registry.register(self, self._test.logger)
130
131     def remove_vpp_config(self):
132         self._test.vapi.ip_table_add_del(is_ipv6=self.is_ip6, is_add=0,
133                                          table_id=self.table_id)
134
135     def query_vpp_config(self):
136         if self.table_id == 0:
137             # the default table always exists
138             return False
139         # find the default route
140         return find_route(self._test,
141                           "::" if self.is_ip6 else "0.0.0.0",
142                           0,
143                           self.table_id,
144                           inet=AF_INET6 if self.is_ip6 == 1 else AF_INET)
145
146     def object_id(self):
147         return ("table-%s-%d" %
148                 ("v6" if self.is_ip6 == 1 else "v4",
149                  self.table_id))
150
151
152 class VppIpInterfaceAddress(VppObject):
153
154     def __init__(self, test, intf, addr, len):
155         self._test = test
156         self.intf = intf
157         self.prefix = VppIpPrefix(addr, len)
158
159     def add_vpp_config(self):
160         self._test.vapi.sw_interface_add_del_address(
161             sw_if_index=self.intf.sw_if_index, address=self.prefix.bytes,
162             address_length=self.prefix.length, is_ipv6=self.prefix.is_ip6,
163             is_add=1)
164         self._test.registry.register(self, self._test.logger)
165
166     def remove_vpp_config(self):
167         self._test.vapi.sw_interface_add_del_address(
168             sw_if_index=self.intf.sw_if_index, address=self.prefix.bytes,
169             address_length=self.prefix.length, is_ipv6=self.prefix.is_ip6,
170             is_add=0)
171
172     def query_vpp_config(self):
173         return fib_interface_ip_prefix(self._test,
174                                        self.prefix.address,
175                                        self.prefix.length,
176                                        self.intf.sw_if_index)
177
178     def object_id(self):
179         return "interface-ip-%s-%s" % (self.intf, self.prefix)
180
181
182 class VppIpInterfaceBind(VppObject):
183
184     def __init__(self, test, intf, table):
185         self._test = test
186         self.intf = intf
187         self.table = table
188
189     def add_vpp_config(self):
190         if self.table.is_ip6:
191             self.intf.set_table_ip6(self.table.table_id)
192         else:
193             self.intf.set_table_ip4(self.table.table_id)
194         self._test.registry.register(self, self._test.logger)
195
196     def remove_vpp_config(self):
197         if 0 == self.table.table_id:
198             return
199         if self.table.is_ip6:
200             self.intf.set_table_ip6(0)
201         else:
202             self.intf.set_table_ip4(0)
203
204     def query_vpp_config(self):
205         if 0 == self.table.table_id:
206             return False
207         return self._test.vapi.sw_interface_get_table(
208             self.intf.sw_if_index,
209             self.table.is_ip6).vrf_id == self.table.table_id
210
211     def object_id(self):
212         return "interface-bind-%s-%s" % (self.intf, self.table)
213
214
215 class VppMplsLabel(object):
216     def __init__(self, value, mode=MplsLspMode.PIPE, ttl=64, exp=0):
217         self.value = value
218         self.mode = mode
219         self.ttl = ttl
220         self.exp = exp
221
222     def encode(self):
223         is_uniform = 0 if self.mode is MplsLspMode.PIPE else 1
224         return {'label': self.value,
225                 'ttl': self.ttl,
226                 'exp': self.exp,
227                 'is_uniform': is_uniform}
228
229     def __eq__(self, other):
230         if isinstance(other, self.__class__):
231             return (self.value == other.value and
232                     self.ttl == other.ttl and
233                     self.exp == other.exp and
234                     self.mode == other.mode)
235         elif hasattr(other, 'label'):
236             return (self.value == other.label and
237                     self.ttl == other.ttl and
238                     self.exp == other.exp and
239                     (self.mode == MplsLspMode.UNIFORM) == other.is_uniform)
240         else:
241             return False
242
243     def __ne__(self, other):
244         return not (self == other)
245
246
247 class VppRoutePath(object):
248
249     def __init__(
250             self,
251             nh_addr,
252             nh_sw_if_index,
253             nh_table_id=0,
254             labels=[],
255             nh_via_label=MPLS_LABEL_INVALID,
256             rpf_id=0,
257             is_interface_rx=0,
258             is_resolve_host=0,
259             is_resolve_attached=0,
260             is_source_lookup=0,
261             is_udp_encap=0,
262             is_dvr=0,
263             next_hop_id=0xffffffff,
264             proto=DpoProto.DPO_PROTO_IP4):
265         self.proto = proto
266         self.nh_itf = nh_sw_if_index
267         self.nh_table_id = nh_table_id
268         self.nh_via_label = nh_via_label
269         self.nh_labels = labels
270         self.weight = 1
271         self.rpf_id = rpf_id
272         if self.proto is DpoProto.DPO_PROTO_IP6:
273             self.nh_addr = inet_pton(AF_INET6, nh_addr)
274         elif self.proto is DpoProto.DPO_PROTO_IP4:
275             self.nh_addr = inet_pton(AF_INET, nh_addr)
276         else:
277             self.nh_addr = inet_pton(AF_INET6, "::")
278         self.is_resolve_host = is_resolve_host
279         self.is_resolve_attached = is_resolve_attached
280         self.is_interface_rx = is_interface_rx
281         self.is_source_lookup = is_source_lookup
282         self.is_rpf_id = 0
283         if rpf_id != 0:
284             self.is_rpf_id = 1
285             self.nh_itf = rpf_id
286         self.is_udp_encap = is_udp_encap
287         self.next_hop_id = next_hop_id
288         self.is_dvr = is_dvr
289
290     def encode_labels(self, pad_labels=False):
291         lstack = []
292         for l in self.nh_labels:
293             if type(l) == VppMplsLabel:
294                 lstack.append(l.encode())
295             else:
296                 lstack.append({'label': l,
297                                'ttl': 255})
298         if (pad_labels):
299             while (len(lstack) < 16):
300                 lstack.append({})
301         return lstack
302
303     def encode(self, pad_labels=False):
304         return {'next_hop': self.nh_addr,
305                 'weight': 1,
306                 'preference': 0,
307                 'table_id': self.nh_table_id,
308                 'next_hop_id': self.next_hop_id,
309                 'sw_if_index': self.nh_itf,
310                 'afi': self.proto,
311                 'is_udp_encap': self.is_udp_encap,
312                 'n_labels': len(self.nh_labels),
313                 'label_stack': self.encode_labels(pad_labels)}
314
315     def __eq__(self, other):
316         if isinstance(other, self.__class__):
317             return self.nh_addr == other.nh_addr
318         elif hasattr(other, 'sw_if_index'):
319             # vl_api_fib_path_t
320             if (len(self.nh_labels) != other.n_labels):
321                 return False
322             for i in range(len(self.nh_labels)):
323                 if (self.nh_labels[i] != other.label_stack[i]):
324                     return False
325             return self.nh_itf == other.sw_if_index
326         else:
327             return False
328
329     def __ne__(self, other):
330         return not (self == other)
331
332
333 class VppMRoutePath(VppRoutePath):
334
335     def __init__(self, nh_sw_if_index, flags,
336                  nh=None,
337                  proto=DpoProto.DPO_PROTO_IP4,
338                  bier_imp=0):
339         if not nh:
340             nh = "::" if proto is DpoProto.DPO_PROTO_IP6 else "0.0.0.0"
341         super(VppMRoutePath, self).__init__(nh,
342                                             nh_sw_if_index,
343                                             proto=proto)
344         self.nh_i_flags = flags
345         self.bier_imp = bier_imp
346
347
348 class VppIpRoute(VppObject):
349     """
350     IP Route
351     """
352
353     def __init__(self, test, dest_addr,
354                  dest_addr_len, paths, table_id=0, is_ip6=0, is_local=0,
355                  is_unreach=0, is_prohibit=0, is_drop=0):
356         self._test = test
357         self.paths = paths
358         self.dest_addr_len = dest_addr_len
359         self.table_id = table_id
360         self.is_ip6 = is_ip6
361         self.is_local = is_local
362         self.is_unreach = is_unreach
363         self.is_prohibit = is_prohibit
364         self.is_drop = is_drop
365         self.dest_addr_p = dest_addr
366         if is_ip6:
367             self.dest_addr = inet_pton(AF_INET6, dest_addr)
368         else:
369             self.dest_addr = inet_pton(AF_INET, dest_addr)
370
371     def modify(self, paths, is_local=0,
372                is_unreach=0, is_prohibit=0):
373         self.paths = paths
374         self.is_local = is_local
375         self.is_unreach = is_unreach
376         self.is_prohibit = is_prohibit
377
378     def add_vpp_config(self):
379         if self.is_unreach or self.is_prohibit or self.is_drop:
380             r = self._test.vapi.ip_add_del_route(
381                 dst_address=self.dest_addr,
382                 dst_address_length=self.dest_addr_len,
383                 next_hop_address=inet_pton(
384                     AF_INET6, "::"),
385                 next_hop_sw_if_index=0xffffffff,
386                 table_id=self.table_id,
387                 is_drop=self.is_drop,
388                 is_unreach=self.is_unreach,
389                 is_prohibit=self.is_prohibit,
390                 is_ipv6=self.is_ip6,
391                 is_local=self.is_local)
392         else:
393             for path in self.paths:
394                 lstack = path.encode_labels()
395
396                 r = self._test.vapi.ip_add_del_route(
397                     dst_address=self.dest_addr,
398                     dst_address_length=self.dest_addr_len,
399                     next_hop_address=path.nh_addr,
400                     next_hop_sw_if_index=path.nh_itf, table_id=self.table_id,
401                     next_hop_table_id=path.nh_table_id,
402                     next_hop_n_out_labels=len(lstack),
403                     next_hop_out_label_stack=lstack,
404                     next_hop_via_label=path.nh_via_label,
405                     next_hop_id=path.next_hop_id,
406                     is_resolve_host=path.is_resolve_host,
407                     is_resolve_attached=path.is_resolve_attached,
408                     is_ipv6=self.is_ip6, is_local=self.is_local,
409                     is_multipath=1 if len(self.paths) > 1 else 0,
410                     is_dvr=path.is_dvr, is_udp_encap=path.is_udp_encap,
411                     is_source_lookup=path.is_source_lookup)
412         self.stats_index = r.stats_index
413         self._test.registry.register(self, self._test.logger)
414
415     def remove_vpp_config(self):
416         if self.is_unreach or self.is_prohibit or self.is_drop:
417             self._test.vapi.ip_add_del_route(
418                 dst_address=self.dest_addr,
419                 dst_address_length=self.dest_addr_len,
420                 next_hop_address=inet_pton(
421                     AF_INET6, "::"),
422                 next_hop_sw_if_index=0xffffffff,
423                 table_id=self.table_id, is_add=0,
424                 is_unreach=self.is_unreach,
425                 is_prohibit=self.is_prohibit,
426                 is_ipv6=self.is_ip6,
427                 is_local=self.is_local)
428         else:
429             for path in self.paths:
430                 self._test.vapi.ip_add_del_route(
431                     dst_address=self.dest_addr,
432                     dst_address_length=self.dest_addr_len,
433                     next_hop_address=path.nh_addr,
434                     next_hop_sw_if_index=path.nh_itf,
435                     table_id=self.table_id,
436                     next_hop_table_id=path.nh_table_id,
437                     next_hop_via_label=path.nh_via_label,
438                     next_hop_id=path.next_hop_id,
439                     is_add=0, is_ipv6=self.is_ip6,
440                     is_dvr=path.is_dvr,
441                     is_udp_encap=path.is_udp_encap)
442
443     def query_vpp_config(self):
444         return find_route(self._test,
445                           self.dest_addr_p,
446                           self.dest_addr_len,
447                           self.table_id,
448                           inet=AF_INET6 if self.is_ip6 == 1 else AF_INET)
449
450     def object_id(self):
451         return ("%d:%s/%d"
452                 % (self.table_id,
453                    self.dest_addr_p,
454                    self.dest_addr_len))
455
456     def get_stats_to(self):
457         c = self._test.statistics.get_counter("/net/route/to")
458         return c[0][self.stats_index]
459
460     def get_stats_via(self):
461         c = self._test.statistics.get_counter("/net/route/via")
462         return c[0][self.stats_index]
463
464
465 class VppIpMRoute(VppObject):
466     """
467     IP Multicast Route
468     """
469
470     def __init__(self, test, src_addr, grp_addr,
471                  grp_addr_len, e_flags, paths, table_id=0,
472                  rpf_id=0, is_ip6=0):
473         self._test = test
474         self.paths = paths
475         self.grp_addr_len = grp_addr_len
476         self.table_id = table_id
477         self.e_flags = e_flags
478         self.is_ip6 = is_ip6
479         self.rpf_id = rpf_id
480
481         self.grp_addr_p = grp_addr
482         self.src_addr_p = src_addr
483         if is_ip6:
484             self.grp_addr = inet_pton(AF_INET6, grp_addr)
485             self.src_addr = inet_pton(AF_INET6, src_addr)
486         else:
487             self.grp_addr = inet_pton(AF_INET, grp_addr)
488             self.src_addr = inet_pton(AF_INET, src_addr)
489
490     def add_vpp_config(self):
491         for path in self.paths:
492             r = self._test.vapi.ip_mroute_add_del(self.src_addr,
493                                                   self.grp_addr,
494                                                   self.grp_addr_len,
495                                                   self.e_flags,
496                                                   path.proto,
497                                                   path.nh_itf,
498                                                   path.nh_addr,
499                                                   path.nh_i_flags,
500                                                   bier_imp=path.bier_imp,
501                                                   rpf_id=self.rpf_id,
502                                                   table_id=self.table_id,
503                                                   is_ipv6=self.is_ip6)
504             self.stats_index = r.stats_index
505         self._test.registry.register(self, self._test.logger)
506
507     def remove_vpp_config(self):
508         for path in self.paths:
509             self._test.vapi.ip_mroute_add_del(self.src_addr,
510                                               self.grp_addr,
511                                               self.grp_addr_len,
512                                               self.e_flags,
513                                               path.proto,
514                                               path.nh_itf,
515                                               path.nh_addr,
516                                               path.nh_i_flags,
517                                               table_id=self.table_id,
518                                               bier_imp=path.bier_imp,
519                                               is_add=0,
520                                               is_ipv6=self.is_ip6)
521
522     def update_entry_flags(self, flags):
523         self.e_flags = flags
524         self._test.vapi.ip_mroute_add_del(self.src_addr,
525                                           self.grp_addr,
526                                           self.grp_addr_len,
527                                           self.e_flags,
528                                           0,
529                                           0xffffffff,
530                                           "",
531                                           0,
532                                           table_id=self.table_id,
533                                           is_ipv6=self.is_ip6)
534
535     def update_rpf_id(self, rpf_id):
536         self.rpf_id = rpf_id
537         self._test.vapi.ip_mroute_add_del(self.src_addr,
538                                           self.grp_addr,
539                                           self.grp_addr_len,
540                                           self.e_flags,
541                                           0,
542                                           0xffffffff,
543                                           "",
544                                           0,
545                                           rpf_id=self.rpf_id,
546                                           table_id=self.table_id,
547                                           is_ipv6=self.is_ip6)
548
549     def update_path_flags(self, itf, flags):
550         for path in self.paths:
551             if path.nh_itf == itf:
552                 path.nh_i_flags = flags
553                 break
554         self._test.vapi.ip_mroute_add_del(self.src_addr,
555                                           self.grp_addr,
556                                           self.grp_addr_len,
557                                           self.e_flags,
558                                           path.proto,
559                                           path.nh_itf,
560                                           path.nh_addr,
561                                           path.nh_i_flags,
562                                           table_id=self.table_id,
563                                           is_ipv6=self.is_ip6)
564
565     def query_vpp_config(self):
566         return find_mroute(self._test,
567                            self.grp_addr_p,
568                            self.src_addr_p,
569                            self.grp_addr_len,
570                            self.table_id,
571                            inet=AF_INET6 if self.is_ip6 == 1 else AF_INET)
572
573     def object_id(self):
574         if self.is_ip6:
575             return ("%d:(%s,%s/%d)"
576                     % (self.table_id,
577                        inet_ntop(AF_INET6, self.src_addr),
578                        inet_ntop(AF_INET6, self.grp_addr),
579                        self.grp_addr_len))
580         else:
581             return ("%d:(%s,%s/%d)"
582                     % (self.table_id,
583                        inet_ntop(AF_INET, self.src_addr),
584                        inet_ntop(AF_INET, self.grp_addr),
585                        self.grp_addr_len))
586
587     def get_stats(self):
588         c = self._test.statistics.get_counter("/net/mroute")
589         return c[0][self.stats_index]
590
591
592 class VppMFibSignal(object):
593     def __init__(self, test, route, interface, packet):
594         self.route = route
595         self.interface = interface
596         self.packet = packet
597         self.test = test
598
599     def compare(self, signal):
600         self.test.assertEqual(self.interface, signal.sw_if_index)
601         self.test.assertEqual(self.route.table_id, signal.table_id)
602         self.test.assertEqual(self.route.grp_addr_len,
603                               signal.grp_address_len)
604         for i in range(self.route.grp_addr_len / 8):
605             self.test.assertEqual(self.route.grp_addr[i],
606                                   signal.grp_address[i])
607         if (self.route.grp_addr_len > 32):
608             for i in range(4):
609                 self.test.assertEqual(self.route.src_addr[i],
610                                       signal.src_address[i])
611
612
613 class VppMplsIpBind(VppObject):
614     """
615     MPLS to IP Binding
616     """
617
618     def __init__(self, test, local_label, dest_addr, dest_addr_len,
619                  table_id=0, ip_table_id=0, is_ip6=0):
620         self._test = test
621         self.dest_addr_len = dest_addr_len
622         self.dest_addr = dest_addr
623         self.local_label = local_label
624         self.table_id = table_id
625         self.ip_table_id = ip_table_id
626         self.is_ip6 = is_ip6
627         if is_ip6:
628             self.dest_addrn = inet_pton(AF_INET6, dest_addr)
629         else:
630             self.dest_addrn = inet_pton(AF_INET, dest_addr)
631
632     def add_vpp_config(self):
633         self._test.vapi.mpls_ip_bind_unbind(self.local_label,
634                                             self.dest_addrn,
635                                             self.dest_addr_len,
636                                             table_id=self.table_id,
637                                             ip_table_id=self.ip_table_id,
638                                             is_ip4=(self.is_ip6 == 0))
639         self._test.registry.register(self, self._test.logger)
640
641     def remove_vpp_config(self):
642         self._test.vapi.mpls_ip_bind_unbind(self.local_label,
643                                             self.dest_addrn,
644                                             self.dest_addr_len,
645                                             table_id=self.table_id,
646                                             ip_table_id=self.ip_table_id,
647                                             is_bind=0,
648                                             is_ip4=(self.is_ip6 == 0))
649
650     def query_vpp_config(self):
651         dump = self._test.vapi.mpls_fib_dump()
652         for e in dump:
653             if self.local_label == e.label \
654                     and self.table_id == e.table_id:
655                 return True
656         return False
657
658     def object_id(self):
659         return ("%d:%s binds %d:%s/%d"
660                 % (self.table_id,
661                    self.local_label,
662                    self.ip_table_id,
663                    self.dest_addr,
664                    self.dest_addr_len))
665
666
667 class VppMplsTable(VppObject):
668
669     def __init__(self,
670                  test,
671                  table_id):
672         self._test = test
673         self.table_id = table_id
674
675     def add_vpp_config(self):
676         self._test.vapi.mpls_table_add_del(
677             self.table_id,
678             is_add=1)
679         self._test.registry.register(self, self._test.logger)
680
681     def remove_vpp_config(self):
682         self._test.vapi.mpls_table_add_del(
683             self.table_id,
684             is_add=0)
685
686     def query_vpp_config(self):
687         # find the default route
688         dump = self._test.vapi.mpls_fib_dump()
689         if len(dump):
690             return True
691         return False
692
693     def object_id(self):
694         return ("table-mpls-%d" % (self.table_id))
695
696
697 class VppMplsRoute(VppObject):
698     """
699     MPLS Route/LSP
700     """
701
702     def __init__(self, test, local_label, eos_bit, paths, table_id=0,
703                  is_multicast=0):
704         self._test = test
705         self.paths = paths
706         self.local_label = local_label
707         self.eos_bit = eos_bit
708         self.table_id = table_id
709         self.is_multicast = is_multicast
710
711     def add_vpp_config(self):
712         is_multipath = len(self.paths) > 1
713         for path in self.paths:
714             lstack = path.encode_labels()
715
716             r = self._test.vapi.mpls_route_add_del(
717                 mr_label=self.local_label,
718                 mr_eos=self.eos_bit,
719                 mr_next_hop_proto=path.proto,
720                 mr_next_hop=path.nh_addr,
721                 mr_next_hop_sw_if_index=path.nh_itf,
722                 mr_table_id=self.table_id,
723                 mr_next_hop_table_id=path.nh_table_id,
724                 mr_next_hop_n_out_labels=len(
725                     lstack),
726                 mr_next_hop_out_label_stack=lstack,
727                 mr_next_hop_via_label=path.nh_via_label,
728                 mr_is_interface_rx=path.is_interface_rx,
729                 mr_is_rpf_id=path.is_rpf_id,
730                 mr_is_multicast=self.is_multicast,
731                 mr_is_multipath=is_multipath)
732         self.stats_index = r.stats_index
733         self._test.registry.register(self, self._test.logger)
734
735     def remove_vpp_config(self):
736         for path in self.paths:
737             self._test.vapi.mpls_route_add_del(
738                 mr_label=self.local_label,
739                 mr_eos=self.eos_bit,
740                 mr_next_hop_proto=path.proto,
741                 mr_next_hop=path.nh_addr,
742                 mr_next_hop_sw_if_index=path.nh_itf,
743                 mr_table_id=self.table_id,
744                 mr_is_rpf_id=path.is_rpf_id,
745                 mr_is_add=0)
746
747     def query_vpp_config(self):
748         return find_mpls_route(self._test, self.table_id,
749                                self.local_label, self.eos_bit)
750
751     def object_id(self):
752         return ("%d:%s/%d"
753                 % (self.table_id,
754                    self.local_label,
755                    20 + self.eos_bit))
756
757     def get_stats_to(self):
758         c = self._test.statistics.get_counter("/net/route/to")
759         return c[0][self.stats_index]
760
761     def get_stats_via(self):
762         c = self._test.statistics.get_counter("/net/route/via")
763         return c[0][self.stats_index]