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