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