f662b19e0cac6652865235332588dc85e2b6ea2f
[oweals/gnunet.git] / src / core / test_core_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 core/test_core_quota_compliance.c
22  * @brief testcase for core_api.c focusing quota compliance on core level
23  *
24  * FIXME:
25  * - make sure connect callback is invoked properly as well!
26  */
27 #include "platform.h"
28 #include "gnunet_common.h"
29 #include "gnunet_constants.h"
30 #include "gnunet_arm_service.h"
31 #include "gnunet_core_service.h"
32 #include "gnunet_getopt_lib.h"
33 #include "gnunet_os_lib.h"
34 #include "gnunet_program_lib.h"
35 #include "gnunet_scheduler_lib.h"
36 #include "gnunet_transport_service.h"
37
38 #define VERBOSE GNUNET_YES
39
40 #define START_ARM GNUNET_YES
41
42 /**
43  * Note that this value must not significantly exceed
44  * 'MAX_PENDING' in 'gnunet-service-transport.c', otherwise
45  * messages may be dropped even for a reliable transport.
46  */
47 #define TOTAL_MSGS (600 * 10)
48
49 #define MEASUREMENT_MSG_SIZE 1024
50 #define MEASUREMENT_MAX_QUOTA 1024 * 1024 * 1024
51 #define MEASUREMENT_MIN_QUOTA 1024
52 #define MEASUREMENT_INTERVALL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
53
54 /**
55  * How long until we give up on transmitting the message?
56  */
57 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 6000)
58
59 /**
60  * What delay do we request from the core service for transmission?
61  * Any value smaller than the CORK delay will disable CORKing, which
62  * is what we want here.
63  */
64 #define FAST_TIMEOUT GNUNET_TIME_relative_divide (GNUNET_CONSTANTS_MAX_CORK_DELAY, 2)
65
66 #define MTYPE 12345
67
68 static int is_asymmetric_send_constant;
69 static int is_asymmetric_recv_constant;
70 static unsigned long long current_quota_p1;
71 static unsigned long long current_quota_p2;
72
73 static unsigned long long total_bytes;
74 static unsigned long long total_bytes_sent;
75
76 static struct GNUNET_TIME_Absolute start_time;
77
78 struct PeerContext
79 {
80   struct GNUNET_CONFIGURATION_Handle *cfg;
81   struct GNUNET_CORE_Handle *ch;
82   struct GNUNET_PeerIdentity id;   
83   struct GNUNET_TRANSPORT_Handle *th;
84   struct GNUNET_MessageHeader *hello;
85   int connect_status;
86 #if START_ARM
87   pid_t arm_pid;
88 #endif
89 };
90
91 static struct PeerContext p1;
92
93 static struct PeerContext p2;
94
95 static struct GNUNET_SCHEDULER_Handle *sched;
96
97 static int ok;
98 static int measurement_running;
99
100 struct GNUNET_CORE_TransmitHandle * ch;
101
102 #if VERBOSE
103 #define OKPP do { ok++; fprintf (stderr, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0)
104 #else
105 #define OKPP do { ok++; } while (0)
106 #endif
107
108 struct TestMessage 
109 {
110   struct GNUNET_MessageHeader header;
111   uint32_t num;
112 };
113
114 static void
115 terminate_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
116 {
117   unsigned long long delta;
118
119   GNUNET_CORE_disconnect (p1.ch);
120   p1.ch = NULL;
121   GNUNET_CORE_disconnect (p2.ch);
122   p2.ch = NULL;
123   GNUNET_TRANSPORT_disconnect (p1.th);
124   p1.th = NULL;
125   GNUNET_TRANSPORT_disconnect (p2.th);
126   p2.th = NULL;
127   delta = GNUNET_TIME_absolute_get_duration (start_time).rel_value;
128   ok = 0;
129 }
130
131
132 static void
133 terminate_task_error (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
134 {
135   GNUNET_break (0);
136   GNUNET_CORE_disconnect (p1.ch);
137   p1.ch = NULL;
138   GNUNET_CORE_disconnect (p2.ch);
139   p2.ch = NULL;
140   GNUNET_TRANSPORT_disconnect (p1.th);
141   p1.th = NULL;
142   GNUNET_TRANSPORT_disconnect (p2.th);
143   p2.th = NULL;
144   ok = 42;
145 }
146
147 static void
148 connect_notify (void *cls,
149                 const struct GNUNET_PeerIdentity *peer,
150                 struct GNUNET_TIME_Relative latency,
151                 uint32_t distance)
152 {
153   struct PeerContext *pc = cls;
154   GNUNET_assert (pc->connect_status == 0);
155   pc->connect_status = 1;
156   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
157               "Encrypted connection established to peer `%4s'\n",
158               GNUNET_i2s (peer));
159 }
160
161
162 static void
163 disconnect_notify (void *cls,
164                    const struct GNUNET_PeerIdentity *peer)
165 {
166   struct PeerContext *pc = cls;
167   pc->connect_status = 0;
168   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
169               "Encrypted connection to `%4s' cut\n", GNUNET_i2s (peer));
170 }
171
172
173 static int
174 inbound_notify (void *cls,
175                 const struct GNUNET_PeerIdentity *other,
176                 const struct GNUNET_MessageHeader *message,
177                 struct GNUNET_TIME_Relative latency,
178                 uint32_t distance)
179 {
180 #if DEBUG_CONNECTIONS
181   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
182               "Core provides inbound data from `%4s'.\n", GNUNET_i2s (other));
183 #endif
184   return GNUNET_OK;
185 }
186
187
188 static int
189 outbound_notify (void *cls,
190                  const struct GNUNET_PeerIdentity *other,
191                  const struct GNUNET_MessageHeader *message,
192                  struct GNUNET_TIME_Relative latency,
193                  uint32_t distance)
194 {
195 #if DEBUG_CONNECTIONS
196   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
197               "Core notifies about outbound data for `%4s'.\n",
198               GNUNET_i2s (other));
199 #endif
200   return GNUNET_OK;
201 }
202
203
204 static GNUNET_SCHEDULER_TaskIdentifier err_task;
205
206 static GNUNET_SCHEDULER_TaskIdentifier measure_task;
207
208
209 static void
210 measurement_end (void *cls,
211            const struct GNUNET_SCHEDULER_TaskContext *tc)
212 {
213           struct GNUNET_TIME_Relative duration;
214
215           measure_task  = GNUNET_SCHEDULER_NO_TASK;
216           if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
217                 return;
218           measurement_running = GNUNET_NO;
219
220           duration = GNUNET_TIME_absolute_get_difference(start_time, GNUNET_TIME_absolute_get());
221
222           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
223                       "TIMEOUT\n");
224
225           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
226                           "\nQuota compliance: \n"\
227                           "Throughput   : %10llu kB/s\n", (total_bytes_sent/(duration.rel_value / 1000)/1024));
228
229           if (err_task != GNUNET_SCHEDULER_NO_TASK)
230                   GNUNET_SCHEDULER_cancel (sched, err_task);
231       GNUNET_SCHEDULER_add_now (sched, &terminate_task, NULL);
232 }
233
234 static size_t
235 transmit_ready (void *cls, size_t size, void *buf);
236
237 static void measure (unsigned long long quota_p1, unsigned long long quota_p2 )
238 {
239 #if VERBOSE
240   if ((is_asymmetric_send_constant == GNUNET_YES) || (is_asymmetric_recv_constant == GNUNET_YES))
241           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
242               "Starting transport level measurement for %u seconds, receiving peer quota %llu kB/s, sending peer quota %llu kB/s\n", MEASUREMENT_INTERVALL.rel_value / 1000 , current_quota_p1 / 1024, current_quota_p2 / 1024);
243   else
244           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
245               "Starting transport level measurement for %u seconds, symmetric quota %llu kB/s\n", MEASUREMENT_INTERVALL.rel_value / 1000 , current_quota_p2 / 1024);
246
247 #endif
248
249   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
250               "Asking core (1) for transmission to peer `%4s'\n",
251               GNUNET_i2s (&p2.id));
252   err_task =
253   GNUNET_SCHEDULER_add_delayed (sched,
254                               TIMEOUT,
255                               &terminate_task_error,
256                               NULL);
257   measure_task = GNUNET_SCHEDULER_add_delayed (sched,
258                               MEASUREMENT_INTERVALL,
259                               &measurement_end,
260                               NULL);
261   start_time = GNUNET_TIME_absolute_get ();
262   measurement_running = GNUNET_YES;
263   GNUNET_break (NULL !=
264             GNUNET_CORE_notify_transmit_ready (p1.ch,
265                                                0,
266                                                TIMEOUT,
267                                                &p2.id,
268                                                sizeof (struct TestMessage) + MEASUREMENT_MSG_SIZE,
269                                                &transmit_ready, &p1));
270
271 }
272
273 static int tr_n;
274
275
276 static int
277 process_mtype (void *cls,
278                const struct GNUNET_PeerIdentity *peer,
279                const struct GNUNET_MessageHeader *message,
280                struct GNUNET_TIME_Relative latency,
281                uint32_t distance)
282 {
283   static int n;
284   unsigned int s;
285   const struct TestMessage *hdr;
286
287   hdr = (const struct TestMessage*) message;
288   s = sizeof (struct TestMessage) + MEASUREMENT_MSG_SIZE;
289   if (MTYPE != ntohs (message->type))
290     return GNUNET_SYSERR;
291
292 #if DEBUG_CONNECTIONS
293   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
294               "Got message %u of size %u\n",
295               ntohl (hdr->num),
296               ntohs (message->size));         
297 #endif
298   n++;
299   if (0 == (n % (TOTAL_MSGS/100)))
300     fprintf (stderr, ".");
301
302   return GNUNET_OK;
303 }
304
305
306 static struct GNUNET_CORE_MessageHandler handlers[] = {
307   {&process_mtype, MTYPE, 0},
308   {NULL, 0, 0}
309 };
310
311
312 static size_t
313 transmit_ready (void *cls, size_t size, void *buf)
314 {
315   char *cbuf = buf;
316   struct TestMessage hdr;
317   unsigned int s;
318   unsigned int ret;
319
320   if (measurement_running != GNUNET_YES)
321         return 0;
322
323   GNUNET_assert (size <= GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE); 
324   if (buf == NULL)
325     {
326       if (p1.ch != NULL)
327       {
328                 ch = GNUNET_CORE_notify_transmit_ready (p1.ch,
329                                                          0,
330                                                          FAST_TIMEOUT,
331                                                          &p2.id,
332                                                          sizeof (struct TestMessage) + MEASUREMENT_MSG_SIZE,
333                                                          &transmit_ready, &p1);
334                 GNUNET_break (NULL != ch);
335       }
336       return 0;
337     }
338   ret = 0;
339   ch = NULL;
340   s = sizeof (struct TestMessage) + MEASUREMENT_MSG_SIZE;
341
342   GNUNET_assert (size >= s);
343   GNUNET_assert (buf != NULL);
344   cbuf = buf;
345   do
346     {
347 #if DEBUG_CONNECTIONS
348       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
349                   "Sending message %u of size %u at offset %u\n",
350                   tr_n,
351                   s,
352                   ret);
353 #endif
354       hdr.header.size = htons (s);
355       hdr.header.type = htons (MTYPE);
356       hdr.num = htonl (tr_n);
357       memcpy (&cbuf[ret], &hdr, sizeof (struct TestMessage));
358       ret += sizeof (struct TestMessage);
359       memset (&cbuf[ret], tr_n, s - sizeof (struct TestMessage));
360       ret += s - sizeof (struct TestMessage);
361       tr_n++;
362       if (0 == GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 16))
363         break; /* sometimes pack buffer full, sometimes not */
364     }
365   while (size - ret >= s);
366   GNUNET_SCHEDULER_cancel (sched, err_task);
367   err_task = GNUNET_SCHEDULER_add_delayed (sched,
368                                   TIMEOUT,
369                                   &terminate_task_error, 
370                                   NULL);
371
372   total_bytes += ret;
373   total_bytes_sent += ret;
374
375   ch = GNUNET_CORE_notify_transmit_ready (p1.ch,
376                                                          0,
377                                                          FAST_TIMEOUT,
378                                                          &p2.id,
379                                                         sizeof (struct TestMessage) + MEASUREMENT_MSG_SIZE,
380                                                          &transmit_ready, &p1);
381   return ret;
382 }
383
384
385
386 static void
387 init_notify (void *cls,
388              struct GNUNET_CORE_Handle *server,
389              const struct GNUNET_PeerIdentity *my_identity,
390              const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
391 {
392   struct PeerContext *p = cls;
393
394   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
395               "Connection to CORE service of `%4s' established\n",
396               GNUNET_i2s (my_identity));
397   GNUNET_assert (server != NULL);
398   p->id = *my_identity;
399   p->ch = server;
400   if (cls == &p1)
401     {
402       GNUNET_assert (ok == 2);
403       OKPP;
404       /* connect p2 */
405       GNUNET_CORE_connect (sched,
406                            p2.cfg,
407                            TIMEOUT,
408                            &p2,
409                            &init_notify,                         
410                            &connect_notify,
411                            &disconnect_notify,
412                            NULL,
413                            &inbound_notify,
414                            GNUNET_YES,
415                            &outbound_notify, GNUNET_YES, handlers);
416     }
417   else
418     {
419       GNUNET_assert (ok == 3);
420       OKPP;
421       GNUNET_assert (cls == &p2);
422
423       measure (MEASUREMENT_MIN_QUOTA, MEASUREMENT_MIN_QUOTA);
424     }
425 }
426
427
428 static void
429 process_hello (void *cls,
430                const struct GNUNET_MessageHeader *message)
431 {
432   struct PeerContext *p = cls;
433
434   GNUNET_TRANSPORT_get_hello_cancel (p->th, &process_hello, p);
435   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
436               "Received (my) `%s' from transport service\n",
437               "HELLO");
438   GNUNET_assert (message != NULL);
439   p->hello = GNUNET_malloc (ntohs (message->size));
440   memcpy (p->hello, message, ntohs (message->size));
441   if ((p == &p1) && (p2.th != NULL))
442     GNUNET_TRANSPORT_offer_hello (p2.th, message);
443   if ((p == &p2) && (p1.th != NULL))
444     GNUNET_TRANSPORT_offer_hello (p1.th, message);
445
446   if ((p == &p1) && (p2.hello != NULL))
447     GNUNET_TRANSPORT_offer_hello (p1.th, p2.hello);
448   if ((p == &p2) && (p1.hello != NULL))
449     GNUNET_TRANSPORT_offer_hello (p2.th, p1.hello);
450 }
451
452
453
454 static void
455 setup_peer (struct PeerContext *p, const char *cfgname)
456 {
457   p->cfg = GNUNET_CONFIGURATION_create ();
458 #if START_ARM
459   p->arm_pid = GNUNET_OS_start_process (NULL, NULL, "gnunet-service-arm",
460                                         "gnunet-service-arm",
461 #if VERBOSE
462                                         "-L", "DEBUG",
463 #endif
464                                         "-c", cfgname, NULL);
465 #endif
466   GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
467   p->th = GNUNET_TRANSPORT_connect (sched, p->cfg, NULL, p, NULL, NULL, NULL);
468   GNUNET_assert (p->th != NULL);
469   GNUNET_TRANSPORT_get_hello (p->th, &process_hello, p);
470 }
471
472
473 static void
474 run (void *cls,
475      struct GNUNET_SCHEDULER_Handle *s,
476      char *const *args,
477      const char *cfgfile,
478      const struct GNUNET_CONFIGURATION_Handle *cfg)
479 {
480   GNUNET_assert (ok == 1);
481   OKPP;
482   sched = s;
483   setup_peer (&p1, "test_core_api_peer1.conf");
484   setup_peer (&p2, "test_core_api_peer2.conf");
485   GNUNET_CORE_connect (sched,
486                        p1.cfg,
487                        TIMEOUT,
488                        &p1,
489                        &init_notify,
490                        &connect_notify,
491                        &disconnect_notify,
492                        NULL,
493                        &inbound_notify,
494                        GNUNET_YES, &outbound_notify, GNUNET_YES, handlers);
495 }
496
497
498 static void
499 stop_arm (struct PeerContext *p)
500 {
501 #if START_ARM
502   if (0 != PLIBC_KILL (p->arm_pid, SIGTERM))
503     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
504   if (GNUNET_OS_process_wait(p->arm_pid) != GNUNET_OK)
505     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
506   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
507               "ARM process %u stopped\n", p->arm_pid);
508 #endif
509   GNUNET_CONFIGURATION_destroy (p->cfg);
510 }
511
512 static int
513 check ()
514 {
515   char *const argv[] = { "test-core-api-reliability",
516     "-c",
517     "test_core_api_data.conf",
518 #if VERBOSE
519     "-L", "DEBUG",
520 #endif
521     NULL
522   };
523   struct GNUNET_GETOPT_CommandLineOption options[] = {
524     GNUNET_GETOPT_OPTION_END
525   };
526   ok = 1;
527   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
528                       argv, "test_core_quota_compliance", "nohelp", options, &run, &ok);
529   stop_arm (&p1);
530   stop_arm (&p2);
531   return ok;
532 }
533
534 int
535 main (int argc, char *argv[])
536 {
537   int ret;
538
539   GNUNET_log_setup ("test-core-api",
540 #if VERBOSE
541                     "DEBUG",
542 #else
543                     "WARNING",
544 #endif
545                     NULL);
546   ret = check ();
547   GNUNET_DISK_directory_remove ("/tmp/test-gnunet-core-peer-1");
548   GNUNET_DISK_directory_remove ("/tmp/test-gnunet-core-peer-2");
549
550   return ret;
551 }
552
553 /* end of test_core_quota_compliance.c */