4d312e5f245cde2c0da9850b3de981f2c7dd8410
[oweals/gnunet.git] / src / transport / test_quota_compliance.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_quota_compliance.c
22  * @brief base test case for transport implementations
23  *
24  * This test case tests quota compliance both on core and transport level
25  */
26 #include "platform.h"
27 #include "gnunet_common.h"
28 #include "gnunet_hello_lib.h"
29 #include "gnunet_getopt_lib.h"
30 #include "gnunet_os_lib.h"
31 #include "gnunet_program_lib.h"
32 #include "gnunet_scheduler_lib.h"
33 #include "gnunet_server_lib.h"
34 #include "gnunet_transport_service.h"
35 #include "transport.h"
36
37 #define VERBOSE GNUNET_YES
38
39 #define VERBOSE_ARM GNUNET_NO
40
41 #define START_ARM GNUNET_YES
42
43 /**
44  * Note that this value must not significantly exceed
45  * 'MAX_PENDING' in 'gnunet-service-transport.c', otherwise
46  * messages may be dropped even for a reliable transport.
47  */
48 #define TOTAL_MSGS (10000 * 2)
49
50 #define MEASUREMENT_INTERVALL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 20)
51 #define MEASUREMENT_MSG_SIZE 1024
52 #define MEASUREMENT_MSG_SIZE_BIG 32768
53
54 /**
55  * Testcase timeout
56  */
57 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 200)
58
59
60 #define MTYPE 12345
61
62 struct PeerContext
63 {
64   struct GNUNET_CONFIGURATION_Handle *cfg;
65   struct GNUNET_TRANSPORT_Handle *th;
66   struct GNUNET_PeerIdentity id;
67 #if START_ARM
68   pid_t arm_pid;
69 #endif
70 };
71
72 static struct PeerContext p1;
73
74 static struct PeerContext p2;
75
76 static struct GNUNET_SCHEDULER_Handle *sched;
77
78 static int ok;
79
80 static int connected;
81
82 static unsigned long long total_bytes;
83 static unsigned long long current_quota_p1;
84 static unsigned long long current_quota_p2;
85
86 static struct GNUNET_TIME_Absolute start_time;
87
88 static GNUNET_SCHEDULER_TaskIdentifier die_task;
89
90 static int msg_scheduled;
91 static int msg_sent;
92 static int msg_recv_expected;
93 static int msg_recv;
94
95
96 #if VERBOSE
97 #define OKPP do { ok++; fprintf (stderr, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0)
98 #else
99 #define OKPP do { ok++; } while (0)
100 #endif
101
102
103 static void
104 end ()
105 {
106   unsigned long long delta;
107
108   GNUNET_SCHEDULER_cancel (sched, die_task);
109   die_task = GNUNET_SCHEDULER_NO_TASK;
110 #if VERBOSE
111   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from transports!\n");
112 #endif
113   GNUNET_TRANSPORT_disconnect (p1.th);
114   GNUNET_TRANSPORT_disconnect (p2.th);
115 #if VERBOSE
116   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
117               "Transports disconnected, returning success!\n");
118 #endif
119   delta = GNUNET_TIME_absolute_get_duration (start_time).value;
120   fprintf (stderr,
121            "\nThroughput was %llu kb/s\n",
122            total_bytes * 1000 / 1024 / delta);
123   ok = 0;
124
125 }
126
127
128
129 static void
130 stop_arm (struct PeerContext *p)
131 {
132 #if START_ARM
133   if (0 != PLIBC_KILL (p->arm_pid, SIGTERM))
134     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
135   GNUNET_OS_process_wait (p->arm_pid);
136 #endif
137   GNUNET_CONFIGURATION_destroy (p->cfg);
138 }
139
140
141 static void
142 end_badly (void *cls,
143            const struct GNUNET_SCHEDULER_TaskContext *tc)
144 {
145
146   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
147                   "end_badly \n ");
148   GNUNET_break (0);
149   if (p1.th != NULL)
150           GNUNET_TRANSPORT_disconnect (p1.th);
151   if (p2.th != NULL)
152           GNUNET_TRANSPORT_disconnect (p2.th);
153   ok = 1;
154 }
155
156
157 struct TestMessage
158 {
159   struct GNUNET_MessageHeader header;
160   uint32_t num;
161 };
162
163
164 static unsigned int
165 get_size (unsigned int iter)
166 {
167   unsigned int ret;
168
169   if (iter < 60000)
170     return iter + sizeof (struct TestMessage);
171   ret = (iter * iter * iter);
172   return sizeof (struct TestMessage) + (ret % 60000);
173 }
174
175 static unsigned int
176 get_size_new (unsigned int iter)
177 {
178   unsigned int ret = 1024;
179   return ret;
180 }
181
182 static void
183 notify_receive (void *cls,
184                 const struct GNUNET_PeerIdentity *peer,
185                 const struct GNUNET_MessageHeader *message,
186                 struct GNUNET_TIME_Relative latency,
187                 uint32_t distance)
188 {
189   static int n;
190   unsigned int s;
191   char cbuf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
192   const struct TestMessage *hdr;
193
194   hdr = (const struct TestMessage*) message;
195   s = get_size (n);
196   if (MTYPE != ntohs (message->type))
197     return;
198   msg_recv_expected = n;
199   msg_recv = ntohl(hdr->num);
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 static void measure ()
323 {
324 #if VERBOSE
325   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
326               "Both peers are connected, starting measurement...\n");
327 #endif
328           GNUNET_TRANSPORT_set_quota (p1.th,
329                           &p2.id,
330                           GNUNET_BANDWIDTH_value_init (current_quota_p1 ),
331                           GNUNET_BANDWIDTH_value_init (current_quota_p1  ),
332                           GNUNET_TIME_UNIT_FOREVER_REL,
333                           NULL, NULL);
334           GNUNET_TRANSPORT_set_quota (p2.th,
335                           &p1.id,
336                           GNUNET_BANDWIDTH_value_init (current_quota_p2),
337                           GNUNET_BANDWIDTH_value_init (current_quota_p2),
338                           GNUNET_TIME_UNIT_FOREVER_REL,
339                           NULL, NULL);
340       GNUNET_TRANSPORT_notify_transmit_ready (p2.th,
341                                               &p1.id,
342                                               get_size (0), 0, TIMEOUT,
343                                               &notify_ready,
344                                               NULL);
345       start_time = GNUNET_TIME_absolute_get ();
346
347 }
348
349 static void
350 notify_connect (void *cls,
351                 const struct GNUNET_PeerIdentity *peer,
352                 struct GNUNET_TIME_Relative latency,
353                 uint32_t distance)
354 {
355 #if VERBOSE
356   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
357               "Peer `%4s' connected to us (%p)!\n", GNUNET_i2s (peer), cls);
358 #endif
359   if (cls == &p1)
360     {
361       connected++;
362     }
363   else
364     {
365       connected++;
366     }
367   if (connected == 2)
368     {
369           measure();
370     }
371 }
372
373
374 static void
375 notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
376 {
377 #if VERBOSE
378   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
379               "Peer `%4s' disconnected (%p)!\n",
380               GNUNET_i2s (peer), cls);
381 #endif
382 }
383
384
385 static void
386 setup_peer (struct PeerContext *p, const char *cfgname)
387 {
388   p->cfg = GNUNET_CONFIGURATION_create ();
389 #if START_ARM
390   p->arm_pid = GNUNET_OS_start_process (NULL, NULL,
391                                         "gnunet-service-arm",
392                                         "gnunet-service-arm",
393 #if VERBOSE_ARM
394                                         "-L", "DEBUG",
395 #endif
396                                         "-c", cfgname, NULL);
397 #endif
398
399   GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
400   p->th = GNUNET_TRANSPORT_connect (sched, p->cfg, NULL,
401                                     p,
402                                     &notify_receive,
403                                     &notify_connect,
404                                     &notify_disconnect);
405   GNUNET_assert (p->th != NULL);
406 }
407
408
409 static void
410 exchange_hello_last (void *cls,
411                      const struct GNUNET_MessageHeader *message)
412 {
413   struct PeerContext *me = cls;
414
415   GNUNET_TRANSPORT_get_hello_cancel (p2.th, &exchange_hello_last, me);
416
417   GNUNET_assert (ok >= 3);
418   OKPP;
419   GNUNET_assert (message != NULL);
420   GNUNET_assert (GNUNET_OK ==
421                  GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message *)
422                                       message, &me->id));
423   /* both HELLOs exchanged, get ready to test transmission! */
424   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
425               "Finished exchanging HELLOs, now waiting for peers to connect!\n");
426 }
427
428
429 static void
430 exchange_hello (void *cls,
431                 const struct GNUNET_MessageHeader *message)
432 {
433   struct PeerContext *me = cls;
434
435   GNUNET_TRANSPORT_get_hello_cancel (p1.th, &exchange_hello, me);
436   GNUNET_assert (ok >= 2);
437   OKPP;
438   GNUNET_assert (message != NULL);
439   GNUNET_assert (GNUNET_OK ==
440                  GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message *)
441                                       message, &me->id));
442   GNUNET_TRANSPORT_offer_hello (p2.th, message);
443   GNUNET_TRANSPORT_get_hello (p2.th, &exchange_hello_last, &p2);
444 }
445
446 static void
447 run (void *cls,
448      struct GNUNET_SCHEDULER_Handle *s,
449      char *const *args,
450      const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg)
451 {
452   GNUNET_assert (ok == 1);
453   OKPP;
454   sched = s;
455
456   die_task = GNUNET_SCHEDULER_add_delayed (sched,
457                                            TIMEOUT,
458                                            &end_badly,
459                                            NULL);
460
461   /* Setting initial quota for both peers */
462   current_quota_p1 = 1024 * 1024 * 1024;
463   current_quota_p2 = 1024 * 1024 * 1024;
464
465   setup_peer (&p1, "test_quota_compliance_peer1.conf");
466   setup_peer (&p2, "test_quota_compliance_peer2.conf");
467
468   GNUNET_assert(p1.th != NULL);
469   GNUNET_assert(p2.th != NULL);
470   GNUNET_TRANSPORT_get_hello (p1.th, &exchange_hello, &p1);
471 }
472
473 int
474 main (int argc, char *argv[])
475 {
476   int ret = 0;
477 #ifdef MINGW
478   return GNUNET_SYSERR;
479 #endif
480   GNUNET_log_setup ("test-quota-compliance",
481 #if VERBOSE
482                     "DEBUG",
483 #else
484                     "WARNING",
485 #endif
486                     NULL);
487   char *const argv1[] = { "test-quota-compliance",
488     "-c",
489     "test_quota_compliance_data.conf",
490 #if VERBOSE
491     "-L", "DEBUG",
492 #endif
493     NULL
494   };
495   struct GNUNET_GETOPT_CommandLineOption options[] = {
496     GNUNET_GETOPT_OPTION_END
497   };
498
499   ok = 1;
500   GNUNET_PROGRAM_run ((sizeof (argv1) / sizeof (char *)) - 1,
501                       argv1, "test-quota-compliance", "nohelp",
502                       options, &run, &ok);
503   ret = ok;
504
505   stop_arm (&p1);
506   stop_arm (&p2);
507   GNUNET_DISK_directory_remove ("/tmp/test_quota_compliance_peer1");
508   GNUNET_DISK_directory_remove ("/tmp/test_quota_compliance_peer2");
509   return ret;
510 }
511
512 /* end of test_quota_compliance.c */