/* $Header: /cvs/src/chrony/clientlog.c,v 1.10 2003/01/20 22:24:20 richard Exp $ ======================================================================= chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2002 * * 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<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; 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; 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; } }