xref: /linux/tools/testing/selftests/drivers/net/psp.py (revision 6dfafbd0299a60bfb5d5e277fdf100037c7ded07)
1#!/usr/bin/env python3
2# SPDX-License-Identifier: GPL-2.0
3
4"""Test suite for PSP capable drivers."""
5
6import errno
7import fcntl
8import socket
9import struct
10import termios
11import time
12
13from lib.py import defer
14from lib.py import ksft_run, ksft_exit, ksft_pr
15from lib.py import ksft_true, ksft_eq, ksft_ne, ksft_gt, ksft_raises
16from lib.py import ksft_not_none
17from lib.py import KsftSkipEx
18from lib.py import NetDrvEpEnv, PSPFamily, NlError
19from lib.py import bkg, rand_port, wait_port_listen
20
21
22def _get_outq(s):
23    one = b'\0' * 4
24    outq = fcntl.ioctl(s.fileno(), termios.TIOCOUTQ, one)
25    return struct.unpack("I", outq)[0]
26
27
28def _send_with_ack(cfg, msg):
29    cfg.comm_sock.send(msg)
30    response = cfg.comm_sock.recv(4)
31    if response != b'ack\0':
32        raise RuntimeError("Unexpected server response", response)
33
34
35def _remote_read_len(cfg):
36    cfg.comm_sock.send(b'read len\0')
37    return int(cfg.comm_sock.recv(1024)[:-1].decode('utf-8'))
38
39
40def _make_clr_conn(cfg, ipver=None):
41    _send_with_ack(cfg, b'conn clr\0')
42    remote_addr = cfg.remote_addr_v[ipver] if ipver else cfg.remote_addr
43    s = socket.create_connection((remote_addr, cfg.comm_port), )
44    return s
45
46
47def _make_psp_conn(cfg, version=0, ipver=None):
48    _send_with_ack(cfg, b'conn psp\0' + struct.pack('BB', version, version))
49    remote_addr = cfg.remote_addr_v[ipver] if ipver else cfg.remote_addr
50    s = socket.create_connection((remote_addr, cfg.comm_port), )
51    return s
52
53
54def _close_conn(cfg, s):
55    _send_with_ack(cfg, b'data close\0')
56    s.close()
57
58
59def _close_psp_conn(cfg, s):
60    _close_conn(cfg, s)
61
62
63def _spi_xchg(s, rx):
64    s.send(struct.pack('I', rx['spi']) + rx['key'])
65    tx = s.recv(4 + len(rx['key']))
66    return {
67        'spi': struct.unpack('I', tx[:4])[0],
68        'key': tx[4:]
69    }
70
71
72def _send_careful(cfg, s, rounds):
73    data = b'0123456789' * 200
74    for i in range(rounds):
75        n = 0
76        for _ in range(10): # allow 10 retries
77            try:
78                n += s.send(data[n:], socket.MSG_DONTWAIT)
79                if n == len(data):
80                    break
81            except BlockingIOError:
82                time.sleep(0.05)
83        else:
84            rlen = _remote_read_len(cfg)
85            outq = _get_outq(s)
86            report = f'sent: {i * len(data) + n} remote len: {rlen} outq: {outq}'
87            raise RuntimeError(report)
88
89    return len(data) * rounds
90
91
92def _check_data_rx(cfg, exp_len):
93    read_len = -1
94    for _ in range(30):
95        cfg.comm_sock.send(b'read len\0')
96        read_len = int(cfg.comm_sock.recv(1024)[:-1].decode('utf-8'))
97        if read_len == exp_len:
98            break
99        time.sleep(0.01)
100    ksft_eq(read_len, exp_len)
101
102
103def _check_data_outq(s, exp_len, force_wait=False):
104    outq = 0
105    for _ in range(10):
106        outq = _get_outq(s)
107        if not force_wait and outq == exp_len:
108            break
109        time.sleep(0.01)
110    ksft_eq(outq, exp_len)
111
112
113def _get_stat(cfg, key):
114    return cfg.pspnl.get_stats({'dev-id': cfg.psp_dev_id})[key]
115
116#
117# Test case boiler plate
118#
119
120def _init_psp_dev(cfg):
121    if not hasattr(cfg, 'psp_dev_id'):
122        # Figure out which local device we are testing against
123        for dev in cfg.pspnl.dev_get({}, dump=True):
124            if dev['ifindex'] == cfg.ifindex:
125                cfg.psp_info = dev
126                cfg.psp_dev_id = cfg.psp_info['id']
127                break
128        else:
129            raise KsftSkipEx("No PSP devices found")
130
131    # Enable PSP if necessary
132    cap = cfg.psp_info['psp-versions-cap']
133    ena = cfg.psp_info['psp-versions-ena']
134    if cap != ena:
135        cfg.pspnl.dev_set({'id': cfg.psp_dev_id, 'psp-versions-ena': cap})
136        defer(cfg.pspnl.dev_set, {'id': cfg.psp_dev_id,
137                                  'psp-versions-ena': ena })
138
139#
140# Test cases
141#
142
143def dev_list_devices(cfg):
144    """ Dump all devices """
145    _init_psp_dev(cfg)
146
147    devices = cfg.pspnl.dev_get({}, dump=True)
148
149    found = False
150    for dev in devices:
151        found |= dev['id'] == cfg.psp_dev_id
152    ksft_true(found)
153
154
155def dev_get_device(cfg):
156    """ Get the device we intend to use """
157    _init_psp_dev(cfg)
158
159    dev = cfg.pspnl.dev_get({'id': cfg.psp_dev_id})
160    ksft_eq(dev['id'], cfg.psp_dev_id)
161
162
163def dev_get_device_bad(cfg):
164    """ Test getting device which doesn't exist """
165    raised = False
166    try:
167        cfg.pspnl.dev_get({'id': 1234567})
168    except NlError as e:
169        ksft_eq(e.nl_msg.error, -errno.ENODEV)
170        raised = True
171    ksft_true(raised)
172
173
174def dev_rotate(cfg):
175    """ Test key rotation """
176    _init_psp_dev(cfg)
177
178    prev_rotations = _get_stat(cfg, 'key-rotations')
179
180    rot = cfg.pspnl.key_rotate({"id": cfg.psp_dev_id})
181    ksft_eq(rot['id'], cfg.psp_dev_id)
182    rot = cfg.pspnl.key_rotate({"id": cfg.psp_dev_id})
183    ksft_eq(rot['id'], cfg.psp_dev_id)
184
185    cur_rotations = _get_stat(cfg, 'key-rotations')
186    ksft_eq(cur_rotations, prev_rotations + 2)
187
188
189def dev_rotate_spi(cfg):
190    """ Test key rotation and SPI check """
191    _init_psp_dev(cfg)
192
193    top_a = top_b = 0
194    with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
195        assoc_a = cfg.pspnl.rx_assoc({"version": 0,
196                                     "dev-id": cfg.psp_dev_id,
197                                     "sock-fd": s.fileno()})
198        top_a = assoc_a['rx-key']['spi'] >> 31
199        s.close()
200    rot = cfg.pspnl.key_rotate({"id": cfg.psp_dev_id})
201    with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
202        ksft_eq(rot['id'], cfg.psp_dev_id)
203        assoc_b = cfg.pspnl.rx_assoc({"version": 0,
204                                    "dev-id": cfg.psp_dev_id,
205                                    "sock-fd": s.fileno()})
206        top_b = assoc_b['rx-key']['spi'] >> 31
207        s.close()
208    ksft_ne(top_a, top_b)
209
210
211def assoc_basic(cfg):
212    """ Test creating associations """
213    _init_psp_dev(cfg)
214
215    with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
216        assoc = cfg.pspnl.rx_assoc({"version": 0,
217                                  "dev-id": cfg.psp_dev_id,
218                                  "sock-fd": s.fileno()})
219        ksft_eq(assoc['dev-id'], cfg.psp_dev_id)
220        ksft_gt(assoc['rx-key']['spi'], 0)
221        ksft_eq(len(assoc['rx-key']['key']), 16)
222
223        assoc = cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,
224                                  "version": 0,
225                                  "tx-key": assoc['rx-key'],
226                                  "sock-fd": s.fileno()})
227        ksft_eq(len(assoc), 0)
228        s.close()
229
230
231def assoc_bad_dev(cfg):
232    """ Test creating associations with bad device ID """
233    _init_psp_dev(cfg)
234
235    with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
236        with ksft_raises(NlError) as cm:
237            cfg.pspnl.rx_assoc({"version": 0,
238                              "dev-id": cfg.psp_dev_id + 1234567,
239                              "sock-fd": s.fileno()})
240        ksft_eq(cm.exception.nl_msg.error, -errno.ENODEV)
241
242
243def assoc_sk_only_conn(cfg):
244    """ Test creating associations based on socket """
245    _init_psp_dev(cfg)
246
247    with _make_clr_conn(cfg) as s:
248        assoc = cfg.pspnl.rx_assoc({"version": 0,
249                                  "sock-fd": s.fileno()})
250        ksft_eq(assoc['dev-id'], cfg.psp_dev_id)
251        cfg.pspnl.tx_assoc({"version": 0,
252                          "tx-key": assoc['rx-key'],
253                          "sock-fd": s.fileno()})
254        _close_conn(cfg, s)
255
256
257def assoc_sk_only_mismatch(cfg):
258    """ Test creating associations based on socket (dev mismatch) """
259    _init_psp_dev(cfg)
260
261    with _make_clr_conn(cfg) as s:
262        with ksft_raises(NlError) as cm:
263            cfg.pspnl.rx_assoc({"version": 0,
264                              "dev-id": cfg.psp_dev_id + 1234567,
265                              "sock-fd": s.fileno()})
266        the_exception = cm.exception
267        ksft_eq(the_exception.nl_msg.extack['bad-attr'], ".dev-id")
268        ksft_eq(the_exception.nl_msg.error, -errno.EINVAL)
269
270
271def assoc_sk_only_mismatch_tx(cfg):
272    """ Test creating associations based on socket (dev mismatch) """
273    _init_psp_dev(cfg)
274
275    with _make_clr_conn(cfg) as s:
276        with ksft_raises(NlError) as cm:
277            assoc = cfg.pspnl.rx_assoc({"version": 0,
278                                      "sock-fd": s.fileno()})
279            cfg.pspnl.tx_assoc({"version": 0,
280                              "tx-key": assoc['rx-key'],
281                              "dev-id": cfg.psp_dev_id + 1234567,
282                              "sock-fd": s.fileno()})
283        the_exception = cm.exception
284        ksft_eq(the_exception.nl_msg.extack['bad-attr'], ".dev-id")
285        ksft_eq(the_exception.nl_msg.error, -errno.EINVAL)
286
287
288def assoc_sk_only_unconn(cfg):
289    """ Test creating associations based on socket (unconnected, should fail) """
290    _init_psp_dev(cfg)
291
292    with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
293        with ksft_raises(NlError) as cm:
294            cfg.pspnl.rx_assoc({"version": 0,
295                              "sock-fd": s.fileno()})
296        the_exception = cm.exception
297        ksft_eq(the_exception.nl_msg.extack['miss-type'], "dev-id")
298        ksft_eq(the_exception.nl_msg.error, -errno.EINVAL)
299
300
301def assoc_version_mismatch(cfg):
302    """ Test creating associations where Rx and Tx PSP versions do not match """
303    _init_psp_dev(cfg)
304
305    versions = list(cfg.psp_info['psp-versions-cap'])
306    if len(versions) < 2:
307        raise KsftSkipEx("Not enough PSP versions supported by the device for the test")
308
309    # Translate versions to integers
310    versions = [cfg.pspnl.consts["version"].entries[v].value for v in versions]
311
312    with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
313        rx = cfg.pspnl.rx_assoc({"version": versions[0],
314                                 "dev-id": cfg.psp_dev_id,
315                                 "sock-fd": s.fileno()})
316
317        for version in versions[1:]:
318            with ksft_raises(NlError) as cm:
319                cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,
320                                    "version": version,
321                                    "tx-key": rx['rx-key'],
322                                    "sock-fd": s.fileno()})
323            the_exception = cm.exception
324            ksft_eq(the_exception.nl_msg.error, -errno.EINVAL)
325
326
327def assoc_twice(cfg):
328    """ Test reusing Tx assoc for two sockets """
329    _init_psp_dev(cfg)
330
331    def rx_assoc_check(s):
332        assoc = cfg.pspnl.rx_assoc({"version": 0,
333                                  "dev-id": cfg.psp_dev_id,
334                                  "sock-fd": s.fileno()})
335        ksft_eq(assoc['dev-id'], cfg.psp_dev_id)
336        ksft_gt(assoc['rx-key']['spi'], 0)
337        ksft_eq(len(assoc['rx-key']['key']), 16)
338
339        return assoc
340
341    with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
342        assoc = rx_assoc_check(s)
343        tx = cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,
344                               "version": 0,
345                               "tx-key": assoc['rx-key'],
346                               "sock-fd": s.fileno()})
347        ksft_eq(len(tx), 0)
348
349        # Use the same Tx assoc second time
350        with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s2:
351            rx_assoc_check(s2)
352            tx = cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,
353                                   "version": 0,
354                                   "tx-key": assoc['rx-key'],
355                                   "sock-fd": s2.fileno()})
356            ksft_eq(len(tx), 0)
357
358        s.close()
359
360
361def _data_basic_send(cfg, version, ipver):
362    """ Test basic data send """
363    _init_psp_dev(cfg)
364
365    # Version 0 is required by spec, don't let it skip
366    if version:
367        name = cfg.pspnl.consts["version"].entries_by_val[version].name
368        if name not in cfg.psp_info['psp-versions-cap']:
369            with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
370                with ksft_raises(NlError) as cm:
371                    cfg.pspnl.rx_assoc({"version": version,
372                                        "dev-id": cfg.psp_dev_id,
373                                        "sock-fd": s.fileno()})
374                ksft_eq(cm.exception.nl_msg.error, -errno.EOPNOTSUPP)
375            raise KsftSkipEx("PSP version not supported", name)
376
377    s = _make_psp_conn(cfg, version, ipver)
378
379    rx_assoc = cfg.pspnl.rx_assoc({"version": version,
380                                   "dev-id": cfg.psp_dev_id,
381                                   "sock-fd": s.fileno()})
382    rx = rx_assoc['rx-key']
383    tx = _spi_xchg(s, rx)
384
385    cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,
386                        "version": version,
387                        "tx-key": tx,
388                        "sock-fd": s.fileno()})
389
390    data_len = _send_careful(cfg, s, 100)
391    _check_data_rx(cfg, data_len)
392    _close_psp_conn(cfg, s)
393
394
395def __bad_xfer_do(cfg, s, tx, version='hdr0-aes-gcm-128'):
396    # Make sure we accept the ACK for the SPI before we seal with the bad assoc
397    _check_data_outq(s, 0)
398
399    cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,
400                        "version": version,
401                        "tx-key": tx,
402                        "sock-fd": s.fileno()})
403
404    data_len = _send_careful(cfg, s, 20)
405    _check_data_outq(s, data_len, force_wait=True)
406    _check_data_rx(cfg, 0)
407    _close_psp_conn(cfg, s)
408
409
410def data_send_bad_key(cfg):
411    """ Test send data with bad key """
412    _init_psp_dev(cfg)
413
414    s = _make_psp_conn(cfg)
415
416    rx_assoc = cfg.pspnl.rx_assoc({"version": 0,
417                                   "dev-id": cfg.psp_dev_id,
418                                   "sock-fd": s.fileno()})
419    rx = rx_assoc['rx-key']
420    tx = _spi_xchg(s, rx)
421    tx['key'] = (tx['key'][0] ^ 0xff).to_bytes(1, 'little') + tx['key'][1:]
422    __bad_xfer_do(cfg, s, tx)
423
424
425def data_send_disconnect(cfg):
426    """ Test socket close after sending data """
427    _init_psp_dev(cfg)
428
429    with _make_psp_conn(cfg) as s:
430        assoc = cfg.pspnl.rx_assoc({"version": 0,
431                                  "sock-fd": s.fileno()})
432        tx = _spi_xchg(s, assoc['rx-key'])
433        cfg.pspnl.tx_assoc({"version": 0,
434                          "tx-key": tx,
435                          "sock-fd": s.fileno()})
436
437        data_len = _send_careful(cfg, s, 100)
438        _check_data_rx(cfg, data_len)
439
440        s.shutdown(socket.SHUT_RDWR)
441        s.close()
442
443
444def _data_mss_adjust(cfg, ipver):
445    _init_psp_dev(cfg)
446
447    # First figure out what the MSS would be without any adjustments
448    s = _make_clr_conn(cfg, ipver)
449    s.send(b"0123456789abcdef" * 1024)
450    _check_data_rx(cfg, 16 * 1024)
451    mss = s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG)
452    _close_conn(cfg, s)
453
454    s = _make_psp_conn(cfg, 0, ipver)
455    try:
456        rx_assoc = cfg.pspnl.rx_assoc({"version": 0,
457                                     "dev-id": cfg.psp_dev_id,
458                                     "sock-fd": s.fileno()})
459        rx = rx_assoc['rx-key']
460        tx = _spi_xchg(s, rx)
461
462        rxmss = s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG)
463        ksft_eq(mss, rxmss)
464
465        cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,
466                          "version": 0,
467                          "tx-key": tx,
468                          "sock-fd": s.fileno()})
469
470        txmss = s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG)
471        ksft_eq(mss, txmss + 40)
472
473        data_len = _send_careful(cfg, s, 100)
474        _check_data_rx(cfg, data_len)
475        _check_data_outq(s, 0)
476
477        txmss = s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG)
478        ksft_eq(mss, txmss + 40)
479    finally:
480        _close_psp_conn(cfg, s)
481
482
483def data_stale_key(cfg):
484    """ Test send on a double-rotated key """
485    _init_psp_dev(cfg)
486
487    prev_stale = _get_stat(cfg, 'stale-events')
488    s = _make_psp_conn(cfg)
489    try:
490        rx_assoc = cfg.pspnl.rx_assoc({"version": 0,
491                                     "dev-id": cfg.psp_dev_id,
492                                     "sock-fd": s.fileno()})
493        rx = rx_assoc['rx-key']
494        tx = _spi_xchg(s, rx)
495
496        cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,
497                          "version": 0,
498                          "tx-key": tx,
499                          "sock-fd": s.fileno()})
500
501        data_len = _send_careful(cfg, s, 100)
502        _check_data_rx(cfg, data_len)
503        _check_data_outq(s, 0)
504
505        cfg.pspnl.key_rotate({"id": cfg.psp_dev_id})
506        cfg.pspnl.key_rotate({"id": cfg.psp_dev_id})
507
508        cur_stale = _get_stat(cfg, 'stale-events')
509        ksft_gt(cur_stale, prev_stale)
510
511        s.send(b'0123456789' * 200)
512        _check_data_outq(s, 2000, force_wait=True)
513    finally:
514        _close_psp_conn(cfg, s)
515
516
517def __nsim_psp_rereg(cfg):
518    # The PSP dev ID will change, remember what was there before
519    before = set([x['id'] for x in cfg.pspnl.dev_get({}, dump=True)])
520
521    cfg._ns.nsims[0].dfs_write('psp_rereg', '1')
522
523    after = set([x['id'] for x in cfg.pspnl.dev_get({}, dump=True)])
524
525    new_devs = list(after - before)
526    ksft_eq(len(new_devs), 1)
527    cfg.psp_dev_id = list(after - before)[0]
528
529
530def removal_device_rx(cfg):
531    """ Test removing a netdev / PSD with active Rx assoc """
532
533    # We could technically devlink reload real devices, too
534    # but that kills the control socket. So test this on
535    # netdevsim only for now
536    cfg.require_nsim()
537
538    s = _make_clr_conn(cfg)
539    try:
540        rx_assoc = cfg.pspnl.rx_assoc({"version": 0,
541                                       "dev-id": cfg.psp_dev_id,
542                                       "sock-fd": s.fileno()})
543        ksft_not_none(rx_assoc)
544
545        __nsim_psp_rereg(cfg)
546    finally:
547        _close_conn(cfg, s)
548
549
550def removal_device_bi(cfg):
551    """ Test removing a netdev / PSD with active Rx/Tx assoc """
552
553    # We could technically devlink reload real devices, too
554    # but that kills the control socket. So test this on
555    # netdevsim only for now
556    cfg.require_nsim()
557
558    s = _make_clr_conn(cfg)
559    try:
560        rx_assoc = cfg.pspnl.rx_assoc({"version": 0,
561                                       "dev-id": cfg.psp_dev_id,
562                                       "sock-fd": s.fileno()})
563        cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id,
564                            "version": 0,
565                            "tx-key": rx_assoc['rx-key'],
566                            "sock-fd": s.fileno()})
567        __nsim_psp_rereg(cfg)
568    finally:
569        _close_conn(cfg, s)
570
571
572def psp_ip_ver_test_builder(name, test_func, psp_ver, ipver):
573    """Build test cases for each combo of PSP version and IP version"""
574    def test_case(cfg):
575        cfg.require_ipver(ipver)
576        test_case.__name__ = f"{name}_v{psp_ver}_ip{ipver}"
577        test_func(cfg, psp_ver, ipver)
578    return test_case
579
580
581def ipver_test_builder(name, test_func, ipver):
582    """Build test cases for each IP version"""
583    def test_case(cfg):
584        cfg.require_ipver(ipver)
585        test_case.__name__ = f"{name}_ip{ipver}"
586        test_func(cfg, ipver)
587    return test_case
588
589
590def main() -> None:
591    """ Ksft boiler plate main """
592
593    with NetDrvEpEnv(__file__) as cfg:
594        cfg.pspnl = PSPFamily()
595
596        # Set up responder and communication sock
597        responder = cfg.remote.deploy("psp_responder")
598
599        cfg.comm_port = rand_port()
600        srv = None
601        try:
602            with bkg(responder + f" -p {cfg.comm_port}", host=cfg.remote,
603                     exit_wait=True) as srv:
604                wait_port_listen(cfg.comm_port, host=cfg.remote)
605
606                cfg.comm_sock = socket.create_connection((cfg.remote_addr,
607                                                          cfg.comm_port),
608                                                         timeout=1)
609
610                cases = [
611                    psp_ip_ver_test_builder(
612                        "data_basic_send", _data_basic_send, version, ipver
613                    )
614                    for version in range(0, 4)
615                    for ipver in ("4", "6")
616                ]
617                cases += [
618                    ipver_test_builder("data_mss_adjust", _data_mss_adjust, ipver)
619                    for ipver in ("4", "6")
620                ]
621
622                ksft_run(cases=cases, globs=globals(),
623                         case_pfx={"dev_", "data_", "assoc_", "removal_"},
624                         args=(cfg, ))
625
626                cfg.comm_sock.send(b"exit\0")
627                cfg.comm_sock.close()
628        finally:
629            if srv and (srv.stdout or srv.stderr):
630                ksft_pr("")
631                ksft_pr(f"Responder logs ({srv.ret}):")
632            if srv and srv.stdout:
633                ksft_pr("STDOUT:\n#  " + srv.stdout.strip().replace("\n", "\n#  "))
634            if srv and srv.stderr:
635                ksft_pr("STDERR:\n#  " + srv.stderr.strip().replace("\n", "\n#  "))
636    ksft_exit()
637
638
639if __name__ == "__main__":
640    main()
641