chrony/clientlog.c
2006-01-19 21:37:13 +00:00

392 lines
9.7 KiB
C

/*
$Header: /cvs/src/chrony/clientlog.c,v 1.11 2003/09/22 21:22:30 richard Exp $
=======================================================================
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*
**********************************************************************
=======================================================================
This module keeps a count of the number of successful accesses by
clients, and the times of the last accesses.
This can be used for status reporting, and (in the case of a
server), if it needs to know which clients have made use of its data
recently.
*/
#include "sysincl.h"
#include "clientlog.h"
#include "conf.h"
#include "memory.h"
#include "reports.h"
#include "util.h"
/* Number of bits of address per layer of the table. This value has
been chosen on the basis that a server will predominantly be serving
a lot of hosts in a few subnets, rather than a few hosts scattered
across many subnets. */
#define NBITS 8
/* Number of entries in each subtable */
#define TABLE_SIZE (1UL<<NBITS)
typedef struct _Node {
unsigned long ip_addr;
unsigned long client_hits;
unsigned long peer_hits;
unsigned long cmd_hits_bad;
unsigned long cmd_hits_normal;
unsigned long cmd_hits_auth;
time_t last_ntp_hit;
time_t last_cmd_hit;
} Node;
typedef struct _Subnet {
void *entry[TABLE_SIZE];
} Subnet;
/* ================================================== */
/* Table for the class A subnet */
static Subnet top_subnet;
/* Table containing pointers directly to all nodes that have been
allocated. */
static Node **nodes = NULL;
/* Number of nodes actually in the table. */
static int n_nodes = 0;
/* Number of entries for which the table has been sized. */
static int max_nodes = 0;
#define NODE_TABLE_INCREMENT 4
/* Flag indicating whether facility is turned on or not */
static int active = 0;
/* ================================================== */
static void
clear_subnet(Subnet *subnet)
{
int i;
for (i=0; i<TABLE_SIZE; i++) {
subnet->entry[i] = NULL;
}
}
/* ================================================== */
static void
clear_node(Node *node)
{
node->client_hits = 0;
node->peer_hits = 0;
node->cmd_hits_auth = 0;
node->cmd_hits_normal = 0;
node->cmd_hits_bad = 0;
node->last_ntp_hit = (time_t) 0;
node->last_cmd_hit = (time_t) 0;
}
/* ================================================== */
void
CLG_Initialise(void)
{
clear_subnet(&top_subnet);
if (CNF_GetNoClientLog()) {
active = 0;
} else {
active = 1;
}
nodes = NULL;
max_nodes = 0;
n_nodes = 0;
}
/* ================================================== */
void
CLG_Finalise(void)
{
return;
}
/* ================================================== */
static void
create_subnet(Subnet *parent_subnet, int the_entry)
{
parent_subnet->entry[the_entry] = (void *) MallocNew(Subnet);
clear_subnet((Subnet *) parent_subnet->entry[the_entry]);
}
/* ================================================== */
static void
create_node(Subnet *parent_subnet, int the_entry)
{
Node *new_node;
new_node = MallocNew(Node);
parent_subnet->entry[the_entry] = (void *) new_node;
clear_node(new_node);
if (n_nodes == max_nodes) {
if (nodes) {
max_nodes += NODE_TABLE_INCREMENT;
nodes = ReallocArray(Node *, max_nodes, nodes);
} else {
if (max_nodes != 0) {
CROAK("max_nodes should be 0");
}
max_nodes = NODE_TABLE_INCREMENT;
nodes = MallocArray(Node *, max_nodes);
}
}
nodes[n_nodes++] = (Node *) new_node;
}
/* ================================================== */
/* Recursively seek out the Node entry for a particular address,
expanding subnet tables and node entries as we go if necessary. */
static void *
find_subnet(Subnet *subnet, CLG_IP_Addr addr, int bits_left)
{
unsigned long this_subnet, new_subnet, mask, shift;
unsigned long new_bits_left;
shift = 32 - NBITS;
mask = (1UL<<shift) - 1;
this_subnet = addr >> shift;
new_subnet = (addr & mask) << NBITS;
new_bits_left = bits_left - NBITS;
#if 0
fprintf(stderr, "fs addr=%08lx bl=%d ma=%08lx this=%08lx newsn=%08lx nbl=%d\n",
addr, bits_left, mask, this_subnet, new_subnet, new_bits_left);
#endif
if (new_bits_left > 0) {
if (!subnet->entry[this_subnet]) {
create_subnet(subnet, this_subnet);
}
return find_subnet((Subnet *) subnet->entry[this_subnet], new_subnet, new_bits_left);
} else {
if (!subnet->entry[this_subnet]) {
create_node(subnet, this_subnet);
}
return subnet->entry[this_subnet];
}
}
/* ================================================== */
/* Search for the record for a particular subnet, but return NULL if
one of the parents does not exist - never open a node out */
static void *
find_subnet_dont_open(Subnet *subnet, CLG_IP_Addr addr, int bits_left)
{
unsigned long this_subnet, new_subnet, mask, shift;
unsigned long new_bits_left;
if (bits_left == 0) {
return subnet;
} else {
shift = 32 - NBITS;
mask = (1UL<<shift) - 1;
this_subnet = addr >> shift;
new_subnet = (addr & mask) << NBITS;
new_bits_left = bits_left - NBITS;
#if 0
fprintf(stderr, "fsdo addr=%08lx bl=%d this=%08lx newsn=%08lx nbl=%d\n",
addr, bits_left, this_subnet, new_subnet, new_bits_left);
#endif
if (!subnet->entry[this_subnet]) {
return NULL;
} else {
return find_subnet_dont_open((Subnet *) subnet->entry[this_subnet], new_subnet, new_bits_left);
}
}
}
/* ================================================== */
void
CLG_LogNTPClientAccess (CLG_IP_Addr client, time_t now)
{
Node *node;
if (active) {
node = (Node *) find_subnet(&top_subnet, client, 32);
node->ip_addr = client;
++node->client_hits;
node->last_ntp_hit = now;
}
}
/* ================================================== */
void
CLG_LogNTPPeerAccess(CLG_IP_Addr client, time_t now)
{
Node *node;
if (active) {
node = (Node *) find_subnet(&top_subnet, client, 32);
node->ip_addr = client;
++node->peer_hits;
node->last_ntp_hit = now;
}
}
/* ================================================== */
void
CLG_LogCommandAccess(CLG_IP_Addr client, CLG_Command_Type type, time_t now)
{
Node *node;
if (active) {
node = (Node *) find_subnet(&top_subnet, client, 32);
node->ip_addr = client;
node->last_cmd_hit = now;
switch (type) {
case CLG_CMD_AUTH:
++node->cmd_hits_auth;
break;
case CLG_CMD_NORMAL:
++node->cmd_hits_normal;
break;
case CLG_CMD_BAD_PKT:
++node->cmd_hits_bad;
break;
default:
CROAK("Impossible");
break;
}
}
}
/* ================================================== */
CLG_Status
CLG_GetSubnetBitmap(CLG_IP_Addr subnet, int bits, CLG_Bitmap result)
{
Subnet *s;
unsigned long i;
unsigned long word, bit, mask;
if ((bits == 0) || (bits == 8) || (bits == 16) || (bits == 24)) {
memset (result, 0, TABLE_SIZE/8);
if (active) {
s = find_subnet_dont_open(&top_subnet, subnet, bits);
if (s) {
for (i=0; i<256; i++) {
if (s->entry[i]) {
word = i / 32;
bit = i % 32;
mask = 1UL << bit;
result[word] |= mask;
}
}
return CLG_SUCCESS;
} else {
return CLG_EMPTYSUBNET;
}
} else {
return CLG_INACTIVE;
}
} else {
return CLG_BADSUBNET;
}
}
/* ================================================== */
CLG_Status
CLG_GetClientAccessReportByIP(unsigned long ip, RPT_ClientAccess_Report *report, time_t now)
{
Node *node;
if (!active) {
return CLG_INACTIVE;
} else {
node = (Node *) find_subnet_dont_open(&top_subnet, ip, 32);
if (!node) {
return CLG_EMPTYSUBNET;
} else {
report->client_hits = node->client_hits;
report->peer_hits = node->peer_hits;
report->cmd_hits_auth = node->cmd_hits_auth;
report->cmd_hits_normal = node->cmd_hits_normal;
report->cmd_hits_bad = node->cmd_hits_bad;
report->last_ntp_hit_ago = now - node->last_ntp_hit;
report->last_cmd_hit_ago = now - node->last_cmd_hit;
return CLG_SUCCESS;
}
}
}
/* ================================================== */
CLG_Status
CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report,
time_t now, unsigned long *n_indices)
{
Node *node;
*n_indices = n_nodes;
if (!active) {
return CLG_INACTIVE;
} else {
if ((index < 0) || (index >= n_nodes)) {
return CLG_INDEXTOOLARGE;
}
node = nodes[index];
report->ip_addr = node->ip_addr;
report->client_hits = node->client_hits;
report->peer_hits = node->peer_hits;
report->cmd_hits_auth = node->cmd_hits_auth;
report->cmd_hits_normal = node->cmd_hits_normal;
report->cmd_hits_bad = node->cmd_hits_bad;
report->last_ntp_hit_ago = now - node->last_ntp_hit;
report->last_cmd_hit_ago = now - node->last_cmd_hit;
return CLG_SUCCESS;
}
}