analyse: critical bug in the counting code

The problem is, that you simply count all lease occurrences in
dhcpd.leases, but only the last ones for each ip address are
valid.  The lease file is more like a logfile of what has been
done, than a real database.  To fix the counting issue, I'm using
a single hash (from uthash.h [1]) for the counting.  This way
only the last lease entry for each IP gets into my counting
structure.

When you remove the duplicates in prepare_data(), you don't have
the information anymore, if the active lease entry or the free
lease entry came last.  Simply deleting each ip from the touches
array, that is already in the leases array, gives you a big
chance to count wrong.  Another way of fixing this would be to
not only store the ips in your arrays, but a structure containing
the ip and a global lease entry counter.  Then you could delete
all entries except for the latest.

[1] http://uthash.sourceforge.net/

Reported-by: Huangy
Signed-off-by: Enno Grper <groepeen@cms.hu-berlin.de>
Signed-off-by: Sami Kerola <kerolasa@iki.fi>
This commit is contained in:
Enno Grper 2012-05-02 14:01:07 +02:00 committed by Sami Kerola
parent 3ef5d6c07f
commit ae7747db87
10 changed files with 166 additions and 150 deletions

View file

@ -42,45 +42,15 @@
#include "dhcpd-pools.h"
/* Clean up data */
int ip_sort(struct leases_t *a, struct leases_t *b)
{
return (a->ip - b->ip);
}
int prepare_data(void)
{
unsigned long int i, j, k;
/* Sort leases */
qsort(leases, (size_t) num_leases, sizeof(uint32_t), &intcomp);
/* Get rid of lease dublicates */
for (k = j = i = 0; i < num_leases; i++) {
if (j != leases[i]) {
leases[k] = leases[i];
j = leases[i];
k++;
}
}
num_leases = k;
/* Delete touched IPs that are in use. */
j = 0;
for (i = 0; i < num_touches; i++) {
if (bsearch
(&touches[i], leases, (size_t) num_leases,
sizeof(uint32_t), &intcomp) == NULL) {
touches[j] = touches[i];
j++;
}
}
num_touches = j;
qsort(touches, (size_t) num_touches, sizeof(uint32_t), &intcomp);
/* Sort ranges */
qsort(ranges, (size_t) num_ranges, sizeof(struct range_t), &rangecomp);
/* Sort backups */
if (0 < num_backups) {
qsort(backups, (size_t) num_backups, sizeof(uint32_t),
&intcomp);
}
HASH_SORT(leases, ip_sort);
return 0;
}
@ -88,47 +58,43 @@ int prepare_data(void)
int do_counting(void)
{
struct range_t *range_p;
unsigned long i, j, k, l, block_size;
struct leases_t *l;
unsigned long i, k, block_size;
range_p = ranges;
/* Walk through ranges */
for (i = j = k = l = 0; i < num_ranges; i++) {
for (i = 0; i < num_ranges; i++) {
/* Count IPs in use */
for (; leases[j] < range_p->last_ip && j < num_leases; j++) {
if (leases[j] < range_p->first_ip) {
for (l = leases; l != NULL && range_p->last_ip >= l->ip; l = l->hh.next) {
if (l->ip < range_p->first_ip) {
/* should not be necessary */
continue;
}
/* IP with in range */
range_p->count++;
if (range_p->shared_net) {
range_p->shared_net->used++;
}
}
/* Count touched IPs */
for (; touches[k] < range_p->last_ip && k < num_touches; k++) {
if (touches[k] < range_p->first_ip) {
continue;
}
/* IP with in range */
range_p->touched++;
if (range_p->shared_net) {
range_p->shared_net->touched++;
}
}
/* Count backup IPs */
if (0 < num_backups) {
for (; backups[l] < range_p->last_ip
&& l < num_touches; l++) {
if (touches[l] < range_p->first_ip) {
continue;
}
/* IP with in range */
/* IP in range */
switch (l->type) {
case ACTIVE:
range_p->count++;
break;
case FREE:
range_p->touched++;
break;
case BACKUP:
range_p->backups++;
if (range_p->shared_net) {
break;
}
if (range_p->shared_net) {
switch (l->type) {
case ACTIVE:
range_p->shared_net->used++;
break;
case FREE:
range_p->shared_net->touched++;
break;
case BACKUP:
range_p->shared_net->backups++;
break;
}
}
}
@ -140,15 +106,6 @@ int do_counting(void)
range_p->shared_net->available += block_size;
}
/* Go backwards one step so that not even a one IP will be
* missed. This is possibly always unnecessary. */
if (j) {
j--;
}
if (k) {
k--;
}
range_p++;
}