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