75 |
|
static void read_conf(FILE *); |
76 |
|
static void clear_out_old_conf(void); |
77 |
|
static void expire_tklines(dlink_list *); |
78 |
< |
static void garbage_collect_ip_entries(void); |
79 |
< |
static int hash_ip(struct irc_ssaddr *); |
78 |
> |
static void ipcache_remove_expired_entries(void *); |
79 |
> |
static uint32_t hash_ip(const struct irc_ssaddr *); |
80 |
|
static int verify_access(struct Client *); |
81 |
|
static int attach_iline(struct Client *, struct MaskItem *); |
82 |
|
static struct ip_entry *find_or_add_ip(struct irc_ssaddr *); |
92 |
|
|
93 |
|
struct ip_entry |
94 |
|
{ |
95 |
+ |
dlink_node node; /**< Doubly linked list node. */ |
96 |
|
struct irc_ssaddr ip; |
97 |
< |
unsigned int count; /**< Number of registered users using this IP */ |
97 |
> |
unsigned int count; /**< Number of registered users using this IP */ |
98 |
|
unsigned int connection_count; /**< Number of connections from this IP in the last throttle_time duration */ |
99 |
< |
time_t last_attempt; /**< The last time someone connected from this IP */ |
99 |
< |
struct ip_entry *next; |
99 |
> |
time_t last_attempt; /**< The last time someone connected from this IP */ |
100 |
|
}; |
101 |
|
|
102 |
< |
static struct ip_entry *ip_hash_table[IP_HASH_SIZE]; |
102 |
> |
static dlink_list ip_hash_table[IP_HASH_SIZE]; |
103 |
|
static mp_pool_t *ip_entry_pool = NULL; |
104 |
– |
static int ip_entries_count = 0; |
104 |
|
|
105 |
|
|
106 |
|
/* conf_dns_callback() |
426 |
|
* - clear the ip hash table |
427 |
|
*/ |
428 |
|
void |
429 |
< |
init_ip_hash_table(void) |
429 |
> |
ipcache_init(void) |
430 |
|
{ |
431 |
+ |
static struct event event_expire_ipcache = |
432 |
+ |
{ |
433 |
+ |
.name = "ipcache_remove_expired_entries", |
434 |
+ |
.handler = ipcache_remove_expired_entries, |
435 |
+ |
.when = 123 |
436 |
+ |
}; |
437 |
+ |
|
438 |
+ |
event_add(&event_expire_ipcache, NULL); |
439 |
|
ip_entry_pool = mp_pool_new(sizeof(struct ip_entry), MP_CHUNK_SIZE_IP_ENTRY); |
433 |
– |
memset(ip_hash_table, 0, sizeof(ip_hash_table)); |
440 |
|
} |
441 |
|
|
442 |
|
/* find_or_add_ip() |
449 |
|
* count set to 0. |
450 |
|
*/ |
451 |
|
static struct ip_entry * |
452 |
< |
find_or_add_ip(struct irc_ssaddr *ip_in) |
452 |
> |
find_or_add_ip(struct irc_ssaddr *addr) |
453 |
|
{ |
454 |
< |
struct ip_entry *ptr, *newptr; |
455 |
< |
int hash_index = hash_ip(ip_in), res; |
456 |
< |
struct sockaddr_in *v4 = (struct sockaddr_in *)ip_in, *ptr_v4; |
454 |
> |
dlink_node *ptr = NULL; |
455 |
> |
struct ip_entry *iptr = NULL; |
456 |
> |
uint32_t hash_index = hash_ip(addr); |
457 |
> |
int res = 0; |
458 |
> |
struct sockaddr_in *v4 = (struct sockaddr_in *)addr, *ptr_v4; |
459 |
|
#ifdef IPV6 |
460 |
< |
struct sockaddr_in6 *v6 = (struct sockaddr_in6 *)ip_in, *ptr_v6; |
460 |
> |
struct sockaddr_in6 *v6 = (struct sockaddr_in6 *)addr, *ptr_v6; |
461 |
|
#endif |
462 |
|
|
463 |
< |
for (ptr = ip_hash_table[hash_index]; ptr; ptr = ptr->next) |
463 |
> |
DLINK_FOREACH(ptr, ip_hash_table[hash_index].head) |
464 |
|
{ |
465 |
+ |
iptr = ptr->data; |
466 |
|
#ifdef IPV6 |
467 |
< |
if (ptr->ip.ss.ss_family != ip_in->ss.ss_family) |
467 |
> |
if (iptr->ip.ss.ss_family != addr->ss.ss_family) |
468 |
|
continue; |
469 |
< |
if (ip_in->ss.ss_family == AF_INET6) |
469 |
> |
|
470 |
> |
if (addr->ss.ss_family == AF_INET6) |
471 |
|
{ |
472 |
< |
ptr_v6 = (struct sockaddr_in6 *)&ptr->ip; |
472 |
> |
ptr_v6 = (struct sockaddr_in6 *)&iptr->ip; |
473 |
|
res = memcmp(&v6->sin6_addr, &ptr_v6->sin6_addr, sizeof(struct in6_addr)); |
474 |
|
} |
475 |
|
else |
476 |
|
#endif |
477 |
|
{ |
478 |
< |
ptr_v4 = (struct sockaddr_in *)&ptr->ip; |
478 |
> |
ptr_v4 = (struct sockaddr_in *)&iptr->ip; |
479 |
|
res = memcmp(&v4->sin_addr, &ptr_v4->sin_addr, sizeof(struct in_addr)); |
480 |
|
} |
481 |
+ |
|
482 |
|
if (res == 0) |
483 |
< |
{ |
473 |
< |
/* Found entry already in hash, return it. */ |
474 |
< |
return ptr; |
475 |
< |
} |
483 |
> |
return iptr; /* Found entry already in hash, return it. */ |
484 |
|
} |
485 |
|
|
486 |
< |
if (ip_entries_count >= 2 * hard_fdlimit) |
487 |
< |
garbage_collect_ip_entries(); |
480 |
< |
|
481 |
< |
newptr = mp_pool_get(ip_entry_pool); |
482 |
< |
|
483 |
< |
ip_entries_count++; |
484 |
< |
memcpy(&newptr->ip, ip_in, sizeof(struct irc_ssaddr)); |
486 |
> |
iptr = mp_pool_get(ip_entry_pool); |
487 |
> |
memcpy(&iptr->ip, addr, sizeof(struct irc_ssaddr)); |
488 |
|
|
489 |
< |
newptr->next = ip_hash_table[hash_index]; |
487 |
< |
ip_hash_table[hash_index] = newptr; |
489 |
> |
dlinkAdd(iptr, &iptr->node, &atable[hash_index]); |
490 |
|
|
491 |
< |
return newptr; |
491 |
> |
return iptr; |
492 |
|
} |
493 |
|
|
494 |
|
/* remove_one_ip() |
501 |
|
* the struct ip_entry is returned to the ip_entry_heap |
502 |
|
*/ |
503 |
|
void |
504 |
< |
remove_one_ip(struct irc_ssaddr *ip_in) |
504 |
> |
remove_one_ip(struct irc_ssaddr *addr) |
505 |
|
{ |
506 |
< |
struct ip_entry *ptr; |
507 |
< |
struct ip_entry *last_ptr = NULL; |
508 |
< |
int hash_index = hash_ip(ip_in), res; |
509 |
< |
struct sockaddr_in *v4 = (struct sockaddr_in *)ip_in, *ptr_v4; |
506 |
> |
dlink_node *ptr = NULL; |
507 |
> |
uint32_t hash_index = hash_ip(addr); |
508 |
> |
int res = 0; |
509 |
> |
struct sockaddr_in *v4 = (struct sockaddr_in *)addr, *ptr_v4; |
510 |
|
#ifdef IPV6 |
511 |
< |
struct sockaddr_in6 *v6 = (struct sockaddr_in6 *)ip_in, *ptr_v6; |
511 |
> |
struct sockaddr_in6 *v6 = (struct sockaddr_in6 *)addr, *ptr_v6; |
512 |
|
#endif |
513 |
|
|
514 |
< |
for (ptr = ip_hash_table[hash_index]; ptr; ptr = ptr->next) |
514 |
> |
DLINK_FOREACH(ptr, ip_hash_table[hash_index].head) |
515 |
|
{ |
516 |
+ |
struct ip_entry *iptr = ptr->data; |
517 |
|
#ifdef IPV6 |
518 |
< |
if (ptr->ip.ss.ss_family != ip_in->ss.ss_family) |
518 |
> |
if (iptr->ip.ss.ss_family != addr->ss.ss_family) |
519 |
|
continue; |
520 |
< |
if (ip_in->ss.ss_family == AF_INET6) |
520 |
> |
if (addr->ss.ss_family == AF_INET6) |
521 |
|
{ |
522 |
< |
ptr_v6 = (struct sockaddr_in6 *)&ptr->ip; |
522 |
> |
ptr_v6 = (struct sockaddr_in6 *)&iptr->ip; |
523 |
|
res = memcmp(&v6->sin6_addr, &ptr_v6->sin6_addr, sizeof(struct in6_addr)); |
524 |
|
} |
525 |
|
else |
526 |
|
#endif |
527 |
|
{ |
528 |
< |
ptr_v4 = (struct sockaddr_in *)&ptr->ip; |
528 |
> |
ptr_v4 = (struct sockaddr_in *)&iptr->ip; |
529 |
|
res = memcmp(&v4->sin_addr, &ptr_v4->sin_addr, sizeof(struct in_addr)); |
530 |
|
} |
531 |
+ |
|
532 |
|
if (res) |
533 |
|
continue; |
534 |
< |
if (ptr->count > 0) |
531 |
< |
ptr->count--; |
532 |
< |
if (ptr->count == 0 && |
533 |
< |
(CurrentTime-ptr->last_attempt) >= ConfigFileEntry.throttle_time) |
534 |
< |
{ |
535 |
< |
if (last_ptr != NULL) |
536 |
< |
last_ptr->next = ptr->next; |
537 |
< |
else |
538 |
< |
ip_hash_table[hash_index] = ptr->next; |
534 |
> |
assert(iptr->count > 0); |
535 |
|
|
536 |
< |
mp_pool_release(ptr); |
537 |
< |
ip_entries_count--; |
536 |
> |
if (--iptr->count == 0 && |
537 |
> |
(CurrentTime - iptr->last_attempt) >= ConfigFileEntry.throttle_time) |
538 |
> |
{ |
539 |
> |
dlinkDelete(&iptr->node, &ip_hash_table[hash_index]); |
540 |
> |
mp_pool_release(iptr); |
541 |
|
return; |
542 |
|
} |
544 |
– |
last_ptr = ptr; |
543 |
|
} |
544 |
|
} |
545 |
|
|
549 |
|
* output - integer value used as index into hash table |
550 |
|
* side effects - hopefully, none |
551 |
|
*/ |
552 |
< |
static int |
553 |
< |
hash_ip(struct irc_ssaddr *addr) |
552 |
> |
static uint32_t |
553 |
> |
hash_ip(const struct irc_ssaddr *addr) |
554 |
|
{ |
555 |
|
if (addr->ss.ss_family == AF_INET) |
556 |
|
{ |
557 |
< |
struct sockaddr_in *v4 = (struct sockaddr_in *)addr; |
558 |
< |
int hash; |
561 |
< |
uint32_t ip; |
557 |
> |
const struct sockaddr_in *v4 = (const struct sockaddr_in *)addr; |
558 |
> |
uint32_t hash = 0, ip = ntohl(v4->sin_addr.s_addr); |
559 |
|
|
560 |
< |
ip = ntohl(v4->sin_addr.s_addr); |
564 |
< |
hash = ((ip >> 12) + ip) & (IP_HASH_SIZE-1); |
560 |
> |
hash = ((ip >> 12) + ip) & (IP_HASH_SIZE - 1); |
561 |
|
return hash; |
562 |
|
} |
563 |
|
#ifdef IPV6 |
564 |
|
else |
565 |
|
{ |
566 |
< |
int hash; |
567 |
< |
struct sockaddr_in6 *v6 = (struct sockaddr_in6 *)addr; |
572 |
< |
uint32_t *ip = (uint32_t *)&v6->sin6_addr.s6_addr; |
566 |
> |
const struct sockaddr_in6 *v6 = (const struct sockaddr_in6 *)addr; |
567 |
> |
uint32_t hash = 0, *ip = (uint32_t *)&v6->sin6_addr.s6_addr; |
568 |
|
|
569 |
|
hash = ip[0] ^ ip[3]; |
570 |
|
hash ^= hash >> 16; |
590 |
|
void |
591 |
|
count_ip_hash(unsigned int *number_ips_stored, uint64_t *mem_ips_stored) |
592 |
|
{ |
598 |
– |
struct ip_entry *ptr; |
599 |
– |
|
593 |
|
*number_ips_stored = 0; |
594 |
|
*mem_ips_stored = 0; |
595 |
|
|
596 |
|
for (unsigned int i = 0; i < IP_HASH_SIZE; ++i) |
597 |
|
{ |
598 |
< |
for (ptr = ip_hash_table[i]; ptr; ptr = ptr->next) |
599 |
< |
{ |
607 |
< |
*number_ips_stored += 1; |
608 |
< |
*mem_ips_stored += sizeof(struct ip_entry); |
609 |
< |
} |
598 |
> |
*number_ips_stored += dlink_list_length(&ip_hash_table[i]); |
599 |
> |
*mem_ips_stored += dlink_list_length(&ip_hash_table[i]) * sizeof(struct ip_entry); |
600 |
|
} |
601 |
|
} |
602 |
|
|
607 |
|
* side effects - free up all ip entries with no connections |
608 |
|
*/ |
609 |
|
static void |
610 |
< |
garbage_collect_ip_entries(void) |
610 |
> |
ipcache_remove_expired_entries(void *unused) |
611 |
|
{ |
612 |
< |
struct ip_entry *ptr; |
623 |
< |
struct ip_entry *last_ptr; |
624 |
< |
struct ip_entry *next_ptr; |
612 |
> |
dlink_node *ptr = NULL; |
613 |
|
|
614 |
|
for (unsigned int i = 0; i < IP_HASH_SIZE; ++i) |
615 |
|
{ |
616 |
< |
last_ptr = NULL; |
629 |
< |
|
630 |
< |
for (ptr = ip_hash_table[i]; ptr; ptr = next_ptr) |
616 |
> |
DLINK_FOREACH(ptr, ip_hash_table[i].head) |
617 |
|
{ |
618 |
< |
next_ptr = ptr->next; |
618 |
> |
struct ip_entry *iptr = ptr->data; |
619 |
|
|
620 |
< |
if (ptr->count == 0 && |
621 |
< |
(CurrentTime - ptr->last_attempt) >= ConfigFileEntry.throttle_time) |
620 |
> |
if (iptr->count == 0 && |
621 |
> |
(CurrentTime - iptr->last_attempt) >= ConfigFileEntry.throttle_time) |
622 |
|
{ |
623 |
< |
if (last_ptr != NULL) |
624 |
< |
last_ptr->next = ptr->next; |
639 |
< |
else |
640 |
< |
ip_hash_table[i] = ptr->next; |
641 |
< |
mp_pool_release(ptr); |
642 |
< |
ip_entries_count--; |
623 |
> |
dlinkDelete(&iptr->node, &ip_hash_table[i]); |
624 |
> |
mp_pool_release(iptr); |
625 |
|
} |
644 |
– |
else |
645 |
– |
last_ptr = ptr; |
626 |
|
} |
627 |
|
} |
628 |
|
} |