fib: Allow the creation of new source on the 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, INVALID_INDEX, VppIpAddressUnion, \
10     VppIpMPrefix
11 from ipaddress import ip_network, 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 FibPathProto:
24     FIB_PATH_NH_PROTO_IP4 = 0
25     FIB_PATH_NH_PROTO_IP6 = 1
26     FIB_PATH_NH_PROTO_MPLS = 2
27     FIB_PATH_NH_PROTO_ETHERNET = 3
28     FIB_PATH_NH_PROTO_BIER = 4
29     FIB_PATH_NH_PROTO_NSH = 5
30
31
32 class FibPathType:
33     FIB_PATH_TYPE_NORMAL = 0
34     FIB_PATH_TYPE_LOCAL = 1
35     FIB_PATH_TYPE_DROP = 2
36     FIB_PATH_TYPE_UDP_ENCAP = 3
37     FIB_PATH_TYPE_BIER_IMP = 4
38     FIB_PATH_TYPE_ICMP_UNREACH = 5
39     FIB_PATH_TYPE_ICMP_PROHIBIT = 6
40     FIB_PATH_TYPE_SOURCE_LOOKUP = 7
41     FIB_PATH_TYPE_DVR = 8
42     FIB_PATH_TYPE_INTERFACE_RX = 9
43     FIB_PATH_TYPE_CLASSIFY = 10
44
45
46 class FibPathFlags:
47     FIB_PATH_FLAG_NONE = 0
48     FIB_PATH_FLAG_RESOLVE_VIA_ATTACHED = 1
49     FIB_PATH_FLAG_RESOLVE_VIA_HOST = 2
50     FIB_PATH_FLAG_POP_PW_CW = 4
51
52
53 class MplsLspMode:
54     PIPE = 0
55     UNIFORM = 1
56
57
58 def mk_network(addr, len):
59     if ip_address(text_type(addr)).version == 4:
60         return IPv4Network("%s/%d" % (addr, len), strict=False)
61     else:
62         return IPv6Network("%s/%d" % (addr, len), strict=False)
63
64
65 def ip_to_dpo_proto(addr):
66     if addr.version == 6:
67         return DpoProto.DPO_PROTO_IP6
68     else:
69         return DpoProto.DPO_PROTO_IP4
70
71
72 def address_proto(ip_addr):
73     if ip_addr.ip_addr.version == 4:
74         return FibPathProto.FIB_PATH_NH_PROTO_IP4
75     else:
76         return FibPathProto.FIB_PATH_NH_PROTO_IP6
77
78
79 def find_route(test, addr, len, table_id=0, sw_if_index=None):
80     prefix = mk_network(addr, len)
81
82     if 4 == prefix.version:
83         routes = test.vapi.ip_route_dump(table_id, False)
84     else:
85         routes = test.vapi.ip_route_dump(table_id, True)
86
87     for e in routes:
88         if table_id == e.route.table_id \
89            and str(e.route.prefix) == str(prefix):
90             if not sw_if_index:
91                 return True
92             else:
93                 # should be only one path if the user is looking
94                 # for the interface the route is reachable through
95                 if e.route.n_paths != 1:
96                     return False
97                 else:
98                     return (e.route.paths[0].sw_if_index == sw_if_index)
99
100     return False
101
102
103 def find_route_in_dump(dump, route, table):
104     for r in dump:
105         if table.table_id == r.route.table_id \
106            and route.prefix == r.route.prefix:
107             if len(route.paths) == r.route.n_paths:
108                 return True
109     return False
110
111
112 def find_mroute_in_dump(dump, route, table):
113     for r in dump:
114         if table.table_id == r.route.table_id \
115            and route.prefix == r.route.prefix:
116             return True
117     return False
118
119
120 def find_mroute(test, grp_addr, src_addr, grp_addr_len,
121                 table_id=0):
122     ip_mprefix = VppIpMPrefix(text_type(src_addr),
123                               text_type(grp_addr),
124                               grp_addr_len)
125
126     if 4 == ip_mprefix.version:
127         routes = test.vapi.ip_mroute_dump(table_id, False)
128     else:
129         routes = test.vapi.ip_mroute_dump(table_id, True)
130
131     for e in routes:
132         if table_id == e.route.table_id and ip_mprefix == e.route.prefix:
133             return True
134     return False
135
136
137 def find_mpls_route(test, table_id, label, eos_bit, paths=None):
138     dump = test.vapi.mpls_route_dump(table_id)
139     for e in dump:
140         if label == e.mr_route.mr_label \
141            and eos_bit == e.mr_route.mr_eos \
142            and table_id == e.mr_route.mr_table_id:
143             if not paths:
144                 return True
145             else:
146                 if (len(paths) != len(e.mr_route.mr_paths)):
147                     return False
148                 for i in range(len(paths)):
149                     if (paths[i] != e.mr_route.mr_paths[i]):
150                         return False
151                 return True
152     return False
153
154
155 def fib_interface_ip_prefix(test, addr, len, sw_if_index):
156     # can't use python net here since we need the host bits in the prefix
157     prefix = "%s/%d" % (addr, len)
158     addrs = test.vapi.ip_address_dump(
159         sw_if_index,
160         is_ipv6=(6 == ip_address(addr).version))
161
162     for a in addrs:
163         if a.sw_if_index == sw_if_index and \
164            str(a.prefix) == prefix:
165             return True
166     return False
167
168
169 class VppIpTable(VppObject):
170
171     def __init__(self,
172                  test,
173                  table_id,
174                  is_ip6=0,
175                  register=True):
176         self._test = test
177         self.table_id = table_id
178         self.is_ip6 = is_ip6
179         self.register = register
180
181     def add_vpp_config(self):
182         self._test.vapi.ip_table_add_del(is_add=1,
183                                          table={'is_ip6': self.is_ip6,
184                                                 'table_id': self.table_id})
185         if self.register:
186             self._test.registry.register(self, self._test.logger)
187         return self
188
189     def remove_vpp_config(self):
190         self._test.vapi.ip_table_add_del(is_add=0,
191                                          table={'is_ip6': self.is_ip6,
192                                                 'table_id': self.table_id})
193
194     def replace_begin(self):
195         self._test.vapi.ip_table_replace_begin(
196             table={'is_ip6': self.is_ip6,
197                    'table_id': self.table_id})
198
199     def replace_end(self):
200         self._test.vapi.ip_table_replace_end(
201             table={'is_ip6': self.is_ip6,
202                    'table_id': self.table_id})
203
204     def flush(self):
205         self._test.vapi.ip_table_flush(table={'is_ip6': self.is_ip6,
206                                               'table_id': self.table_id})
207
208     def dump(self):
209         return self._test.vapi.ip_route_dump(self.table_id, self.is_ip6)
210
211     def mdump(self):
212         return self._test.vapi.ip_mroute_dump(self.table_id, self.is_ip6)
213
214     def query_vpp_config(self):
215         if self.table_id == 0:
216             # the default table always exists
217             return False
218         # find the default route
219         return find_route(self._test,
220                           "::" if self.is_ip6 else "0.0.0.0",
221                           0,
222                           self.table_id)
223
224     def object_id(self):
225         return ("table-%s-%d" %
226                 ("v6" if self.is_ip6 == 1 else "v4",
227                  self.table_id))
228
229
230 class VppIpInterfaceAddress(VppObject):
231
232     def __init__(self, test, intf, addr, len, bind=None):
233         self._test = test
234         self.intf = intf
235         self.addr = addr
236         self.len = len
237         self.prefix = "%s/%d" % (addr, len)
238         self.host_len = ip_network(self.prefix, strict=False).max_prefixlen
239         self.table_id = 0
240         if bind:
241             self.table_id = bind.table.table_id
242
243     def add_vpp_config(self):
244         self._test.vapi.sw_interface_add_del_address(
245             sw_if_index=self.intf.sw_if_index, prefix=self.prefix,
246             is_add=1)
247         self._test.registry.register(self, self._test.logger)
248         return self
249
250     def remove_vpp_config(self):
251         self._test.vapi.sw_interface_add_del_address(
252             sw_if_index=self.intf.sw_if_index, prefix=self.prefix,
253             is_add=0)
254
255     def query_vpp_config(self):
256         # search for the IP address mapping and the two expected
257         # FIB entries
258         v = ip_address(self.addr).version
259
260         if ((v == 4 and self.len < 31) or (v == 6 and self.len < 127)):
261             return (fib_interface_ip_prefix(self._test,
262                                             self.addr,
263                                             self.len,
264                                             self.intf.sw_if_index) &
265                     find_route(self._test,
266                                self.addr,
267                                self.len,
268                                table_id=self.table_id,
269                                sw_if_index=self.intf.sw_if_index) &
270                     find_route(self._test,
271                                self.addr,
272                                self.host_len,
273                                table_id=self.table_id,
274                                sw_if_index=self.intf.sw_if_index))
275         else:
276             return (fib_interface_ip_prefix(self._test,
277                                             self.addr,
278                                             self.len,
279                                             self.intf.sw_if_index) &
280                     find_route(self._test,
281                                self.addr,
282                                self.host_len,
283                                table_id=self.table_id,
284                                sw_if_index=self.intf.sw_if_index))
285
286     def object_id(self):
287         return "interface-ip-%s-%d-%s" % (self.intf,
288                                           self.table_id,
289                                           self.prefix)
290
291
292 class VppIp6LinkLocalAddress(VppObject):
293
294     def __init__(self, test, intf, addr):
295         self._test = test
296         self.intf = intf
297         self.addr = addr
298
299     def add_vpp_config(self):
300         self._test.vapi.sw_interface_ip6_set_link_local_address(
301             sw_if_index=self.intf.sw_if_index, ip=self.addr)
302         self._test.registry.register(self, self._test.logger)
303         return self
304
305     def remove_vpp_config(self):
306         # link locals can't be removed, only changed
307         pass
308
309     def query_vpp_config(self):
310         # no API to query
311         return False
312
313     def object_id(self):
314         return "ip6-link-local-%s-%s" % (self.intf, self.addr)
315
316
317 class VppIpInterfaceBind(VppObject):
318
319     def __init__(self, test, intf, table):
320         self._test = test
321         self.intf = intf
322         self.table = table
323
324     def add_vpp_config(self):
325         if self.table.is_ip6:
326             self.intf.set_table_ip6(self.table.table_id)
327         else:
328             self.intf.set_table_ip4(self.table.table_id)
329         self._test.registry.register(self, self._test.logger)
330         return self
331
332     def remove_vpp_config(self):
333         if 0 == self.table.table_id:
334             return
335         if self.table.is_ip6:
336             self.intf.set_table_ip6(0)
337         else:
338             self.intf.set_table_ip4(0)
339
340     def query_vpp_config(self):
341         if 0 == self.table.table_id:
342             return False
343         return self._test.vapi.sw_interface_get_table(
344             self.intf.sw_if_index,
345             self.table.is_ip6).vrf_id == self.table.table_id
346
347     def object_id(self):
348         return "interface-bind-%s-%s" % (self.intf, self.table)
349
350
351 class VppMplsLabel:
352     def __init__(self, value, mode=MplsLspMode.PIPE, ttl=64, exp=0):
353         self.value = value
354         self.mode = mode
355         self.ttl = ttl
356         self.exp = exp
357
358     def encode(self):
359         is_uniform = 0 if self.mode is MplsLspMode.PIPE else 1
360         return {'label': self.value,
361                 'ttl': self.ttl,
362                 'exp': self.exp,
363                 'is_uniform': is_uniform}
364
365     def __eq__(self, other):
366         if isinstance(other, self.__class__):
367             return (self.value == other.value and
368                     self.ttl == other.ttl and
369                     self.exp == other.exp and
370                     self.mode == other.mode)
371         elif hasattr(other, 'label'):
372             return (self.value == other.label and
373                     self.ttl == other.ttl and
374                     self.exp == other.exp and
375                     (self.mode == MplsLspMode.UNIFORM) == other.is_uniform)
376         else:
377             return False
378
379     def __ne__(self, other):
380         return not (self == other)
381
382
383 class VppFibPathNextHop:
384     def __init__(self, addr,
385                  via_label=MPLS_LABEL_INVALID,
386                  next_hop_id=INVALID_INDEX):
387         self.addr = VppIpAddressUnion(addr)
388         self.via_label = via_label
389         self.obj_id = next_hop_id
390
391     def encode(self):
392         if self.via_label is not MPLS_LABEL_INVALID:
393             return {'via_label': self.via_label}
394         if self.obj_id is not INVALID_INDEX:
395             return {'obj_id': self.obj_id}
396         else:
397             return {'address': self.addr.encode()}
398
399     def proto(self):
400         if self.via_label is MPLS_LABEL_INVALID:
401             return address_proto(self.addr)
402         else:
403             return FibPathProto.FIB_PATH_NH_PROTO_MPLS
404
405     def __eq__(self, other):
406         if not isinstance(other, self.__class__):
407             # try the other instance's __eq__.
408             return NotImplemented
409         return (self.addr == other.addr and
410                 self.via_label == other.via_label and
411                 self.obj_id == other.obj_id)
412
413
414 class VppRoutePath:
415
416     def __init__(
417             self,
418             nh_addr,
419             nh_sw_if_index,
420             nh_table_id=0,
421             labels=[],
422             nh_via_label=MPLS_LABEL_INVALID,
423             rpf_id=0,
424             next_hop_id=INVALID_INDEX,
425             proto=None,
426             flags=FibPathFlags.FIB_PATH_FLAG_NONE,
427             type=FibPathType.FIB_PATH_TYPE_NORMAL):
428         self.nh_itf = nh_sw_if_index
429         self.nh_table_id = nh_table_id
430         self.nh_labels = labels
431         self.weight = 1
432         self.rpf_id = rpf_id
433         self.proto = proto
434         self.flags = flags
435         self.type = type
436         self.nh = VppFibPathNextHop(nh_addr, nh_via_label, next_hop_id)
437         if proto is None:
438             self.proto = self.nh.proto()
439         else:
440             self.proto = proto
441         self.next_hop_id = next_hop_id
442
443     def encode_labels(self):
444         lstack = []
445         for l in self.nh_labels:
446             if type(l) == VppMplsLabel:
447                 lstack.append(l.encode())
448             else:
449                 lstack.append({'label': l,
450                                'ttl': 255})
451         while (len(lstack) < 16):
452             lstack.append({})
453
454         return lstack
455
456     def encode(self):
457         return {'weight': 1,
458                 'preference': 0,
459                 'table_id': self.nh_table_id,
460                 'nh': self.nh.encode(),
461                 'next_hop_id': self.next_hop_id,
462                 'sw_if_index': self.nh_itf,
463                 'rpf_id': self.rpf_id,
464                 'proto': self.proto,
465                 'type': self.type,
466                 'flags': self.flags,
467                 'n_labels': len(self.nh_labels),
468                 'label_stack': self.encode_labels()}
469
470     def __eq__(self, other):
471         if isinstance(other, self.__class__):
472             return self.nh == other.nh
473         elif hasattr(other, 'sw_if_index'):
474             # vl_api_fib_path_t
475             if (len(self.nh_labels) != other.n_labels):
476                 return False
477             for i in range(len(self.nh_labels)):
478                 if (self.nh_labels[i] != other.label_stack[i]):
479                     return False
480             return self.nh_itf == other.sw_if_index
481         else:
482             return False
483
484     def __ne__(self, other):
485         return not (self == other)
486
487
488 class VppMRoutePath(VppRoutePath):
489
490     def __init__(self, nh_sw_if_index, flags,
491                  nh=None,
492                  proto=FibPathProto.FIB_PATH_NH_PROTO_IP4,
493                  type=FibPathType.FIB_PATH_TYPE_NORMAL,
494                  bier_imp=INVALID_INDEX):
495         if not nh:
496             nh = "::" if proto is FibPathProto.FIB_PATH_NH_PROTO_IP6 \
497                  else "0.0.0.0"
498         super(VppMRoutePath, self).__init__(nh,
499                                             nh_sw_if_index,
500                                             proto=proto,
501                                             type=type,
502                                             next_hop_id=bier_imp)
503         self.nh_i_flags = flags
504         self.bier_imp = bier_imp
505
506     def encode(self):
507         return {'path': super(VppMRoutePath, self).encode(),
508                 'itf_flags': self.nh_i_flags}
509
510
511 class VppIpRoute(VppObject):
512     """
513     IP Route
514     """
515
516     def __init__(self, test, dest_addr,
517                  dest_addr_len, paths, table_id=0, register=True):
518         self._test = test
519         self.paths = paths
520         self.table_id = table_id
521         self.prefix = mk_network(dest_addr, dest_addr_len)
522         self.register = register
523         self.stats_index = None
524         self.modified = False
525
526         self.encoded_paths = []
527         for path in self.paths:
528             self.encoded_paths.append(path.encode())
529
530     def __eq__(self, other):
531         if self.table_id == other.table_id and \
532            self.prefix == other.prefix:
533             return True
534         return False
535
536     def modify(self, paths):
537         self.paths = paths
538         self.encoded_paths = []
539         for path in self.paths:
540             self.encoded_paths.append(path.encode())
541         self.modified = True
542
543         self._test.vapi.ip_route_add_del(route={'table_id': self.table_id,
544                                                 'prefix': self.prefix,
545                                                 'n_paths': len(
546                                                     self.encoded_paths),
547                                                 'paths': self.encoded_paths,
548                                                 },
549                                          is_add=1,
550                                          is_multipath=0)
551
552     def add_vpp_config(self):
553         r = self._test.vapi.ip_route_add_del(
554             route={'table_id': self.table_id,
555                    'prefix': self.prefix,
556                    'n_paths': len(self.encoded_paths),
557                    'paths': self.encoded_paths,
558                    },
559             is_add=1,
560             is_multipath=0)
561         self.stats_index = r.stats_index
562         if self.register:
563             self._test.registry.register(self, self._test.logger)
564         return self
565
566     def remove_vpp_config(self):
567         # there's no need to issue different deletes for modified routes
568         # we do this only to test the two different ways to delete routes
569         # eiter by passing all the paths to remove and mutlipath=1 or
570         # passing no paths and multipath=0
571         if self.modified:
572             self._test.vapi.ip_route_add_del(
573                 route={'table_id': self.table_id,
574                        'prefix': self.prefix,
575                        'n_paths': len(
576                            self.encoded_paths),
577                        'paths': self.encoded_paths},
578                 is_add=0,
579                 is_multipath=1)
580         else:
581             self._test.vapi.ip_route_add_del(
582                 route={'table_id': self.table_id,
583                        'prefix': self.prefix,
584                        'n_paths': 0},
585                 is_add=0,
586                 is_multipath=0)
587
588     def query_vpp_config(self):
589         return find_route(self._test,
590                           self.prefix.network_address,
591                           self.prefix.prefixlen,
592                           self.table_id)
593
594     def object_id(self):
595         return ("%s:table-%d-%s" % (
596             'ip6-route' if self.prefix.version == 6 else 'ip-route',
597                 self.table_id,
598                 self.prefix))
599
600     def get_stats_to(self):
601         c = self._test.statistics.get_counter("/net/route/to")
602         return c[0][self.stats_index]
603
604     def get_stats_via(self):
605         c = self._test.statistics.get_counter("/net/route/via")
606         return c[0][self.stats_index]
607
608
609 class VppIpRouteV2(VppObject):
610     """
611     IP Route V2
612     """
613
614     def __init__(self, test, dest_addr,
615                  dest_addr_len, paths, table_id=0,
616                  register=True, src=0):
617         self._test = test
618         self.paths = paths
619         self.table_id = table_id
620         self.prefix = mk_network(dest_addr, dest_addr_len)
621         self.register = register
622         self.stats_index = None
623         self.modified = False
624         self.src = src
625
626         self.encoded_paths = []
627         for path in self.paths:
628             self.encoded_paths.append(path.encode())
629
630     def __eq__(self, other):
631         if self.table_id == other.table_id and \
632            self.prefix == other.prefix:
633             return True
634         return False
635
636     def modify(self, paths):
637         self.paths = paths
638         self.encoded_paths = []
639         for path in self.paths:
640             self.encoded_paths.append(path.encode())
641         self.modified = True
642
643         self._test.vapi.ip_route_add_del_v2(route={'table_id': self.table_id,
644                                                    'prefix': self.prefix,
645                                                    'src': self.src,
646                                                    'n_paths': len(
647                                                        self.encoded_paths),
648                                                    'paths': self.encoded_paths,
649                                                    },
650                                             is_add=1,
651                                             is_multipath=0)
652
653     def add_vpp_config(self):
654         r = self._test.vapi.ip_route_add_del_v2(
655             route={'table_id': self.table_id,
656                    'prefix': self.prefix,
657                    'n_paths': len(self.encoded_paths),
658                    'paths': self.encoded_paths,
659                    'src': self.src,
660                    },
661             is_add=1,
662             is_multipath=0)
663         self.stats_index = r.stats_index
664         if self.register:
665             self._test.registry.register(self, self._test.logger)
666         return self
667
668     def remove_vpp_config(self):
669         # there's no need to issue different deletes for modified routes
670         # we do this only to test the two different ways to delete routes
671         # eiter by passing all the paths to remove and mutlipath=1 or
672         # passing no paths and multipath=0
673         if self.modified:
674             self._test.vapi.ip_route_add_del_v2(
675                 route={'table_id': self.table_id,
676                        'prefix': self.prefix,
677                        'src': self.src,
678                        'n_paths': len(
679                            self.encoded_paths),
680                        'paths': self.encoded_paths},
681                 is_add=0,
682                 is_multipath=1)
683         else:
684             self._test.vapi.ip_route_add_del_v2(
685                 route={'table_id': self.table_id,
686                        'prefix': self.prefix,
687                        'src': self.src,
688                        'n_paths': 0},
689                 is_add=0,
690                 is_multipath=0)
691
692     def query_vpp_config(self):
693         return find_route(self._test,
694                           self.prefix.network_address,
695                           self.prefix.prefixlen,
696                           self.table_id)
697
698     def object_id(self):
699         return ("%s:table-%d-%s" % (
700             'ip6-route' if self.prefix.version == 6 else 'ip-route',
701                 self.table_id,
702                 self.prefix))
703
704     def get_stats_to(self):
705         c = self._test.statistics.get_counter("/net/route/to")
706         return c[0][self.stats_index]
707
708     def get_stats_via(self):
709         c = self._test.statistics.get_counter("/net/route/via")
710         return c[0][self.stats_index]
711
712
713 class VppIpMRoute(VppObject):
714     """
715     IP Multicast Route
716     """
717
718     def __init__(self, test, src_addr, grp_addr,
719                  grp_addr_len, e_flags, paths, table_id=0,
720                  rpf_id=0):
721         self._test = test
722         self.paths = paths
723         self.table_id = table_id
724         self.e_flags = e_flags
725         self.rpf_id = rpf_id
726
727         self.prefix = VppIpMPrefix(src_addr, grp_addr, grp_addr_len)
728         self.encoded_paths = []
729         for path in self.paths:
730             self.encoded_paths.append(path.encode())
731
732     def encode(self, paths=None):
733         _paths = self.encoded_paths if paths is None else paths
734         return {'table_id': self.table_id,
735                 'entry_flags': self.e_flags,
736                 'rpf_id': self.rpf_id,
737                 'prefix': self.prefix.encode(),
738                 'n_paths': len(_paths),
739                 'paths': _paths,
740                 }
741
742     def add_vpp_config(self):
743         r = self._test.vapi.ip_mroute_add_del(route=self.encode(),
744                                               is_multipath=1,
745                                               is_add=1)
746         self.stats_index = r.stats_index
747         self._test.registry.register(self, self._test.logger)
748         return self
749
750     def remove_vpp_config(self):
751         self._test.vapi.ip_mroute_add_del(route=self.encode(),
752                                           is_multipath=1,
753                                           is_add=0)
754
755     def update_entry_flags(self, flags):
756         self.e_flags = flags
757         self._test.vapi.ip_mroute_add_del(route=self.encode(paths=[]),
758                                           is_multipath=1,
759                                           is_add=1)
760
761     def update_rpf_id(self, rpf_id):
762         self.rpf_id = rpf_id
763         self._test.vapi.ip_mroute_add_del(route=self.encode(paths=[]),
764                                           is_multipath=1,
765                                           is_add=1)
766
767     def update_path_flags(self, itf, flags):
768         for p in range(len(self.paths)):
769             if self.paths[p].nh_itf == itf:
770                 self.paths[p].nh_i_flags = flags
771                 self.encoded_paths[p] = self.paths[p].encode()
772                 break
773
774         self._test.vapi.ip_mroute_add_del(
775             route=self.encode(
776                 paths=[self.encoded_paths[p]]),
777             is_add=1,
778             is_multipath=0)
779
780     def query_vpp_config(self):
781         return find_mroute(self._test,
782                            self.prefix.gaddr,
783                            self.prefix.saddr,
784                            self.prefix.length,
785                            self.table_id)
786
787     def object_id(self):
788         return ("%d:(%s,%s/%d)" % (self.table_id,
789                                    self.prefix.saddr,
790                                    self.prefix.gaddr,
791                                    self.prefix.length))
792
793     def get_stats(self):
794         c = self._test.statistics.get_counter("/net/mroute")
795         return c[0][self.stats_index]
796
797
798 class VppMFibSignal:
799     def __init__(self, test, route, interface, packet):
800         self.route = route
801         self.interface = interface
802         self.packet = packet
803         self.test = test
804
805     def compare(self, signal):
806         self.test.assertEqual(self.interface, signal.sw_if_index)
807         self.test.assertEqual(self.route.table_id, signal.table_id)
808         self.test.assertEqual(self.route.prefix, signal.prefix)
809
810
811 class VppMplsIpBind(VppObject):
812     """
813     MPLS to IP Binding
814     """
815
816     def __init__(self, test, local_label, dest_addr, dest_addr_len,
817                  table_id=0, ip_table_id=0, is_ip6=0):
818         self._test = test
819         self.dest_addr_len = dest_addr_len
820         self.dest_addr = dest_addr
821         self.ip_addr = ip_address(text_type(dest_addr))
822         self.local_label = local_label
823         self.table_id = table_id
824         self.ip_table_id = ip_table_id
825         self.prefix = mk_network(dest_addr, dest_addr_len)
826
827     def add_vpp_config(self):
828         self._test.vapi.mpls_ip_bind_unbind(self.local_label,
829                                             self.prefix,
830                                             table_id=self.table_id,
831                                             ip_table_id=self.ip_table_id)
832         self._test.registry.register(self, self._test.logger)
833
834     def remove_vpp_config(self):
835         self._test.vapi.mpls_ip_bind_unbind(self.local_label,
836                                             self.prefix,
837                                             table_id=self.table_id,
838                                             ip_table_id=self.ip_table_id,
839                                             is_bind=0)
840
841     def query_vpp_config(self):
842         dump = self._test.vapi.mpls_route_dump(self.table_id)
843         for e in dump:
844             if self.local_label == e.mr_route.mr_label \
845                and self.table_id == e.mr_route.mr_table_id:
846                 return True
847         return False
848
849     def object_id(self):
850         return ("%d:%s binds %d:%s/%d"
851                 % (self.table_id,
852                    self.local_label,
853                    self.ip_table_id,
854                    self.dest_addr,
855                    self.dest_addr_len))
856
857
858 class VppMplsTable(VppObject):
859
860     def __init__(self,
861                  test,
862                  table_id):
863         self._test = test
864         self.table_id = table_id
865
866     def add_vpp_config(self):
867         self._test.vapi.mpls_table_add_del(
868             self.table_id,
869             is_add=1)
870         self._test.registry.register(self, self._test.logger)
871
872     def remove_vpp_config(self):
873         self._test.vapi.mpls_table_add_del(
874             self.table_id,
875             is_add=0)
876
877     def query_vpp_config(self):
878         dump = self._test.vapi.mpls_table_dump()
879         for d in dump:
880             if d.mt_table.mt_table_id == self.table_id:
881                 return True
882         return False
883
884     def object_id(self):
885         return ("table-mpls-%d" % (self.table_id))
886
887
888 class VppMplsRoute(VppObject):
889     """
890     MPLS Route/LSP
891     """
892
893     def __init__(self, test, local_label, eos_bit, paths, table_id=0,
894                  is_multicast=0,
895                  eos_proto=FibPathProto.FIB_PATH_NH_PROTO_IP4):
896         self._test = test
897         self.paths = paths
898         self.local_label = local_label
899         self.eos_bit = eos_bit
900         self.eos_proto = eos_proto
901         self.table_id = table_id
902         self.is_multicast = is_multicast
903
904     def add_vpp_config(self):
905         paths = []
906         for path in self.paths:
907             paths.append(path.encode())
908
909         r = self._test.vapi.mpls_route_add_del(self.table_id,
910                                                self.local_label,
911                                                self.eos_bit,
912                                                self.eos_proto,
913                                                self.is_multicast,
914                                                paths, 1, 0)
915         self.stats_index = r.stats_index
916         self._test.registry.register(self, self._test.logger)
917
918     def remove_vpp_config(self):
919         paths = []
920         for path in self.paths:
921             paths.append(path.encode())
922
923         self._test.vapi.mpls_route_add_del(self.table_id,
924                                            self.local_label,
925                                            self.eos_bit,
926                                            self.eos_proto,
927                                            self.is_multicast,
928                                            paths, 0, 0)
929
930     def query_vpp_config(self):
931         return find_mpls_route(self._test, self.table_id,
932                                self.local_label, self.eos_bit)
933
934     def object_id(self):
935         return ("mpls-route-%d:%s/%d"
936                 % (self.table_id,
937                    self.local_label,
938                    20 + self.eos_bit))
939
940     def get_stats_to(self):
941         c = self._test.statistics.get_counter("/net/route/to")
942         return c[0][self.stats_index]
943
944     def get_stats_via(self):
945         c = self._test.statistics.get_counter("/net/route/via")
946         return c[0][self.stats_index]