#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
#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
#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)
#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 };
tnd_tnrhdb_t **tnrh_entire_table[IP_MASK_TABLE_SIZE];
tnd_tnrhdb_t **tnrh_entire_table_v6[IPV6_MASK_TABLE_SIZE];
rwlock_t entire_rwlp;
rwlock_t entire_rwlp_v6;
tnrh_tlb_t *tnrh_cache_table[TNRH_TABLE_HASH_SIZE];
tnrh_tlb_ipv6_t *tnrh_cache_table_v6[TNRH_TABLE_HASH_SIZE];
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
#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;
(void) setlocale(LC_ALL, "");
#ifndef TEXT_DOMAIN
#define TEXT_DOMAIN "SYS_TEST"
#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);
}
(void) parse_opts(argc, argv);
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);
}
(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();
return (0);
}
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));
}
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);
}
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));
}
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);
}
static in_addr_t
rh_index_to_mask(uint_t masklen)
{
if (masklen == 0)
return (0);
return (htonl(RH_HOST_MASK << (IP_ABITS - masklen)));
}
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;
} 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);
}
(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;
while (c = *s++)
if (!isdigit(c))
return (0);
return (1);
}
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));
}
static int
nss_get_tp()
{
tsol_tpent_t tp;
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);
}
static void
load_tp()
{
twalk(tp_tree, load_tp_entry);
}
static void
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);
}
}
static int
nss_get_rh()
{
int found_entry = 0;
int count = 0;
int newflag = 0;
struct tsol_rhent rh;
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) {
(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++;
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);
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);
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 (!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);
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);
count += handle_unvisited_nodes_v6();
(void) rw_unlock(&cache_rwlp_v6);
(void) rw_unlock(&entire_rwlp_v6);
}
return (count);
}
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) {
tlbt->reload = TNDB_DELETE;
cnt++;
}
tlbt = tlbt->next;
} while (tlbt != NULL);
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) {
if (rhent == tnrh_entire_table[i][j]) {
prev = tnrh_entire_table[i][j] =
rhent->rh_next;
} else {
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);
}
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) {
tlbt->reload = TNDB_DELETE;
cnt++;
}
tlbt = tlbt->next;
} while (tlbt != NULL);
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) {
if (rhent == tnrh_entire_table_v6[i][j]) {
prev = tnrh_entire_table_v6[i][j] =
rhent->rh_next;
} else {
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);
}
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) {
if ((rhent->visited == 1) && duplflag)
return (0);
if (duplflag)
rhent->visited = 1;
if (strcmp(ent->rh_template,
rhent->rh_ent.rh_template) != 0) {
(void) strcpy(rhent->rh_ent.rh_template,
ent->rh_template);
rflag = update_cache_table(ent, rhent);
return (rflag);
} else
return (0);
}
rhent = rhent->rh_next;
}
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;
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
rflag = update_cache_table(ent, new);
#ifdef DEBUG
(void) fprintf(logf, gettext("search_and_update rflag=%d\n"), rflag);
#endif
return (rflag);
}
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) {
if ((rhent->visited == 1) && duplflag)
return (0);
if (duplflag)
rhent->visited = 1;
if (strcmp(ent->rh_template,
rhent->rh_ent.rh_template) != 0) {
(void) strcpy(rhent->rh_ent.rh_template,
ent->rh_template);
rflag = update_cache_table_v6(ent, rhent);
return (rflag);
} else
return (0);
}
rhent = rhent->rh_next;
}
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;
new->rh_next = tnrh_entire_table_v6[i][hash];
tnrh_entire_table_v6[i][hash] = new;
rflag = update_cache_table_v6(ent, new);
return (rflag);
}
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
return (NULL);
}
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;
}
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);
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
if (tnrh_cache_table[hash] == NULL) {
tnrh_cache_table[hash] = tlbt;
} else {
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);
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;
if (tnrh_cache_table_v6[hash] == NULL) {
tnrh_cache_table_v6[hash] = tlbt;
} else {
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);
}
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);
(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);
}
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));
(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);
(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);
}
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);
}
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;
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);
}
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);
}
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;
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);
}
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)) {
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;
}
}
}
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)) {
(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;
}
}
}
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);
load_rh_marked();
(void) rw_unlock(&cache_rwlp);
(void) rw_wrlock(&cache_rwlp_v6);
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);
}
#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");
}
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;
}
}
}
}
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