tests: refactor vpp_object.py
[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):
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         return lstack
299
300     def encode(self):
301         return {'next_hop': self.nh_addr,
302                 'weight': 1,
303                 'preference': 0,
304                 'table_id': self.nh_table_id,
305                 'next_hop_id': self.next_hop_id,
306                 'sw_if_index': self.nh_itf,
307                 'afi': self.proto,
308                 'is_udp_encap': self.is_udp_encap,
309                 'n_labels': len(self.nh_labels),
310                 'label_stack': self.encode_labels()}
311
312     def __eq__(self, other):
313         if isinstance(other, self.__class__):
314             return self.nh_addr == other.nh_addr
315         elif hasattr(other, 'sw_if_index'):
316             # vl_api_fib_path_t
317             if (len(self.nh_labels) != other.n_labels):
318                 return False
319             for i in range(len(self.nh_labels)):
320                 if (self.nh_labels[i] != other.label_stack[i]):
321                     return False
322             return self.nh_itf == other.sw_if_index
323         else:
324             return False
325
326     def __ne__(self, other):
327         return not (self == other)
328
329
330 class VppMRoutePath(VppRoutePath):
331
332     def __init__(self, nh_sw_if_index, flags,
333                  nh=None,
334                  proto=DpoProto.DPO_PROTO_IP4,
335                  bier_imp=0):
336         if not nh:
337             nh = "::" if proto is DpoProto.DPO_PROTO_IP6 else "0.0.0.0"
338         super(VppMRoutePath, self).__init__(nh,
339                                             nh_sw_if_index,
340                                             proto=proto)
341         self.nh_i_flags = flags
342         self.bier_imp = bier_imp
343
344
345 class VppIpRoute(VppObject):
346     """
347     IP Route
348     """
349
350     def __init__(self, test, dest_addr,
351                  dest_addr_len, paths, table_id=0, is_ip6=0, is_local=0,
352                  is_unreach=0, is_prohibit=0, is_drop=0):
353         self._test = test
354         self.paths = paths
355         self.dest_addr_len = dest_addr_len
356         self.table_id = table_id
357         self.is_ip6 = is_ip6
358         self.is_local = is_local
359         self.is_unreach = is_unreach
360         self.is_prohibit = is_prohibit
361         self.is_drop = is_drop
362         self.dest_addr_p = dest_addr
363         if is_ip6:
364             self.dest_addr = inet_pton(AF_INET6, dest_addr)
365         else:
366             self.dest_addr = inet_pton(AF_INET, dest_addr)
367
368     def modify(self, paths, is_local=0,
369                is_unreach=0, is_prohibit=0):
370         self.paths = paths
371         self.is_local = is_local
372         self.is_unreach = is_unreach
373         self.is_prohibit = is_prohibit
374
375     def add_vpp_config(self):
376         if self.is_unreach or self.is_prohibit or self.is_drop:
377             r = self._test.vapi.ip_add_del_route(
378                 dst_address=self.dest_addr,
379                 dst_address_length=self.dest_addr_len,
380                 next_hop_address=inet_pton(
381                     AF_INET6, "::"),
382                 next_hop_sw_if_index=0xffffffff,
383                 table_id=self.table_id,
384                 is_drop=self.is_drop,
385                 is_unreach=self.is_unreach,
386                 is_prohibit=self.is_prohibit,
387                 is_ipv6=self.is_ip6,
388                 is_local=self.is_local)
389         else:
390             for path in self.paths:
391                 lstack = path.encode_labels()
392
393                 r = self._test.vapi.ip_add_del_route(
394                     dst_address=self.dest_addr,
395                     dst_address_length=self.dest_addr_len,
396                     next_hop_address=path.nh_addr,
397                     next_hop_sw_if_index=path.nh_itf, table_id=self.table_id,
398                     next_hop_table_id=path.nh_table_id,
399                     next_hop_n_out_labels=len(lstack),
400                     next_hop_out_label_stack=lstack,
401                     next_hop_via_label=path.nh_via_label,
402                     next_hop_id=path.next_hop_id,
403                     is_resolve_host=path.is_resolve_host,
404                     is_resolve_attached=path.is_resolve_attached,
405                     is_ipv6=self.is_ip6, is_local=self.is_local,
406                     is_multipath=1 if len(self.paths) > 1 else 0,
407                     is_dvr=path.is_dvr, is_udp_encap=path.is_udp_encap,
408                     is_source_lookup=path.is_source_lookup)
409         self.stats_index = r.stats_index
410         self._test.registry.register(self, self._test.logger)
411
412     def remove_vpp_config(self):
413         if self.is_unreach or self.is_prohibit or self.is_drop:
414             self._test.vapi.ip_add_del_route(
415                 dst_address=self.dest_addr,
416                 dst_address_length=self.dest_addr_len,
417                 next_hop_address=inet_pton(
418                     AF_INET6, "::"),
419                 next_hop_sw_if_index=0xffffffff,
420                 table_id=self.table_id, is_add=0,
421                 is_unreach=self.is_unreach,
422                 is_prohibit=self.is_prohibit,
423                 is_ipv6=self.is_ip6,
424                 is_local=self.is_local)
425         else:
426             for path in self.paths:
427                 self._test.vapi.ip_add_del_route(
428                     dst_address=self.dest_addr,
429                     dst_address_length=self.dest_addr_len,
430                     next_hop_address=path.nh_addr,
431                     next_hop_sw_if_index=path.nh_itf,
432                     table_id=self.table_id,
433                     next_hop_table_id=path.nh_table_id,
434                     next_hop_via_label=path.nh_via_label,
435                     next_hop_id=path.next_hop_id,
436                     is_add=0, is_ipv6=self.is_ip6,
437                     is_dvr=path.is_dvr,
438                     is_udp_encap=path.is_udp_encap)
439
440     def query_vpp_config(self):
441         return find_route(self._test,
442                           self.dest_addr_p,
443                           self.dest_addr_len,
444                           self.table_id,
445                           inet=AF_INET6 if self.is_ip6 == 1 else AF_INET)
446
447     def object_id(self):
448         return ("%d:%s/%d"
449                 % (self.table_id,
450                    self.dest_addr_p,
451                    self.dest_addr_len))
452
453     def get_stats_to(self):
454         c = self._test.statistics.get_counter("/net/route/to")
455         return c[0][self.stats_index]
456
457     def get_stats_via(self):
458         c = self._test.statistics.get_counter("/net/route/via")
459         return c[0][self.stats_index]
460
461
462 class VppIpMRoute(VppObject):
463     """
464     IP Multicast Route
465     """
466
467     def __init__(self, test, src_addr, grp_addr,
468                  grp_addr_len, e_flags, paths, table_id=0,
469                  rpf_id=0, is_ip6=0):
470         self._test = test
471         self.paths = paths
472         self.grp_addr_len = grp_addr_len
473         self.table_id = table_id
474         self.e_flags = e_flags
475         self.is_ip6 = is_ip6
476         self.rpf_id = rpf_id
477
478         self.grp_addr_p = grp_addr
479         self.src_addr_p = src_addr
480         if is_ip6:
481             self.grp_addr = inet_pton(AF_INET6, grp_addr)
482             self.src_addr = inet_pton(AF_INET6, src_addr)
483         else:
484             self.grp_addr = inet_pton(AF_INET, grp_addr)
485             self.src_addr = inet_pton(AF_INET, src_addr)
486
487     def add_vpp_config(self):
488         for path in self.paths:
489             r = self._test.vapi.ip_mroute_add_del(self.src_addr,
490                                                   self.grp_addr,
491                                                   self.grp_addr_len,
492                                                   self.e_flags,
493                                                   path.proto,
494                                                   path.nh_itf,
495                                                   path.nh_addr,
496                                                   path.nh_i_flags,
497                                                   bier_imp=path.bier_imp,
498                                                   rpf_id=self.rpf_id,
499                                                   table_id=self.table_id,
500                                                   is_ipv6=self.is_ip6)
501             self.stats_index = r.stats_index
502         self._test.registry.register(self, self._test.logger)
503
504     def remove_vpp_config(self):
505         for path in self.paths:
506             self._test.vapi.ip_mroute_add_del(self.src_addr,
507                                               self.grp_addr,
508                                               self.grp_addr_len,
509                                               self.e_flags,
510                                               path.proto,
511                                               path.nh_itf,
512                                               path.nh_addr,
513                                               path.nh_i_flags,
514                                               table_id=self.table_id,
515                                               bier_imp=path.bier_imp,
516                                               is_add=0,
517                                               is_ipv6=self.is_ip6)
518
519     def update_entry_flags(self, flags):
520         self.e_flags = flags
521         self._test.vapi.ip_mroute_add_del(self.src_addr,
522                                           self.grp_addr,
523                                           self.grp_addr_len,
524                                           self.e_flags,
525                                           0,
526                                           0xffffffff,
527                                           "",
528                                           0,
529                                           table_id=self.table_id,
530                                           is_ipv6=self.is_ip6)
531
532     def update_rpf_id(self, rpf_id):
533         self.rpf_id = rpf_id
534         self._test.vapi.ip_mroute_add_del(self.src_addr,
535                                           self.grp_addr,
536                                           self.grp_addr_len,
537                                           self.e_flags,
538                                           0,
539                                           0xffffffff,
540                                           "",
541                                           0,
542                                           rpf_id=self.rpf_id,
543                                           table_id=self.table_id,
544                                           is_ipv6=self.is_ip6)
545
546     def update_path_flags(self, itf, flags):
547         for path in self.paths:
548             if path.nh_itf == itf:
549                 path.nh_i_flags = flags
550                 break
551         self._test.vapi.ip_mroute_add_del(self.src_addr,
552                                           self.grp_addr,
553                                           self.grp_addr_len,
554                                           self.e_flags,
555                                           path.proto,
556                                           path.nh_itf,
557                                           path.nh_addr,
558                                           path.nh_i_flags,
559                                           table_id=self.table_id,
560                                           is_ipv6=self.is_ip6)
561
562     def query_vpp_config(self):
563         return find_mroute(self._test,
564                            self.grp_addr_p,
565                            self.src_addr_p,
566                            self.grp_addr_len,
567                            self.table_id,
568                            inet=AF_INET6 if self.is_ip6 == 1 else AF_INET)
569
570     def object_id(self):
571         if self.is_ip6:
572             return ("%d:(%s,%s/%d)"
573                     % (self.table_id,
574                        inet_ntop(AF_INET6, self.src_addr),
575                        inet_ntop(AF_INET6, self.grp_addr),
576                        self.grp_addr_len))
577         else:
578             return ("%d:(%s,%s/%d)"
579                     % (self.table_id,
580                        inet_ntop(AF_INET, self.src_addr),
581                        inet_ntop(AF_INET, self.grp_addr),
582                        self.grp_addr_len))
583
584     def get_stats(self):
585         c = self._test.statistics.get_counter("/net/mroute")
586         return c[0][self.stats_index]
587
588
589 class VppMFibSignal(object):
590     def __init__(self, test, route, interface, packet):
591         self.route = route
592         self.interface = interface
593         self.packet = packet
594         self.test = test
595
596     def compare(self, signal):
597         self.test.assertEqual(self.interface, signal.sw_if_index)
598         self.test.assertEqual(self.route.table_id, signal.table_id)
599         self.test.assertEqual(self.route.grp_addr_len,
600                               signal.grp_address_len)
601         for i in range(self.route.grp_addr_len / 8):
602             self.test.assertEqual(self.route.grp_addr[i],
603                                   signal.grp_address[i])
604         if (self.route.grp_addr_len > 32):
605             for i in range(4):
606                 self.test.assertEqual(self.route.src_addr[i],
607                                       signal.src_address[i])
608
609
610 class VppMplsIpBind(VppObject):
611     """
612     MPLS to IP Binding
613     """
614
615     def __init__(self, test, local_label, dest_addr, dest_addr_len,
616                  table_id=0, ip_table_id=0, is_ip6=0):
617         self._test = test
618         self.dest_addr_len = dest_addr_len
619         self.dest_addr = dest_addr
620         self.local_label = local_label
621         self.table_id = table_id
622         self.ip_table_id = ip_table_id
623         self.is_ip6 = is_ip6
624         if is_ip6:
625             self.dest_addrn = inet_pton(AF_INET6, dest_addr)
626         else:
627             self.dest_addrn = inet_pton(AF_INET, dest_addr)
628
629     def add_vpp_config(self):
630         self._test.vapi.mpls_ip_bind_unbind(self.local_label,
631                                             self.dest_addrn,
632                                             self.dest_addr_len,
633                                             table_id=self.table_id,
634                                             ip_table_id=self.ip_table_id,
635                                             is_ip4=(self.is_ip6 == 0))
636         self._test.registry.register(self, self._test.logger)
637
638     def remove_vpp_config(self):
639         self._test.vapi.mpls_ip_bind_unbind(self.local_label,
640                                             self.dest_addrn,
641                                             self.dest_addr_len,
642                                             table_id=self.table_id,
643                                             ip_table_id=self.ip_table_id,
644                                             is_bind=0,
645                                             is_ip4=(self.is_ip6 == 0))
646
647     def query_vpp_config(self):
648         dump = self._test.vapi.mpls_fib_dump()
649         for e in dump:
650             if self.local_label == e.label \
651                     and self.table_id == e.table_id:
652                 return True
653         return False
654
655     def object_id(self):
656         return ("%d:%s binds %d:%s/%d"
657                 % (self.table_id,
658                    self.local_label,
659                    self.ip_table_id,
660                    self.dest_addr,
661                    self.dest_addr_len))
662
663
664 class VppMplsTable(VppObject):
665
666     def __init__(self,
667                  test,
668                  table_id):
669         self._test = test
670         self.table_id = table_id
671
672     def add_vpp_config(self):
673         self._test.vapi.mpls_table_add_del(
674             self.table_id,
675             is_add=1)
676         self._test.registry.register(self, self._test.logger)
677
678     def remove_vpp_config(self):
679         self._test.vapi.mpls_table_add_del(
680             self.table_id,
681             is_add=0)
682
683     def query_vpp_config(self):
684         # find the default route
685         dump = self._test.vapi.mpls_fib_dump()
686         if len(dump):
687             return True
688         return False
689
690     def object_id(self):
691         return ("table-mpls-%d" % (self.table_id))
692
693
694 class VppMplsRoute(VppObject):
695     """
696     MPLS Route/LSP
697     """
698
699     def __init__(self, test, local_label, eos_bit, paths, table_id=0,
700                  is_multicast=0):
701         self._test = test
702         self.paths = paths
703         self.local_label = local_label
704         self.eos_bit = eos_bit
705         self.table_id = table_id
706         self.is_multicast = is_multicast
707
708     def add_vpp_config(self):
709         is_multipath = len(self.paths) > 1
710         for path in self.paths:
711             lstack = path.encode_labels()
712
713             r = self._test.vapi.mpls_route_add_del(
714                 mr_label=self.local_label,
715                 mr_eos=self.eos_bit,
716                 mr_next_hop_proto=path.proto,
717                 mr_next_hop=path.nh_addr,
718                 mr_next_hop_sw_if_index=path.nh_itf,
719                 mr_table_id=self.table_id,
720                 mr_next_hop_table_id=path.nh_table_id,
721                 mr_next_hop_n_out_labels=len(
722                     lstack),
723                 mr_next_hop_out_label_stack=lstack,
724                 mr_next_hop_via_label=path.nh_via_label,
725                 mr_is_interface_rx=path.is_interface_rx,
726                 mr_is_rpf_id=path.is_rpf_id,
727                 mr_is_multicast=self.is_multicast,
728                 mr_is_multipath=is_multipath)
729         self.stats_index = r.stats_index
730         self._test.registry.register(self, self._test.logger)
731
732     def remove_vpp_config(self):
733         for path in self.paths:
734             self._test.vapi.mpls_route_add_del(
735                 mr_label=self.local_label,
736                 mr_eos=self.eos_bit,
737                 mr_next_hop_proto=path.proto,
738                 mr_next_hop=path.nh_addr,
739                 mr_next_hop_sw_if_index=path.nh_itf,
740                 mr_table_id=self.table_id,
741                 mr_is_rpf_id=path.is_rpf_id,
742                 mr_is_add=0)
743
744     def query_vpp_config(self):
745         return find_mpls_route(self._test, self.table_id,
746                                self.local_label, self.eos_bit)
747
748     def object_id(self):
749         return ("%d:%s/%d"
750                 % (self.table_id,
751                    self.local_label,
752                    20 + self.eos_bit))
753
754     def get_stats_to(self):
755         c = self._test.statistics.get_counter("/net/route/to")
756         return c[0][self.stats_index]
757
758     def get_stats_via(self):
759         c = self._test.statistics.get_counter("/net/route/via")
760         return c[0][self.stats_index]