1# SPDX-License-Identifier: GPL-2.0
2# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
3
4# Test various network-related functionality, such as the dhcp, ping, and
5# tftpboot commands.
6
7"""
8Note: This test relies on boardenv_* containing configuration values to define
9which network environment is available for testing. Without this, this test
10will be automatically skipped.
11
12For example:
13
14.. code-block:: python
15
16    # Boolean indicating whether the Ethernet device is attached to USB, and hence
17    # USB enumeration needs to be performed prior to network tests.
18    # This variable may be omitted if its value is False.
19    env__net_uses_usb = False
20
21    # Boolean indicating whether the Ethernet device is attached to PCI, and hence
22    # PCI enumeration needs to be performed prior to network tests.
23    # This variable may be omitted if its value is False.
24    env__net_uses_pci = True
25
26    # True if a DHCP server is attached to the network, and should be tested.
27    # If DHCP testing is not possible or desired, this variable may be omitted or
28    # set to False.
29    env__net_dhcp_server = True
30
31    # False or omitted if a DHCP server is attached to the network, and dhcp abort
32    # case should be tested.
33    # If DHCP abort testing is not possible or desired, set this variable to True.
34    # For example: On some setup, dhcp is too fast and this case may not work.
35    env__dhcp_abort_test_skip = True
36
37    # True if a DHCPv6 server is attached to the network, and should be tested.
38    # If DHCPv6 testing is not possible or desired, this variable may be omitted or
39    # set to False.
40    env__net_dhcp6_server = True
41
42    # A list of environment variables that should be set in order to configure a
43    # static IP. If solely relying on DHCP, this variable may be omitted or set to
44    # an empty list.
45    env__net_static_env_vars = [
46        ('ipaddr', '10.0.0.100'),
47        ('netmask', '255.255.255.0'),
48        ('serverip', '10.0.0.1'),
49    ]
50
51    # Details regarding a file that may be read from a TFTP server. This variable
52    # may be omitted or set to None if TFTP testing is not possible or desired.
53    env__net_tftp_readable_file = {
54        'fn': 'ubtest-readable.bin',
55        'addr': 0x10000000,
56        'size': 5058624,
57        'crc32': 'c2244b26',
58        'timeout': 50000,
59        'fnu': 'ubtest-upload.bin',
60    }
61
62    # Details regarding a file that may be read from a NFS server. This variable
63    # may be omitted or set to None if NFS testing is not possible or desired.
64    env__net_nfs_readable_file = {
65        'fn': 'ubtest-readable.bin',
66        'addr': 0x10000000,
67        'size': 5058624,
68        'crc32': 'c2244b26',
69    }
70
71    # Details regarding a file that may be read from a TFTP server. This variable
72    # may be omitted or set to None if PXE testing is not possible or desired.
73    env__net_pxe_readable_file = {
74        'fn': 'default',
75        'addr': 0x2000000,
76        'size': 74,
77        'timeout': 50000,
78        'pattern': 'Linux',
79    }
80
81    # True if a router advertisement service is connected to the network, and should
82    # be tested. If router advertisement testing is not possible or desired, this
83    variable may be omitted or set to False.
84    env__router_on_net = True
85"""
86
87import pytest
88import utils
89import uuid
90import datetime
91import re
92
93net_set_up = False
94net6_set_up = False
95
96
97@pytest.mark.buildconfigspec('cmd_net')
98def test_net_pre_commands(ubman):
99    """Execute any commands required to enable network hardware.
100
101    These commands are provided by the boardenv_* file; see the comment at the
102    beginning of this file.
103    """
104
105    init_usb = ubman.config.env.get('env__net_uses_usb', False)
106    if init_usb:
107        ubman.run_command('usb start')
108
109    init_pci = ubman.config.env.get('env__net_uses_pci', False)
110    if init_pci:
111        ubman.run_command('pci enum')
112
113    ubman.run_command('net list')
114
115@pytest.mark.buildconfigspec('cmd_dhcp')
116def test_net_dhcp(ubman):
117    """Test the dhcp command.
118
119    The boardenv_* file may be used to enable/disable this test; see the
120    comment at the beginning of this file.
121    """
122
123    test_dhcp = ubman.config.env.get('env__net_dhcp_server', False)
124    if not test_dhcp:
125        pytest.skip('No DHCP server available')
126
127    ubman.run_command('setenv autoload no')
128    output = ubman.run_command('dhcp')
129    assert 'DHCP client bound to address ' in output
130
131    global net_set_up
132    net_set_up = True
133
134@pytest.mark.buildconfigspec('cmd_dhcp')
135@pytest.mark.buildconfigspec('cmd_mii')
136def test_net_dhcp_abort(ubman):
137    """Test the dhcp command by pressing ctrl+c in the middle of dhcp request
138
139    The boardenv_* file may be used to enable/disable this test; see the
140    comment at the beginning of this file.
141    """
142
143    test_dhcp = ubman.config.env.get('env__net_dhcp_server', False)
144    if not test_dhcp:
145        pytest.skip('No DHCP server available')
146
147    if ubman.config.env.get('env__dhcp_abort_test_skip', True):
148        pytest.skip('DHCP abort test is not enabled!')
149
150    ubman.run_command('setenv autoload no')
151
152    # Phy reset before running dhcp command
153    output = ubman.run_command('mii device')
154    if not re.search(r"Current device: '(.+?)'", output):
155        pytest.skip('PHY device does not exist!')
156    eth_num = re.search(r"Current device: '(.+?)'", output).groups()[0]
157    ubman.run_command(f'mii device {eth_num}')
158    output = ubman.run_command('mii info')
159    eth_addr = hex(int(re.search(r'PHY (.+?):', output).groups()[0], 16))
160    ubman.run_command(f'mii modify {eth_addr} 0 0x8000 0x8000')
161
162    ubman.run_command('dhcp', wait_for_prompt=False)
163    try:
164        ubman.wait_for('Waiting for PHY auto negotiation to complete')
165    except:
166        pytest.skip('Timeout waiting for PHY auto negotiation to complete')
167
168    ubman.wait_for('done')
169
170    try:
171        # Sending Ctrl-C
172        output = ubman.run_command(
173            chr(3), wait_for_echo=False, send_nl=False
174        )
175        assert 'TIMEOUT' not in output
176        assert 'DHCP client bound to address ' not in output
177        assert 'Abort' in output
178    finally:
179        # Provide a time to recover from Abort - if it is not performed
180        # There is message like: ethernet@ff0e0000: No link.
181        ubman.run_command('sleep 1')
182        # Run the dhcp test to setup the network configuration
183        test_net_dhcp(ubman)
184
185@pytest.mark.buildconfigspec('cmd_dhcp6')
186def test_net_dhcp6(ubman):
187    """Test the dhcp6 command.
188
189    The boardenv_* file may be used to enable/disable this test; see the
190    comment at the beginning of this file.
191    """
192
193    test_dhcp6 = ubman.config.env.get('env__net_dhcp6_server', False)
194    if not test_dhcp6:
195        pytest.skip('No DHCP6 server available')
196
197    ubman.run_command('setenv autoload no')
198    output = ubman.run_command('dhcp6')
199    assert 'DHCP6 client bound to ' in output
200
201    global net6_set_up
202    net6_set_up = True
203
204@pytest.mark.buildconfigspec('net', 'net_lwip')
205def test_net_setup_static(ubman):
206    """Set up a static IP configuration.
207
208    The configuration is provided by the boardenv_* file; see the comment at
209    the beginning of this file.
210    """
211
212    env_vars = ubman.config.env.get('env__net_static_env_vars', None)
213    if not env_vars:
214        pytest.skip('No static network configuration is defined')
215
216    for (var, val) in env_vars:
217        ubman.run_command('setenv %s %s' % (var, val))
218
219    global net_set_up
220    net_set_up = True
221
222@pytest.mark.buildconfigspec('cmd_ping')
223def test_net_ping(ubman):
224    """Test the ping command.
225
226    The $serverip (as set up by either test_net_dhcp or test_net_setup_static)
227    is pinged. The test validates that the host is alive, as reported by the
228    ping command's output.
229    """
230
231    if not net_set_up:
232        pytest.skip('Network not initialized')
233
234    output = ubman.run_command('ping $serverip')
235    assert 'is alive' in output
236
237@pytest.mark.buildconfigspec('IPV6_ROUTER_DISCOVERY')
238def test_net_network_discovery(ubman):
239    """Test the network discovery feature of IPv6.
240
241    An IPv6 network command (ping6 in this case) is run to make U-Boot send a
242    router solicitation packet, receive a router advertisement message, and
243    parse it.
244    A router advertisement service needs to be running for this test to succeed.
245    U-Boot receives the RA, processes it, and if successful, assigns the gateway
246    IP and prefix length.
247    The configuration is provided by the boardenv_* file; see the comment at
248    the beginning of this file.
249    """
250
251    router_on_net = ubman.config.env.get('env__router_on_net', False)
252    if not router_on_net:
253        pytest.skip('No router on network')
254
255    fake_host_ip = 'fe80::215:5dff:fef6:2ec6'
256    output = ubman.run_command('ping6 ' + fake_host_ip)
257    assert 'ROUTER SOLICITATION 1' in output
258    assert 'Set gatewayip6:' in output
259    assert '0000:0000:0000:0000:0000:0000:0000:0000' not in output
260
261@pytest.mark.buildconfigspec('cmd_tftpboot')
262def test_net_tftpboot(ubman):
263    """Test the tftpboot command.
264
265    A file is downloaded from the TFTP server, its size and optionally its
266    CRC32 are validated.
267
268    The details of the file to download are provided by the boardenv_* file;
269    see the comment at the beginning of this file.
270    """
271
272    if not net_set_up:
273        pytest.skip('Network not initialized')
274
275    f = ubman.config.env.get('env__net_tftp_readable_file', None)
276    if not f:
277        pytest.skip('No TFTP readable file to read')
278
279    addr = f.get('addr', None)
280
281    fn = f['fn']
282    if not addr:
283        output = ubman.run_command('tftpboot %s' % (fn))
284    else:
285        output = ubman.run_command('tftpboot %x %s' % (addr, fn))
286    expected_text = 'Bytes transferred = '
287    sz = f.get('size', None)
288    if sz:
289        expected_text += '%d' % sz
290    assert expected_text in output
291
292    expected_crc = f.get('crc32', None)
293    if not expected_crc:
294        return
295
296    if ubman.config.buildconfig.get('config_cmd_crc32', 'n') != 'y':
297        return
298
299    output = ubman.run_command('crc32 $fileaddr $filesize')
300    assert expected_crc in output
301
302@pytest.mark.buildconfigspec('cmd_nfs')
303def test_net_nfs(ubman):
304    """Test the nfs command.
305
306    A file is downloaded from the NFS server, its size and optionally its
307    CRC32 are validated.
308
309    The details of the file to download are provided by the boardenv_* file;
310    see the comment at the beginning of this file.
311    """
312
313    if not net_set_up:
314        pytest.skip('Network not initialized')
315
316    f = ubman.config.env.get('env__net_nfs_readable_file', None)
317    if not f:
318        pytest.skip('No NFS readable file to read')
319
320    addr = f.get('addr', None)
321    if not addr:
322        addr = utils.find_ram_base(ubman)
323
324    fn = f['fn']
325    output = ubman.run_command('nfs %x %s' % (addr, fn))
326    expected_text = 'Bytes transferred = '
327    sz = f.get('size', None)
328    if sz:
329        expected_text += '%d' % sz
330    assert expected_text in output
331
332    expected_crc = f.get('crc32', None)
333    if not expected_crc:
334        return
335
336    if ubman.config.buildconfig.get('config_cmd_crc32', 'n') != 'y':
337        return
338
339    output = ubman.run_command('crc32 %x $filesize' % addr)
340    assert expected_crc in output
341
342@pytest.mark.buildconfigspec("cmd_pxe")
343def test_net_pxe_get(ubman):
344    """Test the pxe get command.
345
346    A pxe configuration file is downloaded from the TFTP server and interpreted
347    to boot the images mentioned in pxe configuration file.
348
349    The details of the file to download are provided by the boardenv_* file;
350    see the comment at the beginning of this file.
351    """
352
353    if not net_set_up:
354        pytest.skip("Network not initialized")
355
356    test_net_setup_static(ubman)
357
358    f = ubman.config.env.get("env__net_pxe_readable_file", None)
359    if not f:
360        pytest.skip("No PXE readable file to read")
361
362    addr = f.get("addr", None)
363    timeout = f.get("timeout", ubman.p.timeout)
364
365    pxeuuid = uuid.uuid1()
366    ubman.run_command(f"setenv pxeuuid {pxeuuid}")
367    expected_text_uuid = f"Retrieving file: pxelinux.cfg/{pxeuuid}"
368
369    ethaddr = ubman.run_command("echo $ethaddr")
370    ethaddr = ethaddr.replace(':', '-')
371    expected_text_ethaddr = f"Retrieving file: pxelinux.cfg/01-{ethaddr}"
372
373    ip = ubman.run_command("echo $ipaddr")
374    ip = ip.split('.')
375    ipaddr_file = "".join(['%02x' % int(x) for x in ip]).upper()
376    expected_text_ipaddr = f"Retrieving file: pxelinux.cfg/{ipaddr_file}"
377    expected_text_default = f"Retrieving file: pxelinux.cfg/default"
378
379    with ubman.temporary_timeout(timeout):
380        output = ubman.run_command("pxe get")
381
382    assert "TIMEOUT" not in output
383    assert expected_text_uuid in output
384    assert expected_text_ethaddr in output
385    assert expected_text_ipaddr in output
386
387    i = 1
388    for i in range(0, len(ipaddr_file) - 1):
389        expected_text_ip = f"Retrieving file: pxelinux.cfg/{ipaddr_file[:-i]}"
390        assert expected_text_ip in output
391        i += 1
392
393    assert expected_text_default in output
394    assert "Config file 'default.boot' found" in output
395
396@pytest.mark.buildconfigspec("cmd_crc32")
397@pytest.mark.buildconfigspec("cmd_tftpboot")
398@pytest.mark.buildconfigspec("cmd_tftpput")
399def test_net_tftpput(ubman):
400    """Test the tftpput command.
401
402    A file is downloaded from the TFTP server and then uploaded to the TFTP
403    server, its size and its CRC32 are validated.
404
405    The details of the file to download are provided by the boardenv_* file;
406    see the comment at the beginning of this file.
407    """
408
409    if not net_set_up:
410        pytest.skip("Network not initialized")
411
412    f = ubman.config.env.get("env__net_tftp_readable_file", None)
413    if not f:
414        pytest.skip("No TFTP readable file to read")
415
416    addr = f.get("addr", None)
417    if not addr:
418        addr = utils.find_ram_base(ubman)
419
420    sz = f.get("size", None)
421    timeout = f.get("timeout", ubman.p.timeout)
422    fn = f["fn"]
423    fnu = f.get("fnu", "_".join([datetime.datetime.now().strftime("%y%m%d%H%M%S"), fn]))
424    expected_text = "Bytes transferred = "
425    if sz:
426        expected_text += "%d" % sz
427
428    with ubman.temporary_timeout(timeout):
429        output = ubman.run_command("tftpboot %x %s" % (addr, fn))
430
431    assert "TIMEOUT" not in output
432    assert expected_text in output
433
434    expected_tftpb_crc = f.get("crc32", None)
435
436    output = ubman.run_command("crc32 $fileaddr $filesize")
437    assert expected_tftpb_crc in output
438
439    with ubman.temporary_timeout(timeout):
440        output = ubman.run_command(
441            "tftpput $fileaddr $filesize $serverip:%s" % (fnu)
442        )
443
444    expected_text = "Bytes transferred = "
445    if sz:
446        expected_text += "%d" % sz
447        addr = addr + sz
448    assert "TIMEOUT" not in output
449    assert "Access violation" not in output
450    assert expected_text in output
451
452    with ubman.temporary_timeout(timeout):
453        output = ubman.run_command("tftpboot %x %s" % (addr, fnu))
454
455    expected_text = "Bytes transferred = "
456    if sz:
457        expected_text += "%d" % sz
458    assert "TIMEOUT" not in output
459    assert expected_text in output
460
461    output = ubman.run_command("crc32 $fileaddr $filesize")
462    assert expected_tftpb_crc in output
463