FIB table add/delete API
[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_resolve_host=path.is_resolve_host,
197                     is_resolve_attached=path.is_resolve_attached,
198                     is_multipath=1 if len(self.paths) > 1 else 0)
199         self._test.registry.register(self, self._test.logger)
200
201     def remove_vpp_config(self):
202         if self.is_local or self.is_unreach or self.is_prohibit:
203             self._test.vapi.ip_add_del_route(
204                 self.dest_addr,
205                 self.dest_addr_len,
206                 inet_pton(AF_INET6, "::"),
207                 0xffffffff,
208                 is_local=self.is_local,
209                 is_unreach=self.is_unreach,
210                 is_prohibit=self.is_prohibit,
211                 is_add=0,
212                 table_id=self.table_id,
213                 is_ipv6=self.is_ip6)
214         else:
215             for path in self.paths:
216                 self._test.vapi.ip_add_del_route(
217                     self.dest_addr,
218                     self.dest_addr_len,
219                     path.nh_addr,
220                     path.nh_itf,
221                     table_id=self.table_id,
222                     next_hop_table_id=path.nh_table_id,
223                     next_hop_via_label=path.nh_via_label,
224                     is_add=0,
225                     is_ipv6=self.is_ip6)
226
227     def query_vpp_config(self):
228         return find_route(self._test,
229                           self.dest_addr_p,
230                           self.dest_addr_len,
231                           self.table_id,
232                           inet=AF_INET6 if self.is_ip6 == 1 else AF_INET)
233
234     def __str__(self):
235         return self.object_id()
236
237     def object_id(self):
238         return ("%d:%s/%d"
239                 % (self.table_id,
240                    self.dest_addr_p,
241                    self.dest_addr_len))
242
243
244 class VppIpMRoute(VppObject):
245     """
246     IP Multicast Route
247     """
248
249     def __init__(self, test, src_addr, grp_addr,
250                  grp_addr_len, e_flags, paths, table_id=0,
251                  rpf_id=0, is_ip6=0):
252         self._test = test
253         self.paths = paths
254         self.grp_addr_len = grp_addr_len
255         self.table_id = table_id
256         self.e_flags = e_flags
257         self.is_ip6 = is_ip6
258         self.rpf_id = rpf_id
259
260         if is_ip6:
261             self.grp_addr = inet_pton(AF_INET6, grp_addr)
262             self.src_addr = inet_pton(AF_INET6, src_addr)
263         else:
264             self.grp_addr = inet_pton(AF_INET, grp_addr)
265             self.src_addr = inet_pton(AF_INET, src_addr)
266
267     def add_vpp_config(self):
268         for path in self.paths:
269             self._test.vapi.ip_mroute_add_del(self.src_addr,
270                                               self.grp_addr,
271                                               self.grp_addr_len,
272                                               self.e_flags,
273                                               path.nh_itf,
274                                               path.nh_i_flags,
275                                               rpf_id=self.rpf_id,
276                                               table_id=self.table_id,
277                                               is_ipv6=self.is_ip6)
278         self._test.registry.register(self, self._test.logger)
279
280     def remove_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                                               table_id=self.table_id,
289                                               is_add=0,
290                                               is_ipv6=self.is_ip6)
291
292     def update_entry_flags(self, flags):
293         self.e_flags = flags
294         self._test.vapi.ip_mroute_add_del(self.src_addr,
295                                           self.grp_addr,
296                                           self.grp_addr_len,
297                                           self.e_flags,
298                                           0xffffffff,
299                                           0,
300                                           table_id=self.table_id,
301                                           is_ipv6=self.is_ip6)
302
303     def update_rpf_id(self, rpf_id):
304         self.rpf_id = rpf_id
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                                           0xffffffff,
310                                           0,
311                                           rpf_id=self.rpf_id,
312                                           table_id=self.table_id,
313                                           is_ipv6=self.is_ip6)
314
315     def update_path_flags(self, itf, flags):
316         for path in self.paths:
317             if path.nh_itf == itf:
318                 path.nh_i_flags = flags
319                 break
320         self._test.vapi.ip_mroute_add_del(self.src_addr,
321                                           self.grp_addr,
322                                           self.grp_addr_len,
323                                           self.e_flags,
324                                           path.nh_itf,
325                                           path.nh_i_flags,
326                                           table_id=self.table_id,
327                                           is_ipv6=self.is_ip6)
328
329     def query_vpp_config(self):
330         dump = self._test.vapi.ip_fib_dump()
331         for e in dump:
332             if self.grp_addr == e.address \
333                and self.grp_addr_len == e.address_length \
334                and self.table_id == e.table_id:
335                 return True
336         return False
337
338     def __str__(self):
339         return self.object_id()
340
341     def object_id(self):
342         if self.is_ip6:
343             return ("%d:(%s,%s/%d)"
344                     % (self.table_id,
345                        inet_ntop(AF_INET6, self.src_addr),
346                        inet_ntop(AF_INET6, self.grp_addr),
347                        self.grp_addr_len))
348         else:
349             return ("%d:(%s,%s/%d)"
350                     % (self.table_id,
351                        inet_ntop(AF_INET, self.src_addr),
352                        inet_ntop(AF_INET, self.grp_addr),
353                        self.grp_addr_len))
354
355
356 class VppMFibSignal(object):
357     def __init__(self, test, route, interface, packet):
358         self.route = route
359         self.interface = interface
360         self.packet = packet
361         self.test = test
362
363     def compare(self, signal):
364         self.test.assertEqual(self.interface, signal.sw_if_index)
365         self.test.assertEqual(self.route.table_id, signal.table_id)
366         self.test.assertEqual(self.route.grp_addr_len,
367                               signal.grp_address_len)
368         for i in range(self.route.grp_addr_len / 8):
369             self.test.assertEqual(self.route.grp_addr[i],
370                                   signal.grp_address[i])
371         if (self.route.grp_addr_len > 32):
372             for i in range(4):
373                 self.test.assertEqual(self.route.src_addr[i],
374                                       signal.src_address[i])
375
376
377 class VppMplsIpBind(VppObject):
378     """
379     MPLS to IP Binding
380     """
381
382     def __init__(self, test, local_label, dest_addr, dest_addr_len,
383                  table_id=0, ip_table_id=0, is_ip6=0):
384         self._test = test
385         self.dest_addr_len = dest_addr_len
386         self.dest_addr = dest_addr
387         self.local_label = local_label
388         self.table_id = table_id
389         self.ip_table_id = ip_table_id
390         self.is_ip6 = is_ip6
391         if is_ip6:
392             self.dest_addrn = inet_pton(AF_INET6, dest_addr)
393         else:
394             self.dest_addrn = inet_pton(AF_INET, dest_addr)
395
396     def add_vpp_config(self):
397         self._test.vapi.mpls_ip_bind_unbind(self.local_label,
398                                             self.dest_addrn,
399                                             self.dest_addr_len,
400                                             table_id=self.table_id,
401                                             ip_table_id=self.ip_table_id,
402                                             is_ip4=(self.is_ip6 == 0))
403         self._test.registry.register(self, self._test.logger)
404
405     def remove_vpp_config(self):
406         self._test.vapi.mpls_ip_bind_unbind(self.local_label,
407                                             self.dest_addrn,
408                                             self.dest_addr_len,
409                                             table_id=self.table_id,
410                                             ip_table_id=self.ip_table_id,
411                                             is_bind=0,
412                                             is_ip4=(self.is_ip6 == 0))
413
414     def query_vpp_config(self):
415         dump = self._test.vapi.mpls_fib_dump()
416         for e in dump:
417             if self.local_label == e.label \
418                and self.table_id == e.table_id:
419                 return True
420         return False
421
422     def __str__(self):
423         return self.object_id()
424
425     def object_id(self):
426         return ("%d:%s binds %d:%s/%d"
427                 % (self.table_id,
428                    self.local_label,
429                    self.ip_table_id,
430                    self.dest_addr,
431                    self.dest_addr_len))
432
433
434 class VppMplsTable(VppObject):
435
436     def __init__(self,
437                  test,
438                  table_id):
439         self._test = test
440         self.table_id = table_id
441
442     def add_vpp_config(self):
443         self._test.vapi.mpls_table_add_del(
444             self.table_id,
445             is_add=1)
446         self._test.registry.register(self, self._test.logger)
447
448     def remove_vpp_config(self):
449         self._test.vapi.mpls_table_add_del(
450             self.table_id,
451             is_add=0)
452
453     def query_vpp_config(self):
454         # find the default route
455         dump = self._test.vapi.mpls_fib_dump()
456         if len(dump):
457             return True
458         return False
459
460     def __str__(self):
461         return self.object_id()
462
463     def object_id(self):
464         return ("table-mpls-%d" % (self.table_id))
465
466
467 class VppMplsRoute(VppObject):
468     """
469     MPLS Route/LSP
470     """
471
472     def __init__(self, test, local_label, eos_bit, paths, table_id=0,
473                  is_multicast=0):
474         self._test = test
475         self.paths = paths
476         self.local_label = local_label
477         self.eos_bit = eos_bit
478         self.table_id = table_id
479         self.is_multicast = is_multicast
480
481     def add_vpp_config(self):
482         is_multipath = len(self.paths) > 1
483         for path in self.paths:
484             self._test.vapi.mpls_route_add_del(
485                 self.local_label,
486                 self.eos_bit,
487                 path.proto,
488                 path.nh_addr,
489                 path.nh_itf,
490                 is_multicast=self.is_multicast,
491                 is_multipath=is_multipath,
492                 table_id=self.table_id,
493                 is_interface_rx=path.is_interface_rx,
494                 is_rpf_id=path.is_rpf_id,
495                 next_hop_out_label_stack=path.nh_labels,
496                 next_hop_n_out_labels=len(
497                     path.nh_labels),
498                 next_hop_via_label=path.nh_via_label,
499                 next_hop_table_id=path.nh_table_id)
500         self._test.registry.register(self, self._test.logger)
501
502     def remove_vpp_config(self):
503         for path in self.paths:
504             self._test.vapi.mpls_route_add_del(self.local_label,
505                                                self.eos_bit,
506                                                path.proto,
507                                                path.nh_addr,
508                                                path.nh_itf,
509                                                is_rpf_id=path.is_rpf_id,
510                                                table_id=self.table_id,
511                                                is_add=0)
512
513     def query_vpp_config(self):
514         dump = self._test.vapi.mpls_fib_dump()
515         for e in dump:
516             if self.local_label == e.label \
517                and self.eos_bit == e.eos_bit \
518                and self.table_id == e.table_id:
519                 return True
520         return False
521
522     def __str__(self):
523         return self.object_id()
524
525     def object_id(self):
526         return ("%d:%s/%d"
527                 % (self.table_id,
528                    self.local_label,
529                    20+self.eos_bit))