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