xref: /xnu-12377.81.4/tests/ipv6_bind_race.c (revision 043036a2b3718f7f0be807e2870f8f47d3fa0796)
1 /*
2  * Copyright (c) 2023-2024 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 #include <sys/fcntl.h>
30 #include <sys/socket.h>
31 #include <net/if.h>
32 #include <netinet/in.h>
33 #include <netinet/tcp.h>
34 #include <arpa/inet.h>
35 
36 #include <darwintest.h>
37 #include <pthread.h>
38 #include <stdbool.h>
39 #include <string.h>
40 #include <unistd.h>
41 
42 #include "net_test_lib.h"
43 
44 /*
45  * The test is disabled on platforms that could be limited in term of CPU
46  * or memory because this stress test that cycles rapidly through a lot of socket
47  */
48 T_GLOBAL_META(
49 	T_META_NAMESPACE("xnu.net"),
50 	T_META_RADAR_COMPONENT_NAME("xnu"),
51 	T_META_RADAR_COMPONENT_VERSION("networking"),
52 	T_META_CHECK_LEAKS(false),
53 	T_META_ENABLED(TARGET_OS_OSX || TARGET_OS_IPHONE));
54 
55 #define SECONDS_TO_SLEEP 3
56 
57 #if 0
58 
59 static int fd = -1;
60 static bool finished = false;
61 static bool is_tcp = false;
62 
63 static void
64 init_sin6_address(struct sockaddr_in6 *sin6)
65 {
66 	memset(sin6, 0, sizeof(struct sockaddr_in6));
67 	sin6->sin6_len = sizeof(struct sockaddr_in6);
68 	sin6->sin6_family = AF_INET6;
69 }
70 
71 static void
72 setnonblocking(int s)
73 {
74 	int flags;
75 
76 	T_QUIET; T_EXPECT_POSIX_SUCCESS(flags = fcntl(s, F_GETFL, 0), NULL);
77 
78 	flags |= O_NONBLOCK;
79 
80 	T_QUIET; T_EXPECT_POSIX_SUCCESS(flags = fcntl(s, F_SETFL, flags), NULL);
81 }
82 
83 static void *
84 bind4_racer(void *arg)
85 {
86 #pragma unused(arg)
87 	struct sockaddr_in6 sin6 = { 0 };
88 
89 	init_sin6_address(&sin6);
90 	T_ASSERT_EQ(inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6.sin6_addr), 1, NULL);
91 	sin6.sin6_port = htons(1234);
92 
93 	while (finished == false) {
94 		(void)bind(fd, (struct sockaddr*)&sin6, sin6.sin6_len);
95 	}
96 	return NULL;
97 }
98 
99 static void *
100 bind6_racer(void *arg)
101 {
102 #pragma unused(arg)
103 	struct sockaddr_in6 sin6 = { 0 };
104 
105 	init_sin6_address(&sin6);
106 	T_ASSERT_EQ(inet_pton(AF_INET6, "::1", &sin6.sin6_addr), 1, NULL);
107 	sin6.sin6_port = htons(1234);
108 
109 	while (finished == false) {
110 		(void)bind(fd, (struct sockaddr*)&sin6, sin6.sin6_len);
111 	}
112 	return NULL;
113 }
114 
115 static void *
116 connect6_racer(void *arg)
117 {
118 #pragma unused(arg)
119 	struct sockaddr_in6 sin6 = { 0 };
120 
121 	init_sin6_address(&sin6);
122 	T_ASSERT_EQ(inet_pton(AF_INET6, "::1", &sin6.sin6_addr), 1, NULL);
123 	sin6.sin6_port = htons(3456);
124 
125 	while (finished == false) {
126 		(void)connect(fd, (struct sockaddr*)&sin6, sin6.sin6_len);
127 	}
128 	return NULL;
129 }
130 
131 static void *
132 connect4_racer(void *arg)
133 {
134 #pragma unused(arg)
135 	struct sockaddr_in6 sin6 = { 0 };
136 
137 	init_sin6_address(&sin6);
138 	T_ASSERT_EQ(inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6.sin6_addr), 1, NULL);
139 	sin6.sin6_port = htons(3456);
140 
141 	while (finished == false) {
142 		(void)connect(fd, (struct sockaddr*)&sin6, sin6.sin6_len);
143 	}
144 	return NULL;
145 }
146 
147 static void *
148 bind6_leader(void *arg)
149 {
150 #pragma unused(arg)
151 	struct sockaddr_in6 sin6 = { 0 };
152 
153 	init_sin6_address(&sin6);
154 	T_ASSERT_EQ(inet_pton(AF_INET6, "::1", &sin6.sin6_addr), 1, NULL);
155 
156 	while (finished == false) {
157 		T_QUIET; T_EXPECT_POSIX_SUCCESS(fd = socket(AF_INET6, is_tcp ? SOCK_STREAM : SOCK_DGRAM, 0), "socket");
158 
159 		setnonblocking(fd);
160 
161 		(void)bind(fd, (struct sockaddr*)&sin6, sin6.sin6_len);
162 
163 		close(fd);
164 	}
165 	return NULL;
166 }
167 
168 static void *
169 bind4_leader(void *arg)
170 {
171 #pragma unused(arg)
172 	struct sockaddr_in6 sin6 = { 0 };
173 
174 	init_sin6_address(&sin6);
175 	T_ASSERT_EQ(inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6.sin6_addr), 1, NULL);
176 
177 	while (finished == false) {
178 		T_QUIET; T_EXPECT_POSIX_SUCCESS(fd = socket(AF_INET6, is_tcp ? SOCK_STREAM : SOCK_DGRAM, 0), "socket");
179 
180 		setnonblocking(fd);
181 
182 		(void)bind(fd, (struct sockaddr*)&sin6, sin6.sin6_len);
183 
184 		close(fd);
185 	}
186 	return NULL;
187 }
188 
189 static void *
190 send6_racer(void *arg)
191 {
192 #pragma unused(arg)
193 	struct sockaddr_in6 sin6 = { 0 };
194 
195 	init_sin6_address(&sin6);
196 	T_ASSERT_EQ(inet_pton(AF_INET6, "::1", &sin6.sin6_addr), 1, NULL);
197 	sin6.sin6_port = htons(3456);
198 
199 	while (finished == false) {
200 		(void)sendto(fd, "", 1, 0, (struct sockaddr*)&sin6, sin6.sin6_len);
201 	}
202 	return NULL;
203 }
204 
205 static void *
206 send4_racer(void *arg)
207 {
208 #pragma unused(arg)
209 	struct sockaddr_in6 sin6 = { 0 };
210 
211 	init_sin6_address(&sin6);
212 	T_ASSERT_EQ(inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6.sin6_addr), 1, NULL);
213 	sin6.sin6_port = htons(3456);
214 
215 	while (finished == false) {
216 		(void)sendto(fd, "", 1, 0, (struct sockaddr*)&sin6, sin6.sin6_len);
217 	}
218 	return NULL;
219 }
220 
221 static void *
222 send6_leader(void *arg)
223 {
224 #pragma unused(arg)
225 	struct sockaddr_in6 sin6 = { 0 };
226 
227 	init_sin6_address(&sin6);
228 	T_ASSERT_EQ(inet_pton(AF_INET6, "::1", &sin6.sin6_addr), 1, NULL);
229 	sin6.sin6_port = htons(3456);
230 
231 	while (finished == false) {
232 		T_QUIET; T_EXPECT_POSIX_SUCCESS(fd = socket(AF_INET6, is_tcp ? SOCK_STREAM : SOCK_DGRAM, 0), "socket");
233 
234 		setnonblocking(fd);
235 
236 		(void)sendto(fd, "", 1, 0, (struct sockaddr*)&sin6, sin6.sin6_len);
237 
238 		close(fd);
239 	}
240 	return NULL;
241 }
242 
243 static void *
244 connectx6_leader(void *arg)
245 {
246 #pragma unused(arg)
247 	struct sockaddr_in6 sin6_dst = { 0 };
248 	sa_endpoints_t sae = { 0 };
249 
250 	init_sin6_address(&sin6_dst);
251 	T_ASSERT_EQ(inet_pton(AF_INET6, "::1", &sin6_dst.sin6_addr), 1, NULL);
252 	sin6_dst.sin6_port = htons(3456);
253 	sae.sae_dstaddr = (struct sockaddr *)&sin6_dst;
254 	sae.sae_dstaddrlen = sin6_dst.sin6_len;
255 
256 	while (finished == false) {
257 		T_QUIET; T_EXPECT_POSIX_SUCCESS(fd = socket(AF_INET6, is_tcp ? SOCK_STREAM : SOCK_DGRAM, 0), "socket");
258 
259 		setnonblocking(fd);
260 
261 		(void)connectx(fd, &sae, SAE_ASSOCID_ANY, 0, NULL, 0, 0, NULL);
262 
263 		close(fd);
264 	}
265 	return NULL;
266 }
267 
268 static void *
269 connectx4_leader(void *arg)
270 {
271 #pragma unused(arg)
272 	struct sockaddr_in6 sin6_dst = { 0 };
273 	sa_endpoints_t sae = { 0 };
274 
275 	init_sin6_address(&sin6_dst);
276 	T_ASSERT_EQ(inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_dst.sin6_addr), 1, NULL);
277 	sin6_dst.sin6_port = htons(3456);
278 	sae.sae_dstaddr = (struct sockaddr *)&sin6_dst;
279 	sae.sae_dstaddrlen = sin6_dst.sin6_len;
280 
281 	while (finished == false) {
282 		T_QUIET; T_EXPECT_POSIX_SUCCESS(fd = socket(AF_INET6, is_tcp ? SOCK_STREAM : SOCK_DGRAM, 0), "socket");
283 
284 		setnonblocking(fd);
285 
286 		(void)connectx(fd, &sae, SAE_ASSOCID_ANY, 0, NULL, 0, 0, NULL);
287 
288 		close(fd);
289 	}
290 	return NULL;
291 }
292 
293 static void *
294 connectx6_binding_leader(void *arg)
295 {
296 #pragma unused(arg)
297 	struct sockaddr_in6 sin6_src = { 0 };
298 	struct sockaddr_in6 sin6_dst = { 0 };
299 	sa_endpoints_t sae = { 0 };
300 
301 	init_sin6_address(&sin6_src);
302 	sin6_src.sin6_port = htons(1234);
303 	sae.sae_srcaddr = (struct sockaddr *)&sin6_src;
304 	sae.sae_srcaddrlen = sin6_src.sin6_len;
305 
306 	init_sin6_address(&sin6_dst);
307 	T_ASSERT_EQ(inet_pton(AF_INET6, "::1", &sin6_dst.sin6_addr), 1, NULL);
308 	sin6_dst.sin6_port = htons(3456);
309 	sae.sae_dstaddr = (struct sockaddr *)&sin6_dst;
310 	sae.sae_dstaddrlen = sin6_dst.sin6_len;
311 
312 	while (finished == false) {
313 		T_QUIET; T_EXPECT_POSIX_SUCCESS(fd = socket(AF_INET6, is_tcp ? SOCK_STREAM : SOCK_DGRAM, 0), "socket");
314 
315 		setnonblocking(fd);
316 
317 		(void)connectx(fd, &sae, SAE_ASSOCID_ANY, 0, NULL, 0, 0, NULL);
318 
319 		close(fd);
320 	}
321 	return NULL;
322 }
323 
324 static void *
325 connectx4_binding_leader(void *arg)
326 {
327 #pragma unused(arg)
328 	struct sockaddr_in6 sin6_src = { 0 };
329 	struct sockaddr_in6 sin6_dst = { 0 };
330 	sa_endpoints_t sae = { 0 };
331 
332 	init_sin6_address(&sin6_src);
333 	sin6_src.sin6_port = htons(1234);
334 	sae.sae_srcaddr = (struct sockaddr *)&sin6_src;
335 	sae.sae_srcaddrlen = sin6_src.sin6_len;
336 
337 	init_sin6_address(&sin6_dst);
338 	T_ASSERT_EQ(inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_dst.sin6_addr), 1, NULL);
339 	sin6_dst.sin6_port = htons(3456);
340 	sae.sae_dstaddr = (struct sockaddr *)&sin6_dst;
341 	sae.sae_dstaddrlen = sin6_dst.sin6_len;
342 
343 	while (finished == false) {
344 		T_QUIET; T_EXPECT_POSIX_SUCCESS(fd = socket(AF_INET6, is_tcp ? SOCK_STREAM : SOCK_DGRAM, 0), "socket");
345 
346 		setnonblocking(fd);
347 
348 		(void)connectx(fd, &sae, SAE_ASSOCID_ANY, 0, NULL, 0, 0, NULL);
349 
350 		close(fd);
351 	}
352 	return NULL;
353 }
354 
355 static void *
356 connectx6_racer(void *arg)
357 {
358 #pragma unused(arg)
359 	struct sockaddr_in6 sin6_dst = { 0 };
360 	sa_endpoints_t sae = { 0 };
361 
362 	init_sin6_address(&sin6_dst);
363 	T_ASSERT_EQ(inet_pton(AF_INET6, "::1", &sin6_dst.sin6_addr), 1, NULL);
364 	sin6_dst.sin6_port = htons(3456);
365 	sae.sae_dstaddr = (struct sockaddr *)&sin6_dst;
366 	sae.sae_dstaddrlen = sin6_dst.sin6_len;
367 
368 	while (finished == false) {
369 		(void)connectx(fd, &sae, SAE_ASSOCID_ANY, 0, NULL, 0, 0, NULL);
370 	}
371 	return NULL;
372 }
373 
374 static void *
375 connectx4_racer(void *arg)
376 {
377 #pragma unused(arg)
378 	struct sockaddr_in6 sin6_dst = { 0 };
379 	sa_endpoints_t sae = { 0 };
380 
381 	init_sin6_address(&sin6_dst);
382 	T_ASSERT_EQ(inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_dst.sin6_addr), 1, NULL);
383 	sin6_dst.sin6_port = htons(3456);
384 	sae.sae_dstaddr = (struct sockaddr *)&sin6_dst;
385 	sae.sae_dstaddrlen = sin6_dst.sin6_len;
386 
387 	while (finished == false) {
388 		(void)connectx(fd, &sae, SAE_ASSOCID_ANY, 0, NULL, 0, 0, NULL);
389 	}
390 	return NULL;
391 }
392 
393 static void *
394 listen6_leader(void *arg)
395 {
396 #pragma unused(arg)
397 
398 	while (finished == false) {
399 		int val = 1;
400 
401 		T_QUIET; T_EXPECT_POSIX_SUCCESS(fd = socket(AF_INET6, is_tcp ? SOCK_STREAM : SOCK_DGRAM, 0), "socket");
402 
403 		setnonblocking(fd);
404 
405 		/* Note: The following may fail of an IPv4 racer has won the bind race */
406 		(void)setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
407 
408 		(void)listen(fd, 5);
409 
410 		close(fd);
411 	}
412 	return NULL;
413 }
414 
415 static void *
416 listen4_leader(void *arg)
417 {
418 #pragma unused(arg)
419 
420 	while (finished == false) {
421 		int val = 0;
422 
423 		T_QUIET; T_EXPECT_POSIX_SUCCESS(fd = socket(AF_INET6, is_tcp ? SOCK_STREAM : SOCK_DGRAM, 0), "socket");
424 
425 		setnonblocking(fd);
426 
427 		/* Note: The following may fail of an IPv4 racer has won the bind race */
428 		(void)setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
429 
430 		(void)listen(fd, 5);
431 
432 		close(fd);
433 	}
434 	return NULL;
435 }
436 
437 static void
438 do_bind_race(bool do_test_tcp, void *(*leader)(void *), void *(*racer)(void *))
439 {
440 	pthread_t runner1;
441 	pthread_t runner2;
442 
443 	is_tcp = do_test_tcp;
444 
445 	if (pthread_create(&runner1, NULL, leader, NULL)) {
446 		T_ASSERT_FAIL("pthread_create failed");
447 	}
448 
449 	if (pthread_create(&runner2, NULL, racer, NULL)) {
450 		T_ASSERT_FAIL("pthread_create failed");
451 	}
452 
453 	sleep(SECONDS_TO_SLEEP);
454 
455 	finished = true;
456 
457 	pthread_join(runner1, 0);
458 	pthread_join(runner2, 0);
459 
460 	force_zone_gc();
461 }
462 
463 T_DECL(ipv6_tcp_bind6_bind4_race, "race bind calls with TCP sockets")
464 {
465 	do_bind_race(true, bind6_leader, bind4_racer);
466 }
467 
468 T_DECL(ipv6_tcp_bind6_connect4_race, "race bind calls with TCP sockets")
469 {
470 	do_bind_race(true, bind6_leader, connect4_racer);
471 }
472 
473 T_DECL(ipv6_tcp_bind4_connect6_race, "race bind calls with TCP sockets")
474 {
475 	do_bind_race(true, bind4_leader, connect6_racer);
476 }
477 
478 T_DECL(ipv6_tcp_bind6_send4_race, "race bind calls with TCP sockets")
479 {
480 	do_bind_race(true, bind6_leader, send4_racer);
481 }
482 
483 T_DECL(ipv6_tcp_bind4_send6_race, "race bind calls with TCP sockets")
484 {
485 	do_bind_race(true, bind4_leader, send6_racer);
486 }
487 
488 T_DECL(ipv6_tcp_send6_send4_race, "race bind calls with TCP sockets")
489 {
490 	do_bind_race(true, send6_leader, send4_racer);
491 }
492 
493 T_DECL(ipv6_tcp_bind6_connectx4_race, "race bind calls with TCP sockets", T_META_ENABLED(false))
494 {
495 	do_bind_race(true, bind6_leader, connectx4_racer);
496 }
497 
498 T_DECL(ipv6_tcp_bind4_connectx6_race, "race bind calls with TCP sockets", T_META_ENABLED(false))
499 {
500 	do_bind_race(true, bind4_leader, connectx6_racer);
501 }
502 
503 T_DECL(ipv6_tcp_connectx4_bind6_race, "race bind calls with TCP sockets", T_META_ENABLED(false))
504 {
505 	do_bind_race(true, connectx4_leader, bind6_racer);
506 }
507 
508 T_DECL(ipv6_tcp_connectx6_bind4_race, "race bind calls with TCP sockets", T_META_ENABLED(false))
509 {
510 	do_bind_race(true, connectx6_leader, bind4_racer);
511 }
512 
513 T_DECL(ipv6_tcp_connectx4_connect6_race, "race bind calls with TCP sockets", T_META_ENABLED(false))
514 {
515 	do_bind_race(true, connectx4_leader, connect6_racer);
516 }
517 
518 T_DECL(ipv6_tcp_connectx6_connect4_race, "race bind calls with TCP sockets", T_META_ENABLED(false))
519 {
520 	do_bind_race(true, connectx6_leader, connect4_racer);
521 }
522 
523 T_DECL(ipv6_tcp_connectx4_binding_bind6_race, "race bind calls with TCP sockets", T_META_ENABLED(false))
524 {
525 	do_bind_race(true, connectx4_binding_leader, bind6_racer);
526 }
527 
528 T_DECL(ipv6_tcp_connectx6_binding_bind4_race, "race bind calls with TCP sockets", T_META_ENABLED(false))
529 {
530 	do_bind_race(true, connectx6_binding_leader, bind4_racer);
531 }
532 
533 T_DECL(ipv6_tcp_connectx4_binding_connect6_race, "race bind calls with TCP sockets", T_META_ENABLED(false))
534 {
535 	do_bind_race(true, connectx4_binding_leader, connect6_racer);
536 }
537 
538 T_DECL(ipv6_tcp_connectx6_binding_connect4_race, "race bind calls with TCP sockets", T_META_ENABLED(false))
539 {
540 	do_bind_race(true, connectx6_binding_leader, connect4_racer);
541 }
542 
543 T_DECL(ipv6_tcp_listen6_bind4_race, "race bind calls with TCP sockets")
544 {
545 	do_bind_race(true, listen6_leader, bind4_racer);
546 }
547 
548 T_DECL(ipv6_tcp_listen4_bind6_race, "race bind calls with TCP sockets")
549 {
550 	do_bind_race(true, listen4_leader, bind6_racer);
551 }
552 
553 
554 T_DECL(ipv6_udp_bind6_bind4_race, "race bind  calls with UDP sockets")
555 {
556 	do_bind_race(false, bind6_leader, bind4_racer);
557 }
558 
559 T_DECL(ipv6_udp_bind6_connect4_race, "race bind calls with UDP sockets")
560 {
561 	do_bind_race(false, bind6_leader, connect4_racer);
562 }
563 
564 T_DECL(ipv6_udp_bind4_connect6_race, "race bind calls with UDP sockets")
565 {
566 	do_bind_race(false, bind4_leader, connect6_racer);
567 }
568 
569 T_DECL(ipv6_udp_bind6_send4_race, "race bind calls with UDP sockets")
570 {
571 	do_bind_race(false, bind6_leader, send4_racer);
572 }
573 
574 T_DECL(ipv6_udp_bind4_send6_race, "race bind calls with UDP sockets")
575 {
576 	do_bind_race(false, bind4_leader, send6_racer);
577 }
578 
579 T_DECL(ipv6_udp_send6_send4_race, "race bind calls with UDP sockets")
580 {
581 	do_bind_race(false, send6_leader, send4_racer);
582 }
583 
584 T_DECL(ipv6_udp_bind6_connectx4_race, "race bind calls with UDP sockets", T_META_ENABLED(false))
585 {
586 	do_bind_race(false, bind6_leader, connectx4_racer);
587 }
588 
589 T_DECL(ipv6_udp_bind4_connectx6_race, "race bind calls with UDP sockets", T_META_ENABLED(false))
590 {
591 	do_bind_race(false, bind4_leader, connectx6_racer);
592 }
593 
594 T_DECL(ipv6_udp_connectx4_bind6_race, "race bind calls with UDP sockets", T_META_ENABLED(false))
595 {
596 	do_bind_race(false, connectx4_leader, bind6_racer);
597 }
598 
599 T_DECL(ipv6_udp_connectx6_bind4_race, "race bind calls with UDP sockets", T_META_ENABLED(false))
600 {
601 	do_bind_race(false, connectx6_leader, bind4_racer);
602 }
603 
604 T_DECL(ipv6_udp_connectx4_connect6_race, "race bind calls with UDP sockets", T_META_ENABLED(false))
605 {
606 	do_bind_race(false, connectx4_leader, connect6_racer);
607 }
608 
609 T_DECL(ipv6_udp_connectx6_connect4_race, "race bind calls with UDP sockets", T_META_ENABLED(false))
610 {
611 	do_bind_race(false, connectx6_leader, connect4_racer);
612 }
613 #else
614 
615 T_DECL(stub, "test suite disabled")
616 {
617 	T_EXPECT_TRUE(true, "disabled by rdar://137741815");
618 }
619 
620 #endif /* 0 */
621