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