api: Cleanup APIs interface.api
[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, prefix=self.prefix.encode(),
206             is_add=1)
207         self._test.registry.register(self, self._test.logger)
208
209     def remove_vpp_config(self):
210         self._test.vapi.sw_interface_add_del_address(
211             sw_if_index=self.intf.sw_if_index, prefix=self.prefix.encode(),
212             is_add=0)
213
214     def query_vpp_config(self):
215         return fib_interface_ip_prefix(self._test,
216                                        self.prefix.address,
217                                        self.prefix.length,
218                                        self.intf.sw_if_index)
219
220     def object_id(self):
221         return "interface-ip-%s-%s" % (self.intf, self.prefix)
222
223
224 class VppIpInterfaceBind(VppObject):
225
226     def __init__(self, test, intf, table):
227         self._test = test
228         self.intf = intf
229         self.table = table
230
231     def add_vpp_config(self):
232         if self.table.is_ip6:
233             self.intf.set_table_ip6(self.table.table_id)
234         else:
235             self.intf.set_table_ip4(self.table.table_id)
236         self._test.registry.register(self, self._test.logger)
237
238     def remove_vpp_config(self):
239         if 0 == self.table.table_id:
240             return
241         if self.table.is_ip6:
242             self.intf.set_table_ip6(0)
243         else:
244             self.intf.set_table_ip4(0)
245
246     def query_vpp_config(self):
247         if 0 == self.table.table_id:
248             return False
249         return self._test.vapi.sw_interface_get_table(
250             self.intf.sw_if_index,
251             self.table.is_ip6).vrf_id == self.table.table_id
252
253     def object_id(self):
254         return "interface-bind-%s-%s" % (self.intf, self.table)
255
256
257 class VppMplsLabel(object):
258     def __init__(self, value, mode=MplsLspMode.PIPE, ttl=64, exp=0):
259         self.value = value
260         self.mode = mode
261         self.ttl = ttl
262         self.exp = exp
263
264     def encode(self):
265         is_uniform = 0 if self.mode is MplsLspMode.PIPE else 1
266         return {'label': self.value,
267                 'ttl': self.ttl,
268                 'exp': self.exp,
269                 'is_uniform': is_uniform}
270
271     def __eq__(self, other):
272         if isinstance(other, self.__class__):
273             return (self.value == other.value and
274                     self.ttl == other.ttl and
275                     self.exp == other.exp and
276                     self.mode == other.mode)
277         elif hasattr(other, 'label'):
278             return (self.value == other.label and
279                     self.ttl == other.ttl and
280                     self.exp == other.exp and
281                     (self.mode == MplsLspMode.UNIFORM) == other.is_uniform)
282         else:
283             return False
284
285     def __ne__(self, other):
286         return not (self == other)
287
288
289 class VppFibPathNextHop(object):
290     def __init__(self, addr,
291                  via_label=MPLS_LABEL_INVALID,
292                  next_hop_id=INVALID_INDEX):
293         self.addr = VppIpAddressUnion(addr)
294         self.via_label = via_label
295         self.obj_id = next_hop_id
296
297     def encode(self):
298         if self.via_label is not MPLS_LABEL_INVALID:
299             return {'via_label': self.via_label}
300         if self.obj_id is not INVALID_INDEX:
301             return {'obj_id': self.obj_id}
302         else:
303             return {'address': self.addr.encode()}
304
305     def proto(self):
306         if self.via_label is MPLS_LABEL_INVALID:
307             return address_proto(self.addr)
308         else:
309             return FibPathProto.FIB_PATH_NH_PROTO_MPLS
310
311     def __eq__(self, other):
312         if not isinstance(other, self.__class__):
313             # try the other instance's __eq__.
314             return NotImplemented
315         return (self.addr == other.addr and
316                 self.via_label == other.via_label and
317                 self.obj_id == other.obj_id)
318
319
320 class VppRoutePath(object):
321
322     def __init__(
323             self,
324             nh_addr,
325             nh_sw_if_index,
326             nh_table_id=0,
327             labels=[],
328             nh_via_label=MPLS_LABEL_INVALID,
329             rpf_id=0,
330             next_hop_id=INVALID_INDEX,
331             proto=None,
332             flags=FibPathFlags.FIB_PATH_FLAG_NONE,
333             type=FibPathType.FIB_PATH_TYPE_NORMAL):
334         self.nh_itf = nh_sw_if_index
335         self.nh_table_id = nh_table_id
336         self.nh_labels = labels
337         self.weight = 1
338         self.rpf_id = rpf_id
339         self.proto = proto
340         self.flags = flags
341         self.type = type
342         self.nh = VppFibPathNextHop(nh_addr, nh_via_label, next_hop_id)
343         if proto is None:
344             self.proto = self.nh.proto()
345         else:
346             self.proto = proto
347         self.next_hop_id = next_hop_id
348
349     def encode_labels(self):
350         lstack = []
351         for l in self.nh_labels:
352             if type(l) == VppMplsLabel:
353                 lstack.append(l.encode())
354             else:
355                 lstack.append({'label': l,
356                                'ttl': 255})
357         while (len(lstack) < 16):
358             lstack.append({})
359
360         return lstack
361
362     def encode(self):
363         return {'weight': 1,
364                 'preference': 0,
365                 'table_id': self.nh_table_id,
366                 'nh': self.nh.encode(),
367                 'next_hop_id': self.next_hop_id,
368                 'sw_if_index': self.nh_itf,
369                 'rpf_id': self.rpf_id,
370                 'proto': self.proto,
371                 'type': self.type,
372                 'flags': self.flags,
373                 'n_labels': len(self.nh_labels),
374                 'label_stack': self.encode_labels()}
375
376     def __eq__(self, other):
377         if isinstance(other, self.__class__):
378             return self.nh == other.nh
379         elif hasattr(other, 'sw_if_index'):
380             # vl_api_fib_path_t
381             if (len(self.nh_labels) != other.n_labels):
382                 return False
383             for i in range(len(self.nh_labels)):
384                 if (self.nh_labels[i] != other.label_stack[i]):
385                     return False
386             return self.nh_itf == other.sw_if_index
387         else:
388             return False
389
390     def __ne__(self, other):
391         return not (self == other)
392
393
394 class VppMRoutePath(VppRoutePath):
395
396     def __init__(self, nh_sw_if_index, flags,
397                  nh=None,
398                  proto=FibPathProto.FIB_PATH_NH_PROTO_IP4,
399                  type=FibPathType.FIB_PATH_TYPE_NORMAL,
400                  bier_imp=INVALID_INDEX):
401         if not nh:
402             nh = "::" if proto is FibPathProto.FIB_PATH_NH_PROTO_IP6 \
403                  else "0.0.0.0"
404         super(VppMRoutePath, self).__init__(nh,
405                                             nh_sw_if_index,
406                                             proto=proto,
407                                             type=type,
408                                             next_hop_id=bier_imp)
409         self.nh_i_flags = flags
410         self.bier_imp = bier_imp
411
412     def encode(self):
413         return {'path': super(VppMRoutePath, self).encode(),
414                 'itf_flags': self.nh_i_flags}
415
416
417 class VppIpRoute(VppObject):
418     """
419     IP Route
420     """
421
422     def __init__(self, test, dest_addr,
423                  dest_addr_len, paths, table_id=0, register=True):
424         self._test = test
425         self.paths = paths
426         self.table_id = table_id
427         self.prefix = VppIpPrefix(dest_addr, dest_addr_len)
428         self.register = register
429         self.stats_index = None
430         self.modified = False
431
432         self.encoded_paths = []
433         for path in self.paths:
434             self.encoded_paths.append(path.encode())
435
436     def __eq__(self, other):
437         if self.table_id == other.table_id and \
438            self.prefix == other.prefix:
439             return True
440         return False
441
442     def modify(self, paths):
443         self.paths = paths
444         self.encoded_paths = []
445         for path in self.paths:
446             self.encoded_paths.append(path.encode())
447         self.modified = True
448
449         self._test.vapi.ip_route_add_del(route={'table_id': self.table_id,
450                                                 'prefix': self.prefix.encode(),
451                                                 'n_paths': len(
452                                                     self.encoded_paths),
453                                                 'paths': self.encoded_paths,
454                                                 },
455                                          is_add=1,
456                                          is_multipath=0)
457
458     def add_vpp_config(self):
459         r = self._test.vapi.ip_route_add_del(
460             route={'table_id': self.table_id,
461                    'prefix': self.prefix.encode(),
462                    'n_paths': len(self.encoded_paths),
463                    'paths': self.encoded_paths,
464                    },
465             is_add=1,
466             is_multipath=0)
467         self.stats_index = r.stats_index
468         if self.register:
469             self._test.registry.register(self, self._test.logger)
470
471     def remove_vpp_config(self):
472         # there's no need to issue different deletes for modified routes
473         # we do this only to test the two different ways to delete routes
474         # eiter by passing all the paths to remove and mutlipath=1 or
475         # passing no paths and multipath=0
476         if self.modified:
477             self._test.vapi.ip_route_add_del(
478                 route={'table_id': self.table_id,
479                        'prefix': self.prefix.encode(),
480                        'n_paths': len(
481                            self.encoded_paths),
482                        'paths': self.encoded_paths},
483                 is_add=0,
484                 is_multipath=1)
485         else:
486             self._test.vapi.ip_route_add_del(
487                 route={'table_id': self.table_id,
488                        'prefix': self.prefix.encode(),
489                        'n_paths': 0},
490                 is_add=0,
491                 is_multipath=0)
492
493     def query_vpp_config(self):
494         return find_route(self._test,
495                           self.prefix.address,
496                           self.prefix.len,
497                           self.table_id)
498
499     def object_id(self):
500         return ("%s:table-%d-%s/%d" % (
501             'ip6-route' if self.prefix.addr.version == 6 else 'ip-route',
502                 self.table_id,
503                 self.prefix.address,
504                 self.prefix.len))
505
506     def get_stats_to(self):
507         c = self._test.statistics.get_counter("/net/route/to")
508         return c[0][self.stats_index]
509
510     def get_stats_via(self):
511         c = self._test.statistics.get_counter("/net/route/via")
512         return c[0][self.stats_index]
513
514
515 class VppIpMRoute(VppObject):
516     """
517     IP Multicast Route
518     """
519
520     def __init__(self, test, src_addr, grp_addr,
521                  grp_addr_len, e_flags, paths, table_id=0,
522                  rpf_id=0):
523         self._test = test
524         self.paths = paths
525         self.table_id = table_id
526         self.e_flags = e_flags
527         self.rpf_id = rpf_id
528
529         self.prefix = VppIpMPrefix(src_addr, grp_addr, grp_addr_len)
530         self.encoded_paths = []
531         for path in self.paths:
532             self.encoded_paths.append(path.encode())
533
534     def add_vpp_config(self):
535         r = self._test.vapi.ip_mroute_add_del(self.table_id,
536                                               self.prefix.encode(),
537                                               self.e_flags,
538                                               self.rpf_id,
539                                               self.encoded_paths,
540                                               is_add=1)
541         self.stats_index = r.stats_index
542         self._test.registry.register(self, self._test.logger)
543
544     def remove_vpp_config(self):
545         self._test.vapi.ip_mroute_add_del(self.table_id,
546                                           self.prefix.encode(),
547                                           self.e_flags,
548                                           self.rpf_id,
549                                           self.encoded_paths,
550                                           is_add=0)
551
552     def update_entry_flags(self, flags):
553         self.e_flags = flags
554         self._test.vapi.ip_mroute_add_del(self.table_id,
555                                           self.prefix.encode(),
556                                           self.e_flags,
557                                           self.rpf_id,
558                                           [],
559                                           is_add=1)
560
561     def update_rpf_id(self, rpf_id):
562         self.rpf_id = rpf_id
563         self._test.vapi.ip_mroute_add_del(self.table_id,
564                                           self.prefix.encode(),
565                                           self.e_flags,
566                                           self.rpf_id,
567                                           [],
568                                           is_add=1)
569
570     def update_path_flags(self, itf, flags):
571         for p in range(len(self.paths)):
572             if self.paths[p].nh_itf == itf:
573                 self.paths[p].nh_i_flags = flags
574             self.encoded_paths[p] = self.paths[p].encode()
575             break
576
577         self._test.vapi.ip_mroute_add_del(self.table_id,
578                                           self.prefix.encode(),
579                                           self.e_flags,
580                                           self.rpf_id,
581                                           [self.encoded_paths[p]],
582                                           is_add=1,
583                                           is_multipath=0)
584
585     def query_vpp_config(self):
586         return find_mroute(self._test,
587                            self.prefix.gaddr,
588                            self.prefix.saddr,
589                            self.prefix.length,
590                            self.table_id)
591
592     def object_id(self):
593         return ("%d:(%s,%s/%d)" % (self.table_id,
594                                    self.prefix.saddr,
595                                    self.prefix.gaddr,
596                                    self.prefix.length))
597
598     def get_stats(self):
599         c = self._test.statistics.get_counter("/net/mroute")
600         return c[0][self.stats_index]
601
602
603 class VppMFibSignal(object):
604     def __init__(self, test, route, interface, packet):
605         self.route = route
606         self.interface = interface
607         self.packet = packet
608         self.test = test
609
610     def compare(self, signal):
611         self.test.assertEqual(self.interface, signal.sw_if_index)
612         self.test.assertEqual(self.route.table_id, signal.table_id)
613         self.test.assertEqual(self.route.prefix, signal.prefix)
614
615
616 class VppMplsIpBind(VppObject):
617     """
618     MPLS to IP Binding
619     """
620
621     def __init__(self, test, local_label, dest_addr, dest_addr_len,
622                  table_id=0, ip_table_id=0, is_ip6=0):
623         self._test = test
624         self.dest_addr_len = dest_addr_len
625         self.dest_addr = dest_addr
626         self.ip_addr = ip_address(text_type(dest_addr))
627         self.local_label = local_label
628         self.table_id = table_id
629         self.ip_table_id = ip_table_id
630         self.prefix = VppIpPrefix(dest_addr, dest_addr_len)
631
632     def add_vpp_config(self):
633         self._test.vapi.mpls_ip_bind_unbind(self.local_label,
634                                             self.prefix.encode(),
635                                             table_id=self.table_id,
636                                             ip_table_id=self.ip_table_id)
637         self._test.registry.register(self, self._test.logger)
638
639     def remove_vpp_config(self):
640         self._test.vapi.mpls_ip_bind_unbind(self.local_label,
641                                             self.prefix.encode(),
642                                             table_id=self.table_id,
643                                             ip_table_id=self.ip_table_id,
644                                             is_bind=0)
645
646     def query_vpp_config(self):
647         dump = self._test.vapi.mpls_route_dump(self.table_id)
648         for e in dump:
649             if self.local_label == e.mr_route.mr_label \
650                and self.table_id == e.mr_route.mr_table_id:
651                 return True
652         return False
653
654     def object_id(self):
655         return ("%d:%s binds %d:%s/%d"
656                 % (self.table_id,
657                    self.local_label,
658                    self.ip_table_id,
659                    self.dest_addr,
660                    self.dest_addr_len))
661
662
663 class VppMplsTable(VppObject):
664
665     def __init__(self,
666                  test,
667                  table_id):
668         self._test = test
669         self.table_id = table_id
670
671     def add_vpp_config(self):
672         self._test.vapi.mpls_table_add_del(
673             self.table_id,
674             is_add=1)
675         self._test.registry.register(self, self._test.logger)
676
677     def remove_vpp_config(self):
678         self._test.vapi.mpls_table_add_del(
679             self.table_id,
680             is_add=0)
681
682     def query_vpp_config(self):
683         dump = self._test.vapi.mpls_table_dump()
684         for d in dump:
685             if d.mt_table.mt_table_id == self.table_id:
686                 return True
687         return False
688
689     def object_id(self):
690         return ("table-mpls-%d" % (self.table_id))
691
692
693 class VppMplsRoute(VppObject):
694     """
695     MPLS Route/LSP
696     """
697
698     def __init__(self, test, local_label, eos_bit, paths, table_id=0,
699                  is_multicast=0,
700                  eos_proto=FibPathProto.FIB_PATH_NH_PROTO_IP4):
701         self._test = test
702         self.paths = paths
703         self.local_label = local_label
704         self.eos_bit = eos_bit
705         self.eos_proto = eos_proto
706         self.table_id = table_id
707         self.is_multicast = is_multicast
708
709     def add_vpp_config(self):
710         paths = []
711         for path in self.paths:
712             paths.append(path.encode())
713
714         r = self._test.vapi.mpls_route_add_del(self.table_id,
715                                                self.local_label,
716                                                self.eos_bit,
717                                                self.eos_proto,
718                                                self.is_multicast,
719                                                paths, 1, 0)
720         self.stats_index = r.stats_index
721         self._test.registry.register(self, self._test.logger)
722
723     def remove_vpp_config(self):
724         paths = []
725         for path in self.paths:
726             paths.append(path.encode())
727
728         self._test.vapi.mpls_route_add_del(self.table_id,
729                                            self.local_label,
730                                            self.eos_bit,
731                                            self.eos_proto,
732                                            self.is_multicast,
733                                            paths, 0, 0)
734
735     def query_vpp_config(self):
736         return find_mpls_route(self._test, self.table_id,
737                                self.local_label, self.eos_bit)
738
739     def object_id(self):
740         return ("mpls-route-%d:%s/%d"
741                 % (self.table_id,
742                    self.local_label,
743                    20 + self.eos_bit))
744
745     def get_stats_to(self):
746         c = self._test.statistics.get_counter("/net/route/to")
747         return c[0][self.stats_index]
748
749     def get_stats_via(self):
750         c = self._test.statistics.get_counter("/net/route/via")
751         return c[0][self.stats_index]