tests: Remove the unrequired VPP IP address/prefix class wrappers
[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_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 mk_network(addr, len):
76     if ip_address(text_type(addr)).version == 4:
77         return IPv4Network("%s/%d" % (addr, len), strict=False)
78     else:
79         return IPv6Network("%s/%d" % (addr, len), strict=False)
80
81
82 def ip_to_dpo_proto(addr):
83     if addr.version == 6:
84         return DpoProto.DPO_PROTO_IP6
85     else:
86         return DpoProto.DPO_PROTO_IP4
87
88
89 def address_proto(ip_addr):
90     if ip_addr.ip_addr.version is 4:
91         return FibPathProto.FIB_PATH_NH_PROTO_IP4
92     else:
93         return FibPathProto.FIB_PATH_NH_PROTO_IP6
94
95
96 def find_route(test, addr, len, table_id=0):
97     prefix = mk_network(addr, len)
98
99     if 4 is prefix.version:
100         routes = test.vapi.ip_route_dump(table_id, False)
101     else:
102         routes = test.vapi.ip_route_dump(table_id, True)
103
104     for e in routes:
105         if table_id == e.route.table_id \
106            and str(e.route.prefix) == str(prefix):
107             return True
108     return False
109
110
111 def find_mroute(test, grp_addr, src_addr, grp_addr_len,
112                 table_id=0):
113     ip_mprefix = VppIpMPrefix(text_type(src_addr),
114                               text_type(grp_addr),
115                               grp_addr_len)
116
117     if 4 is ip_mprefix.version:
118         routes = test.vapi.ip_mroute_dump(table_id, False)
119     else:
120         routes = test.vapi.ip_mroute_dump(table_id, True)
121
122     for e in routes:
123         if table_id == e.route.table_id and ip_mprefix == e.route.prefix:
124             return True
125     return False
126
127
128 def find_mpls_route(test, table_id, label, eos_bit, paths=None):
129     dump = test.vapi.mpls_route_dump(table_id)
130     for e in dump:
131         if label == e.mr_route.mr_label \
132            and eos_bit == e.mr_route.mr_eos \
133            and table_id == e.mr_route.mr_table_id:
134             if not paths:
135                 return True
136             else:
137                 if (len(paths) != len(e.mr_route.mr_paths)):
138                     return False
139                 for i in range(len(paths)):
140                     if (paths[i] != e.mr_route.mr_paths[i]):
141                         return False
142                 return True
143     return False
144
145
146 def fib_interface_ip_prefix(test, addr, len, sw_if_index):
147     # can't use python net here since we need the host bits in the prefix
148     prefix = "%s/%d" % (addr, len)
149     addrs = test.vapi.ip_address_dump(
150         sw_if_index,
151         is_ipv6=(6 == ip_address(addr).version))
152
153     for a in addrs:
154         if a.sw_if_index == sw_if_index and \
155            str(a.prefix) == prefix:
156             return True
157     return False
158
159
160 class VppIpTable(VppObject):
161
162     def __init__(self,
163                  test,
164                  table_id,
165                  is_ip6=0):
166         self._test = test
167         self.table_id = table_id
168         self.is_ip6 = is_ip6
169
170     def add_vpp_config(self):
171         self._test.vapi.ip_table_add_del(is_ipv6=self.is_ip6, is_add=1,
172                                          table_id=self.table_id)
173         self._test.registry.register(self, self._test.logger)
174
175     def remove_vpp_config(self):
176         self._test.vapi.ip_table_add_del(is_ipv6=self.is_ip6, is_add=0,
177                                          table_id=self.table_id)
178
179     def query_vpp_config(self):
180         if self.table_id == 0:
181             # the default table always exists
182             return False
183         # find the default route
184         return find_route(self._test,
185                           "::" if self.is_ip6 else "0.0.0.0",
186                           0,
187                           self.table_id)
188
189     def object_id(self):
190         return ("table-%s-%d" %
191                 ("v6" if self.is_ip6 == 1 else "v4",
192                  self.table_id))
193
194
195 class VppIpInterfaceAddress(VppObject):
196
197     def __init__(self, test, intf, addr, len):
198         self._test = test
199         self.intf = intf
200         self.addr = addr
201         self.len = len
202         self.prefix = "%s/%d" % (addr, len)
203
204     def add_vpp_config(self):
205         self._test.vapi.sw_interface_add_del_address(
206             sw_if_index=self.intf.sw_if_index, prefix=self.prefix,
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, prefix=self.prefix,
213             is_add=0)
214
215     def query_vpp_config(self):
216         return fib_interface_ip_prefix(self._test,
217                                        self.addr,
218                                        self.len,
219                                        self.intf.sw_if_index)
220
221     def object_id(self):
222         return "interface-ip-%s-%s" % (self.intf, self.prefix)
223
224
225 class VppIpInterfaceBind(VppObject):
226
227     def __init__(self, test, intf, table):
228         self._test = test
229         self.intf = intf
230         self.table = table
231
232     def add_vpp_config(self):
233         if self.table.is_ip6:
234             self.intf.set_table_ip6(self.table.table_id)
235         else:
236             self.intf.set_table_ip4(self.table.table_id)
237         self._test.registry.register(self, self._test.logger)
238
239     def remove_vpp_config(self):
240         if 0 == self.table.table_id:
241             return
242         if self.table.is_ip6:
243             self.intf.set_table_ip6(0)
244         else:
245             self.intf.set_table_ip4(0)
246
247     def query_vpp_config(self):
248         if 0 == self.table.table_id:
249             return False
250         return self._test.vapi.sw_interface_get_table(
251             self.intf.sw_if_index,
252             self.table.is_ip6).vrf_id == self.table.table_id
253
254     def object_id(self):
255         return "interface-bind-%s-%s" % (self.intf, self.table)
256
257
258 class VppMplsLabel(object):
259     def __init__(self, value, mode=MplsLspMode.PIPE, ttl=64, exp=0):
260         self.value = value
261         self.mode = mode
262         self.ttl = ttl
263         self.exp = exp
264
265     def encode(self):
266         is_uniform = 0 if self.mode is MplsLspMode.PIPE else 1
267         return {'label': self.value,
268                 'ttl': self.ttl,
269                 'exp': self.exp,
270                 'is_uniform': is_uniform}
271
272     def __eq__(self, other):
273         if isinstance(other, self.__class__):
274             return (self.value == other.value and
275                     self.ttl == other.ttl and
276                     self.exp == other.exp and
277                     self.mode == other.mode)
278         elif hasattr(other, 'label'):
279             return (self.value == other.label and
280                     self.ttl == other.ttl and
281                     self.exp == other.exp and
282                     (self.mode == MplsLspMode.UNIFORM) == other.is_uniform)
283         else:
284             return False
285
286     def __ne__(self, other):
287         return not (self == other)
288
289
290 class VppFibPathNextHop(object):
291     def __init__(self, addr,
292                  via_label=MPLS_LABEL_INVALID,
293                  next_hop_id=INVALID_INDEX):
294         self.addr = VppIpAddressUnion(addr)
295         self.via_label = via_label
296         self.obj_id = next_hop_id
297
298     def encode(self):
299         if self.via_label is not MPLS_LABEL_INVALID:
300             return {'via_label': self.via_label}
301         if self.obj_id is not INVALID_INDEX:
302             return {'obj_id': self.obj_id}
303         else:
304             return {'address': self.addr.encode()}
305
306     def proto(self):
307         if self.via_label is MPLS_LABEL_INVALID:
308             return address_proto(self.addr)
309         else:
310             return FibPathProto.FIB_PATH_NH_PROTO_MPLS
311
312     def __eq__(self, other):
313         if not isinstance(other, self.__class__):
314             # try the other instance's __eq__.
315             return NotImplemented
316         return (self.addr == other.addr and
317                 self.via_label == other.via_label and
318                 self.obj_id == other.obj_id)
319
320
321 class VppRoutePath(object):
322
323     def __init__(
324             self,
325             nh_addr,
326             nh_sw_if_index,
327             nh_table_id=0,
328             labels=[],
329             nh_via_label=MPLS_LABEL_INVALID,
330             rpf_id=0,
331             next_hop_id=INVALID_INDEX,
332             proto=None,
333             flags=FibPathFlags.FIB_PATH_FLAG_NONE,
334             type=FibPathType.FIB_PATH_TYPE_NORMAL):
335         self.nh_itf = nh_sw_if_index
336         self.nh_table_id = nh_table_id
337         self.nh_labels = labels
338         self.weight = 1
339         self.rpf_id = rpf_id
340         self.proto = proto
341         self.flags = flags
342         self.type = type
343         self.nh = VppFibPathNextHop(nh_addr, nh_via_label, next_hop_id)
344         if proto is None:
345             self.proto = self.nh.proto()
346         else:
347             self.proto = proto
348         self.next_hop_id = next_hop_id
349
350     def encode_labels(self):
351         lstack = []
352         for l in self.nh_labels:
353             if type(l) == VppMplsLabel:
354                 lstack.append(l.encode())
355             else:
356                 lstack.append({'label': l,
357                                'ttl': 255})
358         while (len(lstack) < 16):
359             lstack.append({})
360
361         return lstack
362
363     def encode(self):
364         return {'weight': 1,
365                 'preference': 0,
366                 'table_id': self.nh_table_id,
367                 'nh': self.nh.encode(),
368                 'next_hop_id': self.next_hop_id,
369                 'sw_if_index': self.nh_itf,
370                 'rpf_id': self.rpf_id,
371                 'proto': self.proto,
372                 'type': self.type,
373                 'flags': self.flags,
374                 'n_labels': len(self.nh_labels),
375                 'label_stack': self.encode_labels()}
376
377     def __eq__(self, other):
378         if isinstance(other, self.__class__):
379             return self.nh == other.nh
380         elif hasattr(other, 'sw_if_index'):
381             # vl_api_fib_path_t
382             if (len(self.nh_labels) != other.n_labels):
383                 return False
384             for i in range(len(self.nh_labels)):
385                 if (self.nh_labels[i] != other.label_stack[i]):
386                     return False
387             return self.nh_itf == other.sw_if_index
388         else:
389             return False
390
391     def __ne__(self, other):
392         return not (self == other)
393
394
395 class VppMRoutePath(VppRoutePath):
396
397     def __init__(self, nh_sw_if_index, flags,
398                  nh=None,
399                  proto=FibPathProto.FIB_PATH_NH_PROTO_IP4,
400                  type=FibPathType.FIB_PATH_TYPE_NORMAL,
401                  bier_imp=INVALID_INDEX):
402         if not nh:
403             nh = "::" if proto is FibPathProto.FIB_PATH_NH_PROTO_IP6 \
404                  else "0.0.0.0"
405         super(VppMRoutePath, self).__init__(nh,
406                                             nh_sw_if_index,
407                                             proto=proto,
408                                             type=type,
409                                             next_hop_id=bier_imp)
410         self.nh_i_flags = flags
411         self.bier_imp = bier_imp
412
413     def encode(self):
414         return {'path': super(VppMRoutePath, self).encode(),
415                 'itf_flags': self.nh_i_flags}
416
417
418 class VppIpRoute(VppObject):
419     """
420     IP Route
421     """
422
423     def __init__(self, test, dest_addr,
424                  dest_addr_len, paths, table_id=0, register=True):
425         self._test = test
426         self.paths = paths
427         self.table_id = table_id
428         self.prefix = mk_network(dest_addr, dest_addr_len)
429         self.register = register
430         self.stats_index = None
431         self.modified = False
432
433         self.encoded_paths = []
434         for path in self.paths:
435             self.encoded_paths.append(path.encode())
436
437     def __eq__(self, other):
438         if self.table_id == other.table_id and \
439            self.prefix == other.prefix:
440             return True
441         return False
442
443     def modify(self, paths):
444         self.paths = paths
445         self.encoded_paths = []
446         for path in self.paths:
447             self.encoded_paths.append(path.encode())
448         self.modified = True
449
450         self._test.vapi.ip_route_add_del(route={'table_id': self.table_id,
451                                                 'prefix': self.prefix,
452                                                 'n_paths': len(
453                                                     self.encoded_paths),
454                                                 'paths': self.encoded_paths,
455                                                 },
456                                          is_add=1,
457                                          is_multipath=0)
458
459     def add_vpp_config(self):
460         r = self._test.vapi.ip_route_add_del(
461             route={'table_id': self.table_id,
462                    'prefix': self.prefix,
463                    'n_paths': len(self.encoded_paths),
464                    'paths': self.encoded_paths,
465                    },
466             is_add=1,
467             is_multipath=0)
468         self.stats_index = r.stats_index
469         if self.register:
470             self._test.registry.register(self, self._test.logger)
471
472     def remove_vpp_config(self):
473         # there's no need to issue different deletes for modified routes
474         # we do this only to test the two different ways to delete routes
475         # eiter by passing all the paths to remove and mutlipath=1 or
476         # passing no paths and multipath=0
477         if self.modified:
478             self._test.vapi.ip_route_add_del(
479                 route={'table_id': self.table_id,
480                        'prefix': self.prefix,
481                        'n_paths': len(
482                            self.encoded_paths),
483                        'paths': self.encoded_paths},
484                 is_add=0,
485                 is_multipath=1)
486         else:
487             self._test.vapi.ip_route_add_del(
488                 route={'table_id': self.table_id,
489                        'prefix': self.prefix,
490                        'n_paths': 0},
491                 is_add=0,
492                 is_multipath=0)
493
494     def query_vpp_config(self):
495         return find_route(self._test,
496                           self.prefix.network_address,
497                           self.prefix.prefixlen,
498                           self.table_id)
499
500     def object_id(self):
501         return ("%s:table-%d-%s" % (
502             'ip6-route' if self.prefix.version == 6 else 'ip-route',
503                 self.table_id,
504                 self.prefix))
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 = mk_network(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,
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,
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]