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