1#!/usr/bin/env atf-sh 2#- 3# SPDX-License-Identifier: BSD-2-Clause 4# 5# Copyright (c) 2020 Alexander V. Chernikov 6# 7# Redistribution and use in source and binary forms, with or without 8# modification, are permitted provided that the following conditions 9# are met: 10# 1. Redistributions of source code must retain the above copyright 11# notice, this list of conditions and the following disclaimer. 12# 2. Redistributions in binary form must reproduce the above copyright 13# notice, this list of conditions and the following disclaimer in the 14# documentation and/or other materials provided with the distribution. 15# 16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26# SUCH DAMAGE. 27# 28# $FreeBSD$ 29# 30 31. $(atf_get_srcdir)/../common/vnet.subr 32 33atf_test_case "output_tcp_setup_success" "cleanup" 34output_tcp_setup_success_head() 35{ 36 37 atf_set descr 'Test valid IPv4 TCP output' 38 atf_set require.user root 39} 40 41output_tcp_setup_success_body() 42{ 43 44 vnet_init 45 46 net_src="192.0.2." 47 net_dst="192.0.2." 48 ip_src="${net_src}1" 49 ip_dst="${net_dst}2" 50 plen=24 51 text="testtesttst" 52 port=4242 53 54 script_name=`dirname $0`/../common/net_receiver.py 55 script_name=`realpath ${script_name}` 56 jname="v4t-output_tcp_setup_success" 57 58 epair=$(vnet_mkepair) 59 60 vnet_mkjail ${jname}a ${epair}a 61 jexec ${jname}a ifconfig ${epair}a up 62 jexec ${jname}a ifconfig ${epair}a inet ${ip_src}/${plen} 63 64 vnet_mkjail ${jname}b ${epair}b 65 jexec ${jname}b ifconfig ${epair}b up 66 67 jexec ${jname}b ifconfig ${epair}b inet ${ip_dst}/${plen} 68 69 # run listener 70 args="--family inet --ports ${port} --match_str ${text}" 71 echo jexec ${jname}b ${script_name} ${args} 72 jexec ${jname}b ${script_name} --test_name "test_listen_tcp" ${args} & 73 cmd_pid=$! 74 75 # wait for the script init 76 counter=0 77 while [ `jexec ${jname}b sockstat -4qlp ${port} | wc -l` != "1" ]; do 78 sleep 0.01 79 counter=$((counter+1)) 80 if [ ${counter} -ge 50 ]; then break; fi 81 done 82 if [ `jexec ${jname}b sockstat -4qlp ${port} | wc -l` != "1" ]; then 83 echo "App setup failed" 84 exit 1 85 fi 86 87 # run sender 88 echo -n "${text}" | jexec ${jname}a nc -N ${ip_dst} ${port} 89 exit_code=$? 90 if [ $exit_code -ne 0 ]; then atf_fail "sender exit code $exit_code" ; fi 91 92 wait ${cmd_pid} 93 exit_code=$? 94 if [ $exit_code -ne 0 ]; then atf_fail "receiver exit code $exit_code" ; fi 95} 96 97output_tcp_setup_success_cleanup() 98{ 99 vnet_cleanup 100} 101 102 103atf_test_case "output_udp_setup_success" "cleanup" 104output_udp_setup_success_head() 105{ 106 107 atf_set descr 'Test valid IPv4 UDP output' 108 atf_set require.user root 109} 110 111output_udp_setup_success_body() 112{ 113 114 vnet_init 115 116 net_src="192.0.2." 117 net_dst="192.0.2." 118 ip_src="${net_src}1" 119 ip_dst="${net_dst}2" 120 plen=24 121 text="testtesttst" 122 port=4242 123 124 script_name=`dirname $0`/../common/net_receiver.py 125 script_name=`realpath ${script_name}` 126 jname="v4t-output_udp_setup_success" 127 128 epair=$(vnet_mkepair) 129 130 vnet_mkjail ${jname}a ${epair}a 131 jexec ${jname}a ifconfig ${epair}a up 132 jexec ${jname}a ifconfig ${epair}a inet ${ip_src}/${plen} 133 134 vnet_mkjail ${jname}b ${epair}b 135 jexec ${jname}b ifconfig ${epair}b up 136 jexec ${jname}b ifconfig ${epair}b inet ${ip_dst}/${plen} 137 138 # run listener 139 args="--family inet --ports ${port} --match_str ${text}" 140 echo jexec ${jname}b ${script_name} ${args} 141 jexec ${jname}b ${script_name} --test_name "test_listen_udp" ${args} & 142 cmd_pid=$! 143 144 # wait for the script init 145 counter=0 146 while [ `jexec ${jname}b sockstat -4qlp ${port} | wc -l` != "1" ]; do 147 sleep 0.1 148 counterc=$((counter+1)) 149 if [ ${counter} -ge 50 ]; then break; fi 150 done 151 if [ `jexec ${jname}b sockstat -4qlp ${port} | wc -l` != "1" ]; then 152 echo "App setup failed" 153 exit 1 154 fi 155 156 # run sender 157 # TODO: switch from nc to some alternative to avoid 1-second delay 158 echo -n "${text}" | jexec ${jname}a nc -uNw1 ${ip_dst} ${port} 159 exit_code=$? 160 if [ $exit_code -ne 0 ]; then atf_fail "sender exit code $exit_code" ; fi 161 162 wait ${cmd_pid} 163 exit_code=$? 164 if [ $exit_code -ne 0 ]; then atf_fail "receiver exit code $exit_code" ; fi 165} 166 167output_udp_setup_success_cleanup() 168{ 169 vnet_cleanup 170} 171 172atf_test_case "output_raw_success" "cleanup" 173output_raw_success_head() 174{ 175 176 atf_set descr 'Test valid IPv4 raw output' 177 atf_set require.user root 178} 179 180output_raw_success_body() 181{ 182 183 vnet_init 184 185 net_src="192.0.2." 186 net_dst="192.0.2." 187 ip_src="${net_src}1" 188 ip_dst="${net_dst}2" 189 plen=24 190 191 script_name=`dirname $0`/../common/net_receiver.py 192 script_name=`realpath ${script_name}` 193 jname="v4t-output_raw_success" 194 195 epair=$(vnet_mkepair) 196 197 vnet_mkjail ${jname}a ${epair}a 198 jexec ${jname}a ifconfig ${epair}a up 199 jexec ${jname}a ifconfig ${epair}a inet ${ip_src}/${plen} 200 201 vnet_mkjail ${jname}b ${epair}b 202 jexec ${jname}b ifconfig ${epair}b up 203 204 jexec ${jname}b ifconfig ${epair}b inet ${ip_dst}/${plen} 205 206 atf_check -o match:'1 packets transmitted, 1 packets received' jexec ${jname}a ping -nc1 ${ip_dst} 207} 208 209output_raw_success_cleanup() 210{ 211 vnet_cleanup 212} 213 214# Multipath tests are done the following way: 215# epair0 216# jailA lo < > lo jailB 217# epair1 218# jailA has 2 routes towards /24 prefix on jailB loopback, via 2 epairs 219# jailB has 1 route towards /24 prefix on jailA loopback, via epair0 220# 221# jailA initiates connections/sends packets towards IPs on jailB loopback. 222# Script then compares amount of packets sent via epair0 and epair1 223 224mpath_check() 225{ 226 if [ `sysctl -iW net.route.multipath | wc -l` != "1" ]; then 227 atf_skip "This test requires ROUTE_MPATH enabled" 228 fi 229} 230 231mpath_enable() 232{ 233 jexec $1 sysctl net.route.multipath=1 234 if [ $? != 0 ]; then 235 atf_fail "Setting multipath in jail $1 failed". 236 fi 237} 238 239atf_test_case "output_tcp_flowid_mpath_success" "cleanup" 240output_tcp_flowid_mpath_success_head() 241{ 242 243 atf_set descr 'Test valid IPv4 TCP output flowid generation' 244 atf_set require.user root 245} 246 247output_tcp_flowid_mpath_success_body() 248{ 249 vnet_init 250 mpath_check 251 252 net_src="192.0.2." 253 net_dst="198.51.100." 254 ip_src="${net_src}1" 255 ip_dst="${net_dst}1" 256 plen=24 257 text="testtesttst" 258 259 script_name=`dirname $0`/../common/net_receiver.py 260 script_name=`realpath ${script_name}` 261 jname="v4t-output_tcp_flowid_mpath_success" 262 263 epair0=$(vnet_mkepair) 264 epair1=$(vnet_mkepair) 265 lo_src=$(vnet_mkloopback) 266 lo_dst=$(vnet_mkloopback) 267 268 vnet_mkjail ${jname}a ${epair0}a ${epair1}a ${lo_src} 269 mpath_enable ${jname}a 270 # Setup transit IPv4 networks 271 jexec ${jname}a ifconfig ${epair0}a up 272 jexec ${jname}a ifconfig ${epair0}a inet 203.0.113.1/30 273 jexec ${jname}a ifconfig ${epair1}a up 274 jexec ${jname}a ifconfig ${epair1}a inet 203.0.113.5/30 275 jexec ${jname}a ifconfig ${lo_src} up 276 277 vnet_mkjail ${jname}b ${epair0}b ${epair1}b ${lo_dst} 278 jexec ${jname}b ifconfig ${epair0}b up 279 jexec ${jname}b ifconfig ${epair0}b inet 203.0.113.2/30 280 jexec ${jname}b ifconfig ${epair1}b up 281 jexec ${jname}b ifconfig ${epair1}b inet 203.0.113.6/30 282 jexec ${jname}b ifconfig ${lo_dst} up 283 284 # DST ips/ports to test 285 ips="4 29 48 53 55 61 71 80 84 87 90 91 119 131 137 153 154 158 162 169 169 171 176 187 197 228 233 235 236 237 245 251" 286 ports="53540 49743 43067 9131 16734 5150 14379 40292 20634 51302 3387 24387 9282 14275 42103 26881 42461 29520 45714 11096" 287 288 jexec ${jname}a ifconfig ${lo_src} inet ${ip_src}/32 289 290 jexec ${jname}b ifconfig ${lo_dst} inet ${ip_dst}/32 291 for i in ${ips}; do 292 jexec ${jname}b ifconfig ${lo_dst} alias ${net_dst}${i}/32 293 done 294 295 # Add routes 296 # A -> towards B via epair0a 297 jexec ${jname}a route add -4 -net ${net_dst}0/${plen} 203.0.113.2 298 # A -> towards B via epair1a 299 jexec ${jname}a route add -4 -net ${net_dst}0/${plen} 203.0.113.6 300 301 # B towards A via epair0b 302 jexec ${jname}b route add -4 -net ${net_src}0/${plen} 203.0.113.1 303 304 # Base setup verification 305 atf_check -o match:'1 packets transmitted, 1 packets received' jexec ${jname}a ping -nc1 ${ip_dst} 306 307 # run listener 308 num_ports=`echo ${ports} | wc -w` 309 num_ips=`echo ${ips} | wc -w` 310 count_examples=$((num_ports*num_ips)) 311 listener_ports=`echo ${ports} | tr ' ' '\n' | sort -n | tr '\n' ',' | sed -e 's?,$??'` 312 args="--family inet --ports ${listener_ports} --count ${count_examples} --match_str ${text}" 313 echo jexec ${jname}b ${script_name} ${args} 314 jexec ${jname}b ${script_name} --test_name "test_listen_tcp" ${args} & 315 cmd_pid=$! 316 317 # wait for the app init 318 counter=0 319 init=0 320 while [ ${counter} -le 50 ]; do 321 _ports=`jexec ${jname}b sockstat -4ql | awk "\\\$3 == ${cmd_pid} {print \\\$6}"|awk -F: "{print \\\$2}" | sort -n | tr '\n' ','` 322 if [ "${_ports}" = "${listener_ports}," ]; then 323 init=1 324 break; 325 fi 326 done 327 if [ ${init} -eq 0 ]; then 328 jexec ${jname}b sockstat -6ql | awk "\$3 == ${cmd_pid}" 329 echo "App setup failed" 330 exit 1 331 fi 332 echo "App setup done" 333 334 # run sender 335 for _ip in ${ips}; do 336 ip="${net_dst}${_ip}" 337 for port in ${ports}; do 338 echo -n "${text}" | jexec ${jname}a nc -nN ${ip} ${port} 339 exit_code=$? 340 if [ $exit_code -ne 0 ]; then atf_fail "sender exit code $exit_code" ; fi 341 done 342 done 343 344 wait ${cmd_pid} 345 exit_code=$? 346 if [ $exit_code -ne 0 ]; then atf_fail "receiver exit code $exit_code" ; fi 347 348 pkt_0=`jexec ${jname}a netstat -Wf link -I ${epair0}a | head | awk '$1!~/^Name/{print$8}'` 349 pkt_1=`jexec ${jname}a netstat -Wf link -I ${epair1}a | head | awk '$1!~/^Name/{print$8}'` 350 if [ ${pkt_0} -le 10 ]; then 351 atf_fail "Balancing failure: 1: ${pkt_0} 2: ${pkt_1}" 352 fi 353 if [ ${pkt_1} -le 10 ]; then 354 atf_fail "Balancing failure: 1: ${pkt_0} 2: ${pkt_1}" 355 exit 1 356 fi 357 echo "TCP Balancing: 1: ${pkt_0} 2: ${pkt_1}" 358} 359 360output_tcp_flowid_mpath_success_cleanup() 361{ 362 vnet_cleanup 363} 364 365atf_test_case "output_udp_flowid_mpath_success" "cleanup" 366output_udp_flowid_mpath_success_head() 367{ 368 369 atf_set descr 'Test valid IPv4 UDP output flowid generation' 370 atf_set require.user root 371} 372 373output_udp_flowid_mpath_success_body() 374{ 375 376 vnet_init 377 mpath_check 378 379 # Note this test will spawn around ~100 nc processes 380 381 net_src="192.0.2." 382 net_dst="198.51.100." 383 ip_src="${net_src}1" 384 ip_dst="${net_dst}1" 385 plen=24 386 text="testtesttst" 387 388 script_name=`dirname $0`/../common/net_receiver.py 389 script_name=`realpath ${script_name}` 390 jname="v4t-output_tcp_flowid_mpath_success" 391 392 epair0=$(vnet_mkepair) 393 epair1=$(vnet_mkepair) 394 lo_src=$(vnet_mkloopback) 395 lo_dst=$(vnet_mkloopback) 396 397 vnet_mkjail ${jname}a ${epair0}a ${epair1}a ${lo_src} 398 mpath_enable ${jname}a 399 # Setup transit IPv4 networks 400 jexec ${jname}a ifconfig ${epair0}a up 401 jexec ${jname}a ifconfig ${epair0}a inet 203.0.113.1/30 402 jexec ${jname}a ifconfig ${epair1}a up 403 jexec ${jname}a ifconfig ${epair1}a inet 203.0.113.5/30 404 jexec ${jname}a ifconfig ${lo_src} up 405 406 vnet_mkjail ${jname}b ${epair0}b ${epair1}b ${lo_dst} 407 jexec ${jname}b ifconfig ${epair0}b up 408 jexec ${jname}b ifconfig ${epair0}b inet 203.0.113.2/30 409 jexec ${jname}b ifconfig ${epair1}b up 410 jexec ${jname}b ifconfig ${epair1}b inet 203.0.113.6/30 411 jexec ${jname}b ifconfig ${lo_dst} up 412 413 # DST ips/ports to test 414 ips="4 29 48 53 55 61 71 80 84 87 90 91 119 131 137 153 154 158 162 169 169 171 176 187 197 228 233 235 236 237 245 251" 415 ports="53540 49743 43067 9131 16734 5150 14379 40292 20634 51302 3387 24387 9282 14275 42103 26881 42461 29520 45714 11096" 416 417 jexec ${jname}a ifconfig ${lo_src} inet ${ip_src}/32 418 419 jexec ${jname}b ifconfig ${lo_dst} inet ${ip_dst}/32 420 for i in ${ips}; do 421 jexec ${jname}b ifconfig ${lo_dst} alias ${net_dst}${i}/32 422 done 423 424 # Add routes 425 # A -> towards B via epair0a 426 jexec ${jname}a route add -4 -net ${net_dst}0/${plen} 203.0.113.2 427 # A -> towards B via epair1a 428 jexec ${jname}a route add -4 -net ${net_dst}0/${plen} 203.0.113.6 429 430 # B towards A via epair0b 431 jexec ${jname}b route add -4 -net ${net_src}0/${plen} 203.0.113.1 432 433 # Base setup verification 434 atf_check -o match:'1 packets transmitted, 1 packets received' jexec ${jname}a ping -nc1 ${ip_dst} 435 436 # run listener 437 num_ports=`echo ${ports} | wc -w` 438 num_ips=`echo ${ips} | wc -w` 439 count_examples=$((num_ports*num_ips)) 440 listener_ports=`echo ${ports} | tr ' ' '\n' | sort -n | tr '\n' ',' | sed -e 's?,$??'` 441 args="--family inet --ports ${listener_ports} --count ${count_examples} --match_str ${text}" 442 echo jexec ${jname}b ${script_name} ${args} 443 jexec ${jname}b ${script_name} --test_name "test_listen_udp" ${args} & 444 cmd_pid=$! 445 446 # wait for the app init 447 counter=0 448 init=0 449 while [ ${counter} -le 50 ]; do 450 _ports=`jexec ${jname}b sockstat -4ql | awk "\\\$3 == ${cmd_pid} {print \\\$6}"|awk -F: "{print \\\$2}" | sort -n | tr '\n' ','` 451 if [ "${_ports}" = "${listener_ports}," ]; then 452 init=1 453 break; 454 fi 455 done 456 if [ ${init} -eq 0 ]; then 457 jexec ${jname}b sockstat -4ql | awk "\$3 == ${cmd_pid}" 458 echo "App setup failed" 459 exit 1 460 fi 461 echo "App setup done" 462 463 # run sender 464 for _ip in ${ips}; do 465 ip="${net_dst}${_ip}" 466 for port in ${ports}; do 467 # XXX: switch to something that allows immediate exit 468 echo -n "${text}" | jexec ${jname}a nc -nuNw1 ${ip} ${port} & 469 sleep 0.01 470 done 471 done 472 473 wait ${cmd_pid} 474 exit_code=$? 475 if [ $exit_code -ne 0 ]; then atf_fail "receiver exit code $exit_code" ; fi 476 477 pkt_0=`jexec ${jname}a netstat -Wf link -I ${epair0}a | head | awk '$1!~/^Name/{print$8}'` 478 pkt_1=`jexec ${jname}a netstat -Wf link -I ${epair1}a | head | awk '$1!~/^Name/{print$8}'` 479 if [ ${pkt_0} -le 10 ]; then 480 atf_fail "Balancing failure: 1: ${pkt_0} 2: ${pkt_1}" 481 fi 482 if [ ${pkt_1} -le 10 ]; then 483 atf_fail "Balancing failure: 1: ${pkt_0} 2: ${pkt_1}" 484 fi 485 echo "UDP BALANCING: 1: ${pkt_0} 2: ${pkt_1}" 486} 487 488output_udp_flowid_mpath_success_cleanup() 489{ 490 vnet_cleanup 491} 492 493atf_test_case "output_raw_flowid_mpath_success" "cleanup" 494output_raw_flowid_mpath_success_head() 495{ 496 497 atf_set descr 'Test valid IPv4 raw output flowid generation' 498 atf_set require.user root 499} 500 501output_raw_flowid_mpath_success_body() 502{ 503 504 vnet_init 505 mpath_check 506 507 net_src="192.0.2." 508 net_dst="198.51.100." 509 ip_src="${net_src}1" 510 ip_dst="${net_dst}1" 511 plen=24 512 text="testtesttst" 513 514 jname="v4t-output_tcp_flowid_mpath_success" 515 516 epair0=$(vnet_mkepair) 517 epair1=$(vnet_mkepair) 518 lo_src=$(vnet_mkloopback) 519 lo_dst=$(vnet_mkloopback) 520 521 vnet_mkjail ${jname}a ${epair0}a ${epair1}a ${lo_src} 522 mpath_enable ${jname}a 523 # Setup transit IPv4 networks 524 jexec ${jname}a ifconfig ${epair0}a up 525 jexec ${jname}a ifconfig ${epair0}a inet 203.0.113.1/30 526 jexec ${jname}a ifconfig ${epair1}a up 527 jexec ${jname}a ifconfig ${epair1}a inet 203.0.113.5/30 528 jexec ${jname}a ifconfig ${lo_src} up 529 530 vnet_mkjail ${jname}b ${epair0}b ${epair1}b ${lo_dst} 531 jexec ${jname}b ifconfig ${epair0}b up 532 jexec ${jname}b ifconfig ${epair0}b inet 203.0.113.2/30 533 jexec ${jname}b ifconfig ${epair1}b up 534 jexec ${jname}b ifconfig ${epair1}b inet 203.0.113.6/30 535 jexec ${jname}b ifconfig ${lo_dst} up 536 537 # DST ips/ports to test 538 ips="4 29 48 53 55 61 71 80 84 87 90 91 119 131 137 153 154 158 162 169 169 171 176 187 197 228 233 235 236 237 245 251" 539 540 jexec ${jname}a ifconfig ${lo_src} inet ${ip_src}/32 541 542 jexec ${jname}b ifconfig ${lo_dst} inet ${ip_dst}/32 543 for i in ${ips}; do 544 jexec ${jname}b ifconfig ${lo_dst} alias ${net_dst}${i}/32 545 done 546 547 # Add routes 548 # A -> towards B via epair0a 549 jexec ${jname}a route add -4 -net ${net_dst}0/${plen} 203.0.113.2 550 # A -> towards B via epair1a 551 jexec ${jname}a route add -4 -net ${net_dst}0/${plen} 203.0.113.6 552 553 # B towards A via epair0b 554 jexec ${jname}b route add -4 -net ${net_src}0/${plen} 203.0.113.1 555 556 # Base setup verification 557 atf_check -o match:'1 packets transmitted, 1 packets received' jexec ${jname}a ping -nc1 ${ip_dst} 558 559 # run sender 560 valid_message='1 packets transmitted, 1 packets received' 561 for _ip in ${ips}; do 562 ip="${net_dst}${_ip}" 563 atf_check -o match:"${valid_message}" jexec ${jname}a ping -nc1 ${ip} 564 done 565 566 pkt_0=`jexec ${jname}a netstat -Wf link -I ${epair0}a | head | awk '$1!~/^Name/{print$8}'` 567 pkt_1=`jexec ${jname}a netstat -Wf link -I ${epair1}a | head | awk '$1!~/^Name/{print$8}'` 568 569 jexec ${jname}a netstat -bWf link -I ${epair0}a 570 jexec ${jname}a netstat -bWf link -I ${epair1}a 571 if [ ${pkt_0} -le 10 ]; then 572 atf_fail "Balancing failure: 1: ${pkt_0} 2: ${pkt_1}" 573 fi 574 if [ ${pkt_1} -le 10 ]; then 575 atf_fail "Balancing failure: 1: ${pkt_0} 2: ${pkt_1}" 576 fi 577 echo "RAW BALANCING: 1: ${pkt_0} 2: ${pkt_1}" 578} 579 580output_raw_flowid_mpath_success_cleanup() 581{ 582 vnet_cleanup 583} 584 585atf_init_test_cases() 586{ 587 atf_add_test_case "output_tcp_setup_success" 588 atf_add_test_case "output_udp_setup_success" 589 atf_add_test_case "output_raw_success" 590 atf_add_test_case "output_tcp_flowid_mpath_success" 591 atf_add_test_case "output_udp_flowid_mpath_success" 592 atf_add_test_case "output_raw_flowid_mpath_success" 593} 594 595