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