Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/drivers/net/psp.py
49639 views
1
#!/usr/bin/env python3
2
# SPDX-License-Identifier: GPL-2.0
3
4
"""Test suite for PSP capable drivers."""
5
6
import errno
7
import fcntl
8
import socket
9
import struct
10
import termios
11
import time
12
13
from lib.py import defer
14
from lib.py import ksft_run, ksft_exit, ksft_pr
15
from lib.py import ksft_true, ksft_eq, ksft_ne, ksft_gt, ksft_raises
16
from lib.py import ksft_not_none
17
from lib.py import KsftSkipEx
18
from lib.py import NetDrvEpEnv, PSPFamily, NlError
19
from lib.py import bkg, rand_port, wait_port_listen
20
21
22
def _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
28
def _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
35
def _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
40
def _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
47
def _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
54
def _close_conn(cfg, s):
55
_send_with_ack(cfg, b'data close\0')
56
s.close()
57
58
59
def _close_psp_conn(cfg, s):
60
_close_conn(cfg, s)
61
62
63
def _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
72
def _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
92
def _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
103
def _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
113
def _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
120
def _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
143
def 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
155
def 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
163
def 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
174
def 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
189
def 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
211
def 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
231
def 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
243
def 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
257
def 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
271
def 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
288
def 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
301
def 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
327
def 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
361
def _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
395
def __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
410
def 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
425
def 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
444
def _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
483
def 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
517
def __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
530
def 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
550
def 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
572
def 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_func(cfg, psp_ver, ipver)
577
578
test_case.__name__ = f"{name}_v{psp_ver}_ip{ipver}"
579
return test_case
580
581
582
def ipver_test_builder(name, test_func, ipver):
583
"""Build test cases for each IP version"""
584
def test_case(cfg):
585
cfg.require_ipver(ipver)
586
test_func(cfg, ipver)
587
588
test_case.__name__ = f"{name}_ip{ipver}"
589
return test_case
590
591
592
def main() -> None:
593
""" Ksft boiler plate main """
594
595
with NetDrvEpEnv(__file__) as cfg:
596
cfg.pspnl = PSPFamily()
597
598
# Set up responder and communication sock
599
responder = cfg.remote.deploy("psp_responder")
600
601
cfg.comm_port = rand_port()
602
srv = None
603
try:
604
with bkg(responder + f" -p {cfg.comm_port}", host=cfg.remote,
605
exit_wait=True) as srv:
606
wait_port_listen(cfg.comm_port, host=cfg.remote)
607
608
cfg.comm_sock = socket.create_connection((cfg.remote_addr,
609
cfg.comm_port),
610
timeout=1)
611
612
cases = [
613
psp_ip_ver_test_builder(
614
"data_basic_send", _data_basic_send, version, ipver
615
)
616
for version in range(0, 4)
617
for ipver in ("4", "6")
618
]
619
cases += [
620
ipver_test_builder("data_mss_adjust", _data_mss_adjust, ipver)
621
for ipver in ("4", "6")
622
]
623
624
ksft_run(cases=cases, globs=globals(),
625
case_pfx={"dev_", "data_", "assoc_", "removal_"},
626
args=(cfg, ))
627
628
cfg.comm_sock.send(b"exit\0")
629
cfg.comm_sock.close()
630
finally:
631
if srv and (srv.stdout or srv.stderr):
632
ksft_pr("")
633
ksft_pr(f"Responder logs ({srv.ret}):")
634
if srv and srv.stdout:
635
ksft_pr("STDOUT:\n# " + srv.stdout.strip().replace("\n", "\n# "))
636
if srv and srv.stderr:
637
ksft_pr("STDERR:\n# " + srv.stderr.strip().replace("\n", "\n# "))
638
ksft_exit()
639
640
641
if __name__ == "__main__":
642
main()
643
644