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