/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <locale.h>
#include <langinfo.h>
#include <search.h>
#include <tsol/label.h>
#include <errno.h>
#include <sys/tsol/tndb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <signal.h>
#include <sys/signal.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <syslog.h>
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
#include <door.h>
#include <synch.h>
#include <sys/tsol/tsyscall.h>
#include <nss_dbdefs.h>
#include <libtsnet.h>
#include <zone.h>
#include "tnd.h"
static FILE *tnlog_open(char *);
static void usage();
static void parse_opts(int, char **);
static int check_debugl(int);
static void load_tp();
static void load_tp_entry();
static void tnd_serve();
static void detachfromtty();
static void terminate();
static void noop();
static char *gettime();
static int isnumber(char *);
static void poll_now();
static int nss_get_tp();
static int nss_get_rh();
static void timer();
static void load_rh_marked();
static int rhtable_search_and_update(struct tsol_rhent *ent, int duplflag);
static int is_better_match(in_addr_t newaddr, int indx, tnrh_tlb_t *tlbt);
static int walk_cache_table(in_addr_t newaddr, char *name,
int indx, tnd_tnrhdb_t *src);
static tnrh_tlb_t *lookup_cache_table(in_addr_t addr);
static int update_cache_table(tsol_rhent_t *ent, tnd_tnrhdb_t *src);
static void update_rh_entry(int op, struct tsol_rhent *rhentp);
static int handle_unvisited_nodes();
static in_addr_t rh_index_to_mask(uint_t masklen);
static tnrh_tlb_ipv6_t *lookup_cache_table_v6(in6_addr_t addr);
static in6_addr_t *rh_index_to_mask_v6(uint_t masklen, in6_addr_t *bitmask);
static void load_rh_marked_v6();
static int
rhtable_search_and_update_v6(struct tsol_rhent *ent, int duplflag);
static int walk_cache_table_v6(in6_addr_t newaddr, char *name,
int indx, tnd_tnrhdb_t *src);
static int update_cache_table_v6(tsol_rhent_t *ent, tnd_tnrhdb_t *src);
static int handle_unvisited_nodes_v6();
#ifdef DEBUG
static void print_entry(tsol_rhent_t *ent, int af);
static void print_tlbt(tnrh_tlb_t *tlbt);
static void rhtable_print();
static void cachetable_print();
static void rhtable_walk(void (*action)());
static void cachetable_print_v6();
static void rhtable_print_v6();
static void rhtable_walk_v6(void (*action)());
#endif /* DEBUG */
/*
* The following constants and structures and the functions
* that operate on them are similar to the ip_ire.c and ip6_ire.c
* code in the kernel.
*/
#define TNRH_TABLE_HASH_SIZE 256
#define IP_ABITS 32
#define IP_MASK_TABLE_SIZE (IP_ABITS + 1)
#define RH_HOST_MASK (in_addr_t)0xffffffffU
#define IPV6_ABITS 128
#define IPV6_MASK_TABLE_SIZE (IPV6_ABITS + 1)
#define s6_addr8 _S6_un._S6_u8
#define s6_addr32 _S6_un._S6_u32
/*
* Exclusive-or the 6 bytes that are likely to contain the MAC
* address. Assumes table_size does not exceed 256.
* Assumes EUI-64 format for good hashing.
*/
#define TNRH_ADDR_HASH_V6(addr) \
(((addr).s6_addr8[8] ^ (addr).s6_addr8[9] ^ \
(addr).s6_addr8[10] ^ (addr).s6_addr8[13] ^ \
(addr).s6_addr8[14] ^ (addr).s6_addr8[15]) % TNRH_TABLE_HASH_SIZE)
#define TNRH_ADDR_MASK_HASH_V6(addr, mask) \
((((addr).s6_addr8[8] & (mask).s6_addr8[8]) ^ \
((addr).s6_addr8[9] & (mask).s6_addr8[9]) ^ \
((addr).s6_addr8[10] & (mask).s6_addr8[10]) ^ \
((addr).s6_addr8[13] & (mask).s6_addr8[13]) ^ \
((addr).s6_addr8[14] & (mask).s6_addr8[14]) ^ \
((addr).s6_addr8[15] & (mask).s6_addr8[15])) % TNRH_TABLE_HASH_SIZE)
/* Mask comparison: is IPv6 addr a, and'ed with mask m, equal to addr b? */
#define V6_MASK_EQ(a, m, b) \
((((a).s6_addr32[0] & (m).s6_addr32[0]) == (b).s6_addr32[0]) && \
(((a).s6_addr32[1] & (m).s6_addr32[1]) == (b).s6_addr32[1]) && \
(((a).s6_addr32[2] & (m).s6_addr32[2]) == (b).s6_addr32[2]) && \
(((a).s6_addr32[3] & (m).s6_addr32[3]) == (b).s6_addr32[3]))
const in6_addr_t ipv6_all_zeros = { 0, 0, 0, 0 };
/*
* This is a table of hash tables to keep
* all the name service entries. We don't have
* a separate hash bucket structure, instead mantain
* a pointer to the hash chain.
*/
tnd_tnrhdb_t **tnrh_entire_table[IP_MASK_TABLE_SIZE];
tnd_tnrhdb_t **tnrh_entire_table_v6[IPV6_MASK_TABLE_SIZE];
/* reader/writer lock for tnrh_entire_table */
rwlock_t entire_rwlp;
rwlock_t entire_rwlp_v6;
/*
* This is a hash table which keeps fully resolved
* tnrhdb entries <IP address, Host type>. We don't have
* a separate hash bucket structure, instead
* mantain a pointer to the hash chain.
*/
tnrh_tlb_t *tnrh_cache_table[TNRH_TABLE_HASH_SIZE];
tnrh_tlb_ipv6_t *tnrh_cache_table_v6[TNRH_TABLE_HASH_SIZE];
/* reader/writer lock for tnrh_cache_table */
rwlock_t cache_rwlp;
rwlock_t cache_rwlp_v6;
FILE *logf;
int debugl = 0;
int poll_interval = TND_DEF_POLL_TIME;
int delay_poll_flag = 0;
void *tp_tree;
#define _SZ_TIME_BUF 100
char time_buf[_SZ_TIME_BUF];
#define cprint(s, param) { \
register FILE *consl; \
\
if ((consl = fopen("/dev/msglog", "w")) != NULL) { \
setbuf(consl, NULL); \
(void) fprintf(consl, "tnd: "); \
(void) fprintf(consl, s, param); \
(void) fclose(consl); \
} \
}
#define RHENT_BUF_SIZE 300
#define TPENT_BUF_SIZE 2000
/* 128 privs * (24 bytes + 1 deliminator)= 3200 bytes + 1200 cushion */
#define STRING_PRIVS_SIZE 4800
#define ID_ENT_SIZE 500
int
main(int argc, char **argv)
{
const ucred_t *uc = NULL;
const priv_set_t *pset;
struct sigaction act;
/* set the locale for only the messages system (all else is clean) */
(void) setlocale(LC_ALL, "");
#ifndef TEXT_DOMAIN /* Should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
#endif
(void) textdomain(TEXT_DOMAIN);
if (getzoneid() != GLOBAL_ZONEID) {
syslog(LOG_ERR, "can not run tnd from a local zone");
exit(-1);
}
if (((uc = ucred_get(getpid())) == NULL) ||
((pset = ucred_getprivset(uc, PRIV_EFFECTIVE)) == NULL)) {
syslog(LOG_ERR, "don't have privilege set");
exit(-1);
}
if (!priv_ismember(pset, PRIV_SYS_NET_CONFIG)) {
syslog(LOG_ERR, "don't have privilege to run tnd");
exit(-1);
}
/* parse command line options */
(void) parse_opts(argc, argv);
/*
* Initialize reader/writer locks. To be
* used within this process only.
*/
if ((rwlock_init(&entire_rwlp, USYNC_THREAD, 0) != 0) ||
(rwlock_init(&entire_rwlp_v6, USYNC_THREAD, 0) != 0) ||
(rwlock_init(&cache_rwlp, USYNC_THREAD, 0) != 0) ||
(rwlock_init(&cache_rwlp_v6, USYNC_THREAD, 0) != 0)) {
syslog(LOG_ERR, "cannot initialize lock");
exit(-1);
}
/* catch the usual termination signals for graceful exit */
(void) sigset(SIGINT, terminate);
(void) sigset(SIGTERM, terminate);
(void) sigset(SIGQUIT, terminate);
(void) sigset(SIGUSR1, noop);
act.sa_handler = timer;
act.sa_flags = SA_RESTART;
(void *) sigemptyset(&act.sa_mask);
(void *) sigaddset(&act.sa_mask, SIGALRM);
(void *) sigaddset(&act.sa_mask, SIGHUP);
(void *) sigaction(SIGALRM, &act, NULL);
(void *) sigaction(SIGHUP, &act, NULL);
if (debugl == MAX_TND_DEBUG) {
(void) fprintf(logf, "%s : ", gettime());
(void) fprintf(logf, gettext("tnd started. pid= %d\n"),
getpid());
(void) fprintf(logf, "%s : ", gettime());
(void) fprintf(logf,
gettext("max level debugging! not forking\n"));
(void) fflush(logf);
} else {
detachfromtty();
}
if (!delay_poll_flag) {
(void) sigprocmask(SIG_BLOCK, &act.sa_mask, NULL);
timer();
(void) sigprocmask(SIG_UNBLOCK, &act.sa_mask, NULL);
}
if (debugl != MAX_TND_DEBUG) {
(void) sigsend(P_PID, getppid(), SIGUSR1);
}
(void) tnd_serve();
/* NOT REACHED */
return (0);
}
/*
* Compare addresses after masking off unneeded bits.
* We do this to handle addresses where prefix_len is
* less than the bit length.
*/
static int
rhaddr_compar_mask(struct sockaddr_in *tp1, struct tnd_tnrhdb_c *tp2, int i)
{
struct sockaddr_in *saddrp;
in_addr_t tmpmask = rh_index_to_mask(i);
saddrp = (struct sockaddr_in *)(&tp2->rh_ent.rh_address.ip_addr_v4);
#ifdef DEBUG
(void) fprintf(logf, gettext("rhaddr_compar_mask mask = 0x%4x, \
tp1 = 0x%4x, tp2 = 0x%4x\n"), tmpmask, (tp1->sin_addr),
(saddrp->sin_addr.s_addr & tmpmask));
(void) fprintf(logf, gettext("rhaddr_compar_mask return = %d\n"),
(tp1->sin_addr.s_addr == (saddrp->sin_addr.s_addr & tmpmask)));
#endif
return (tp1->sin_addr.s_addr == (saddrp->sin_addr.s_addr & tmpmask));
}
/*
* we use this where exact match is needed.
*/
static int
rhaddr_compar(struct sockaddr_in *tp1, struct tnd_tnrhdb_c *tp2)
{
struct sockaddr_in *saddrp;
saddrp = (struct sockaddr_in *)(&tp2->rh_ent.rh_address.ip_addr_v4);
#ifdef DEBUG
(void) fprintf(logf, gettext("\t tp1 saddrp IP : %s %s\n"),
inet_ntoa(tp1->sin_addr), inet_ntoa(saddrp->sin_addr));
#endif
return (tp1->sin_addr.s_addr == saddrp->sin_addr.s_addr);
}
/*
* Compare v6 addresses after masking off unneeded bits.
* We do this to handle addresses where prefix_len is
* less than the bit length.
*/
static int
rhaddr_compar_mask_v6(struct sockaddr_in6 *tp1, struct tnd_tnrhdb_c *tp2, int i)
{
struct sockaddr_in6 *saddrp;
in6_addr_t tmpmask;
(void) rh_index_to_mask_v6(i, &tmpmask);
saddrp = (struct sockaddr_in6 *)(&tp2->rh_ent.rh_address.ip_addr_v6);
return (V6_MASK_EQ(tp1->sin6_addr, tmpmask, saddrp->sin6_addr));
}
/*
* we use this where v6 exact match is needed.
*/
static int
rhaddr_compar_v6(struct sockaddr_in6 *tp1, struct tnd_tnrhdb_c *tp2)
{
struct sockaddr_in6 *saddrp;
saddrp = (struct sockaddr_in6 *)(&tp2->rh_ent.rh_address.ip_addr_v6);
return (IN6_ARE_ADDR_EQUAL(&tp1->sin6_addr, &saddrp->sin6_addr));
}
static int
get_hashvalue(in_addr_t addr)
{
unsigned char *bp;
bp = (unsigned char *) &addr;
return ((bp[0] ^ bp[1] ^ bp[2] ^ bp[3]) % TNRH_TABLE_HASH_SIZE);
}
/*
* Convert length for a mask to the mask.
*/
static in_addr_t
rh_index_to_mask(uint_t masklen)
{
if (masklen == 0)
return (0);
return (htonl(RH_HOST_MASK << (IP_ABITS - masklen)));
}
/*
* Convert length for a mask to the mask.
* Returns the argument bitmask.
*/
static in6_addr_t *
rh_index_to_mask_v6(uint_t masklen, in6_addr_t *bitmask)
{
uint32_t *ptr;
*bitmask = ipv6_all_zeros;
ptr = (uint32_t *)bitmask;
while (masklen > 32) {
*ptr++ = 0xffffffffU;
masklen -= 32;
}
*ptr = htonl(0xffffffffU << (32 - masklen));
return (bitmask);
}
static void
parse_opts(argc, argv)
int argc;
char **argv;
{
char *logfile = TNDLOG;
extern char *optarg;
int c;
while ((c = getopt(argc, argv, "d:f:p:n")) != EOF)
switch (c) {
case 'd':
if (isnumber(optarg)) {
debugl = atoi(optarg);
if (check_debugl(debugl) == -1)
debugl = 1; /* default to 1 */
} else {
usage();
exit(1);
}
break;
case 'f':
logfile = optarg;
break;
case 'p':
if (isnumber(optarg)) {
poll_interval = atoi(optarg);
if (poll_interval == 0)
usage();
} else {
usage();
}
break;
case 'n':
delay_poll_flag = 1;
break;
case '?':
usage();
}
logf = tnlog_open(logfile);
}
static int
check_debugl(debug_level)
int debug_level;
{
if (debug_level > MAX_TND_DEBUG) {
if ((debugl > 0) && (logf != NULL)) {
(void) fprintf(logf, "%s : ", gettime());
(void) fprintf(logf,
gettext("invalid debug level: %d, not changed!\n"),
debug_level);
(void) fflush(logf);
}
cprint("invalid debug level: %d, not changed!\n",
debug_level);
return (-1);
}
return (0);
}
static FILE *
tnlog_open(logfile)
char *logfile;
{
FILE *fp;
if ((fp = fopen(logfile, "a")) == NULL) {
syslog(LOG_ERR, "unable to open logfile %s",
logfile);
exit(-1);
}
(void) fprintf(fp, "%s : ", gettime());
(void) fprintf(fp, gettext("tnd starting\n"));
return (fp);
}
static void
detachfromtty()
{
pid_t tnd_pid;
(void) close(0);
(void) close(1);
(void) close(2);
switch (tnd_pid = fork()) {
case (pid_t)-1:
if (debugl && (logf != NULL)) {
(void) fprintf(logf, "%s : ", gettime());
(void) fprintf(logf,
gettext("fork() failed: %s\n"), strerror(errno));
(void) fflush(logf);
}
cprint("fork() failed: %s\n", strerror(errno));
break;
case 0:
break;
default:
if (debugl && (logf != NULL)) {
(void) fprintf(logf, "%s : ", gettime());
(void) fprintf(logf,
gettext("tnd started. pid= %d\n"), tnd_pid);
(void) fflush(logf);
}
/*
* Suspend parent till child signals it. We catch the signal
* in order to return correct exit value.
*/
(void) pause();
exit(0);
}
(void) setsid();
(void) open("/dev/null", O_RDWR, 0);
(void) dup(0);
(void) dup(0);
}
static void
usage()
{
(void) fprintf(stderr, gettext(
"Usage:\n\ttnd [-d debug-level][-f debug-file]"
"[-p poll-interval]\n"));
exit(1);
}
static int
isnumber(s)
char *s;
{
register int c;
/* LINTED */
while (c = *s++)
if (!isdigit(c))
return (0);
return (1);
}
/*
* match any entry in any tree
* used in tree removal
*/
/* ARGSUSED */
static int
any_compar(const void *v1, const void *v2)
{
return (0);
}
static int
tp_compar(const void *v1, const void *v2)
{
struct tnd_tnrhtp_c *tp1 = (struct tnd_tnrhtp_c *)v1;
struct tnd_tnrhtp_c *tp2 = (struct tnd_tnrhtp_c *)v2;
return (strcmp(tp1->tp_ent.name, tp2->tp_ent.name));
}
/*
* Build tree of tp entries, tossing duplicates
*/
static int
nss_get_tp()
{
tsol_tpent_t tp; /* to store result */
tsol_tpent_t *tpp;
struct tnd_tnrhtp_c *new, **old;
int count = 0;
tpp = &tp;
tsol_settpent(1);
while ((tpp = (tsol_tpent_t *)tsol_gettpent()) != NULL) {
if ((new = (struct tnd_tnrhtp_c *)
calloc(1, sizeof (struct tnd_tnrhtp_c))) == NULL)
continue;
(void) memcpy(&new->tp_ent, tpp, sizeof (tp));
old = (struct tnd_tnrhtp_c **)tsearch(new, &tp_tree, tp_compar);
if (*old != new)
free(new);
else
count++;
}
tsol_endtpent();
return (count);
}
/* load tp ents into kernel */
static void
load_tp()
{
twalk(tp_tree, load_tp_entry);
}
static void
/* LINTED */
load_tp_entry(struct tnd_tnrhtp_c **tppp, VISIT visit, int level)
{
struct tnd_tnrhtp_c *tpp;
if (!(visit == postorder || visit == leaf))
return;
tpp = *tppp;
if (tnrhtp(TNDB_LOAD, &tpp->tp_ent)) {
if (debugl && (logf != NULL)) {
(void) fprintf(logf, "%s : ", gettime());
(void) fprintf(logf, gettext("tnrhtp() failed 0: %s\n"),
strerror(errno));
(void) fprintf(logf,
gettext("load of remote-host template "
"%s into kernel cache failed\n"),
tpp->tp_ent.name);
(void) fflush(logf);
}
cprint("tnrhtp() failed here 1: %s\n", strerror(errno));
}
}
static void
tp_flush_cache()
{
struct tnd_tnrhtp_c dummy;
struct tnd_tnrhtp_c *tp;
while (tp = tfind(&dummy, tp_tree, any_compar)) {
(void) tdelete(tp, &tp_tree, tp_compar);
free(tp);
}
}
/*
* Build/update the table of rh entries from the
* name service sources, files, ldap etc.
*/
static int
nss_get_rh()
{
int found_entry = 0;
int count = 0;
int newflag = 0;
struct tsol_rhent rh; /* to store result */
struct tsol_rhent *rhp;
tsol_tpent_t tp;
sa_family_t af;
int v6cnt = 0;
rhp = &rh;
tsol_setrhent(1);
while ((rhp = (struct tsol_rhent *)
tsol_getrhent()) != NULL) {
/*
* Check if this is a known template name
* Entries with missing template in kernel will be logged
* and not added to cache.
*/
(void) fprintf(logf, gettext("getrhent template name: %s\n"),
rhp->rh_template);
(void) strncpy(tp.name, rhp->rh_template, TNTNAMSIZ - 1);
if (tnrhtp(TNDB_GET, &tp) != 0) {
if (debugl && (logf != NULL))
(void) fprintf(logf,
gettext("Unknown template name: %s\n"),
rhp->rh_template);
cprint(gettext("Unknown template name: %s\n"),
rhp->rh_template);
continue;
}
found_entry++; /* found a valid tnrhdb entry */
af = rhp->rh_address.ta_family;
if (af == AF_INET) {
#ifdef DEBUG
(void) fprintf(logf, gettext("nss_get_rh() v4\n"));
#endif
(void) rw_wrlock(&entire_rwlp);
(void) rw_wrlock(&cache_rwlp);
/*
* Both cache table and entire table can be modified
* by this function. So, get both locks.
*/
newflag = rhtable_search_and_update(rhp, 1);
(void) rw_unlock(&cache_rwlp);
(void) rw_unlock(&entire_rwlp);
} else if (af == AF_INET6) {
#ifdef DEBUG
(void) fprintf(logf, gettext("nss_get_rh() v6\n"));
#endif
v6cnt++;
(void) rw_wrlock(&entire_rwlp_v6);
(void) rw_wrlock(&cache_rwlp_v6);
/*
* Both cache table and entire table can be modified
* by this function. So, get both locks.
*/
newflag = rhtable_search_and_update_v6(rhp, 1);
(void) rw_unlock(&cache_rwlp_v6);
(void) rw_unlock(&entire_rwlp_v6);
}
if (newflag)
count++;
}
tsol_endrhent();
/*
* If the first tsol_getrhent() failed, we bail out and
* try again at the next poll interval, just in case the
* name service was not reachable the first time.
*/
if (!found_entry) {
#ifdef DEBUG
if (logf != NULL)
(void) fprintf(logf,
gettext("Unable to contact ldap server?\n"));
#endif
return (count);
}
(void) rw_wrlock(&entire_rwlp);
(void) rw_wrlock(&cache_rwlp);
/*
* Handle deletions in the name service entries
* Both cache table and entire table can be modified
* by this function. So, get both locks.
*/
count += handle_unvisited_nodes();
(void) rw_unlock(&cache_rwlp);
(void) rw_unlock(&entire_rwlp);
if (v6cnt > 0) {
(void) rw_wrlock(&entire_rwlp_v6);
(void) rw_wrlock(&cache_rwlp_v6);
/*
* Handle deletions in the name service entries
* Both cache table and entire table can be modified
* by this function. So, get both locks.
*/
count += handle_unvisited_nodes_v6();
(void) rw_unlock(&cache_rwlp_v6);
(void) rw_unlock(&entire_rwlp_v6);
}
return (count);
}
/*
* Check if any deletions in the name service tables
* affect the cache entries. We need to do this
* in order to not flush the entrie kernel tnrhdb
* cache every time we poll the name services.
*/
static int
handle_unvisited_nodes()
{
int i, j, cnt = 0;
tnrh_tlb_t *tlbt;
tnd_tnrhdb_t *rhent, *prev;
for (i = 0; i < TNRH_TABLE_HASH_SIZE; i++)
if ((tlbt = tnrh_cache_table[i]) != NULL)
do {
if (tlbt->src->visited == 0) {
/*
* Mark for deletion of both our cache
* entry and the kernel cache entry.
*/
tlbt->reload = TNDB_DELETE;
cnt++;
}
tlbt = tlbt->next;
} while (tlbt != NULL);
/*
* Remove any unvisited nodes. This can
* happen if they are not in use by any cache entry. Then,
* mark all nodes in entire_table, un-visited, for next iteration.
*/
for (i = 0; i <= IP_ABITS; i++) {
if (tnrh_entire_table[i] == NULL)
continue;
for (j = 0; j < TNRH_TABLE_HASH_SIZE; j++) {
prev = rhent = tnrh_entire_table[i][j];
while (rhent != NULL) {
if (rhent->visited == 0) {
/*
* Check if start node
*/
if (rhent == tnrh_entire_table[i][j]) {
prev = tnrh_entire_table[i][j] =
rhent->rh_next;
} else {
/* bypass the deleted node */
prev->rh_next = rhent->rh_next;
prev = prev->rh_next;
}
free(rhent);
if (prev == NULL)
break;
else {
rhent = prev;
continue;
}
} else
rhent->visited = 0;
prev = rhent;
rhent = rhent->rh_next;
}
}
}
return (cnt);
}
/*
* Check if any deletions in the name service tables
* affect the cache entries. We need to do this
* in order to not flush the entrie kernel tnrhdb
* cache every time we poll the name services.
*/
static int
handle_unvisited_nodes_v6()
{
int i, j, cnt = 0;
tnrh_tlb_ipv6_t *tlbt;
tnd_tnrhdb_t *rhent, *prev;
for (i = 0; i < TNRH_TABLE_HASH_SIZE; i++)
if ((tlbt = tnrh_cache_table_v6[i]) != NULL)
do {
if (tlbt->src->visited == 0) {
/*
* Mark for deletion of both our cache entry
* and the kernel cache entry.
*/
tlbt->reload = TNDB_DELETE;
cnt++;
}
tlbt = tlbt->next;
} while (tlbt != NULL);
/*
* Remove any unvisited nodes. This can
* happen if they are not in use by any cache entry. Then,
* mark all nodes in entire_table, un-visited, for next iteration.
*/
for (i = 0; i <= IPV6_ABITS; i++) {
if (tnrh_entire_table_v6[i] == NULL)
continue;
for (j = 0; j < TNRH_TABLE_HASH_SIZE; j++) {
prev = rhent = tnrh_entire_table_v6[i][j];
while (rhent != NULL) {
if (rhent->visited == 0) { /* delete the node */
/* Check if start node */
if (rhent == tnrh_entire_table_v6[i][j]) {
prev = tnrh_entire_table_v6[i][j] =
rhent->rh_next;
} else {
/* bypass the deleted node */
prev->rh_next = rhent->rh_next;
prev = prev->rh_next;
}
free(rhent);
if (prev == NULL)
break;
else {
rhent = prev;
continue;
}
} else
rhent->visited = 0;
prev = rhent;
rhent = rhent->rh_next;
}
}
}
return (cnt);
}
/*
* Search the hash chain for the address. If not found,
* add the entry to the hash table. If necessary,
* construct the hash table.
* If the rh entry is in table, we may update its template name
*/
static int
rhtable_search_and_update(struct tsol_rhent *ent, int duplflag)
{
struct sockaddr_in *saddrp;
unsigned char hash;
tnd_tnrhdb_t *rhent;
int i;
int rflag = 1;
struct tnd_tnrhdb_c *new;
saddrp = (struct sockaddr_in *)&ent->rh_address.ip_addr_v4;
hash = (unsigned char) get_hashvalue(saddrp->sin_addr.s_addr);
i = ent->rh_prefix;
#ifdef DEBUG
(void) fprintf(logf, gettext("\trhtable_search_and_update IP address:\
%s\n"), inet_ntoa(saddrp->sin_addr));
#endif
if (tnrh_entire_table[i] == NULL) {
if ((tnrh_entire_table[i] = (tnd_tnrhdb_t **)calloc(
TNRH_TABLE_HASH_SIZE, sizeof (tnd_tnrhdb_t *))) == NULL) {
return (0);
}
}
rhent = tnrh_entire_table[i][hash];
#ifdef DEBUG
(void) fprintf(logf, gettext("\tsearch_and_update i = %d hash = %d\n"),
i, hash);
if (rhent != NULL) {
(void) fprintf(logf, gettext("\trhent visited = %d\n"),
rhent->visited);
print_entry(&rhent->rh_ent, AF_INET);
} else {
(void) fprintf(logf, gettext("\tsearch_and_update null\n"));
}
#endif
while (rhent != NULL) {
if (rhaddr_compar(saddrp, rhent) == 1) {
/* Check if this is a duplicate entry */
if ((rhent->visited == 1) && duplflag)
return (0);
if (duplflag)
rhent->visited = 1;
if (strcmp(ent->rh_template,
rhent->rh_ent.rh_template) != 0) {
/*
* Template is changed in the name service.
* Use the new template.
*/
(void) strcpy(rhent->rh_ent.rh_template,
ent->rh_template);
/*
* Check if this modified entry
* affects the cache table.
*/
rflag = update_cache_table(ent, rhent);
return (rflag);
} else
return (0);
}
rhent = rhent->rh_next;
}
/* Not found. Add the entry */
new = (struct tnd_tnrhdb_c *)calloc(1,
sizeof (struct tnd_tnrhdb_c));
if (new == NULL)
return (0);
(void) memcpy(&new->rh_ent, ent, sizeof (struct tsol_rhent));
if (duplflag)
new->visited = 1; /* Mark all new nodes visited */
/* linked list. Insert in the beginning */
new->rh_next = tnrh_entire_table[i][hash];
tnrh_entire_table[i][hash] = new;
#ifdef DEBUG
(void) fprintf(logf, gettext("rhtable added i = %d, hash = %d\n"),
i, hash);
#endif
/* Check if the new entry affects the cache table */
rflag = update_cache_table(ent, new);
#ifdef DEBUG
(void) fprintf(logf, gettext("search_and_update rflag=%d\n"), rflag);
#endif
return (rflag);
}
/*
* Search the hash chain for the address. If not found,
* add the entry to the hash table. If necessary,
* construct the hash table.
*/
static int
rhtable_search_and_update_v6(struct tsol_rhent *ent, int duplflag)
{
struct sockaddr_in6 *saddrp;
unsigned char hash;
tnd_tnrhdb_t *rhent;
int i;
int rflag = 1;
struct tnd_tnrhdb_c *new;
in6_addr_t tmpmask6;
saddrp = (struct sockaddr_in6 *)&ent->rh_address.ip_addr_v6;
i = ent->rh_prefix;
(void) rh_index_to_mask_v6(i, &tmpmask6);
hash = (unsigned char) TNRH_ADDR_MASK_HASH_V6(saddrp->sin6_addr,
tmpmask6);
if (tnrh_entire_table_v6[i] == NULL) {
if ((tnrh_entire_table_v6[i] = (tnd_tnrhdb_t **)calloc(
TNRH_TABLE_HASH_SIZE, sizeof (tnd_tnrhdb_t *))) == NULL) {
return (0);
}
}
rhent = tnrh_entire_table_v6[i][hash];
while (rhent != NULL) {
if (rhaddr_compar_v6(saddrp, rhent) == 1) {
/* Check if this is a duplicate entry */
if ((rhent->visited == 1) && duplflag)
return (0);
if (duplflag)
rhent->visited = 1;
if (strcmp(ent->rh_template,
rhent->rh_ent.rh_template) != 0) {
/*
* Template is changed in the name service.
* Use the new template.
*/
(void) strcpy(rhent->rh_ent.rh_template,
ent->rh_template);
/*
* Check if this modified entry
* affects the cache table.
*/
rflag = update_cache_table_v6(ent, rhent);
return (rflag);
} else
return (0);
}
rhent = rhent->rh_next;
}
/* Not found. Add the entry */
new = (struct tnd_tnrhdb_c *)calloc(1, sizeof (struct tnd_tnrhdb_c));
if (new == NULL)
return (0);
(void) memcpy(&new->rh_ent, ent, sizeof (struct tsol_rhent));
if (duplflag)
new->visited = 1; /* Mark all new nodes visited */
/* linked list. Insert in the beginning */
new->rh_next = tnrh_entire_table_v6[i][hash];
tnrh_entire_table_v6[i][hash] = new;
/* Check if the new entry affects the cache table */
rflag = update_cache_table_v6(ent, new);
return (rflag);
}
/*
* The array element i points to the hash table.
* Search the hash chain for the address.
*/
static struct tnd_tnrhdb_c *
rhtable_lookup(struct sockaddr_in *saddrp, int i)
{
unsigned char hash;
tnd_tnrhdb_t *rhent;
if (tnrh_entire_table[i] == NULL)
return (NULL);
hash = (unsigned char) get_hashvalue(saddrp->sin_addr.s_addr);
rhent = tnrh_entire_table[i][hash];
#ifdef DEBUG
(void) fprintf(logf, gettext("rhtable_lookup i = %d, hash = %d\n"),
i, hash);
#endif
while (rhent != NULL) {
#ifdef DEBUG
struct sockaddr_in *saddrp2;
saddrp2 = (struct sockaddr_in *)(&rhent->rh_ent.rh_address.ip_addr_v4);
(void) fprintf(logf, gettext("rhtable_lookup addr = %s, tmpl = %s\n"),
inet_ntoa(saddrp2->sin_addr), rhent->rh_ent.rh_template);
#endif
if (rhaddr_compar_mask(saddrp, rhent, i) == 1)
return (rhent);
rhent = rhent->rh_next;
}
#ifdef DEBUG
(void) fprintf(logf, gettext("\trhtable_lookup failed\n"));
#endif
/* Not found */
return (NULL);
}
/*
* The array element i points to the hash table.
* Search the hash chain for the address.
*/
static struct tnd_tnrhdb_c *
rhtable_lookup_v6(struct sockaddr_in6 *saddrp, in6_addr_t mask, int i)
{
unsigned char hash;
tnd_tnrhdb_t *rhent;
if (tnrh_entire_table_v6[i] == NULL)
return (NULL);
hash = (unsigned char) TNRH_ADDR_MASK_HASH_V6(saddrp->sin6_addr, mask);
rhent = tnrh_entire_table_v6[i][hash];
while (rhent != NULL) {
if (rhaddr_compar_mask_v6(saddrp, rhent, i) == 1)
return (rhent);
rhent = rhent->rh_next;
}
/* Not found */
return (NULL);
}
void
add_cache_entry(in_addr_t addr, char *name, int indx,
tnd_tnrhdb_t *src)
{
unsigned char hash;
tnrh_tlb_t *tlbt;
hash = (unsigned char) get_hashvalue(addr);
/* Look if some other thread already added this entry */
if (lookup_cache_table(addr) != NULL)
return;
#ifdef DEBUG
(void) fprintf(logf, gettext("\tenter add_cache_entry\n"));
#endif
if ((tlbt = (tnrh_tlb_t *)calloc(1, sizeof (tnrh_tlb_t))) == NULL)
return;
tlbt->addr = addr;
(void) strncpy(tlbt->template_name, name, TNTNAMSIZ-1);
tlbt->masklen_used = indx;
tlbt->reload = TNDB_LOAD;
tlbt->src = src;
#ifdef DEBUG
(void) fprintf(logf, gettext("adding cache entry\n"));
print_tlbt(tlbt);
#endif
/* Add to the chain */
if (tnrh_cache_table[hash] == NULL) {
tnrh_cache_table[hash] = tlbt;
} else {
/* Add in the beginning */
tlbt->next = tnrh_cache_table[hash];
tnrh_cache_table[hash] = tlbt;
}
}
static tnrh_tlb_t *
lookup_cache_table(in_addr_t addr)
{
tnrh_tlb_t *tlbt = NULL;
unsigned char hash;
hash = (unsigned char) get_hashvalue(addr);
tlbt = tnrh_cache_table[hash];
while (tlbt != NULL) {
if (addr == tlbt->addr)
break;
tlbt = tlbt->next;
}
return (tlbt);
}
static void
add_cache_entry_v6(in6_addr_t addr, char *name, int indx,
tnd_tnrhdb_t *src)
{
unsigned char hash;
tnrh_tlb_ipv6_t *tlbt;
hash = (unsigned char) TNRH_ADDR_HASH_V6(addr);
/* Look if some other thread already added this entry */
if (lookup_cache_table_v6(addr) != NULL)
return;
if ((tlbt = (tnrh_tlb_ipv6_t *)calloc(1,
sizeof (tnrh_tlb_ipv6_t))) == NULL)
return;
(void) memcpy(&tlbt->addr, &addr, sizeof (in6_addr_t));
(void) strncpy(tlbt->template_name, name, TNTNAMSIZ-1);
tlbt->masklen_used = indx;
tlbt->reload = TNDB_LOAD;
tlbt->src = src;
/* Add to the chain */
if (tnrh_cache_table_v6[hash] == NULL) {
tnrh_cache_table_v6[hash] = tlbt;
} else {
/* Add in the beginning */
tlbt->next = tnrh_cache_table_v6[hash];
tnrh_cache_table_v6[hash] = tlbt;
}
}
static tnrh_tlb_ipv6_t *
lookup_cache_table_v6(in6_addr_t addr)
{
tnrh_tlb_ipv6_t *tlbt = NULL;
unsigned char hash;
hash = (unsigned char) TNRH_ADDR_HASH_V6(addr);
tlbt = tnrh_cache_table_v6[hash];
while (tlbt != NULL) {
if (IN6_ARE_ADDR_EQUAL(&addr, &tlbt->addr))
break;
tlbt = tlbt->next;
}
return (tlbt);
}
/*
* Walk the cache table and check if this IP address/address prefix
* will be a better match for an existing entry in the cache.
* will add cache if not already exists
*/
static int
update_cache_table(tsol_rhent_t *ent, tnd_tnrhdb_t *src)
{
int i;
char result[TNTNAMSIZ];
in_addr_t tmpmask;
in_addr_t addr;
struct sockaddr_in *saddrp;
tnrh_tlb_t *tlbt;
struct tnd_tnrhdb_c *rhp;
int rflag = 0;
saddrp = (struct sockaddr_in *)&ent->rh_address.ip_addr_v4;
addr = saddrp->sin_addr.s_addr;
(void) rw_rdlock(&cache_rwlp);
tlbt = lookup_cache_table(addr);
(void) rw_unlock(&cache_rwlp);
if (tlbt == NULL) {
(void) rw_rdlock(&entire_rwlp);
for (i = (IP_MASK_TABLE_SIZE - 1); i >= 0; i--) {
#ifdef DEBUG
(void) fprintf(logf, "update_cache_table i = %d\n", i);
#endif
if (tnrh_entire_table[i] == NULL)
continue;
tmpmask = rh_index_to_mask(i);
saddrp->sin_addr.s_addr &= tmpmask;
#ifdef DEBUG
(void) fprintf(logf,
"update_cache_table found i = %d\n", i);
(void) fprintf(logf, "\ti = %d, tmpmask = 0x%4x\n",
i, tmpmask);
#endif
rhp = (struct tnd_tnrhdb_c *)rhtable_lookup(saddrp, i);
if (rhp != NULL) {
(void) strcpy(result, rhp->rh_ent.rh_template);
/* Add this result to the cache also */
(void) rw_wrlock(&cache_rwlp);
add_cache_entry(addr, result, i, rhp);
rflag++;
(void) rw_unlock(&cache_rwlp);
break;
} else {
#ifdef DEBUG
(void) fprintf(logf,
"rhtable_lookup return null !!");
#endif
}
}
(void) rw_unlock(&entire_rwlp);
}
rflag += walk_cache_table(addr, ent->rh_template, ent->rh_prefix, src);
return (rflag);
}
/*
* Walk the cache table and check if this IP address/address prefix
* will be a better match for an existing entry in the cache.
*/
static int
update_cache_table_v6(tsol_rhent_t *ent, tnd_tnrhdb_t *src)
{
int i;
char result[TNTNAMSIZ];
in6_addr_t addr;
struct sockaddr_in6 *saddrp;
tnrh_tlb_ipv6_t *tlbt;
struct tnd_tnrhdb_c *rhp;
in6_addr_t tmpmask6;
int rflag = 0;
saddrp = (struct sockaddr_in6 *)&ent->rh_address.ip_addr_v6;
(void) memcpy(&addr, &saddrp->sin6_addr, sizeof (in6_addr_t));
/* Look in the cache first */
(void) rw_rdlock(&cache_rwlp);
tlbt = lookup_cache_table_v6(addr);
(void) rw_unlock(&cache_rwlp);
if (tlbt == NULL) {
(void) rw_rdlock(&entire_rwlp_v6);
for (i = (IPV6_MASK_TABLE_SIZE - 1); i >= 0; i--) {
if (tnrh_entire_table_v6[i] == NULL)
continue;
(void) rh_index_to_mask_v6(i, &tmpmask6);
rhp = (struct tnd_tnrhdb_c *)
rhtable_lookup_v6(saddrp, tmpmask6, i);
if (rhp != NULL) {
(void) strcpy(result, rhp->rh_ent.rh_template);
/* Add this result to the cache also */
(void) rw_wrlock(&cache_rwlp_v6);
add_cache_entry_v6(addr, result, i, rhp);
rflag++;
(void) rw_unlock(&cache_rwlp_v6);
break;
}
}
(void) rw_unlock(&entire_rwlp_v6);
}
rflag += walk_cache_table_v6(addr, ent->rh_template,
ent->rh_prefix, src);
return (rflag);
}
/*
* Check if this prefix addr will be a better match
* for an existing entry.
*/
static int
is_better_match(in_addr_t newaddr, int indx, tnrh_tlb_t *tlbt)
{
if (tlbt->masklen_used <= indx) {
in_addr_t tmpmask = rh_index_to_mask(indx);
if ((newaddr) == (tlbt->addr & tmpmask))
return (1);
}
return (0);
}
/*
* Walk the cache table and update entries if needed.
* Mark entries for reload to kernel, if somehow their
* template changed.
* why is_better_match() is called???
*/
static int
walk_cache_table(in_addr_t newaddr, char *name, int indx, tnd_tnrhdb_t *src)
{
int i;
tnrh_tlb_t *tlbt;
int rflag = 0;
for (i = 0; i < TNRH_TABLE_HASH_SIZE; i++) {
tlbt = tnrh_cache_table[i];
while (tlbt != NULL) {
if (is_better_match(newaddr, indx, tlbt)) {
tlbt->masklen_used = indx;
tlbt->src = src;
/*
* Reload to the kernel only if the
* host type changed. There is no need
* to load, if only the mask used has changed,
* since the kernel does not need that
* information.
*/
if (strcmp(name, tlbt->template_name) != 0) {
(void) strncpy(tlbt->template_name,
name, TNTNAMSIZ-1);
tlbt->reload = TNDB_LOAD;
rflag ++;
}
}
tlbt = tlbt->next;
}
}
#ifdef DEBUG
(void) fprintf(logf, gettext("walk_cache_table rflag=%d\n"), rflag);
#endif
return (rflag);
}
/*
* Check if this prefix addr will be a better match
* for an existing entry.
*/
static int
is_better_match_v6(in6_addr_t newaddr, int indx, tnrh_tlb_ipv6_t *tlbt)
{
in6_addr_t tmpmask;
if (tlbt->masklen_used <= indx) {
(void) rh_index_to_mask_v6(indx, &tmpmask);
if (V6_MASK_EQ(newaddr, tmpmask, tlbt->addr))
return (1);
}
return (0);
}
/*
* Walk the cache table and update entries if needed.
* Mark entries for reload to kernel, if somehow their
* template changed.
*/
static int
walk_cache_table_v6(in6_addr_t newaddr, char *name, int indx, tnd_tnrhdb_t *src)
{
int i;
tnrh_tlb_ipv6_t *tlbt;
int rflag = 0;
for (i = 0; i < TNRH_TABLE_HASH_SIZE; i++) {
tlbt = tnrh_cache_table_v6[i];
while (tlbt != NULL) {
if (is_better_match_v6(newaddr, indx, tlbt)) {
tlbt->masklen_used = indx;
tlbt->src = src;
/*
* Reload to the kernel only if the
* host type changed. There is no need
* to load, if only the mask used has changed,
* since the kernel does not need that
* information.
*/
if (strcmp(name, tlbt->template_name) != 0) {
(void) strncpy(tlbt->template_name,
name, TNTNAMSIZ-1);
tlbt->reload = TNDB_LOAD;
rflag ++;
}
}
tlbt = tlbt->next;
}
}
return (rflag);
}
/*
* load/delete marked rh ents into kernel
* depending on the reload flag by invoking tnrh().
* It will mark other entries as TNDB_NOOP
*/
static void
load_rh_marked()
{
int i;
tnrh_tlb_t *tlbt, *prev;
struct tsol_rhent rhentp;
(void) memset((char *)&rhentp, '\0', sizeof (rhentp));
for (i = 0; i < TNRH_TABLE_HASH_SIZE; i++) {
prev = tlbt = tnrh_cache_table[i];
while (tlbt != NULL) {
if ((tlbt->reload == TNDB_LOAD) ||
(tlbt->reload == TNDB_DELETE)) {
/*
* We have to call tnrh() with tsol_rhent argument.
* Construct such a struct from the tlbt struct we have.
*/
rhentp.rh_address.ip_addr_v4.sin_addr.s_addr =
tlbt->addr;
rhentp.rh_address.ip_addr_v4.sin_family =
AF_INET;
rhentp.rh_prefix = tlbt->masklen_used;
(void) strcpy(rhentp.rh_template,
tlbt->template_name);
#ifdef DEBUG
(void) fprintf(logf, "load op =%d\n",
tlbt->reload);
print_tlbt(tlbt);
#endif
update_rh_entry(tlbt->reload, &rhentp);
if (tlbt->reload == TNDB_DELETE) {
if (tlbt == tnrh_cache_table[i]) {
tnrh_cache_table[i] =
tlbt->next;
prev = tnrh_cache_table[i];
} else {
prev->next = tlbt->next;
prev = prev->next;
}
free(tlbt);
if (prev == NULL)
break;
else {
tlbt = prev;
continue;
}
}
tlbt->reload = TNDB_NOOP;
}
prev = tlbt;
tlbt = tlbt->next;
}
}
}
/* load marked rh ents into kernel */
static void
load_rh_marked_v6()
{
int i;
tnrh_tlb_ipv6_t *tlbt, *prev;
struct tsol_rhent rhentp;
(void) memset((char *)&rhentp, '\0', sizeof (rhentp));
for (i = 0; i < TNRH_TABLE_HASH_SIZE; i++) {
prev = tlbt = tnrh_cache_table_v6[i];
while (tlbt != NULL) {
if ((tlbt->reload == TNDB_LOAD) ||
(tlbt->reload == TNDB_DELETE)) {
/*
* We have to call tnrh() with tsol_rhent argument.
* Construct such a struct from the tlbt struct we have.
*/
(void) memcpy(&rhentp.rh_address.ip_addr_v6.sin6_addr,
&tlbt->addr, sizeof (in6_addr_t));
rhentp.rh_address.ip_addr_v6.sin6_family = AF_INET6;
rhentp.rh_prefix = tlbt->masklen_used;
(void) strcpy(rhentp.rh_template, tlbt->template_name);
update_rh_entry(tlbt->reload, &rhentp);
if (tlbt->reload == TNDB_DELETE) {
if (tlbt == tnrh_cache_table_v6[i]) {
tnrh_cache_table_v6[i] =
tlbt->next;
prev = tnrh_cache_table_v6[i];
} else {
prev->next = tlbt->next;
prev = prev->next;
}
free(tlbt);
if (prev == NULL)
break;
else {
tlbt = prev;
continue;
}
}
tlbt->reload = TNDB_NOOP;
}
prev = tlbt;
tlbt = tlbt->next;
}
}
}
/*
* Does the real load/delete for the entry depending on op code.
*/
static void
update_rh_entry(int op, struct tsol_rhent *rhentp)
{
#ifdef DEBUG
(void) fprintf(logf, gettext("\t###update_rh_entry op = %d\n"), op);
print_entry(rhentp, AF_INET);
#endif
if (tnrh(op, rhentp) != 0) {
if (debugl && (logf != NULL)) {
(void) fprintf(logf, "%s : ", gettime());
(void) fprintf(logf, gettext("tnrh() failed: %s\n"),
strerror(errno));
if (op == TNDB_LOAD)
(void) fprintf(logf,
gettext("load of remote host database "
"%s into kernel cache failed\n"),
rhentp->rh_template);
if (op == TNDB_DELETE)
(void) fprintf(logf,
gettext("delete of remote host database "
"%s from kernel cache failed\n"),
rhentp->rh_template);
(void) fflush(logf);
}
cprint("tnrh() failed..: %s\n", strerror(errno));
}
}
static void
timer()
{
poll_now();
(void) alarm(poll_interval);
}
#define max(a, b) ((a) > (b) ? (a) : (b))
static void
poll_now()
{
(void) fprintf(logf, "enter poll_now at %s \n", gettime());
(void) fflush(logf);
if (nss_get_tp() > 0) {
load_tp();
tp_flush_cache();
}
#ifdef DEBUG
(void) fprintf(logf, "now search for tnrhdb update %s \n", gettime());
#endif
if (nss_get_rh() > 0) {
if (logf != NULL) {
(void) fprintf(logf, "tnrhdb needs update %s \n",
gettime());
}
(void) rw_wrlock(&cache_rwlp);
/* This function will cleanup cache table */
load_rh_marked();
(void) rw_unlock(&cache_rwlp);
(void) rw_wrlock(&cache_rwlp_v6);
/* This function will cleanup cache table */
load_rh_marked_v6();
(void) rw_unlock(&cache_rwlp_v6);
}
#ifdef DEBUG
if (logf != NULL) {
cachetable_print();
cachetable_print_v6();
(void) fprintf(logf, "rh table begin\n");
rhtable_print();
rhtable_print_v6();
(void) fprintf(logf, "rh table end \n");
(void) fprintf(logf, "-------------------------\n\n");
(void) fflush(logf);
}
#endif
}
static void
tnd_serve()
{
for (;;) {
(void) pause();
}
}
static void
terminate()
{
if (debugl && (logf != NULL)) {
(void) fprintf(logf, "%s : ", gettime());
(void) fprintf(logf, gettext("tnd terminating on signal.\n"));
(void) fflush(logf);
}
exit(1);
}
static void
noop()
{
}
static char *
gettime()
{
time_t now;
struct tm *tp, tm;
char *fmt;
(void) time(&now);
tp = localtime(&now);
(void) memcpy(&tm, tp, sizeof (struct tm));
fmt = nl_langinfo(_DATE_FMT);
(void) strftime(time_buf, _SZ_TIME_BUF, fmt, &tm);
return (time_buf);
}
/*
* debugging routines
*/
#ifdef DEBUG
static void
print_cache_entry(tnrh_tlb_t *tlbt)
{
struct in_addr addr;
addr.s_addr = tlbt->addr;
(void) fprintf(logf, "\tIP address: %s", inet_ntoa(addr));
(void) fprintf(logf, "\tTemplate name: %s", tlbt->template_name);
(void) fprintf(logf, "\tMask length used: %d\n", tlbt->masklen_used);
}
static void
print_cache_entry_v6(tnrh_tlb_ipv6_t *tlbt)
{
char abuf[INET6_ADDRSTRLEN];
(void) fprintf(logf, "\tIP address: %s",
inet_ntop(AF_INET6, &tlbt->addr, abuf, sizeof (abuf)));
(void) fprintf(logf, "\tTemplate name: %s", tlbt->template_name);
(void) fprintf(logf, "\tMask length used: %d\n", tlbt->masklen_used);
}
static void
cachetable_print()
{
int i;
tnrh_tlb_t *tlbt;
(void) fprintf(logf, "-------------------------\n");
(void) fprintf(logf, "Cache table begin\n");
for (i = 0; i < TNRH_TABLE_HASH_SIZE; i++) {
if ((tlbt = tnrh_cache_table[i]) != NULL)
print_cache_entry(tlbt);
}
(void) fprintf(logf, "Cache table end \n");
(void) fprintf(logf, "-------------------------\n\n");
}
static void
cachetable_print_v6()
{
int i;
tnrh_tlb_ipv6_t *tlbt;
(void) fprintf(logf, "-------------------------\n");
(void) fprintf(logf, "Cache table begin\n");
for (i = 0; i < TNRH_TABLE_HASH_SIZE; i++) {
if ((tlbt = tnrh_cache_table_v6[i]) != NULL)
print_cache_entry_v6(tlbt);
}
(void) fprintf(logf, "Cache table end \n");
(void) fprintf(logf, "-------------------------\n\n");
}
static void
print_entry(tsol_rhent_t *ent, int af)
{
struct sockaddr_in *saddrp;
struct sockaddr_in6 *saddrp6;
char abuf[INET6_ADDRSTRLEN];
if (af == AF_INET) {
saddrp = (struct sockaddr_in *)&ent->rh_address.ip_addr_v4;
(void) fprintf(logf, gettext("\tIP address: %s"),
inet_ntoa(saddrp->sin_addr));
} else if (af == AF_INET6) {
saddrp6 = (struct sockaddr_in6 *)&ent->rh_address.ip_addr_v6;
(void) fprintf(logf, gettext("\tIP address: %s"),
inet_ntop(AF_INET6, &saddrp6->sin6_addr, abuf,
sizeof (abuf)));
}
(void) fprintf(logf,
gettext("\tTemplate name: %s"), ent->rh_template);
(void) fprintf(logf, gettext("\tprefix_len: %d\n"), ent->rh_prefix);
(void) fflush(logf);
}
static void
print_tlbt(tnrh_tlb_t *tlbt)
{
(void) fprintf(logf, "tlbt addr = 0x%4x name = %s \
mask = %u, reload = %d\n", tlbt->addr, tlbt->template_name,
tlbt->masklen_used, tlbt->reload);
}
static void
rhtable_print()
{
rhtable_walk(print_entry);
(void) fprintf(logf, "-----------------------------\n\n");
}
static void
rhtable_print_v6()
{
rhtable_walk_v6(print_entry);
(void) fprintf(logf, "-----------------------------\n\n");
}
/*
* Walk through all the entries in tnrh_entire_table[][]
* and execute the function passing the entry as argument.
*/
static void
rhtable_walk(void (*action)())
{
int i, j;
tnd_tnrhdb_t *rhent;
for (i = 0; i <= IP_ABITS; i++) {
if (tnrh_entire_table[i] == NULL)
continue;
for (j = 0; j < TNRH_TABLE_HASH_SIZE; j++) {
rhent = tnrh_entire_table[i][j];
while (rhent != NULL) {
action(&rhent->rh_ent, AF_INET);
rhent = rhent->rh_next;
}
}
}
}
/*
* Walk through all the entries in tnrh_entire_table_v6[][]
* and execute the function passing the entry as argument.
*/
static void
rhtable_walk_v6(void (*action)())
{
int i, j;
tnd_tnrhdb_t *rhent;
for (i = 0; i <= IPV6_ABITS; i++) {
if (tnrh_entire_table_v6[i] == NULL)
continue;
for (j = 0; j < TNRH_TABLE_HASH_SIZE; j++) {
rhent = tnrh_entire_table_v6[i][j];
while (rhent != NULL) {
action(&rhent->rh_ent, AF_INET6);
rhent = rhent->rh_next;
}
}
}
}
#endif /* DEBUG */