FIB Interpose Source
[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                  proto=DpoProto.DPO_PROTO_IP4,
189                  bier_imp=0):
190         super(VppMRoutePath, self).__init__(
191             "::" if proto is DpoProto.DPO_PROTO_IP6 else "0.0.0.0",
192             nh_sw_if_index,
193             proto=proto)
194         self.nh_i_flags = flags
195         self.bier_imp = bier_imp
196
197
198 class VppIpRoute(VppObject):
199     """
200     IP Route
201     """
202
203     def __init__(self, test, dest_addr,
204                  dest_addr_len, paths, table_id=0, is_ip6=0, is_local=0,
205                  is_unreach=0, is_prohibit=0):
206         self._test = test
207         self.paths = paths
208         self.dest_addr_len = dest_addr_len
209         self.table_id = table_id
210         self.is_ip6 = is_ip6
211         self.is_local = is_local
212         self.is_unreach = is_unreach
213         self.is_prohibit = is_prohibit
214         self.dest_addr_p = dest_addr
215         if is_ip6:
216             self.dest_addr = inet_pton(AF_INET6, dest_addr)
217         else:
218             self.dest_addr = inet_pton(AF_INET, dest_addr)
219
220     def modify(self, paths, is_local=0,
221                is_unreach=0, is_prohibit=0):
222         self.paths = paths
223         self.is_local = is_local
224         self.is_unreach = is_unreach
225         self.is_prohibit = is_prohibit
226
227     def add_vpp_config(self):
228         if self.is_local or self.is_unreach or self.is_prohibit:
229             self._test.vapi.ip_add_del_route(
230                 self.dest_addr,
231                 self.dest_addr_len,
232                 inet_pton(AF_INET6, "::"),
233                 0xffffffff,
234                 is_local=self.is_local,
235                 is_unreach=self.is_unreach,
236                 is_prohibit=self.is_prohibit,
237                 table_id=self.table_id,
238                 is_ipv6=self.is_ip6)
239         else:
240             for path in self.paths:
241                 lstack = path.encode_labels()
242
243                 self._test.vapi.ip_add_del_route(
244                     self.dest_addr,
245                     self.dest_addr_len,
246                     path.nh_addr,
247                     path.nh_itf,
248                     table_id=self.table_id,
249                     next_hop_out_label_stack=lstack,
250                     next_hop_n_out_labels=len(lstack),
251                     next_hop_via_label=path.nh_via_label,
252                     next_hop_table_id=path.nh_table_id,
253                     next_hop_id=path.next_hop_id,
254                     is_ipv6=self.is_ip6,
255                     is_dvr=path.is_dvr,
256                     is_resolve_host=path.is_resolve_host,
257                     is_resolve_attached=path.is_resolve_attached,
258                     is_source_lookup=path.is_source_lookup,
259                     is_udp_encap=path.is_udp_encap,
260                     is_multipath=1 if len(self.paths) > 1 else 0)
261         self._test.registry.register(self, self._test.logger)
262
263     def remove_vpp_config(self):
264         if self.is_local or self.is_unreach or self.is_prohibit:
265             self._test.vapi.ip_add_del_route(
266                 self.dest_addr,
267                 self.dest_addr_len,
268                 inet_pton(AF_INET6, "::"),
269                 0xffffffff,
270                 is_local=self.is_local,
271                 is_unreach=self.is_unreach,
272                 is_prohibit=self.is_prohibit,
273                 is_add=0,
274                 table_id=self.table_id,
275                 is_ipv6=self.is_ip6)
276         else:
277             for path in self.paths:
278                 self._test.vapi.ip_add_del_route(
279                     self.dest_addr,
280                     self.dest_addr_len,
281                     path.nh_addr,
282                     path.nh_itf,
283                     table_id=self.table_id,
284                     next_hop_table_id=path.nh_table_id,
285                     next_hop_via_label=path.nh_via_label,
286                     next_hop_id=path.next_hop_id,
287                     is_add=0,
288                     is_udp_encap=path.is_udp_encap,
289                     is_ipv6=self.is_ip6,
290                     is_dvr=path.is_dvr)
291
292     def query_vpp_config(self):
293         return find_route(self._test,
294                           self.dest_addr_p,
295                           self.dest_addr_len,
296                           self.table_id,
297                           inet=AF_INET6 if self.is_ip6 == 1 else AF_INET)
298
299     def __str__(self):
300         return self.object_id()
301
302     def object_id(self):
303         return ("%d:%s/%d"
304                 % (self.table_id,
305                    self.dest_addr_p,
306                    self.dest_addr_len))
307
308
309 class VppIpMRoute(VppObject):
310     """
311     IP Multicast Route
312     """
313
314     def __init__(self, test, src_addr, grp_addr,
315                  grp_addr_len, e_flags, paths, table_id=0,
316                  rpf_id=0, is_ip6=0):
317         self._test = test
318         self.paths = paths
319         self.grp_addr_len = grp_addr_len
320         self.table_id = table_id
321         self.e_flags = e_flags
322         self.is_ip6 = is_ip6
323         self.rpf_id = rpf_id
324
325         if is_ip6:
326             self.grp_addr = inet_pton(AF_INET6, grp_addr)
327             self.src_addr = inet_pton(AF_INET6, src_addr)
328         else:
329             self.grp_addr = inet_pton(AF_INET, grp_addr)
330             self.src_addr = inet_pton(AF_INET, src_addr)
331
332     def add_vpp_config(self):
333         for path in self.paths:
334             self._test.vapi.ip_mroute_add_del(self.src_addr,
335                                               self.grp_addr,
336                                               self.grp_addr_len,
337                                               self.e_flags,
338                                               path.proto,
339                                               path.nh_itf,
340                                               path.nh_i_flags,
341                                               bier_imp=path.bier_imp,
342                                               rpf_id=self.rpf_id,
343                                               table_id=self.table_id,
344                                               is_ipv6=self.is_ip6)
345         self._test.registry.register(self, self._test.logger)
346
347     def remove_vpp_config(self):
348         for path in self.paths:
349             self._test.vapi.ip_mroute_add_del(self.src_addr,
350                                               self.grp_addr,
351                                               self.grp_addr_len,
352                                               self.e_flags,
353                                               path.proto,
354                                               path.nh_itf,
355                                               path.nh_i_flags,
356                                               table_id=self.table_id,
357                                               bier_imp=path.bier_imp,
358                                               is_add=0,
359                                               is_ipv6=self.is_ip6)
360
361     def update_entry_flags(self, flags):
362         self.e_flags = flags
363         self._test.vapi.ip_mroute_add_del(self.src_addr,
364                                           self.grp_addr,
365                                           self.grp_addr_len,
366                                           self.e_flags,
367                                           0,
368                                           0xffffffff,
369                                           0,
370                                           table_id=self.table_id,
371                                           is_ipv6=self.is_ip6)
372
373     def update_rpf_id(self, rpf_id):
374         self.rpf_id = rpf_id
375         self._test.vapi.ip_mroute_add_del(self.src_addr,
376                                           self.grp_addr,
377                                           self.grp_addr_len,
378                                           self.e_flags,
379                                           0,
380                                           0xffffffff,
381                                           0,
382                                           rpf_id=self.rpf_id,
383                                           table_id=self.table_id,
384                                           is_ipv6=self.is_ip6)
385
386     def update_path_flags(self, itf, flags):
387         for path in self.paths:
388             if path.nh_itf == itf:
389                 path.nh_i_flags = flags
390                 break
391         self._test.vapi.ip_mroute_add_del(self.src_addr,
392                                           self.grp_addr,
393                                           self.grp_addr_len,
394                                           self.e_flags,
395                                           path.proto,
396                                           path.nh_itf,
397                                           path.nh_i_flags,
398                                           table_id=self.table_id,
399                                           is_ipv6=self.is_ip6)
400
401     def query_vpp_config(self):
402         if self.is_ip6:
403             dump = self._test.vapi.ip6_mfib_dump()
404         else:
405             dump = self._test.vapi.ip_mfib_dump()
406         for e in dump:
407             if self.grp_addr == e.grp_address \
408                and self.grp_addr_len == e.address_length \
409                and self.src_addr == e.src_address \
410                and self.table_id == e.table_id:
411                 return True
412         return False
413
414     def __str__(self):
415         return self.object_id()
416
417     def object_id(self):
418         if self.is_ip6:
419             return ("%d:(%s,%s/%d)"
420                     % (self.table_id,
421                        inet_ntop(AF_INET6, self.src_addr),
422                        inet_ntop(AF_INET6, self.grp_addr),
423                        self.grp_addr_len))
424         else:
425             return ("%d:(%s,%s/%d)"
426                     % (self.table_id,
427                        inet_ntop(AF_INET, self.src_addr),
428                        inet_ntop(AF_INET, self.grp_addr),
429                        self.grp_addr_len))
430
431
432 class VppMFibSignal(object):
433     def __init__(self, test, route, interface, packet):
434         self.route = route
435         self.interface = interface
436         self.packet = packet
437         self.test = test
438
439     def compare(self, signal):
440         self.test.assertEqual(self.interface, signal.sw_if_index)
441         self.test.assertEqual(self.route.table_id, signal.table_id)
442         self.test.assertEqual(self.route.grp_addr_len,
443                               signal.grp_address_len)
444         for i in range(self.route.grp_addr_len / 8):
445             self.test.assertEqual(self.route.grp_addr[i],
446                                   signal.grp_address[i])
447         if (self.route.grp_addr_len > 32):
448             for i in range(4):
449                 self.test.assertEqual(self.route.src_addr[i],
450                                       signal.src_address[i])
451
452
453 class VppMplsIpBind(VppObject):
454     """
455     MPLS to IP Binding
456     """
457
458     def __init__(self, test, local_label, dest_addr, dest_addr_len,
459                  table_id=0, ip_table_id=0, is_ip6=0):
460         self._test = test
461         self.dest_addr_len = dest_addr_len
462         self.dest_addr = dest_addr
463         self.local_label = local_label
464         self.table_id = table_id
465         self.ip_table_id = ip_table_id
466         self.is_ip6 = is_ip6
467         if is_ip6:
468             self.dest_addrn = inet_pton(AF_INET6, dest_addr)
469         else:
470             self.dest_addrn = inet_pton(AF_INET, dest_addr)
471
472     def add_vpp_config(self):
473         self._test.vapi.mpls_ip_bind_unbind(self.local_label,
474                                             self.dest_addrn,
475                                             self.dest_addr_len,
476                                             table_id=self.table_id,
477                                             ip_table_id=self.ip_table_id,
478                                             is_ip4=(self.is_ip6 == 0))
479         self._test.registry.register(self, self._test.logger)
480
481     def remove_vpp_config(self):
482         self._test.vapi.mpls_ip_bind_unbind(self.local_label,
483                                             self.dest_addrn,
484                                             self.dest_addr_len,
485                                             table_id=self.table_id,
486                                             ip_table_id=self.ip_table_id,
487                                             is_bind=0,
488                                             is_ip4=(self.is_ip6 == 0))
489
490     def query_vpp_config(self):
491         dump = self._test.vapi.mpls_fib_dump()
492         for e in dump:
493             if self.local_label == e.label \
494                and self.table_id == e.table_id:
495                 return True
496         return False
497
498     def __str__(self):
499         return self.object_id()
500
501     def object_id(self):
502         return ("%d:%s binds %d:%s/%d"
503                 % (self.table_id,
504                    self.local_label,
505                    self.ip_table_id,
506                    self.dest_addr,
507                    self.dest_addr_len))
508
509
510 class VppMplsTable(VppObject):
511
512     def __init__(self,
513                  test,
514                  table_id):
515         self._test = test
516         self.table_id = table_id
517
518     def add_vpp_config(self):
519         self._test.vapi.mpls_table_add_del(
520             self.table_id,
521             is_add=1)
522         self._test.registry.register(self, self._test.logger)
523
524     def remove_vpp_config(self):
525         self._test.vapi.mpls_table_add_del(
526             self.table_id,
527             is_add=0)
528
529     def query_vpp_config(self):
530         # find the default route
531         dump = self._test.vapi.mpls_fib_dump()
532         if len(dump):
533             return True
534         return False
535
536     def __str__(self):
537         return self.object_id()
538
539     def object_id(self):
540         return ("table-mpls-%d" % (self.table_id))
541
542
543 class VppMplsRoute(VppObject):
544     """
545     MPLS Route/LSP
546     """
547
548     def __init__(self, test, local_label, eos_bit, paths, table_id=0,
549                  is_multicast=0):
550         self._test = test
551         self.paths = paths
552         self.local_label = local_label
553         self.eos_bit = eos_bit
554         self.table_id = table_id
555         self.is_multicast = is_multicast
556
557     def add_vpp_config(self):
558         is_multipath = len(self.paths) > 1
559         for path in self.paths:
560             lstack = path.encode_labels()
561
562             self._test.vapi.mpls_route_add_del(
563                 self.local_label,
564                 self.eos_bit,
565                 path.proto,
566                 path.nh_addr,
567                 path.nh_itf,
568                 is_multicast=self.is_multicast,
569                 is_multipath=is_multipath,
570                 table_id=self.table_id,
571                 is_interface_rx=path.is_interface_rx,
572                 is_rpf_id=path.is_rpf_id,
573                 next_hop_out_label_stack=lstack,
574                 next_hop_n_out_labels=len(lstack),
575                 next_hop_via_label=path.nh_via_label,
576                 next_hop_table_id=path.nh_table_id)
577         self._test.registry.register(self, self._test.logger)
578
579     def remove_vpp_config(self):
580         for path in self.paths:
581             self._test.vapi.mpls_route_add_del(self.local_label,
582                                                self.eos_bit,
583                                                path.proto,
584                                                path.nh_addr,
585                                                path.nh_itf,
586                                                is_rpf_id=path.is_rpf_id,
587                                                table_id=self.table_id,
588                                                is_add=0)
589
590     def query_vpp_config(self):
591         dump = self._test.vapi.mpls_fib_dump()
592         for e in dump:
593             if self.local_label == e.label \
594                and self.eos_bit == e.eos_bit \
595                and self.table_id == e.table_id:
596                 return True
597         return False
598
599     def __str__(self):
600         return self.object_id()
601
602     def object_id(self):
603         return ("%d:%s/%d"
604                 % (self.table_id,
605                    self.local_label,
606                    20+self.eos_bit))