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