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