(no commit message)
[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 #define DEBUG_MEASUREMENT GNUNET_NO
43 #define DEBUG_CONNECTIONS GNUNET_NO
44
45 /**
46  * Note that this value must not significantly exceed
47  * 'MAX_PENDING' in 'gnunet-service-transport.c', otherwise
48  * messages may be dropped even for a reliable transport.
49  */
50 #define TOTAL_MSGS (10000 * 2)
51
52 #define MEASUREMENT_INTERVALL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
53 #define MEASUREMENT_MSG_SIZE 10000
54 #define MEASUREMENT_MSG_SIZE_BIG 32768
55 #define MEASUREMENT_MAX_QUOTA 1024 * 1024 * 1024
56 #define MEASUREMENT_MIN_QUOTA 1024 * 10
57 #define SEND_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
58 /**
59  * Testcase timeout
60  */
61 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 200)
62
63
64
65 #define MTYPE 11111
66
67 struct PeerContext
68 {
69   struct GNUNET_CONFIGURATION_Handle *cfg;
70   struct GNUNET_TRANSPORT_Handle *th;
71   struct GNUNET_PeerIdentity id;
72 #if START_ARM
73   pid_t arm_pid;
74 #endif
75 };
76
77 /**
78  * Handle for a transmission-ready request.
79  */
80 struct GNUNET_TRANSPORT_TransmitHandle
81 {
82
83   /**
84    * Neighbour for this handle, NULL for control-traffic.
85    */
86   struct NeighbourList *neighbour;
87
88   /**
89    * Function to call when notify_size bytes are available
90    * for transmission.
91    */
92   GNUNET_CONNECTION_TransmitReadyNotify notify;
93
94   /**
95    * Closure for notify.
96    */
97   void *notify_cls;
98
99   /**
100    * transmit_ready task Id.  The task is used to introduce the
101    * artificial delay that may be required to maintain the bandwidth
102    * limits.  Later, this will be the ID of the "transmit_timeout"
103    * task which is used to signal a timeout if the transmission could
104    * not be done in a timely fashion.
105    */
106   GNUNET_SCHEDULER_TaskIdentifier notify_delay_task;
107
108   /**
109    * Timeout for this request.
110    */
111   struct GNUNET_TIME_Absolute timeout;
112
113   /**
114    * How many bytes is our notify callback waiting for?
115    */
116   size_t notify_size;
117
118   /**
119    * How important is this message?
120    */
121   unsigned int priority;
122
123 };
124
125 static struct PeerContext p1;
126
127 static struct PeerContext p2;
128
129 static struct GNUNET_SCHEDULER_Handle *sched;
130
131 static int ok;
132
133 static int connected;
134 static int measurement_running;
135 static int send_running;
136 static int recv_running;
137
138 static unsigned long long total_bytes;
139 static unsigned long long current_quota_p1;
140 static unsigned long long current_quota_p2;
141
142 static struct GNUNET_TIME_Absolute start_time;
143
144 static GNUNET_SCHEDULER_TaskIdentifier die_task;
145 static GNUNET_SCHEDULER_TaskIdentifier measurement_task;
146 static GNUNET_SCHEDULER_TaskIdentifier measurement_counter_task;
147
148 struct GNUNET_TRANSPORT_TransmitHandle * transmit_handle;
149
150 #if VERBOSE
151 #define OKPP do { ok++; fprintf (stderr, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0)
152 #else
153 #define OKPP do { ok++; } while (0)
154 #endif
155
156
157 static void
158 end ()
159 {
160   GNUNET_SCHEDULER_cancel (sched, die_task);
161   die_task = GNUNET_SCHEDULER_NO_TASK;
162
163   if (measurement_task != GNUNET_SCHEDULER_NO_TASK)
164   {
165             GNUNET_SCHEDULER_cancel (sched, measurement_task);
166             measurement_task = GNUNET_SCHEDULER_NO_TASK;
167   }
168   if (measurement_counter_task != GNUNET_SCHEDULER_NO_TASK)
169   {
170             GNUNET_SCHEDULER_cancel (sched, measurement_counter_task);
171             measurement_counter_task = GNUNET_SCHEDULER_NO_TASK;
172   }
173   GNUNET_SCHEDULER_shutdown (sched);
174 #if DEBUG_CONNECTIONS
175   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from transports!\n");
176 #endif
177   GNUNET_TRANSPORT_disconnect (p1.th);
178   GNUNET_TRANSPORT_disconnect (p2.th);
179 #if DEBUG_CONNECTIONS
180   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
181               "Transports disconnected, returning success!\n");
182 #endif
183   GNUNET_SCHEDULER_shutdown (sched);
184 }
185
186
187
188 static void
189 stop_arm (struct PeerContext *p)
190 {
191 #if START_ARM
192   if (0 != PLIBC_KILL (p->arm_pid, SIGTERM))
193     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
194   GNUNET_OS_process_wait (p->arm_pid);
195 #endif
196   GNUNET_CONFIGURATION_destroy (p->cfg);
197 }
198
199
200 static void
201 end_badly (void *cls,
202            const struct GNUNET_SCHEDULER_TaskContext *tc)
203 {
204   if (measurement_task != GNUNET_SCHEDULER_NO_TASK)
205   {
206             GNUNET_SCHEDULER_cancel (sched, measurement_task);
207             measurement_task = GNUNET_SCHEDULER_NO_TASK;
208   }
209   if (measurement_counter_task != GNUNET_SCHEDULER_NO_TASK)
210   {
211             GNUNET_SCHEDULER_cancel (sched, measurement_counter_task);
212             measurement_counter_task = GNUNET_SCHEDULER_NO_TASK;
213   }
214   GNUNET_break (0);
215   if (p1.th != NULL)
216           GNUNET_TRANSPORT_disconnect (p1.th);
217   if (p2.th != NULL)
218           GNUNET_TRANSPORT_disconnect (p2.th);
219   ok = 1;
220 }
221
222 struct TestMessage
223 {
224   struct GNUNET_MessageHeader header;
225   uint32_t num;
226 };
227
228 static unsigned int
229 get_size (unsigned int iter)
230 {
231   return MEASUREMENT_MSG_SIZE + sizeof (struct TestMessage);
232 }
233
234 static void
235 notify_receive_new (void *cls,
236                 const struct GNUNET_PeerIdentity *peer,
237                 const struct GNUNET_MessageHeader *message,
238                 struct GNUNET_TIME_Relative latency,
239                 uint32_t distance)
240 {
241   static int n;
242   unsigned int s;
243   const struct TestMessage *hdr;
244
245   hdr = (const struct TestMessage*) message;
246   s = get_size (n);
247   if (MTYPE != ntohs (message->type))
248     return;
249 #if DEBUG_MEASUREMENT
250   if (ntohl(hdr->num) % 5000 == 0)
251     {
252       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
253                   "Got message %u of size %u\n",
254                   ntohl (hdr->num),
255                   ntohs (message->size));
256     }
257 #endif
258   n++;
259 }
260
261 static size_t
262 notify_ready_new (void *cls, size_t size, void *buf)
263 {
264   static int n;
265   char *cbuf = buf;
266   struct TestMessage hdr;
267   unsigned int s;
268   unsigned int ret;
269
270   transmit_handle = NULL;
271
272   if (measurement_task == GNUNET_SCHEDULER_NO_TASK)
273           return 0;
274
275   if (buf == NULL)
276     {
277       GNUNET_break (0);
278       ok = 42;
279       return 0;
280     }
281
282   if (measurement_running != GNUNET_YES)
283           return 0;
284
285   ret = 0;
286   s = get_size (n);
287   GNUNET_assert (size >= s);
288   GNUNET_assert (buf != NULL);
289   cbuf = buf;
290   do
291     {
292       hdr.header.size = htons (s);
293       hdr.header.type = htons (MTYPE);
294       hdr.num = htonl (n);
295       memcpy (&cbuf[ret], &hdr, sizeof (struct TestMessage));
296       ret += sizeof (struct TestMessage);
297       memset (&cbuf[ret], n, s - sizeof (struct TestMessage));
298       ret += s - sizeof (struct TestMessage);
299 #if DEBUG_MEASUREMENT
300       if (n % 5000 == 0)
301        {
302           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
303                       "Sending message %u\n",n);
304        }
305 #endif
306       n++;
307       s = get_size (n);
308       if (0 == GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 16))
309         break; /* sometimes pack buffer full, sometimes not */
310     }
311   while (size - ret >= s);
312   transmit_handle = GNUNET_TRANSPORT_notify_transmit_ready (p2.th,
313                                             &p1.id,
314                                             s, 0, SEND_TIMEOUT,
315                                             &notify_ready_new,
316                                             NULL);
317   total_bytes += s;
318   return ret;
319 }
320
321 static void measure (unsigned long long quota_p1, unsigned long long quota_p2 );
322
323 static void measurement_counter
324  (void *cls,
325            const struct GNUNET_SCHEDULER_TaskContext *tc)
326 {
327   measurement_counter_task = GNUNET_SCHEDULER_NO_TASK;
328
329   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
330         return;
331
332 #if VERBOSE
333   fprintf(stderr,".");
334 #endif
335   measurement_counter_task = GNUNET_SCHEDULER_add_delayed (sched,
336                                                            GNUNET_TIME_UNIT_SECONDS,
337                                                            &measurement_counter,
338                                                            NULL);
339 }
340
341 static void
342 measurement_end (void *cls,
343            const struct GNUNET_SCHEDULER_TaskContext *tc)
344 {
345   measurement_task  = GNUNET_SCHEDULER_NO_TASK;
346   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
347         return;
348
349   measurement_running = GNUNET_NO;
350   struct GNUNET_TIME_Relative duration = GNUNET_TIME_absolute_get_difference(start_time, GNUNET_TIME_absolute_get());
351
352   if (measurement_counter_task != GNUNET_SCHEDULER_NO_TASK)
353   {
354     GNUNET_SCHEDULER_cancel (sched, measurement_counter_task);
355     measurement_counter_task = GNUNET_SCHEDULER_NO_TASK;
356   }
357 #if VERBOSE
358   fprintf(stderr,"\n");
359 #endif
360   if (transmit_handle != NULL)
361   {
362           GNUNET_TRANSPORT_notify_transmit_ready_cancel(transmit_handle);
363           transmit_handle = NULL;
364   }
365   if ((total_bytes/(duration.rel_value / 1000)) > (current_quota_p1 + (current_quota_p1 / 10)))
366   {
367           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
368                           "\nQuota compliance failed: \n"\
369                           "Quota allowed: %10llu kB/s\n"\
370                           "Throughput   : %10llu kB/s\n", (current_quota_p1 / (1024)) , (total_bytes/(duration.rel_value / 1000)/1024));
371           ok = 1;
372           end();
373           return;
374   }
375   else
376   {
377
378           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
379                           "\nQuota compliance ok: \n"\
380                           "Quota allowed: %10llu kB/s\n"\
381                           "Throughput   : %10llu kB/s\n", (current_quota_p1 / (1024)) , (total_bytes/(duration.rel_value / 1000)/1024));
382           ok = 0;
383   }
384   if (current_quota_p1 < MEASUREMENT_MIN_QUOTA)
385           end();
386   else
387
388         measure (current_quota_p1 / 1024, current_quota_p2 / 1024);
389 }
390
391 static void measure (unsigned long long quota_p1, unsigned long long quota_p2 )
392 {
393           current_quota_p1 = quota_p1;
394           current_quota_p2 = quota_p2;
395 #if VERBOSE
396   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
397               "Starting transport level measurement for %u and quota %llu kB/s\n", MEASUREMENT_INTERVALL.rel_value / 1000 , current_quota_p1 / 1024);
398 #endif
399                 GNUNET_TRANSPORT_set_quota (p1.th,
400                           &p2.id,
401                           GNUNET_BANDWIDTH_value_init (current_quota_p1 ),
402                           GNUNET_BANDWIDTH_value_init (current_quota_p1 ),
403                           GNUNET_TIME_UNIT_FOREVER_REL,
404                           NULL, NULL);
405                 GNUNET_TRANSPORT_set_quota (p2.th,
406                           &p1.id,
407                           GNUNET_BANDWIDTH_value_init (current_quota_p2),
408                           GNUNET_BANDWIDTH_value_init (current_quota_p2),
409                           GNUNET_TIME_UNIT_FOREVER_REL,
410                           NULL, NULL);
411
412                 GNUNET_SCHEDULER_cancel (sched, die_task);
413                 die_task = GNUNET_SCHEDULER_add_delayed (sched,
414                                                    TIMEOUT,
415                                                    &end_badly,
416                                                    NULL);
417                 if (measurement_counter_task != GNUNET_SCHEDULER_NO_TASK)
418                   GNUNET_SCHEDULER_cancel (sched, measurement_counter_task);
419                 measurement_counter_task = GNUNET_SCHEDULER_add_delayed (sched,
420                                                                    GNUNET_TIME_UNIT_SECONDS,
421                                                                    &measurement_counter,
422                                                                    NULL);
423                 measurement_task = GNUNET_SCHEDULER_add_delayed (sched,
424                                                    MEASUREMENT_INTERVALL,
425                                                    &measurement_end,
426                                                    NULL);
427                 total_bytes = 0;
428                 measurement_running = GNUNET_YES;
429                 start_time = GNUNET_TIME_absolute_get ();
430
431                 if (transmit_handle != NULL)
432                           GNUNET_TRANSPORT_notify_transmit_ready_cancel(transmit_handle);
433
434                 transmit_handle = GNUNET_TRANSPORT_notify_transmit_ready (p2.th,
435                                                                                           &p1.id,
436                                                                                           get_size (0), 0, SEND_TIMEOUT,
437                                                                                           &notify_ready_new,
438                                                                                           NULL);
439 }
440
441 static void
442 notify_connect (void *cls,
443                 const struct GNUNET_PeerIdentity *peer,
444                 struct GNUNET_TIME_Relative latency,
445                 uint32_t distance)
446 {
447   if (cls == &p1)
448     {
449 #if DEBUG_CONNECTIONS
450   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
451               "Peer 1 `%4s' connected to us (%p)!\n", GNUNET_i2s (peer), cls);
452 #endif
453           connected++;
454     }
455   else
456     {
457 #if DEBUG_CONNECTIONS
458   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
459               "Peer 2 `%4s' connected to us (%p)!\n", GNUNET_i2s (peer), cls);
460 #endif
461       connected++;
462     }
463   if (connected == 2)
464     {
465           measure(MEASUREMENT_MAX_QUOTA,MEASUREMENT_MAX_QUOTA);
466     }
467 }
468
469
470 static void
471 notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
472 {
473 #if DEBUG_CONNECTIONS
474   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
475               "Peer `%4s' disconnected (%p)!\n",
476               GNUNET_i2s (peer), cls);
477 #endif
478 }
479
480
481 static void
482 setup_peer (struct PeerContext *p, const char *cfgname)
483 {
484   p->cfg = GNUNET_CONFIGURATION_create ();
485 #if START_ARM
486   p->arm_pid = GNUNET_OS_start_process (NULL, NULL,
487                                         "gnunet-service-arm",
488                                         "gnunet-service-arm",
489 #if VERBOSE_ARM
490                                         "-L", "DEBUG",
491 #endif
492                                         "-c", cfgname, NULL);
493 #endif
494
495   GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
496   p->th = GNUNET_TRANSPORT_connect (sched, p->cfg, NULL,
497                                     p,
498                                     &notify_receive_new,
499                                     &notify_connect,
500                                     &notify_disconnect);
501   GNUNET_assert (p->th != NULL);
502 }
503
504
505 static void
506 exchange_hello_last (void *cls,
507                      const struct GNUNET_MessageHeader *message)
508 {
509   struct PeerContext *me = cls;
510
511   GNUNET_TRANSPORT_get_hello_cancel (p2.th, &exchange_hello_last, me);
512
513   GNUNET_assert (ok >= 3);
514   OKPP;
515   GNUNET_assert (message != NULL);
516   GNUNET_assert (GNUNET_OK ==
517                  GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message *)
518                                       message, &me->id));
519   /* both HELLOs exchanged, get ready to test transmission! */
520 }
521
522
523 static void
524 exchange_hello (void *cls,
525                 const struct GNUNET_MessageHeader *message)
526 {
527   struct PeerContext *me = cls;
528
529   GNUNET_TRANSPORT_get_hello_cancel (p1.th, &exchange_hello, me);
530   GNUNET_assert (ok >= 2);
531   OKPP;
532   GNUNET_assert (message != NULL);
533   GNUNET_assert (GNUNET_OK ==
534                  GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message *)
535                                       message, &me->id));
536   GNUNET_TRANSPORT_offer_hello (p2.th, message);
537   GNUNET_TRANSPORT_get_hello (p2.th, &exchange_hello_last, &p2);
538 }
539
540 static void
541 run (void *cls,
542      struct GNUNET_SCHEDULER_Handle *s,
543      char *const *args,
544      const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg)
545 {
546   GNUNET_assert (ok == 1);
547   OKPP;
548   sched = s;
549
550   die_task = GNUNET_SCHEDULER_add_delayed (sched,
551                                            TIMEOUT,
552                                            &end_badly,
553                                            NULL);
554   measurement_running = GNUNET_NO;
555   send_running = GNUNET_NO;
556   recv_running = GNUNET_NO;
557
558   setup_peer (&p1, "test_quota_compliance_peer1.conf");
559   setup_peer (&p2, "test_quota_compliance_peer2.conf");
560
561   GNUNET_assert(p1.th != NULL);
562   GNUNET_assert(p2.th != NULL);
563   GNUNET_TRANSPORT_get_hello (p1.th, &exchange_hello, &p1);
564 }
565
566 int
567 main (int argc, char *argv[])
568 {
569   int ret = 0;
570 #ifdef MINGW
571   return GNUNET_SYSERR;
572 #endif
573   GNUNET_log_setup ("test-quota-compliance",
574 #if VERBOSE
575                     "DEBUG",
576 #else
577                     "WARNING",
578 #endif
579                     NULL);
580   char *const argv1[] = { "test-quota-compliance",
581     "-c",
582     "test_quota_compliance_data.conf",
583 #if VERBOSE
584     "-L", "DEBUG",
585 #endif
586     NULL
587   };
588   struct GNUNET_GETOPT_CommandLineOption options[] = {
589     GNUNET_GETOPT_OPTION_END
590   };
591
592   ok = 1;
593   GNUNET_PROGRAM_run ((sizeof (argv1) / sizeof (char *)) - 1,
594                       argv1, "test-quota-compliance", "nohelp",
595                       options, &run, &ok);
596   ret = ok;
597   stop_arm (&p1);
598   stop_arm (&p2);
599   GNUNET_DISK_directory_remove ("/tmp/test_quota_compliance_peer1");
600   GNUNET_DISK_directory_remove ("/tmp/test_quota_compliance_peer2");
601   return ret;
602 }
603
604 /* end of test_quota_compliance.c */