-when selecting addresses, kick out peers with insufficient support if we are at...
[oweals/gnunet.git] / src / ats / plugin_ats_proportional.c
1 /*
2  This file is part of GNUnet.
3  Copyright (C) 2011-2015 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 /**
22  * @file ats/plugin_ats_proportional.c
23  * @brief ATS proportional solver
24  * @author Matthias Wachs
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_statistics_service.h"
29 #include "gnunet_ats_plugin.h"
30 #include "gnunet_ats_service.h"
31 #include "gnunet-service-ats_addresses.h"
32
33 #define PROP_STABILITY_FACTOR 1.25
34
35
36 #define LOG(kind,...) GNUNET_log_from (kind, "ats-proportional",__VA_ARGS__)
37
38
39 /**
40  *
41  * NOTE: Do not change this documentation. This documentation is based
42  * on gnunet.org:/vcs/fsnsg/ats-paper.git/tech-doku/ats-tech-guide.tex
43  * use build_txt.sh to generate plaintext output
44  *
45  * ATS addresses : proportional solver
46  *
47  *    The proportional solver ("proportional") distributes the available
48  *    bandwidth fair over all the addresses influenced by the
49  *    preference values. For each available network type an in- and
50  *    outbound quota is configured and the bandwidth available in
51  *    these networks is distributed over the addresses.  The solver
52  *    first assigns every addresses the minimum amount of bandwidth
53  *    #GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT and then distributes the
54  *    remaining bandwidth available according to the preference
55  *    values. For each peer only a single address gets bandwidth
56  *    assigned and only one address marked as active.  The most
57  *    important functionality for the solver is implemented in: *
58  *    find_address_it is an hashmap iterator returning the prefered
59  *    address for an peer * update_quota_per_network distributes
60  *    available bandwidth for a network over active addresses
61  *
62  *    Changes to addresses automatically have an impact on the the
63  *    bandwidth assigned to other addresses in the same network since
64  *    the solver distributes the remaining bandwidth over the
65  *    addresses in the network.  When changes to the addresses occur,
66  *    the solver first performs the changes, like adding or deleting
67  *    addresses, and then updates bandwidth assignment for the
68  *    affected network. Bandwidth assignment is only recalculated on
69  *    demand when an address is requested by a client for a peer or
70  *    when the addresses available have changed or an address changed
71  *    the network it is located in. When the bandwidth assignment has
72  *    changed the callback is called with the new bandwidth
73  *    assignments. The bandwidth distribution for a network is
74  *    recalculated due to: * address suggestion requests * address
75  *    deletions * address switching networks during address update *
76  *    preference changes
77  *
78  *     3.1 Data structures used
79  *
80  *    For each ATS network (e.g. WAN, LAN, loopback) a struct Network
81  *    is used to specify network related information as total adresses
82  *    and active addresses in this network and the configured in- and
83  *    outbound quota. Each network also contains a list of addresses
84  *    added to the solver located in this network. The proportional
85  *    solver uses the addresses' solver_information field to store the
86  *    proportional network it belongs to for each address.
87  *
88  *     3.2 Initializing
89  *
90  *    When the proportional solver is initialized the solver creates a
91  *    new solver handle and initializes the network structures with
92  *    the quotas passed from addresses and returns the handle solver.
93  *
94  *     3.3 Adding an address
95  *
96  *    When a new address is added to the solver using s_add, a lookup
97  *    for the network for this address is done and the address is
98  *    enqueued in in the linked list of the network.
99  *
100  *     3.4 Updating an address
101  *
102  *    The main purpose of address updates is to update the ATS
103  *    information for addresse selection. Important for the proportional
104  *    solver is when an address switches network it is located
105  *    in. This is common because addresses added by transport's
106  *    validation mechanism are commonly located in
107  *    #GNUNET_ATS_NET_UNSPECIFIED. Addresses in validation are located
108  *    in this network type and only if a connection is successful on
109  *    return of payload data transport switches to the real network
110  *    the address is located in.  When an address changes networks it
111  *    is first of all removed from the old network using the solver
112  *    API function #GAS_proportional_address_delete() and the network in
113  *    the address struct is updated. A lookup for the respective new
114  *    proportional network is done and stored in the addresse's
115  *    solver_information field. Next the address is re-added to the
116  *    solver using the solver API function
117  *    #GAS_proportional_address_add(). If the address was marked as in
118  *    active, the solver checks if bandwidth is available in the
119  *    network and if yes sets the address to active and updates the
120  *    bandwidth distribution in this network. If no bandwidth is
121  *    available it sets the bandwidth for this address to 0 and tries
122  *    to suggest an alternative address. If an alternative address was
123  *    found, addresses' callback is called for this address.
124  *
125  *     3.5 Deleting an address
126  *
127  *    When an address is removed from the solver, it removes the
128  *    respective address from the network and if the address was
129  *    marked as active, it updates the bandwidth distribution for this
130  *    network.
131  *
132  *     3.6 Requesting addresses
133  *
134  *    When an address is requested for a peer the solver performs a
135  *    lookup for the peer entry in addresses address hashmap and
136  *    selects the best address.  The selection of the most suitable
137  *    address is done in the find_address_it hashmap iterator
138  *    described in detail in section 3.7. If no address is returned,
139  *    no address can be suggested at the moment. If the address
140  *    returned is marked as active, the solver can return this
141  *    address. If the address is not marked as active, the solver
142  *    checks if another address belongign to this peer is marked as
143  *    active and marks the address as inactive, updates the bandwidth
144  *    for this address to 0, call the bandwidth changed callback for
145  *    this address due to the change and updates quota assignment for
146  *    the addresse's network. the now in-active address is belonging
147  *    to. The solver marks the new address as active and updates the
148  *    bandwidth assignment for this network.
149  *
150  *     3.7 Choosing addresses
151  *
152  *    Choosing the best possible address for suggestion is done by
153  *    iterating over all addresses of a peer stored in addresses'
154  *    hashmap and using the hashmap iterator find_address_it to select
155  *    the best available address.  Several checks are done when an
156  *    address is selected. First if this address is currently blocked
157  *    by addresses from being suggested. An address is blocked for the
158  *    duration of #ATS_BLOCKING_DELTA when it is suggested to
159  *    transport. Next it is checked if at least
160  *    #GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT bytes bandwidth is available
161  *    in the addresse's network, because suggesting an address without
162  *    bandwidth does not make sense. This also ensures that all active
163  *    addresses in this network get at least the minimum amount of
164  *    bandwidth assigned. In the next step the solver ensures that for
165  *    tcp connections inbound connections are prefered over outbound
166  *    connections. In the next stet the solver ensures that
167  *    connections are prefered in the following order: * connections
168  *    are already established and have bandwidth assigned *
169  *    connections with a shorter distance * connectes have a shorter
170  *    latency
171  *
172  *     3.8 Changing preferences
173  *
174  *     3.9 Shutdown
175  *
176  *    During shutdown all network entries and aging processes are
177  *    destroyed and freed.
178  *
179  *
180  * OLD DOCUMENTATION
181  *
182  * This solver assigns in and outbound bandwidth equally for all
183  * addresses in specific network type (WAN, LAN) based on configured
184  * in and outbound quota for this network.
185  *
186  * The solver is notified by addresses about changes to the addresses
187  * and recalculates the bandwith assigned if required. The solver
188  * notifies addresses by calling the GAS_bandwidth_changed_cb
189  * callback.
190  *
191  * - Initialization
192  *
193  *
194  *
195  *
196  * For each peer only a single is selected and marked as "active" in the address
197  * struct.
198  *
199  * E.g.:
200  *
201  * You have the networks WAN and LAN and quotas
202  * WAN_TOTAL_IN, WAN_TOTAL_OUT
203  * LAN_TOTAL_IN, LAN_TOTAL_OUT
204  *
205  * If you have x addresses in the network segment LAN, the quotas are
206  * QUOTA_PER_ADDRESS = LAN_TOTAL_OUT / x
207  *
208  * Quotas are automatically recalculated and reported back when addresses are
209  * - requested
210  *
211  */
212
213 /**
214  * Default value to assume for the proportionality factor,
215  * if none is given in the configuration.
216  */
217 #define PROPORTIONALITY_FACTOR 2.0
218
219
220 /**
221  * Address information stored for the proportional solver in the
222  * `solver_information` member of `struct GNUNET_ATS_Address`.
223  *
224  * They are also stored in the respective `struct Network`'s linked
225  * list.
226  */
227 struct AddressWrapper
228 {
229   /**
230    * Next in DLL
231    */
232   struct AddressWrapper *next;
233
234   /**
235    * Previous in DLL
236    */
237   struct AddressWrapper *prev;
238
239   /**
240    * The address
241    */
242   struct ATS_Address *addr;
243
244     /**
245    * Network scope this address is in
246    */
247   struct Network *network;
248
249   /**
250    * Inbound quota
251    */
252   uint32_t calculated_quota_in;
253
254   /**
255    * Outbound quota
256    */
257   uint32_t calculated_quota_out;
258
259   /**
260    * When was this address activated
261    */
262   struct GNUNET_TIME_Absolute activated;
263
264 };
265
266
267 /**
268  * Representation of a network
269  */
270 struct Network
271 {
272   /**
273    * Network description
274    */
275   const char *desc;
276
277   /**
278    * String for statistics total addresses
279    */
280   char *stat_total;
281
282   /**
283    * String for statistics active addresses
284    */
285   char *stat_active;
286
287   /**
288    * Linked list of addresses in this network: head
289    */
290   struct AddressWrapper *head;
291
292   /**
293    * Linked list of addresses in this network: tail
294    */
295   struct AddressWrapper *tail;
296
297   /**
298    * Total inbound quota
299    */
300   unsigned long long total_quota_in;
301
302   /**
303    * Total outbound quota
304    */
305   unsigned long long total_quota_out;
306
307   /**
308    * ATS network type
309    */
310   enum GNUNET_ATS_Network_Type type;
311
312   /**
313    * Number of active addresses for this network
314    */
315   unsigned int active_addresses;
316
317   /**
318    * Number of total addresses for this network
319    */
320   unsigned int total_addresses;
321
322 };
323
324
325 /**
326  * A handle for the proportional solver
327  */
328 struct GAS_PROPORTIONAL_Handle
329 {
330
331   /**
332    * Our execution environment.
333    */
334   struct GNUNET_ATS_PluginEnvironment *env;
335
336   /**
337    * Networks array
338    */
339   struct Network *network_entries;
340
341   /**
342    * Proportionality factor
343    */
344   double prop_factor;
345
346   /**
347    * Stability factor
348    */
349   double stability_factor;
350
351   /**
352    * Bulk lock counter. If zero, we are not locked.
353    */
354   unsigned int bulk_lock;
355
356   /**
357    * Number of changes made while solver was locked.  We really only
358    * use 0/non-zero to check on unlock if we have to run the update.
359    */
360   unsigned int bulk_requests;
361
362   /**
363    * Number of active addresses for solver
364    */
365   unsigned int active_addresses;
366
367   /**
368    * Number of networks in @a network_entries
369    */
370   unsigned int network_count;
371 };
372
373
374 /**
375  * Test if bandwidth is available in this network to add an additional address.
376  *
377  * @param net the network type to check
378  * @param extra for how many extra addresses do we check?
379  * @return #GNUNET_YES or #GNUNET_NO
380  */
381 static int
382 is_bandwidth_available_in_network (struct Network *net,
383                                    int extra)
384 {
385   unsigned int na;
386   uint32_t min_bw = ntohl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
387
388   GNUNET_assert (net->active_addresses + extra >= 0);
389   na = net->active_addresses + extra;
390   if (0 == na)
391     return GNUNET_YES;
392   if ( ((net->total_quota_in / na) > min_bw) &&
393        ((net->total_quota_out / na) > min_bw) )
394     return GNUNET_YES;
395   return GNUNET_NO;
396 }
397
398
399 /**
400  * Test if all peers in this network require connectivity at level at
401  * least @a con.
402  *
403  * @param s the solver handle
404  * @param net the network type to check
405  * @param con connection return value threshold to check
406  * @return #GNUNET_YES or #GNUNET_NO
407  */
408 static int
409 all_require_connectivity (struct GAS_PROPORTIONAL_Handle *s,
410                           struct Network *net,
411                           unsigned int con)
412 {
413   struct AddressWrapper *aw;
414
415   for (aw = net->head; NULL != aw; aw = aw->next)
416     if (con >
417         s->env->get_connectivity (s->env->cls,
418                                   &aw->addr->peer))
419       return GNUNET_NO;
420   return GNUNET_YES;
421 }
422
423
424 /**
425  * Update bandwidth assigned to peers in this network
426  *
427  * @param s the solver handle
428  * @param net the network type to update
429  * this address
430  */
431 static void
432 distribute_bandwidth (struct GAS_PROPORTIONAL_Handle *s,
433                       struct Network *net)
434 {
435   struct AddressWrapper *aw;
436   unsigned long long remaining_quota_in = 0;
437   unsigned long long quota_out_used = 0;
438   unsigned long long remaining_quota_out = 0;
439   unsigned long long quota_in_used = 0;
440   int count_addresses;
441   uint32_t min_bw = ntohl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
442   double relative_peer_prefence;
443   double sum_relative_peer_prefences; /* Important: has to be double not float due to precision */
444   double cur_pref; /* Important: has to be double not float due to precision */
445   double peer_weight;
446   double total_weight;
447   const double *peer_relative_prefs = NULL; /* Important: has to be double not float due to precision */
448
449   uint32_t assigned_quota_in = 0;
450   uint32_t assigned_quota_out = 0;
451
452
453   LOG (GNUNET_ERROR_TYPE_INFO,
454        "Recalculate quota for network type `%s' for %u addresses (in/out): %llu/%llu \n",
455        net->desc,
456        net->active_addresses,
457        net->total_quota_in,
458        net->total_quota_in);
459
460   if (0 == net->active_addresses)
461     return; /* no addresses to update */
462
463   /* Idea:
464    * Assign every peer in network minimum Bandwidth
465    * Distribute remaining bandwidth proportional to preferences.
466    */
467
468   if ((net->active_addresses * min_bw) > net->total_quota_in)
469   {
470     GNUNET_break(0);
471     return;
472   }
473   if ((net->active_addresses * min_bw) > net->total_quota_out)
474   {
475     GNUNET_break(0);
476     return;
477   }
478
479   remaining_quota_in = net->total_quota_in - (net->active_addresses * min_bw);
480   remaining_quota_out = net->total_quota_out - (net->active_addresses * min_bw);
481   LOG (GNUNET_ERROR_TYPE_DEBUG,
482        "Proportionally distributable bandwidth (in/out): %llu/%llu\n",
483        remaining_quota_in,
484        remaining_quota_out);
485   sum_relative_peer_prefences = 0.0;
486
487   /* Calculate sum of relative preference for active addresses in this
488      network */
489   count_addresses = 0;
490   for (aw = net->head; NULL != aw; aw = aw->next)
491   {
492     if (GNUNET_YES != aw->addr->active)
493       continue;
494     peer_relative_prefs = s->env->get_preferences (s->env->cls,
495                                                    &aw->addr->peer);
496     sum_relative_peer_prefences += peer_relative_prefs[GNUNET_ATS_PREFERENCE_BANDWIDTH];
497     count_addresses++;
498   }
499
500   if (count_addresses != net->active_addresses)
501   {
502     GNUNET_break (0);
503     LOG (GNUNET_ERROR_TYPE_WARNING,
504          "%s: Counted %u active addresses, expected %u active addresses\n",
505          net->desc,
506          count_addresses,
507          net->active_addresses);
508     for (aw = net->head; NULL != aw; aw = aw->next)
509     {
510       if (GNUNET_YES != aw->addr->active)
511         continue;
512
513       LOG (GNUNET_ERROR_TYPE_WARNING,
514            "Active: `%s' `%s' length %u\n",
515            GNUNET_i2s (&aw->addr->peer),
516            aw->addr->plugin,
517            aw->addr->addr_len);
518     }
519   }
520
521   LOG (GNUNET_ERROR_TYPE_INFO,
522        "Total relative preference %.3f for %u addresses in network %s\n",
523        sum_relative_peer_prefences,
524        net->active_addresses,
525        net->desc);
526
527   for (aw = net->head; NULL != aw; aw = aw->next)
528   {
529     if (GNUNET_YES == aw->addr->active)
530     {
531       peer_relative_prefs = s->env->get_preferences (s->env->cls,
532                                                      &aw->addr->peer);
533
534       cur_pref = peer_relative_prefs[GNUNET_ATS_PREFERENCE_BANDWIDTH];
535       total_weight = net->active_addresses +
536           s->prop_factor * sum_relative_peer_prefences;
537       peer_weight = (1.0 + (s->prop_factor * cur_pref));
538
539       assigned_quota_in = min_bw
540           + ((peer_weight / total_weight) * remaining_quota_in);
541       assigned_quota_out = min_bw
542           + ((peer_weight / total_weight) * remaining_quota_out);
543
544       LOG (GNUNET_ERROR_TYPE_INFO,
545           "New quota for peer `%s' with weight (cur/total) %.3f/%.3f (in/out): %llu / %llu\n",
546           GNUNET_i2s (&aw->addr->peer),
547            peer_weight,
548            total_weight,
549           assigned_quota_in,
550            assigned_quota_out);
551     }
552     else
553     {
554       assigned_quota_in = 0;
555       assigned_quota_out = 0;
556     }
557
558     quota_in_used += assigned_quota_in;
559     quota_out_used += assigned_quota_out;
560     /* Prevent overflow due to rounding errors */
561     if (assigned_quota_in > UINT32_MAX)
562       assigned_quota_in = UINT32_MAX;
563     if (assigned_quota_out > UINT32_MAX)
564       assigned_quota_out = UINT32_MAX;
565
566     /* Store for later propagation */
567     aw->calculated_quota_in = assigned_quota_in;
568     aw->calculated_quota_out = assigned_quota_out;
569   }
570   LOG (GNUNET_ERROR_TYPE_DEBUG,
571        "Total bandwidth assigned is (in/out): %llu /%llu\n",
572        quota_in_used,
573        quota_out_used);
574   if (quota_out_used > net->total_quota_out + 1) /* +1 is required due to rounding errors */
575   {
576     LOG (GNUNET_ERROR_TYPE_ERROR,
577          "Total outbound bandwidth assigned is larger than allowed (used/allowed) for %u active addresses: %llu / %llu\n",
578          net->active_addresses,
579          quota_out_used,
580          net->total_quota_out);
581   }
582   if (quota_in_used > net->total_quota_in + 1) /* +1 is required due to rounding errors */
583   {
584     LOG (GNUNET_ERROR_TYPE_ERROR,
585          "Total inbound bandwidth assigned is larger than allowed (used/allowed) for %u active addresses: %llu / %llu\n",
586          net->active_addresses,
587          quota_in_used,
588          net->total_quota_in);
589   }
590 }
591
592
593 /**
594  * Notify ATS service of bandwidth changes to addresses.
595  *
596  * @param s solver handle
597  * @param net the network to propagate changes in
598  */
599 static void
600 propagate_bandwidth (struct GAS_PROPORTIONAL_Handle *s,
601                      struct Network *net)
602 {
603   struct AddressWrapper *cur;
604
605   for (cur = net->head; NULL != cur; cur = cur->next)
606   {
607     if ( (cur->addr->assigned_bw_in == cur->calculated_quota_in) &&
608          (cur->addr->assigned_bw_out == cur->calculated_quota_out) )
609       continue;
610     cur->addr->assigned_bw_in = cur->calculated_quota_in;
611     cur->addr->assigned_bw_out = cur->calculated_quota_out;
612     if (GNUNET_YES == cur->addr->active)
613       s->env->bandwidth_changed_cb (s->env->cls,
614                                     cur->addr);
615   }
616 }
617
618
619 /**
620  * Distribute bandwidth.  The addresses have already been selected,
621  * this is merely distributed the bandwidth among the addresses.
622  *
623  * @param s the solver handle
624  * @param n the network, can be NULL for all networks
625  */
626 static void
627 distribute_bandwidth_in_network (struct GAS_PROPORTIONAL_Handle *s,
628                                  struct Network *n)
629 {
630   unsigned int i;
631
632   if (GNUNET_YES == s->bulk_lock)
633   {
634     s->bulk_requests++;
635     return;
636   }
637   if (NULL != n)
638   {
639     LOG (GNUNET_ERROR_TYPE_DEBUG,
640         "Redistributing bandwidth in network %s with %u active and %u total addresses\n",
641          GNUNET_ATS_print_network_type(n->type),
642          n->active_addresses,
643          n->total_addresses);
644     s->env->info_cb (s->env->cls,
645                      GAS_OP_SOLVE_START,
646                      GAS_STAT_SUCCESS,
647                      GAS_INFO_PROP_SINGLE);
648     distribute_bandwidth(s,
649                          n);
650     s->env->info_cb (s->env->cls,
651                      GAS_OP_SOLVE_STOP,
652                      GAS_STAT_SUCCESS,
653                      GAS_INFO_PROP_SINGLE);
654     s->env->info_cb (s->env->cls,
655                      GAS_OP_SOLVE_UPDATE_NOTIFICATION_START,
656                      GAS_STAT_SUCCESS,
657                      GAS_INFO_PROP_SINGLE);
658     propagate_bandwidth (s,
659                          n);
660
661     s->env->info_cb (s->env->cls,
662                      GAS_OP_SOLVE_UPDATE_NOTIFICATION_STOP,
663                      GAS_STAT_SUCCESS,
664                      GAS_INFO_PROP_SINGLE);
665   }
666   else
667   {
668     s->env->info_cb (s->env->cls,
669                      GAS_OP_SOLVE_START,
670                      GAS_STAT_SUCCESS,
671                      GAS_INFO_PROP_ALL);
672     for (i = 0; i < s->network_count; i++)
673       distribute_bandwidth (s,
674                             &s->network_entries[i]);
675     s->env->info_cb (s->env->cls,
676                      GAS_OP_SOLVE_STOP,
677                      GAS_STAT_SUCCESS,
678                      GAS_INFO_PROP_ALL);
679     s->env->info_cb (s->env->cls,
680                      GAS_OP_SOLVE_UPDATE_NOTIFICATION_START,
681                      GAS_STAT_SUCCESS,
682                      GAS_INFO_PROP_ALL);
683     for (i = 0; i < s->network_count; i++)
684       propagate_bandwidth (s,
685                            &s->network_entries[i]);
686     s->env->info_cb (s->env->cls,
687                      GAS_OP_SOLVE_UPDATE_NOTIFICATION_STOP,
688                      GAS_STAT_SUCCESS,
689                      GAS_INFO_PROP_ALL);
690   }
691 }
692
693
694 /**
695  * Context for finding the best address* Linked list of addresses in this network: head
696  */
697 struct FindBestAddressCtx
698 {
699   /**
700    * The solver handle
701    */
702   struct GAS_PROPORTIONAL_Handle *s;
703
704   /**
705    * The currently best address
706    */
707   struct ATS_Address *best;
708 };
709
710
711 /**
712  * Find index of a ATS property type in the quality properties array.
713  *
714  * @param type ATS property type
715  * @return index in the quality array, #GNUNET_SYSERR if the type
716  *         was not a quality property
717  */
718 static int
719 find_quality_property_index (enum GNUNET_ATS_Property type)
720 {
721   enum GNUNET_ATS_Property existing_types[] = GNUNET_ATS_QualityProperties;
722   unsigned int c;
723
724   for (c = 0; c < GNUNET_ATS_QualityPropertiesCount; c++)
725     if (existing_types[c] == type)
726       return c;
727   GNUNET_break (0);
728   return GNUNET_SYSERR;
729 }
730
731
732 /**
733  * Find a "good" address to use for a peer by iterating over the
734  * addresses for this peer.  If we already have an existing address,
735  * we stick to it.  Otherwise, we pick by lowest distance and then by
736  * lowest latency.
737  *
738  * @param cls the `struct FindBestAddressCtx *' where we store the result
739  * @param key the peer we are trying to find the best address for
740  * @param value another `struct ATS_Address*` to consider using
741  * @return #GNUNET_OK (continue to iterate)
742  */
743 static int
744 find_best_address_it (void *cls,
745                       const struct GNUNET_PeerIdentity *key,
746                       void *value)
747 {
748   struct FindBestAddressCtx *ctx = cls;
749   struct ATS_Address *current = value;
750   struct AddressWrapper *asi = current->solver_information;
751   struct GNUNET_TIME_Relative active_time;
752   double best_delay;
753   double best_distance;
754   double cur_delay;
755   double cur_distance;
756   int index;
757   unsigned int con;
758   int bw_available;
759   int need;
760
761   /* we need +1 slot if 'current' is not yet active */
762   need = (GNUNET_YES == current->active) ? 0 : 1;
763   /* we save -1 slot if 'best' is active and belongs
764      to the same network (as we would replace it) */
765   if ( (NULL != ctx->best) &&
766        (GNUNET_YES == ctx->best->active) &&
767        (((struct AddressWrapper *) ctx->best->solver_information)->network ==
768         asi->network) )
769     need--;
770   /* we can gain -1 slot if this peers connectivity
771      requirement is higher than that of another peer
772      in that network scope */
773   con = ctx->s->env->get_connectivity (ctx->s->env->cls,
774                                        key);
775   if (GNUNET_YES !=
776       all_require_connectivity (ctx->s,
777                                 asi->network,
778                                 con))
779     need--;
780   /* test if minimum bandwidth for 'current' would be available */
781   bw_available
782     = is_bandwidth_available_in_network (asi->network,
783                                          need);
784   if (! bw_available)
785   {
786     /* Bandwidth for this address is unavailable, so we cannot use
787        it. */
788     return GNUNET_OK;
789   }
790   if (GNUNET_YES == current->active)
791   {
792     active_time = GNUNET_TIME_absolute_get_duration (asi->activated);
793     if (active_time.rel_value_us <=
794         ((double) GNUNET_TIME_UNIT_SECONDS.rel_value_us) * ctx->s->stability_factor)
795     {
796       /* Keep active address for stability reasons */
797       ctx->best = current;
798       return GNUNET_NO;
799     }
800   }
801   if (NULL == ctx->best)
802   {
803     /* We so far have nothing else, so go with it! */
804     ctx->best = current;
805     return GNUNET_OK;
806   }
807
808   /* Now compare ATS information */
809   index = find_quality_property_index (GNUNET_ATS_QUALITY_NET_DISTANCE);
810   cur_distance = current->atsin[index].norm;
811   best_distance = ctx->best->atsin[index].norm;
812   index = find_quality_property_index (GNUNET_ATS_QUALITY_NET_DELAY);
813   cur_delay = current->atsin[index].norm;
814   best_delay = ctx->best->atsin[index].norm;
815
816   /* user shorter distance */
817   if (cur_distance < best_distance)
818   {
819     if (GNUNET_NO == ctx->best->active)
820     {
821       /* Activity doesn't influence the equation, use current */
822       ctx->best = current;
823     }
824     else if ((best_distance / cur_distance) > ctx->s->stability_factor)
825     {
826       /* Distance change is significant, switch active address! */
827       ctx->best = current;
828     }
829   }
830
831   /* User connection with less delay */
832   if (cur_delay < best_delay)
833   {
834     if (GNUNET_NO == ctx->best->active)
835     {
836       /* Activity doesn't influence the equation, use current */
837       ctx->best = current;
838     }
839     else if ((best_delay / cur_delay) > ctx->s->stability_factor)
840     {
841       /* Latency change is significant, switch active address! */
842       ctx->best = current;
843     }
844   }
845   return GNUNET_OK;
846 }
847
848
849 /**
850  * Find the currently best address for a peer from the set of
851  * addresses available or return NULL of no address is available.
852  *
853  * @param s the proportional handle
854  * @param addresses the address hashmap
855  * @param id the peer id
856  * @return the address or NULL
857  */
858 struct ATS_Address *
859 get_best_address (struct GAS_PROPORTIONAL_Handle *s,
860                   struct GNUNET_CONTAINER_MultiPeerMap *addresses,
861                   const struct GNUNET_PeerIdentity *id)
862 {
863   struct FindBestAddressCtx fba_ctx;
864
865   fba_ctx.best = NULL;
866   fba_ctx.s = s;
867   GNUNET_CONTAINER_multipeermap_get_multiple (addresses,
868                                               id,
869                                               &find_best_address_it,
870                                               &fba_ctx);
871   return fba_ctx.best;
872 }
873
874
875 /**
876  * Decrease number of active addresses in network.
877  *
878  * @param s the solver handle
879  * @param net the network type
880  */
881 static void
882 address_decrement_active (struct GAS_PROPORTIONAL_Handle *s,
883                           struct Network *net)
884 {
885   GNUNET_assert (net->active_addresses > 0);
886   net->active_addresses--;
887   GNUNET_STATISTICS_update (s->env->stats,
888                             net->stat_active,
889                             -1,
890                             GNUNET_NO);
891   GNUNET_assert (s->active_addresses > 0);
892   s->active_addresses--;
893   GNUNET_STATISTICS_update (s->env->stats,
894                             "# ATS addresses total",
895                             -1,
896                             GNUNET_NO);
897 }
898
899
900 /**
901  * Address map iterator to find current active address for peer.
902  * Asserts that only one address is active per peer.
903  *
904  * @param cls last active address
905  * @param key peer's key
906  * @param value address to check
907  * @return #GNUNET_NO on double active address else #GNUNET_YES;
908  */
909 static int
910 get_active_address_it (void *cls,
911                        const struct GNUNET_PeerIdentity *key,
912                        void *value)
913 {
914   struct ATS_Address **dest = cls;
915   struct ATS_Address *aa = value;
916
917   if (GNUNET_YES != aa->active)
918     return GNUNET_OK;
919   GNUNET_assert (NULL == (*dest));
920   (*dest) = aa;
921   return GNUNET_OK;
922 }
923
924
925 /**
926  * Find current active address for peer
927  *
928  * @param s the solver handle
929  * @param peer the peer
930  * @return active address or NULL
931  */
932 static struct ATS_Address *
933 get_active_address (struct GAS_PROPORTIONAL_Handle *s,
934                     const struct GNUNET_PeerIdentity *peer)
935 {
936   struct ATS_Address *dest;
937
938   dest = NULL;
939   GNUNET_CONTAINER_multipeermap_get_multiple (s->env->addresses,
940                                               peer,
941                                               &get_active_address_it,
942                                               &dest);
943   return dest;
944 }
945
946
947 /**
948  * Update active address for a peer.  Check if active address exists
949  * and what the best address is, if addresses are different switch.
950  * Then reallocate bandwidth within the affected network scopes.
951  *
952  * @param s solver handle
953  * @param current_address the address currently active for the peer,
954  *        NULL for none
955  * @param peer the peer to check
956  */
957 static void
958 update_active_address (struct GAS_PROPORTIONAL_Handle *s,
959                        struct ATS_Address *current_address,
960                        const struct GNUNET_PeerIdentity *peer)
961 {
962   struct ATS_Address *best_address;
963   struct AddressWrapper *asi_cur;
964   struct AddressWrapper *asi_best;
965   struct AddressWrapper *aw;
966   struct AddressWrapper *aw_min;
967   unsigned int a_con;
968   unsigned int con_min;
969
970   best_address = get_best_address (s,
971                                    s->env->addresses,
972                                    peer);
973   if (NULL != best_address)
974     asi_best = best_address->solver_information;
975   else
976     asi_best = NULL;
977   if (current_address == best_address)
978     return; /* no changes */
979   if (NULL != current_address)
980   {
981     /* We switch to a new address (or to none);
982        mark old address as inactive. */
983     asi_cur = current_address->solver_information;
984     GNUNET_assert (GNUNET_YES == current_address->active);
985     LOG (GNUNET_ERROR_TYPE_INFO,
986          "Disabling previous active address for peer `%s'\n",
987          GNUNET_i2s (peer));
988     asi_cur->activated = GNUNET_TIME_UNIT_ZERO_ABS;
989     current_address->active = GNUNET_NO;
990     current_address->assigned_bw_in = 0;
991     current_address->assigned_bw_out = 0;
992     address_decrement_active (s,
993                               asi_cur->network);
994     if ( (NULL == best_address) ||
995          (asi_best->network != asi_cur->network) )
996       distribute_bandwidth_in_network (s,
997                                        asi_cur->network);
998     if (NULL == best_address)
999     {
1000       /* We previously had an active address, but now we cannot
1001        * suggest one.  Therefore we have to disconnect the peer.
1002        * The above call to "distribute_bandwidth_in_network()
1003        * does not see 'current_address' so we need to trigger
1004        * the update here. */
1005       LOG (GNUNET_ERROR_TYPE_DEBUG,
1006            "Disconnecting peer `%s'.\n",
1007            GNUNET_i2s (peer));
1008       s->env->bandwidth_changed_cb (s->env->cls,
1009                                     current_address);
1010       return;
1011     }
1012   }
1013   if (NULL == best_address)
1014   {
1015     /* We do not have a new address, so we are done. */
1016     LOG (GNUNET_ERROR_TYPE_DEBUG,
1017          "Cannot suggest address for peer `%s'\n",
1018          GNUNET_i2s (peer));
1019     return;
1020   }
1021   /* We do have a new address, activate it */
1022   LOG (GNUNET_ERROR_TYPE_DEBUG,
1023        "Suggesting new address %p for peer `%s'\n",
1024        best_address,
1025        GNUNET_i2s (peer));
1026   /* Mark address as active */
1027   best_address->active = GNUNET_YES;
1028   asi_best->activated = GNUNET_TIME_absolute_get ();
1029   asi_best->network->active_addresses++;
1030   s->active_addresses++;
1031   GNUNET_STATISTICS_update (s->env->stats,
1032                             "# ATS active addresses total",
1033                             1,
1034                             GNUNET_NO);
1035   GNUNET_STATISTICS_update (s->env->stats,
1036                             asi_best->network->stat_active,
1037                             1,
1038                             GNUNET_NO);
1039   LOG (GNUNET_ERROR_TYPE_INFO,
1040        "Address %p for peer `%s' is now active\n",
1041        best_address,
1042        GNUNET_i2s (peer));
1043
1044
1045   if (GNUNET_NO ==
1046       is_bandwidth_available_in_network (asi_best->network,
1047                                          0))
1048   {
1049     /* we went over the maximum number of addresses for
1050        this scope; remove the address with the smallest
1051        connectivity requirement */
1052     con_min = UINT32_MAX;
1053     aw_min = NULL;
1054     for (aw = asi_best->network->head; NULL != aw; aw = aw->next)
1055     {
1056       if (con_min >
1057           (a_con = s->env->get_connectivity (s->env->cls,
1058                                              &aw->addr->peer)))
1059       {
1060         aw_min = aw;
1061         con_min = a_con;
1062         if (0 == con_min)
1063           break;
1064       }
1065     }
1066     update_active_address (s,
1067                            aw_min->addr,
1068                            &aw->addr->peer);
1069   }
1070   distribute_bandwidth_in_network (s,
1071                                    asi_best->network);
1072 }
1073
1074
1075 /**
1076  * The preferences for a peer in the problem changed.
1077  *
1078  * @param solver the solver handle
1079  * @param peer the peer to change the preference for
1080  * @param kind the kind to change the preference
1081  * @param pref_rel the normalized preference value for this kind over all clients
1082  */
1083 static void
1084 GAS_proportional_change_preference (void *solver,
1085                                     const struct GNUNET_PeerIdentity *peer,
1086                                     enum GNUNET_ATS_PreferenceKind kind,
1087                                     double pref_rel)
1088 {
1089   struct GAS_PROPORTIONAL_Handle *s = solver;
1090
1091   distribute_bandwidth_in_network (s,
1092                                    NULL);
1093 }
1094
1095
1096 /**
1097  * Get application feedback for a peer
1098  *
1099  * @param solver the solver handle
1100  * @param application the application
1101  * @param peer the peer to change the preference for
1102  * @param scope the time interval for this feedback: [now - scope .. now]
1103  * @param kind the kind to change the preference
1104  * @param score the score
1105  */
1106 static void
1107 GAS_proportional_feedback (void *solver,
1108                            struct GNUNET_SERVER_Client *application,
1109                            const struct GNUNET_PeerIdentity *peer,
1110                            const struct GNUNET_TIME_Relative scope,
1111                            enum GNUNET_ATS_PreferenceKind kind,
1112                            double score)
1113 {
1114   /* Proportional does not care about feedback */
1115 }
1116
1117
1118 /**
1119  * Get the preferred address for a specific peer
1120  *
1121  * @param solver the solver handle
1122  * @param peer the identity of the peer
1123  */
1124 static void
1125 GAS_proportional_start_get_address (void *solver,
1126                                     const struct GNUNET_PeerIdentity *peer)
1127 {
1128   struct GAS_PROPORTIONAL_Handle *s = solver;
1129
1130   update_active_address (s,
1131                          get_active_address (s,
1132                                              peer),
1133                          peer);
1134 }
1135
1136
1137 /**
1138  * Stop notifying about address and bandwidth changes for this peer
1139  *
1140  * @param solver the solver handle
1141  * @param peer the peer
1142  */
1143 static void
1144 GAS_proportional_stop_get_address (void *solver,
1145                                    const struct GNUNET_PeerIdentity *peer)
1146 {
1147   struct GAS_PROPORTIONAL_Handle *s = solver;
1148   struct ATS_Address *cur;
1149   struct AddressWrapper *asi;
1150
1151   cur = get_active_address (s,
1152                             peer);
1153   if (NULL == cur)
1154     return;
1155   asi = cur->solver_information;
1156   distribute_bandwidth_in_network (s,
1157                                    asi->network);
1158 }
1159
1160
1161 /**
1162  * Start a bulk operation
1163  *
1164  * @param solver the solver
1165  */
1166 static void
1167 GAS_proportional_bulk_start (void *solver)
1168 {
1169   struct GAS_PROPORTIONAL_Handle *s = solver;
1170
1171   LOG (GNUNET_ERROR_TYPE_DEBUG,
1172        "Locking solver for bulk operation ...\n");
1173   GNUNET_assert (NULL != solver);
1174   s->bulk_lock++;
1175 }
1176
1177
1178 /**
1179  * Bulk operation done.
1180  *
1181  * @param solver our `struct GAS_PROPORTIONAL_Handle *`
1182  */
1183 static void
1184 GAS_proportional_bulk_stop (void *solver)
1185 {
1186   struct GAS_PROPORTIONAL_Handle *s = solver;
1187
1188   LOG (GNUNET_ERROR_TYPE_DEBUG,
1189        "Unlocking solver from bulk operation ...\n");
1190   if (s->bulk_lock < 1)
1191   {
1192     GNUNET_break(0);
1193     return;
1194   }
1195   s->bulk_lock--;
1196   if ( (0 == s->bulk_lock) &&
1197        (0 < s->bulk_requests) )
1198   {
1199     LOG (GNUNET_ERROR_TYPE_INFO,
1200          "No lock pending, recalculating\n");
1201     distribute_bandwidth_in_network (s,
1202                                      NULL);
1203     s->bulk_requests = 0;
1204   }
1205 }
1206
1207
1208 /**
1209  * Transport properties for this address have changed
1210  *
1211  * @param solver solver handle
1212  * @param address the address
1213  * @param type the ATSI type in HBO
1214  * @param abs_value the absolute value of the property
1215  * @param rel_value the normalized value
1216  */
1217 static void
1218 GAS_proportional_address_property_changed (void *solver,
1219                                            struct ATS_Address *address,
1220                                            enum GNUNET_ATS_Property type,
1221                                            uint32_t abs_value,
1222                                            double rel_value)
1223 {
1224   struct GAS_PROPORTIONAL_Handle *s = solver;
1225   struct AddressWrapper *asi = address->solver_information;
1226
1227   distribute_bandwidth_in_network (s,
1228                                    asi->network);
1229 }
1230
1231
1232 /**
1233  * Add a new single address to a network
1234  *
1235  * @param solver the solver Handle
1236  * @param address the address to add
1237  * @param network network type of this address
1238  */
1239 static void
1240 GAS_proportional_address_add (void *solver,
1241                               struct ATS_Address *address,
1242                               enum GNUNET_ATS_Network_Type network)
1243 {
1244   struct GAS_PROPORTIONAL_Handle *s = solver;
1245   struct Network *net;
1246   struct AddressWrapper *aw;
1247
1248   GNUNET_assert (network < s->env->network_count);
1249   net = &s->network_entries[network];
1250   net->total_addresses++;
1251
1252   aw = GNUNET_new (struct AddressWrapper);
1253   aw->addr = address;
1254   aw->network = net;
1255   address->solver_information = aw;
1256   GNUNET_CONTAINER_DLL_insert (net->head,
1257                                net->tail,
1258                                aw);
1259   GNUNET_STATISTICS_update (s->env->stats,
1260                             "# ATS addresses total",
1261                             1,
1262                             GNUNET_NO);
1263   GNUNET_STATISTICS_update (s->env->stats,
1264                             net->stat_total,
1265                             1,
1266                             GNUNET_NO);
1267   if (0 !=
1268       s->env->get_connectivity (s->env->cls,
1269                                 &address->peer))
1270   {
1271     /* This peer is requested, find best address */
1272     update_active_address (s,
1273                            get_active_address (s,
1274                                                &address->peer),
1275                            &address->peer);
1276   }
1277   LOG (GNUNET_ERROR_TYPE_INFO,
1278        "Added new address for `%s', now total %u and active %u addresses in network `%s'\n",
1279        GNUNET_i2s (&address->peer),
1280        net->total_addresses,
1281        net->active_addresses,
1282        net->desc);
1283 }
1284
1285
1286 /**
1287  * Remove an address from the solver. To do so, we:
1288  * - Removed it from specific network
1289  * - Decrease the number of total addresses
1290  * - If active:
1291  *   - decrease number of active addreses
1292  *   - update quotas
1293  *
1294  * @param solver the solver handle
1295  * @param address the address to remove
1296  */
1297 static void
1298 GAS_proportional_address_delete (void *solver,
1299                                  struct ATS_Address *address)
1300 {
1301   struct GAS_PROPORTIONAL_Handle *s = solver;
1302   struct AddressWrapper *aw = address->solver_information;
1303   struct Network *net = aw->network;
1304
1305   LOG (GNUNET_ERROR_TYPE_DEBUG,
1306        "Deleting %s address for peer `%s' from network `%s' (total: %u/active: %u)\n",
1307        (GNUNET_NO == address->active) ? "inactive" : "active",
1308        GNUNET_i2s (&address->peer),
1309        net->desc,
1310        net->total_addresses,
1311        net->active_addresses);
1312
1313   GNUNET_CONTAINER_DLL_remove (net->head,
1314                                net->tail,
1315                                aw);
1316   GNUNET_assert (net->total_addresses > 0);
1317   net->total_addresses--;
1318   GNUNET_STATISTICS_update (s->env->stats,
1319                             net->stat_total,
1320                             -1,
1321                             GNUNET_NO);
1322   if (GNUNET_YES == address->active)
1323   {
1324     /* Address was active, remove from network and update quotas */
1325     update_active_address (s,
1326                            address,
1327                            &address->peer);
1328     distribute_bandwidth_in_network (s, net);
1329   }
1330   GNUNET_free (aw);
1331   address->solver_information = NULL;
1332   LOG (GNUNET_ERROR_TYPE_DEBUG,
1333        "After deleting address now total %u and active %u addresses in network `%s'\n",
1334        net->total_addresses,
1335        net->active_addresses,
1336        net->desc);
1337 }
1338
1339
1340 /**
1341  * Function invoked when the plugin is loaded.
1342  *
1343  * @param[in,out] cls the `struct GNUNET_ATS_PluginEnvironment *` to use;
1344  *            modified to return the API functions (ugh).
1345  * @return the `struct GAS_PROPORTIONAL_Handle` to pass as a closure
1346  */
1347 void *
1348 libgnunet_plugin_ats_proportional_init (void *cls)
1349 {
1350   static struct GNUNET_ATS_SolverFunctions sf;
1351   struct GNUNET_ATS_PluginEnvironment *env = cls;
1352   struct GAS_PROPORTIONAL_Handle *s;
1353   struct Network * cur;
1354   float f_tmp;
1355   unsigned int c;
1356
1357   s = GNUNET_new (struct GAS_PROPORTIONAL_Handle);
1358   s->env = env;
1359   sf.cls = s;
1360   sf.s_add = &GAS_proportional_address_add;
1361   sf.s_address_update_property = &GAS_proportional_address_property_changed;
1362   sf.s_get = &GAS_proportional_start_get_address;
1363   sf.s_get_stop = &GAS_proportional_stop_get_address;
1364   sf.s_pref = &GAS_proportional_change_preference;
1365   sf.s_feedback = &GAS_proportional_feedback;
1366   sf.s_del = &GAS_proportional_address_delete;
1367   sf.s_bulk_start = &GAS_proportional_bulk_start;
1368   sf.s_bulk_stop = &GAS_proportional_bulk_stop;
1369   s->stability_factor = PROP_STABILITY_FACTOR;
1370   if (GNUNET_SYSERR !=
1371       GNUNET_CONFIGURATION_get_value_float (env->cfg,
1372                                             "ats",
1373                                             "PROP_STABILITY_FACTOR",
1374                                             &f_tmp))
1375   {
1376     if ((f_tmp < 1.0) || (f_tmp > 2.0))
1377     {
1378       LOG (GNUNET_ERROR_TYPE_ERROR,
1379            _("Invalid %s configuration %f \n"),
1380            "PROP_STABILITY_FACTOR",
1381            f_tmp);
1382     }
1383     else
1384     {
1385       s->stability_factor = f_tmp;
1386       LOG (GNUNET_ERROR_TYPE_INFO,
1387            "Using %s of %.3f\n",
1388            "PROP_STABILITY_FACTOR",
1389            f_tmp);
1390     }
1391   }
1392   s->prop_factor = PROPORTIONALITY_FACTOR;
1393   if (GNUNET_SYSERR !=
1394       GNUNET_CONFIGURATION_get_value_float (env->cfg,
1395                                             "ats",
1396                                             "PROP_PROPORTIONALITY_FACTOR",
1397                                             &f_tmp))
1398   {
1399     if (f_tmp < 1.0)
1400     {
1401       LOG (GNUNET_ERROR_TYPE_ERROR,
1402            _("Invalid %s configuration %f\n"),
1403            "PROP_PROPORTIONALITY_FACTOR",
1404            f_tmp);
1405     }
1406     else
1407     {
1408       s->prop_factor = f_tmp;
1409       LOG (GNUNET_ERROR_TYPE_INFO,
1410            "Using %s of %.3f\n",
1411            "PROP_PROPORTIONALITY_FACTOR",
1412            f_tmp);
1413     }
1414   }
1415
1416   s->network_entries = GNUNET_malloc (env->network_count *
1417                                       sizeof (struct Network));
1418   for (c = 0; c < env->network_count; c++)
1419   {
1420     cur = &s->network_entries[c];
1421     cur->type = c;
1422     cur->total_quota_in = env->in_quota[c];
1423     cur->total_quota_out = env->out_quota[c];
1424     cur->desc = GNUNET_ATS_print_network_type (c);
1425     GNUNET_asprintf (&cur->stat_total,
1426                      "# ATS addresses %s total",
1427                      cur->desc);
1428     GNUNET_asprintf (&cur->stat_active,
1429                      "# ATS active addresses %s total",
1430                      cur->desc);
1431     LOG (GNUNET_ERROR_TYPE_INFO,
1432          "Added network %u `%s' (%llu/%llu)\n",
1433          c,
1434          cur->desc,
1435          cur->total_quota_in,
1436          cur->total_quota_out);
1437   }
1438   return &sf;
1439 }
1440
1441
1442 /**
1443  * Function used to unload the plugin.
1444  *
1445  * @param cls return value from #libgnunet_plugin_ats_proportional_init()
1446  */
1447 void *
1448 libgnunet_plugin_ats_proportional_done (void *cls)
1449 {
1450   struct GNUNET_ATS_SolverFunctions *sf = cls;
1451   struct GAS_PROPORTIONAL_Handle *s = sf->cls;
1452   struct AddressWrapper *cur;
1453   struct AddressWrapper *next;
1454   unsigned int c;
1455
1456   for (c = 0; c < s->network_count; c++)
1457   {
1458     GNUNET_break (0 == s->network_entries[c].total_addresses);
1459     GNUNET_break (0 == s->network_entries[c].active_addresses);
1460     next = s->network_entries[c].head;
1461     while (NULL != (cur = next))
1462     {
1463       next = cur->next;
1464       GNUNET_CONTAINER_DLL_remove (s->network_entries[c].head,
1465                                    s->network_entries[c].tail,
1466                                    cur);
1467       GNUNET_free_non_null (cur->addr->solver_information);
1468       GNUNET_free(cur);
1469     }
1470     GNUNET_free (s->network_entries[c].stat_total);
1471     GNUNET_free (s->network_entries[c].stat_active);
1472   }
1473   GNUNET_break (0 == s->active_addresses);
1474   GNUNET_free (s->network_entries);
1475   GNUNET_free (s);
1476   return NULL;
1477 }
1478
1479
1480 /* end of plugin_ats_proportional.c */