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