ip: add API to retrieve IPv6 link-layer address
[vpp.git] / test / vpp_interface.py
1 import binascii
2 import socket
3 import abc
4
5 from util import Host, mk_ll_addr
6 from vpp_papi import mac_ntop, VppEnum
7 from ipaddress import IPv4Network, IPv6Network
8
9 try:
10     text_type = unicode
11 except NameError:
12     text_type = str
13
14
15 class VppInterface(metaclass=abc.ABCMeta):
16     """Generic VPP interface."""
17
18     @property
19     def sw_if_index(self):
20         """Interface index assigned by VPP."""
21         return self._sw_if_index
22
23     @property
24     def remote_mac(self):
25         """MAC-address of the remote interface "connected" to this interface"""
26         return self._remote_hosts[0].mac
27
28     @property
29     def local_mac(self):
30         """MAC-address of the VPP interface."""
31         return str(self._local_mac)
32
33     @property
34     def local_addr(self):
35         return self._local_addr
36
37     @property
38     def remote_addr(self):
39         return self._remote_addr
40
41     @property
42     def local_ip4(self):
43         """Local IPv4 address on VPP interface (string)."""
44         return self._local_ip4
45
46     @local_ip4.setter
47     def local_ip4(self, value):
48         self._local_ip4 = value
49
50     @property
51     def local_ip4_prefix_len(self):
52         """Local IPv4 prefix length """
53         return self._local_ip4_len
54
55     @local_ip4_prefix_len.setter
56     def local_ip4_prefix_len(self, value):
57         self._local_ip4_len = value
58
59     @property
60     def local_ip4_prefix(self):
61         """Local IPv4 prefix """
62         return ("%s/%d" % (self._local_ip4, self._local_ip4_len))
63
64     @property
65     def remote_ip4(self):
66         """IPv4 address of remote peer "connected" to this interface."""
67         return self._remote_hosts[0].ip4
68
69     @property
70     def local_ip6(self):
71         """Local IPv6 address on VPP interface (string)."""
72         return self._local_ip6
73
74     @local_ip6.setter
75     def local_ip6(self, value):
76         self._local_ip6 = value
77
78     @property
79     def local_ip6_prefix_len(self):
80         """Local IPv6 prefix length """
81         return self._local_ip6_len
82
83     @local_ip6_prefix_len.setter
84     def local_ip6_prefix_len(self, value):
85         self._local_ip6_len = value
86
87     @property
88     def local_ip6_prefix(self):
89         """Local IPv4 prefix """
90         return ("%s/%d" % (self._local_ip6, self._local_ip6_len))
91
92     @property
93     def remote_ip6(self):
94         """IPv6 address of remote peer "connected" to this interface."""
95         return self._remote_hosts[0].ip6
96
97     @property
98     def local_ip6_ll(self):
99         """Local IPv6 link-local address on VPP interface (string)."""
100         if not self._local_ip6_ll:
101             self._local_ip6_ll = str(
102                 self.test.vapi.sw_interface_ip6_get_link_local_address(
103                     self.sw_if_index).ip)
104         return self._local_ip6_ll
105
106     @property
107     def remote_ip6_ll(self):
108         """Link-local IPv6 address of remote peer
109         "connected" to this interface."""
110         return self._remote_ip6_ll
111
112     @property
113     def name(self):
114         """Name of the interface."""
115         return self._name
116
117     @property
118     def dump(self):
119         """RAW result of sw_interface_dump for this interface."""
120         return self._dump
121
122     @property
123     def test(self):
124         """Test case creating this interface."""
125         return self._test
126
127     @property
128     def remote_hosts(self):
129         """Remote hosts list"""
130         return self._remote_hosts
131
132     @remote_hosts.setter
133     def remote_hosts(self, value):
134         """
135         :param list value: List of remote hosts.
136         """
137         self._remote_hosts = value
138         self._hosts_by_mac = {}
139         self._hosts_by_ip4 = {}
140         self._hosts_by_ip6 = {}
141         for host in self._remote_hosts:
142             self._hosts_by_mac[host.mac] = host
143             self._hosts_by_ip4[host.ip4] = host
144             self._hosts_by_ip6[host.ip6] = host
145
146     def host_by_mac(self, mac):
147         """
148         :param mac: MAC address to find host by.
149         :return: Host object assigned to interface.
150         """
151         return self._hosts_by_mac[mac]
152
153     def host_by_ip4(self, ip):
154         """
155         :param ip: IPv4 address to find host by.
156         :return: Host object assigned to interface.
157         """
158         return self._hosts_by_ip4[ip]
159
160     def host_by_ip6(self, ip):
161         """
162         :param ip: IPv6 address to find host by.
163         :return: Host object assigned to interface.
164         """
165         return self._hosts_by_ip6[ip]
166
167     def generate_remote_hosts(self, count=1):
168         """Generate and add remote hosts for the interface.
169
170         :param int count: Number of generated remote hosts.
171         """
172         self._remote_hosts = []
173         self._hosts_by_mac = {}
174         self._hosts_by_ip4 = {}
175         self._hosts_by_ip6 = {}
176         for i in range(
177                 2, count + 2):  # 0: network address, 1: local vpp address
178             mac = "02:%02x:00:00:ff:%02x" % (self.sw_if_index, i)
179             ip4 = "172.16.%u.%u" % (self.sw_if_index, i)
180             ip6 = "fd01:%x::%x" % (self.sw_if_index, i)
181             ip6_ll = mk_ll_addr(mac)
182             host = Host(mac, ip4, ip6, ip6_ll)
183             self._remote_hosts.append(host)
184             self._hosts_by_mac[mac] = host
185             self._hosts_by_ip4[ip4] = host
186             self._hosts_by_ip6[ip6] = host
187
188     @abc.abstractmethod
189     def __init__(self, test):
190         self._test = test
191
192         self._remote_hosts = []
193         self._hosts_by_mac = {}
194         self._hosts_by_ip4 = {}
195         self._hosts_by_ip6 = {}
196
197     def set_mac(self, mac):
198         self._local_mac = str(mac)
199         self.test.vapi.sw_interface_set_mac_address(
200             self.sw_if_index, mac.packed)
201         return self
202
203     def set_sw_if_index(self, sw_if_index):
204         if sw_if_index > 255:
205             raise RuntimeError("Don't support sw_if_index values "
206                                "greater than 255.")
207         self._sw_if_index = sw_if_index
208
209         self.generate_remote_hosts()
210
211         self._local_ip4 = "172.16.%u.1" % self.sw_if_index
212         self._local_ip4_len = 24
213         self._local_ip4_subnet = "172.16.%u.0" % self.sw_if_index
214         self._local_ip4_bcast = "172.16.%u.255" % self.sw_if_index
215         self.has_ip4_config = False
216         self.ip4_table_id = 0
217
218         self._local_ip6 = "fd01:%x::1" % self.sw_if_index
219         self._local_ip6_len = 64
220         self.has_ip6_config = False
221         self.ip6_table_id = 0
222
223         self._local_addr = {socket.AF_INET: self.local_ip4,
224                             socket.AF_INET6: self.local_ip6}
225         self._remote_addr = {socket.AF_INET: self.remote_ip4,
226                              socket.AF_INET6: self.remote_ip6}
227
228         r = self.test.vapi.sw_interface_dump(sw_if_index=self.sw_if_index)
229         for intf in r:
230             if intf.sw_if_index == self.sw_if_index:
231                 self._name = intf.interface_name
232                 self._local_mac = intf.l2_address
233                 self._dump = intf
234                 break
235         else:
236             raise Exception(
237                 "Could not find interface with sw_if_index %d "
238                 "in interface dump %s" %
239                 (self.sw_if_index, moves.reprlib.repr(r)))
240         self._remote_ip6_ll = mk_ll_addr(self.remote_mac)
241         self._local_ip6_ll = None
242
243     def config_ip4(self):
244         """Configure IPv4 address on the VPP interface."""
245         self.test.vapi.sw_interface_add_del_address(
246             sw_if_index=self.sw_if_index, prefix=self.local_ip4_prefix)
247         self.has_ip4_config = True
248         return self
249
250     def unconfig_ip4(self):
251         """Remove IPv4 address on the VPP interface."""
252         try:
253             if self.has_ip4_config:
254                 self.test.vapi.sw_interface_add_del_address(
255                     sw_if_index=self.sw_if_index,
256                     prefix=self.local_ip4_prefix, is_add=0)
257         except AttributeError:
258             self.has_ip4_config = False
259         self.has_ip4_config = False
260         return self
261
262     def configure_ipv4_neighbors(self):
263         """For every remote host assign neighbor's MAC to IPv4 addresses.
264
265         :param vrf_id: The FIB table / VRF ID. (Default value = 0)
266         """
267         for host in self._remote_hosts:
268             self.test.vapi.ip_neighbor_add_del(self.sw_if_index,
269                                                host.mac,
270                                                host.ip4)
271         return self
272
273     def config_ip6(self):
274         """Configure IPv6 address on the VPP interface."""
275         self.test.vapi.sw_interface_add_del_address(
276             sw_if_index=self.sw_if_index, prefix=self.local_ip6_prefix)
277         self.has_ip6_config = True
278         return self
279
280     def unconfig_ip6(self):
281         """Remove IPv6 address on the VPP interface."""
282         try:
283             if self.has_ip6_config:
284                 self.test.vapi.sw_interface_add_del_address(
285                     sw_if_index=self.sw_if_index,
286                     prefix=self.local_ip6_prefix, is_add=0)
287         except AttributeError:
288             self.has_ip6_config = False
289         self.has_ip6_config = False
290         return self
291
292     def configure_ipv6_neighbors(self):
293         """For every remote host assign neighbor's MAC to IPv6 addresses.
294
295         :param vrf_id: The FIB table / VRF ID. (Default value = 0)
296         """
297         for host in self._remote_hosts:
298             self.test.vapi.ip_neighbor_add_del(self.sw_if_index,
299                                                host.mac,
300                                                host.ip6)
301
302     def unconfig(self):
303         """Unconfigure IPv6 and IPv4 address on the VPP interface."""
304         self.unconfig_ip4()
305         self.unconfig_ip6()
306         return self
307
308     def set_table_ip4(self, table_id):
309         """Set the interface in a IPv4 Table.
310
311         .. note:: Must be called before configuring IP4 addresses.
312         """
313         self.ip4_table_id = table_id
314         self.test.vapi.sw_interface_set_table(
315             self.sw_if_index, 0, self.ip4_table_id)
316         return self
317
318     def set_table_ip6(self, table_id):
319         """Set the interface in a IPv6 Table.
320
321         .. note:: Must be called before configuring IP6 addresses.
322         """
323         self.ip6_table_id = table_id
324         self.test.vapi.sw_interface_set_table(
325             self.sw_if_index, 1, self.ip6_table_id)
326         return self
327
328     def disable_ipv6_ra(self):
329         """Configure IPv6 RA suppress on the VPP interface."""
330         self.test.vapi.sw_interface_ip6nd_ra_config(
331             sw_if_index=self.sw_if_index,
332             suppress=1)
333         return self
334
335     def ip6_ra_config(self, no=0, suppress=0, send_unicast=0):
336         """Configure IPv6 RA suppress on the VPP interface."""
337         self.test.vapi.sw_interface_ip6nd_ra_config(
338             sw_if_index=self.sw_if_index,
339             is_no=no,
340             suppress=suppress,
341             send_unicast=send_unicast)
342         return self
343
344     def ip6_ra_prefix(self, prefix, is_no=0,
345                       off_link=0, no_autoconfig=0, use_default=0):
346         """Configure IPv6 RA suppress on the VPP interface.
347
348         prefix can be a string in the format of '<address>/<length_in_bits>'
349         or ipaddress.ipnetwork object (if strict.)"""
350
351         self.test.vapi.sw_interface_ip6nd_ra_prefix(
352             sw_if_index=self.sw_if_index,
353             prefix=prefix,
354             use_default=use_default,
355             off_link=off_link, no_autoconfig=no_autoconfig,
356             is_no=is_no)
357         return self
358
359     def admin_up(self):
360         """Put interface ADMIN-UP."""
361         self.test.vapi.sw_interface_set_flags(
362             self.sw_if_index,
363             flags=VppEnum.vl_api_if_status_flags_t.IF_STATUS_API_FLAG_ADMIN_UP)
364         return self
365
366     def admin_down(self):
367         """Put interface ADMIN-down."""
368         self.test.vapi.sw_interface_set_flags(self.sw_if_index,
369                                               flags=0)
370         return self
371
372     def link_up(self):
373         """Put interface link-state-UP."""
374         self.test.vapi.cli("test interface link-state %s up" % self.name)
375
376     def link_down(self):
377         """Put interface link-state-down."""
378         self.test.vapi.cli("test interface link-state %s down" % self.name)
379
380     def ip6_enable(self):
381         """IPv6 Enable interface"""
382         self.test.vapi.sw_interface_ip6_enable_disable(self.sw_if_index,
383                                                        enable=1)
384         return self
385
386     def ip6_disable(self):
387         """Put interface ADMIN-DOWN."""
388         self.test.vapi.sw_interface_ip6_enable_disable(self.sw_if_index,
389                                                        enable=0)
390         return self
391
392     def add_sub_if(self, sub_if):
393         """Register a sub-interface with this interface.
394
395         :param sub_if: sub-interface
396         """
397         if not hasattr(self, 'sub_if'):
398             self.sub_if = sub_if
399         else:
400             if isinstance(self.sub_if, list):
401                 self.sub_if.append(sub_if)
402             else:
403                 self.sub_if = sub_if
404         return self
405
406     def enable_mpls(self):
407         """Enable MPLS on the VPP interface."""
408         self.test.vapi.sw_interface_set_mpls_enable(self.sw_if_index)
409         return self
410
411     def disable_mpls(self):
412         """Enable MPLS on the VPP interface."""
413         self.test.vapi.sw_interface_set_mpls_enable(self.sw_if_index, 0)
414         return self
415
416     def is_ip4_entry_in_fib_dump(self, dump):
417         for i in dump:
418             n = IPv4Network(text_type("%s/%d" % (self.local_ip4,
419                                                  self.local_ip4_prefix_len)))
420             if i.route.prefix == n and \
421                i.route.table_id == self.ip4_table_id:
422                 return True
423         return False
424
425     def set_unnumbered(self, ip_sw_if_index):
426         """ Set the interface to unnumbered via ip_sw_if_index """
427         self.test.vapi.sw_interface_set_unnumbered(ip_sw_if_index,
428                                                    self.sw_if_index)
429         return self
430
431     def unset_unnumbered(self, ip_sw_if_index):
432         """ Unset the interface to unnumbered via ip_sw_if_index """
433         self.test.vapi.sw_interface_set_unnumbered(ip_sw_if_index,
434                                                    self.sw_if_index, is_add=0)
435         return self
436
437     def set_proxy_arp(self, enable=1):
438         """ Set the interface to enable/disable Proxy ARP """
439         self.test.vapi.proxy_arp_intfc_enable_disable(
440             sw_if_index=self.sw_if_index,
441             enable=enable)
442         return self
443
444     def query_vpp_config(self):
445         dump = self.test.vapi.sw_interface_dump(sw_if_index=self.sw_if_index)
446         return self.is_interface_config_in_dump(dump)
447
448     def get_interface_config_from_dump(self, dump):
449         for i in dump:
450             if i.interface_name.rstrip(' \t\r\n\0') == self.name and \
451                     i.sw_if_index == self.sw_if_index:
452                 return i
453         else:
454             return None
455
456     def is_interface_config_in_dump(self, dump):
457         return self.get_interface_config_from_dump(dump) is not None
458
459     def assert_interface_state(self, admin_up_down, link_up_down,
460                                expect_event=False):
461         if expect_event:
462             event = self.test.vapi.wait_for_event(timeout=1,
463                                                   name='sw_interface_event')
464             self.test.assert_equal(event.sw_if_index, self.sw_if_index,
465                                    "sw_if_index")
466             self.test.assert_equal((event.flags & 1), admin_up_down,
467                                    "admin state")
468             self.test.assert_equal((event.flags & 2), link_up_down,
469                                    "link state")
470         dump = self.test.vapi.sw_interface_dump()
471         if_state = self.get_interface_config_from_dump(dump)
472         self.test.assert_equal((if_state.flags & 1), admin_up_down,
473                                "admin state")
474         self.test.assert_equal((if_state.flags & 2), link_up_down,
475                                "link state")
476
477     def __str__(self):
478         return self.name
479
480     def get_rx_stats(self):
481         c = self.test.statistics.get_counter("^/if/rx$")
482         return c[0][self.sw_if_index]
483
484     def get_tx_stats(self):
485         c = self.test.statistics.get_counter("^/if/tx$")
486         return c[0][self.sw_if_index]
487
488     def set_l3_mtu(self, mtu):
489         self.test.vapi.sw_interface_set_mtu(self.sw_if_index, [mtu, 0, 0, 0])
490         return self
491
492     def set_ip4_mtu(self, mtu):
493         self.test.vapi.sw_interface_set_mtu(self.sw_if_index, [0, mtu, 0, 0])
494         return self
495
496     def set_ip6_mtu(self, mtu):
497         self.test.vapi.sw_interface_set_mtu(self.sw_if_index, [0, 0, mtu, 0])
498         return self
499
500     def set_mpls_mtu(self, mtu):
501         self.test.vapi.sw_interface_set_mtu(self.sw_if_index, [0, 0, 0, mtu])
502         return self