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