Added some information when test fails
[oweals/gnunet.git] / src / transport / test_transport_api_reliability.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file transport/test_transport_api_reliability.c
22  * @brief base test case for transport implementations
23  *
24  * This test case serves as a base for tcp and http
25  * transport test cases to check that the transports
26  * achieve reliable message delivery.
27  */
28 #include "platform.h"
29 #include "gnunet_common.h"
30 #include "gnunet_hello_lib.h"
31 #include "gnunet_getopt_lib.h"
32 #include "gnunet_os_lib.h"
33 #include "gnunet_program_lib.h"
34 #include "gnunet_scheduler_lib.h"
35 #include "gnunet_server_lib.h"
36 #include "gnunet_transport_service.h"
37 #include "transport.h"
38
39 #define VERBOSE GNUNET_NO
40
41 #define VERBOSE_ARM GNUNET_NO
42
43 #define START_ARM GNUNET_YES
44
45 /**
46  * Note that this value must not significantly exceed
47  * 'MAX_PENDING' in 'gnunet-service-transport.c', otherwise
48  * messages may be dropped even for a reliable transport.
49  */
50 #define TOTAL_MSGS (60000 * 2)
51
52 /**
53  * How long until we give up on transmitting the message?
54  */
55 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1500)
56
57 #define MTYPE 12345
58
59 struct PeerContext
60 {
61   struct GNUNET_CONFIGURATION_Handle *cfg;
62   struct GNUNET_TRANSPORT_Handle *th;
63   struct GNUNET_PeerIdentity id;
64 #if START_ARM
65   pid_t arm_pid;
66 #endif
67 };
68
69 static struct PeerContext p1;
70
71 static struct PeerContext p2;
72
73 static struct GNUNET_SCHEDULER_Handle *sched;
74
75 static int ok;
76
77 static int is_tcp;
78
79 static int is_tcp_nat;
80
81 static int is_http;
82
83 static int is_https;
84
85 static int is_udp;
86
87 static int connected;
88
89 static unsigned long long total_bytes;
90
91 static struct GNUNET_TIME_Absolute start_time;
92
93 static GNUNET_SCHEDULER_TaskIdentifier die_task;
94
95 static char * key_file_p1;
96 static char * cert_file_p1;
97
98 static char * key_file_p2;
99 static char * cert_file_p2;
100
101 static int msg_scheduled;
102 static int msg_sent;
103 static int msg_recv;
104
105
106 #if VERBOSE
107 #define OKPP do { ok++; fprintf (stderr, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0)
108 #else
109 #define OKPP do { ok++; } while (0)
110 #endif
111
112
113 static void
114 end ()
115 {
116   unsigned long long delta;
117
118   GNUNET_SCHEDULER_cancel (sched, die_task);
119   die_task = GNUNET_SCHEDULER_NO_TASK;
120 #if VERBOSE
121   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from transports!\n");
122 #endif
123   GNUNET_TRANSPORT_disconnect (p1.th);
124   GNUNET_TRANSPORT_disconnect (p2.th);
125 #if VERBOSE
126   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
127               "Transports disconnected, returning success!\n");
128 #endif
129   delta = GNUNET_TIME_absolute_get_duration (start_time).value;
130   fprintf (stderr,
131            "\nThroughput was %llu kb/s\n",
132            total_bytes * 1000 / 1024 / delta);
133   ok = 0;
134
135 }
136
137
138
139 static void
140 stop_arm (struct PeerContext *p)
141 {
142 #if START_ARM
143   if (0 != PLIBC_KILL (p->arm_pid, SIGTERM))
144     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
145   GNUNET_OS_process_wait (p->arm_pid);
146 #endif
147   GNUNET_CONFIGURATION_destroy (p->cfg);
148 }
149
150
151 static void
152 end_badly (void *cls,
153            const struct GNUNET_SCHEDULER_TaskContext *tc)
154 {
155   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
156                   "Reliability failed: \nLast message scheduled %u of %u \nLast message sent %u \nLast message received %u \n ", msg_scheduled, TOTAL_MSGS, msg_sent, msg_recv);
157   GNUNET_break (0);
158   GNUNET_TRANSPORT_disconnect (p1.th);
159   GNUNET_TRANSPORT_disconnect (p2.th);
160   ok = 1;
161 }
162
163
164 struct TestMessage
165 {
166   struct GNUNET_MessageHeader header;
167   uint32_t num;
168 };
169
170
171 static unsigned int
172 get_size (unsigned int iter)
173 {
174   unsigned int ret;
175
176   if (iter < 60000)
177     return iter + sizeof (struct TestMessage);
178   ret = (iter * iter * iter);
179   return sizeof (struct TestMessage) + (ret % 60000);
180 }
181
182
183 static void
184 notify_receive (void *cls,
185                 const struct GNUNET_PeerIdentity *peer,
186                 const struct GNUNET_MessageHeader *message,
187                 struct GNUNET_TIME_Relative latency,
188                 uint32_t distance)
189 {
190   static int n;
191   unsigned int s;
192   char cbuf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
193   const struct TestMessage *hdr;
194
195   hdr = (const struct TestMessage*) message;
196   s = get_size (n);
197   if (MTYPE != ntohs (message->type))
198     return;
199   msg_recv++;
200   if (ntohs (message->size) != s)
201     {
202       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
203                   "Expected message %u of size %u, got %u bytes of message %u\n",
204                   n, s,
205                   ntohs (message->size),
206                   ntohl (hdr->num));
207       GNUNET_SCHEDULER_cancel (sched, die_task);
208       die_task = GNUNET_SCHEDULER_add_now (sched, &end_badly, NULL);
209       return;
210     }
211   if (ntohl (hdr->num) != n)
212     {
213       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
214                   "Expected message %u of size %u, got %u bytes of message %u\n",
215                   n, s,
216                   ntohs (message->size),
217                   ntohl (hdr->num));
218       GNUNET_SCHEDULER_cancel (sched, die_task);
219       die_task = GNUNET_SCHEDULER_add_now (sched, &end_badly, NULL);
220       return;
221     }
222   memset (cbuf, n, s - sizeof (struct TestMessage));
223   if (0 != memcmp (cbuf,
224                    &hdr[1],
225                    s - sizeof (struct TestMessage)))
226     {
227       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
228                   "Expected message %u with bits %u, but body did not match\n",
229                   n, (unsigned char) n);
230       GNUNET_SCHEDULER_cancel (sched, die_task);
231       die_task = GNUNET_SCHEDULER_add_now (sched, &end_badly, NULL);
232       return;
233     }
234 #if VERBOSE
235   if (ntohl(hdr->num) % 5000 == 0)
236     {
237       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
238                   "Got message %u of size %u\n",
239                   ntohl (hdr->num),
240                   ntohs (message->size));
241     }
242 #endif
243   n++;
244   if (0 == (n % (TOTAL_MSGS/100)))
245     {
246       fprintf (stderr, ".");
247       GNUNET_SCHEDULER_cancel (sched, die_task);
248       die_task = GNUNET_SCHEDULER_add_delayed (sched,
249                                                TIMEOUT,
250                                                &end_badly,
251                                                NULL);
252     }
253   if (n == TOTAL_MSGS)
254     end ();
255 }
256
257
258 static size_t
259 notify_ready (void *cls, size_t size, void *buf)
260 {
261   static int n;
262   char *cbuf = buf;
263   struct TestMessage hdr;
264   unsigned int s;
265   unsigned int ret;
266
267   if (buf == NULL)
268     {
269       GNUNET_break (0);
270       ok = 42;
271       return 0;
272     }
273   ret = 0;
274   s = get_size (n);
275   GNUNET_assert (size >= s);
276   GNUNET_assert (buf != NULL);
277   cbuf = buf;
278   do
279     {
280       hdr.header.size = htons (s);
281       hdr.header.type = htons (MTYPE);
282       hdr.num = htonl (n);
283       msg_sent = n;
284       memcpy (&cbuf[ret], &hdr, sizeof (struct TestMessage));
285       ret += sizeof (struct TestMessage);
286       memset (&cbuf[ret], n, s - sizeof (struct TestMessage));
287       ret += s - sizeof (struct TestMessage);
288 #if VERBOSE
289       if (n % 5000 == 0)
290         {
291           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
292                       "Sending message %u of size %u\n",
293                       n,
294                       s);
295         }
296 #endif
297       n++;
298       s = get_size (n);
299       if (0 == GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 16))
300         break; /* sometimes pack buffer full, sometimes not */
301     }
302   while (size - ret >= s);
303   if (n < TOTAL_MSGS)
304   {
305     GNUNET_TRANSPORT_notify_transmit_ready (p2.th,
306                                             &p1.id,
307                                             s, 0, TIMEOUT,
308                                             &notify_ready,
309                                             NULL);
310     msg_scheduled = n;
311   }
312   if (n % 5000 == 0)
313     {
314       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
315                   "Returning total message block of size %u\n",
316                   ret);
317     }
318   total_bytes += ret;
319   return ret;
320 }
321
322
323 static void
324 notify_connect (void *cls,
325                 const struct GNUNET_PeerIdentity *peer,
326                 struct GNUNET_TIME_Relative latency,
327                 uint32_t distance)
328 {
329   if (cls == &p1)
330     {
331       GNUNET_TRANSPORT_set_quota (p1.th,
332                                   &p2.id,
333                                   GNUNET_BANDWIDTH_value_init (1024 * 1024 * 1024),
334                                   GNUNET_BANDWIDTH_value_init (1024 * 1024 * 1024),
335                                   GNUNET_TIME_UNIT_FOREVER_REL,
336                                   NULL, NULL);
337       start_time = GNUNET_TIME_absolute_get ();
338       connected++;
339     }
340   else
341     {
342       GNUNET_TRANSPORT_set_quota (p2.th,
343                                   &p1.id,
344                                   GNUNET_BANDWIDTH_value_init (1024 * 1024 * 1024),
345                                   GNUNET_BANDWIDTH_value_init (1024 * 1024 * 1024),
346                                   GNUNET_TIME_UNIT_FOREVER_REL,
347                                   NULL, NULL);
348       connected++;
349     }
350
351   if (connected == 2)
352     {
353       GNUNET_TRANSPORT_notify_transmit_ready (p2.th,
354                                               &p1.id,
355                                               get_size (0), 0, TIMEOUT,
356                                               &notify_ready,
357                                               NULL);
358     }
359 #if VERBOSE
360   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
361               "Peer `%4s' connected to us (%p)!\n", GNUNET_i2s (peer), cls);
362 #endif
363 }
364
365
366 static void
367 notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
368 {
369 #if VERBOSE
370   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
371               "Peer `%4s' disconnected (%p)!\n",
372               GNUNET_i2s (peer), cls);
373 #endif
374 }
375
376
377 static void
378 setup_peer (struct PeerContext *p, const char *cfgname)
379 {
380   p->cfg = GNUNET_CONFIGURATION_create ();
381 #if START_ARM
382   p->arm_pid = GNUNET_OS_start_process (NULL, NULL,
383                                         "gnunet-service-arm",
384                                         "gnunet-service-arm",
385 #if VERBOSE_ARM
386                                         "-L", "DEBUG",
387 #endif
388                                         "-c", cfgname, NULL);
389 #endif
390   GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
391
392   if (is_https)
393   {
394           struct stat sbuf;
395           if (p==&p1)
396           {
397                   if (GNUNET_CONFIGURATION_have_value (p->cfg,
398                                                                                            "transport-https", "KEY_FILE"))
399                                 GNUNET_CONFIGURATION_get_value_string (p->cfg, "transport-https", "KEY_FILE", &key_file_p1);
400                   if (key_file_p1 == NULL)
401                           GNUNET_asprintf(&key_file_p1,"https_p1.key");
402                   if (0 == stat (key_file_p1, &sbuf ))
403                   {
404                           if (0 == remove(key_file_p1))
405                               GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Successfully removed existing private key file `%s'\n",key_file_p1);
406                           else
407                                   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to remove private key file `%s'\n",key_file_p1);
408                   }
409                   if (GNUNET_CONFIGURATION_have_value (p->cfg,"transport-https", "CERT_FILE"))
410                           GNUNET_CONFIGURATION_get_value_string (p->cfg, "transport-https", "CERT_FILE", &cert_file_p1);
411                   if (cert_file_p1 == NULL)
412                           GNUNET_asprintf(&cert_file_p1,"https_p1.cert");
413                   if (0 == stat (cert_file_p1, &sbuf ))
414                   {
415                           if (0 == remove(cert_file_p1))
416                               GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Successfully removed existing certificate file `%s'\n",cert_file_p1);
417                           else
418                                   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to remove existing certificate file `%s'\n",cert_file_p1);
419                   }
420           }
421           else if (p==&p2)
422           {
423                   if (GNUNET_CONFIGURATION_have_value (p->cfg,
424                                                                                            "transport-https", "KEY_FILE"))
425                                 GNUNET_CONFIGURATION_get_value_string (p->cfg, "transport-https", "KEY_FILE", &key_file_p2);
426                   if (key_file_p2 == NULL)
427                           GNUNET_asprintf(&key_file_p2,"https_p2.key");
428                   if (0 == stat (key_file_p2, &sbuf ))
429                   {
430                           if (0 == remove(key_file_p2))
431                               GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Successfully removed existing private key file `%s'\n",key_file_p2);
432                           else
433                                   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to remove private key file `%s'\n",key_file_p2);
434                   }
435                   if (GNUNET_CONFIGURATION_have_value (p->cfg,"transport-https", "CERT_FILE"))
436                           GNUNET_CONFIGURATION_get_value_string (p->cfg, "transport-https", "CERT_FILE", &cert_file_p2);
437                   if (cert_file_p2 == NULL)
438                           GNUNET_asprintf(&cert_file_p2,"https_p2.cert");
439                   if (0 == stat (cert_file_p2, &sbuf ))
440                   {
441                           if (0 == remove(cert_file_p2))
442                               GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Successfully removed existing certificate file `%s'\n",cert_file_p2);
443                           else
444                                   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to remove existing certificate file `%s'\n",cert_file_p2);
445                   }
446           }
447   }
448
449   p->th = GNUNET_TRANSPORT_connect (sched, p->cfg, NULL,
450                                     p,
451                                     &notify_receive,
452                                     &notify_connect,
453                                     &notify_disconnect);
454   GNUNET_assert (p->th != NULL);
455 }
456
457
458 static void
459 exchange_hello_last (void *cls,
460                      const struct GNUNET_MessageHeader *message)
461 {
462   struct PeerContext *me = cls;
463
464   GNUNET_TRANSPORT_get_hello_cancel (p2.th, &exchange_hello_last, me);
465 #if VERBOSE
466   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
467               "Exchanging HELLO with peer (%p)!\n", cls);
468 #endif
469   GNUNET_assert (ok >= 3);
470   OKPP;
471   GNUNET_assert (message != NULL);
472   GNUNET_assert (GNUNET_OK ==
473                  GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message *)
474                                       message, &me->id));
475   /* both HELLOs exchanged, get ready to test transmission! */
476   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
477               "Finished exchanging HELLOs, now waiting for transmission!\n");
478 }
479
480
481 static void
482 exchange_hello (void *cls,
483                 const struct GNUNET_MessageHeader *message)
484 {
485   struct PeerContext *me = cls;
486
487   GNUNET_TRANSPORT_get_hello_cancel (p1.th, &exchange_hello, me);
488 #if VERBOSE
489   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
490               "Exchanging HELLO with peer (%p)!\n", cls);
491 #endif
492   GNUNET_assert (ok >= 2);
493   OKPP;
494   GNUNET_assert (message != NULL);
495   GNUNET_assert (GNUNET_OK ==
496                  GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message *)
497                                       message, &me->id));
498
499 #if VERBOSE
500   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
501               "Received HELLO size %d\n",
502               GNUNET_HELLO_size((const struct GNUNET_HELLO_Message *)message));
503 #endif
504   GNUNET_TRANSPORT_offer_hello (p2.th, message);
505   GNUNET_TRANSPORT_get_hello (p2.th, &exchange_hello_last, &p2);
506 }
507
508
509 static void
510 run (void *cls,
511      struct GNUNET_SCHEDULER_Handle *s,
512      char *const *args,
513      const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg)
514 {
515   GNUNET_assert (ok == 1);
516   OKPP;
517   sched = s;
518   die_task = GNUNET_SCHEDULER_add_delayed (sched,
519                                            TIMEOUT,
520                                            &end_badly,
521                                            NULL);
522   if (is_tcp)
523     {
524       setup_peer (&p1, "test_transport_api_tcp_peer1.conf");
525       setup_peer (&p2, "test_transport_api_tcp_peer2.conf");
526     }
527   else if (is_http)
528     {
529       setup_peer (&p1, "test_transport_api_rel_http_peer1.conf");
530       setup_peer (&p2, "test_transport_api_rel_http_peer2.conf");
531     }
532   else if (is_https)
533     {
534       setup_peer (&p1, "test_transport_api_rel_https_peer1.conf");
535       setup_peer (&p2, "test_transport_api_rel_https_peer2.conf");
536     }
537   else if (is_udp)
538     {
539       setup_peer (&p1, "test_transport_api_udp_peer1.conf");
540       setup_peer (&p2, "test_transport_api_udp_peer2.conf");
541     }
542   else if (is_tcp_nat)
543     {
544       setup_peer (&p1, "test_transport_api_tcp_nat_peer1.conf");
545       setup_peer (&p2, "test_transport_api_tcp_nat_peer2.conf");
546     }
547   else
548     GNUNET_assert (0);
549   GNUNET_assert(p1.th != NULL);
550   GNUNET_assert(p2.th != NULL);
551   GNUNET_TRANSPORT_get_hello (p1.th, &exchange_hello, &p1);
552 }
553
554
555 static int
556 check ()
557 {
558   char *const argv[] = { "test-transport-api-reliability",
559     "-c",
560     "test_transport_api_data.conf",
561 #if VERBOSE
562     "-L", "DEBUG",
563 #endif
564     NULL
565   };
566   struct GNUNET_GETOPT_CommandLineOption options[] = {
567     GNUNET_GETOPT_OPTION_END
568   };
569
570 #if WRITECONFIG
571   setTransportOptions("test_transport_api_data.conf");
572 #endif
573   ok = 1;
574   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
575                       argv, "test-transport-api-reliability", "nohelp",
576                       options, &run, &ok);
577   stop_arm (&p1);
578   stop_arm (&p2);
579
580   if (is_https)
581   {
582           struct stat sbuf;
583           if (0 == stat (cert_file_p1, &sbuf ))
584           {
585                   if (0 == remove(cert_file_p1))
586                           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Successfully removed existing certificate file `%s'\n",cert_file_p1);
587                   else
588                           GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to remove certfile `%s'\n",cert_file_p1);
589           }
590
591           if (0 == stat (key_file_p1, &sbuf ))
592           {
593                   if (0 == remove(key_file_p1))
594                           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Successfully removed private key file `%s'\n",key_file_p1);
595                   else
596                           GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to private key file `%s'\n",key_file_p1);
597           }
598
599           if (0 == stat (cert_file_p2, &sbuf ))
600           {
601                   if (0 == remove(cert_file_p2))
602                           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Successfully removed existing certificate file `%s'\n",cert_file_p2);
603                   else
604                           GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to remove certfile `%s'\n",cert_file_p2);
605           }
606
607           if (0 == stat (key_file_p2, &sbuf ))
608           {
609                   if (0 == remove(key_file_p2))
610                           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Successfully removed private key file `%s'\n",key_file_p2);
611                   else
612                           GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to private key file `%s'\n",key_file_p2);
613           }
614           GNUNET_free(key_file_p1);
615           GNUNET_free(key_file_p2);
616           GNUNET_free(cert_file_p1);
617           GNUNET_free(cert_file_p2);
618   }
619
620   return ok;
621 }
622
623
624 int
625 main (int argc, char *argv[])
626 {
627   int ret;
628 #ifdef MINGW
629   return GNUNET_SYSERR;
630 #endif
631   if (strstr(argv[0], "tcp_nat") != NULL)
632     {
633       is_tcp_nat = GNUNET_YES;
634     }
635   else if (strstr(argv[0], "tcp") != NULL)
636     {
637       is_tcp = GNUNET_YES;
638     }
639   else if (strstr(argv[0], "https") != NULL)
640     {
641       is_https = GNUNET_YES;
642     }
643   else if (strstr(argv[0], "http") != NULL)
644     {
645       is_http = GNUNET_YES;
646     }
647   else if (strstr(argv[0], "udp") != NULL)
648     {
649       is_udp = GNUNET_YES;
650     }
651   GNUNET_log_setup ("test-transport-api-reliability",
652 #if VERBOSE
653                     "DEBUG",
654 #else
655                     "WARNING",
656 #endif
657                     NULL);
658   ret = check ();
659   GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-transport-peer-1");
660   GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-transport-peer-2");
661   return ret;
662 }
663
664 /* end of test_transport_api_reliability.c */