BIER API and load-balancing fixes
[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 *
8 from socket import inet_pton, inet_ntop, AF_INET, AF_INET6
9 from vpp_ip import *
10
11 # from vnet/vnet/mpls/mpls_types.h
12 MPLS_IETF_MAX_LABEL = 0xfffff
13 MPLS_LABEL_INVALID = MPLS_IETF_MAX_LABEL + 1
14
15
16 class MRouteItfFlags:
17     MFIB_ITF_FLAG_NONE = 0
18     MFIB_ITF_FLAG_NEGATE_SIGNAL = 1
19     MFIB_ITF_FLAG_ACCEPT = 2
20     MFIB_ITF_FLAG_FORWARD = 4
21     MFIB_ITF_FLAG_SIGNAL_PRESENT = 8
22     MFIB_ITF_FLAG_INTERNAL_COPY = 16
23
24
25 class MRouteEntryFlags:
26     MFIB_ENTRY_FLAG_NONE = 0
27     MFIB_ENTRY_FLAG_SIGNAL = 1
28     MFIB_ENTRY_FLAG_DROP = 2
29     MFIB_ENTRY_FLAG_CONNECTED = 4
30     MFIB_ENTRY_FLAG_INHERIT_ACCEPT = 8
31
32
33 class MplsLspMode:
34     PIPE = 0
35     UNIFORM = 1
36
37
38 def find_route(test, ip_addr, len, table_id=0, inet=AF_INET):
39     if inet == AF_INET:
40         s = 4
41         routes = test.vapi.ip_fib_dump()
42     else:
43         s = 16
44         routes = test.vapi.ip6_fib_dump()
45
46     route_addr = inet_pton(inet, ip_addr)
47     for e in routes:
48         if route_addr == e.address[:s] \
49            and len == e.address_length \
50            and table_id == e.table_id:
51             return True
52     return False
53
54
55 def find_mroute(test, grp_addr, src_addr, grp_addr_len,
56                 table_id=0, inet=AF_INET):
57     if inet == AF_INET:
58         s = 4
59         routes = test.vapi.ip_mfib_dump()
60     else:
61         s = 16
62         routes = test.vapi.ip6_mfib_dump()
63     gaddr = inet_pton(inet, grp_addr)
64     saddr = inet_pton(inet, src_addr)
65     for e in routes:
66         if gaddr == e.grp_address[:s] \
67            and grp_addr_len == e.address_length \
68            and saddr == e.src_address[:s] \
69            and table_id == e.table_id:
70             return True
71     return False
72
73
74 class VppIpTable(VppObject):
75
76     def __init__(self,
77                  test,
78                  table_id,
79                  is_ip6=0):
80         self._test = test
81         self.table_id = table_id
82         self.is_ip6 = is_ip6
83
84     def add_vpp_config(self):
85         self._test.vapi.ip_table_add_del(
86             self.table_id,
87             is_ipv6=self.is_ip6,
88             is_add=1)
89         self._test.registry.register(self, self._test.logger)
90
91     def remove_vpp_config(self):
92         self._test.vapi.ip_table_add_del(
93             self.table_id,
94             is_ipv6=self.is_ip6,
95             is_add=0)
96
97     def query_vpp_config(self):
98         # find the default route
99         return find_route(self._test,
100                           "::" if self.is_ip6 else "0.0.0.0",
101                           0,
102                           self.table_id,
103                           inet=AF_INET6 if self.is_ip6 == 1 else AF_INET)
104
105     def __str__(self):
106         return self.object_id()
107
108     def object_id(self):
109         return ("table-%s-%d" %
110                 ("v6" if self.is_ip6 == 1 else "v4",
111                  self.table_id))
112
113
114 class VppMplsLabel(object):
115     def __init__(self, value, mode=MplsLspMode.PIPE, ttl=64, exp=0):
116         self.value = value
117         self.mode = mode
118         self.ttl = ttl
119         self.exp = exp
120
121     def encode(self):
122         is_uniform = 0 if self.mode is MplsLspMode.PIPE else 1
123         return {'label': self.value,
124                 'ttl': self.ttl,
125                 'exp': self.exp,
126                 'is_uniform': is_uniform}
127
128
129 class VppRoutePath(object):
130
131     def __init__(
132             self,
133             nh_addr,
134             nh_sw_if_index,
135             nh_table_id=0,
136             labels=[],
137             nh_via_label=MPLS_LABEL_INVALID,
138             rpf_id=0,
139             is_interface_rx=0,
140             is_resolve_host=0,
141             is_resolve_attached=0,
142             is_source_lookup=0,
143             is_udp_encap=0,
144             is_dvr=0,
145             next_hop_id=0xffffffff,
146             proto=DpoProto.DPO_PROTO_IP4):
147         self.nh_itf = nh_sw_if_index
148         self.nh_table_id = nh_table_id
149         self.nh_via_label = nh_via_label
150         self.nh_labels = labels
151         self.weight = 1
152         self.rpf_id = rpf_id
153         self.proto = proto
154         if self.proto is DpoProto.DPO_PROTO_IP6:
155             self.nh_addr = inet_pton(AF_INET6, nh_addr)
156         elif self.proto is DpoProto.DPO_PROTO_IP4:
157             self.nh_addr = inet_pton(AF_INET, nh_addr)
158         else:
159             self.nh_addr = inet_pton(AF_INET6, "::")
160         self.is_resolve_host = is_resolve_host
161         self.is_resolve_attached = is_resolve_attached
162         self.is_interface_rx = is_interface_rx
163         self.is_source_lookup = is_source_lookup
164         self.is_rpf_id = 0
165         if rpf_id != 0:
166             self.is_rpf_id = 1
167             self.nh_itf = rpf_id
168         self.is_udp_encap = is_udp_encap
169         self.next_hop_id = next_hop_id
170         self.is_dvr = is_dvr
171
172     def encode_labels(self):
173         lstack = []
174         for l in self.nh_labels:
175             if type(l) == VppMplsLabel:
176                 lstack.append(l.encode())
177             else:
178                 lstack.append({'label': l,
179                                'ttl': 255})
180         return lstack
181
182     def encode(self):
183         return {'next_hop': self.nh_addr,
184                 'weight': 1,
185                 'afi': 0,
186                 'preference': 0,
187                 'table_id': self.nh_table_id,
188                 'next_hop_id': self.next_hop_id,
189                 'sw_if_index': self.nh_itf,
190                 'afi': self.proto,
191                 'is_udp_encap': self.is_udp_encap,
192                 'n_labels': len(self.nh_labels),
193                 'label_stack': self.encode_labels()}
194
195     def __eq__(self, other):
196         return self.nh_addr == other.nh_addr
197
198
199 class VppMRoutePath(VppRoutePath):
200
201     def __init__(self, nh_sw_if_index, flags,
202                  nh=None,
203                  proto=DpoProto.DPO_PROTO_IP4,
204                  bier_imp=0):
205         if not nh:
206             nh = "::" if proto is DpoProto.DPO_PROTO_IP6 else "0.0.0.0"
207         super(VppMRoutePath, self).__init__(nh,
208                                             nh_sw_if_index,
209                                             proto=proto)
210         self.nh_i_flags = flags
211         self.bier_imp = bier_imp
212
213
214 class VppIpRoute(VppObject):
215     """
216     IP Route
217     """
218
219     def __init__(self, test, dest_addr,
220                  dest_addr_len, paths, table_id=0, is_ip6=0, is_local=0,
221                  is_unreach=0, is_prohibit=0, is_drop=0):
222         self._test = test
223         self.paths = paths
224         self.dest_addr_len = dest_addr_len
225         self.table_id = table_id
226         self.is_ip6 = is_ip6
227         self.is_local = is_local
228         self.is_unreach = is_unreach
229         self.is_prohibit = is_prohibit
230         self.is_drop = is_drop
231         self.dest_addr_p = dest_addr
232         if is_ip6:
233             self.dest_addr = inet_pton(AF_INET6, dest_addr)
234         else:
235             self.dest_addr = inet_pton(AF_INET, dest_addr)
236
237     def modify(self, paths, is_local=0,
238                is_unreach=0, is_prohibit=0):
239         self.paths = paths
240         self.is_local = is_local
241         self.is_unreach = is_unreach
242         self.is_prohibit = is_prohibit
243
244     def add_vpp_config(self):
245         if self.is_local or self.is_unreach or \
246            self.is_prohibit or self.is_drop:
247             self._test.vapi.ip_add_del_route(
248                 self.dest_addr,
249                 self.dest_addr_len,
250                 inet_pton(AF_INET6, "::"),
251                 0xffffffff,
252                 is_local=self.is_local,
253                 is_unreach=self.is_unreach,
254                 is_prohibit=self.is_prohibit,
255                 is_drop=self.is_drop,
256                 table_id=self.table_id,
257                 is_ipv6=self.is_ip6)
258         else:
259             for path in self.paths:
260                 lstack = path.encode_labels()
261
262                 self._test.vapi.ip_add_del_route(
263                     self.dest_addr,
264                     self.dest_addr_len,
265                     path.nh_addr,
266                     path.nh_itf,
267                     table_id=self.table_id,
268                     next_hop_out_label_stack=lstack,
269                     next_hop_n_out_labels=len(lstack),
270                     next_hop_via_label=path.nh_via_label,
271                     next_hop_table_id=path.nh_table_id,
272                     next_hop_id=path.next_hop_id,
273                     is_ipv6=self.is_ip6,
274                     is_dvr=path.is_dvr,
275                     is_resolve_host=path.is_resolve_host,
276                     is_resolve_attached=path.is_resolve_attached,
277                     is_source_lookup=path.is_source_lookup,
278                     is_udp_encap=path.is_udp_encap,
279                     is_multipath=1 if len(self.paths) > 1 else 0)
280         self._test.registry.register(self, self._test.logger)
281
282     def remove_vpp_config(self):
283         if self.is_local or self.is_unreach or \
284            self.is_prohibit or self.is_drop:
285             self._test.vapi.ip_add_del_route(
286                 self.dest_addr,
287                 self.dest_addr_len,
288                 inet_pton(AF_INET6, "::"),
289                 0xffffffff,
290                 is_local=self.is_local,
291                 is_unreach=self.is_unreach,
292                 is_prohibit=self.is_prohibit,
293                 is_add=0,
294                 table_id=self.table_id,
295                 is_ipv6=self.is_ip6)
296         else:
297             for path in self.paths:
298                 self._test.vapi.ip_add_del_route(
299                     self.dest_addr,
300                     self.dest_addr_len,
301                     path.nh_addr,
302                     path.nh_itf,
303                     table_id=self.table_id,
304                     next_hop_table_id=path.nh_table_id,
305                     next_hop_via_label=path.nh_via_label,
306                     next_hop_id=path.next_hop_id,
307                     is_add=0,
308                     is_udp_encap=path.is_udp_encap,
309                     is_ipv6=self.is_ip6,
310                     is_dvr=path.is_dvr)
311
312     def query_vpp_config(self):
313         return find_route(self._test,
314                           self.dest_addr_p,
315                           self.dest_addr_len,
316                           self.table_id,
317                           inet=AF_INET6 if self.is_ip6 == 1 else AF_INET)
318
319     def __str__(self):
320         return self.object_id()
321
322     def object_id(self):
323         return ("%d:%s/%d"
324                 % (self.table_id,
325                    self.dest_addr_p,
326                    self.dest_addr_len))
327
328
329 class VppIpMRoute(VppObject):
330     """
331     IP Multicast Route
332     """
333
334     def __init__(self, test, src_addr, grp_addr,
335                  grp_addr_len, e_flags, paths, table_id=0,
336                  rpf_id=0, is_ip6=0):
337         self._test = test
338         self.paths = paths
339         self.grp_addr_len = grp_addr_len
340         self.table_id = table_id
341         self.e_flags = e_flags
342         self.is_ip6 = is_ip6
343         self.rpf_id = rpf_id
344
345         self.grp_addr_p = grp_addr
346         self.src_addr_p = src_addr
347         if is_ip6:
348             self.grp_addr = inet_pton(AF_INET6, grp_addr)
349             self.src_addr = inet_pton(AF_INET6, src_addr)
350         else:
351             self.grp_addr = inet_pton(AF_INET, grp_addr)
352             self.src_addr = inet_pton(AF_INET, src_addr)
353
354     def add_vpp_config(self):
355         for path in self.paths:
356             self._test.vapi.ip_mroute_add_del(self.src_addr,
357                                               self.grp_addr,
358                                               self.grp_addr_len,
359                                               self.e_flags,
360                                               path.proto,
361                                               path.nh_itf,
362                                               path.nh_addr,
363                                               path.nh_i_flags,
364                                               bier_imp=path.bier_imp,
365                                               rpf_id=self.rpf_id,
366                                               table_id=self.table_id,
367                                               is_ipv6=self.is_ip6)
368         self._test.registry.register(self, self._test.logger)
369
370     def remove_vpp_config(self):
371         for path in self.paths:
372             self._test.vapi.ip_mroute_add_del(self.src_addr,
373                                               self.grp_addr,
374                                               self.grp_addr_len,
375                                               self.e_flags,
376                                               path.proto,
377                                               path.nh_itf,
378                                               path.nh_addr,
379                                               path.nh_i_flags,
380                                               table_id=self.table_id,
381                                               bier_imp=path.bier_imp,
382                                               is_add=0,
383                                               is_ipv6=self.is_ip6)
384
385     def update_entry_flags(self, flags):
386         self.e_flags = flags
387         self._test.vapi.ip_mroute_add_del(self.src_addr,
388                                           self.grp_addr,
389                                           self.grp_addr_len,
390                                           self.e_flags,
391                                           0,
392                                           0xffffffff,
393                                           "",
394                                           0,
395                                           table_id=self.table_id,
396                                           is_ipv6=self.is_ip6)
397
398     def update_rpf_id(self, rpf_id):
399         self.rpf_id = rpf_id
400         self._test.vapi.ip_mroute_add_del(self.src_addr,
401                                           self.grp_addr,
402                                           self.grp_addr_len,
403                                           self.e_flags,
404                                           0,
405                                           0xffffffff,
406                                           "",
407                                           0,
408                                           rpf_id=self.rpf_id,
409                                           table_id=self.table_id,
410                                           is_ipv6=self.is_ip6)
411
412     def update_path_flags(self, itf, flags):
413         for path in self.paths:
414             if path.nh_itf == itf:
415                 path.nh_i_flags = flags
416                 break
417         self._test.vapi.ip_mroute_add_del(self.src_addr,
418                                           self.grp_addr,
419                                           self.grp_addr_len,
420                                           self.e_flags,
421                                           path.proto,
422                                           path.nh_itf,
423                                           path.nh_addr,
424                                           path.nh_i_flags,
425                                           table_id=self.table_id,
426                                           is_ipv6=self.is_ip6)
427
428     def query_vpp_config(self):
429         return find_mroute(self._test,
430                            self.grp_addr_p,
431                            self.src_addr_p,
432                            self.grp_addr_len,
433                            self.table_id,
434                            inet=AF_INET6 if self.is_ip6 == 1 else AF_INET)
435
436     def __str__(self):
437         return self.object_id()
438
439     def object_id(self):
440         if self.is_ip6:
441             return ("%d:(%s,%s/%d)"
442                     % (self.table_id,
443                        inet_ntop(AF_INET6, self.src_addr),
444                        inet_ntop(AF_INET6, self.grp_addr),
445                        self.grp_addr_len))
446         else:
447             return ("%d:(%s,%s/%d)"
448                     % (self.table_id,
449                        inet_ntop(AF_INET, self.src_addr),
450                        inet_ntop(AF_INET, self.grp_addr),
451                        self.grp_addr_len))
452
453
454 class VppMFibSignal(object):
455     def __init__(self, test, route, interface, packet):
456         self.route = route
457         self.interface = interface
458         self.packet = packet
459         self.test = test
460
461     def compare(self, signal):
462         self.test.assertEqual(self.interface, signal.sw_if_index)
463         self.test.assertEqual(self.route.table_id, signal.table_id)
464         self.test.assertEqual(self.route.grp_addr_len,
465                               signal.grp_address_len)
466         for i in range(self.route.grp_addr_len / 8):
467             self.test.assertEqual(self.route.grp_addr[i],
468                                   signal.grp_address[i])
469         if (self.route.grp_addr_len > 32):
470             for i in range(4):
471                 self.test.assertEqual(self.route.src_addr[i],
472                                       signal.src_address[i])
473
474
475 class VppMplsIpBind(VppObject):
476     """
477     MPLS to IP Binding
478     """
479
480     def __init__(self, test, local_label, dest_addr, dest_addr_len,
481                  table_id=0, ip_table_id=0, is_ip6=0):
482         self._test = test
483         self.dest_addr_len = dest_addr_len
484         self.dest_addr = dest_addr
485         self.local_label = local_label
486         self.table_id = table_id
487         self.ip_table_id = ip_table_id
488         self.is_ip6 = is_ip6
489         if is_ip6:
490             self.dest_addrn = inet_pton(AF_INET6, dest_addr)
491         else:
492             self.dest_addrn = inet_pton(AF_INET, dest_addr)
493
494     def add_vpp_config(self):
495         self._test.vapi.mpls_ip_bind_unbind(self.local_label,
496                                             self.dest_addrn,
497                                             self.dest_addr_len,
498                                             table_id=self.table_id,
499                                             ip_table_id=self.ip_table_id,
500                                             is_ip4=(self.is_ip6 == 0))
501         self._test.registry.register(self, self._test.logger)
502
503     def remove_vpp_config(self):
504         self._test.vapi.mpls_ip_bind_unbind(self.local_label,
505                                             self.dest_addrn,
506                                             self.dest_addr_len,
507                                             table_id=self.table_id,
508                                             ip_table_id=self.ip_table_id,
509                                             is_bind=0,
510                                             is_ip4=(self.is_ip6 == 0))
511
512     def query_vpp_config(self):
513         dump = self._test.vapi.mpls_fib_dump()
514         for e in dump:
515             if self.local_label == e.label \
516                and self.table_id == e.table_id:
517                 return True
518         return False
519
520     def __str__(self):
521         return self.object_id()
522
523     def object_id(self):
524         return ("%d:%s binds %d:%s/%d"
525                 % (self.table_id,
526                    self.local_label,
527                    self.ip_table_id,
528                    self.dest_addr,
529                    self.dest_addr_len))
530
531
532 class VppMplsTable(VppObject):
533
534     def __init__(self,
535                  test,
536                  table_id):
537         self._test = test
538         self.table_id = table_id
539
540     def add_vpp_config(self):
541         self._test.vapi.mpls_table_add_del(
542             self.table_id,
543             is_add=1)
544         self._test.registry.register(self, self._test.logger)
545
546     def remove_vpp_config(self):
547         self._test.vapi.mpls_table_add_del(
548             self.table_id,
549             is_add=0)
550
551     def query_vpp_config(self):
552         # find the default route
553         dump = self._test.vapi.mpls_fib_dump()
554         if len(dump):
555             return True
556         return False
557
558     def __str__(self):
559         return self.object_id()
560
561     def object_id(self):
562         return ("table-mpls-%d" % (self.table_id))
563
564
565 class VppMplsRoute(VppObject):
566     """
567     MPLS Route/LSP
568     """
569
570     def __init__(self, test, local_label, eos_bit, paths, table_id=0,
571                  is_multicast=0):
572         self._test = test
573         self.paths = paths
574         self.local_label = local_label
575         self.eos_bit = eos_bit
576         self.table_id = table_id
577         self.is_multicast = is_multicast
578
579     def add_vpp_config(self):
580         is_multipath = len(self.paths) > 1
581         for path in self.paths:
582             lstack = path.encode_labels()
583
584             self._test.vapi.mpls_route_add_del(
585                 self.local_label,
586                 self.eos_bit,
587                 path.proto,
588                 path.nh_addr,
589                 path.nh_itf,
590                 is_multicast=self.is_multicast,
591                 is_multipath=is_multipath,
592                 table_id=self.table_id,
593                 is_interface_rx=path.is_interface_rx,
594                 is_rpf_id=path.is_rpf_id,
595                 next_hop_out_label_stack=lstack,
596                 next_hop_n_out_labels=len(lstack),
597                 next_hop_via_label=path.nh_via_label,
598                 next_hop_table_id=path.nh_table_id)
599         self._test.registry.register(self, self._test.logger)
600
601     def remove_vpp_config(self):
602         for path in self.paths:
603             self._test.vapi.mpls_route_add_del(self.local_label,
604                                                self.eos_bit,
605                                                path.proto,
606                                                path.nh_addr,
607                                                path.nh_itf,
608                                                is_rpf_id=path.is_rpf_id,
609                                                table_id=self.table_id,
610                                                is_add=0)
611
612     def query_vpp_config(self):
613         dump = self._test.vapi.mpls_fib_dump()
614         for e in dump:
615             if self.local_label == e.label \
616                and self.eos_bit == e.eos_bit \
617                and self.table_id == e.table_id:
618                 return True
619         return False
620
621     def __str__(self):
622         return self.object_id()
623
624     def object_id(self):
625         return ("%d:%s/%d"
626                 % (self.table_id,
627                    self.local_label,
628                    20+self.eos_bit))