/*
* 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
* 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 <time.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <locale.h>
#include <langinfo.h>
#include <search.h>
#include <errno.h>
#include <netdb.h>
#include <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 <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 handle_unvisited_nodes();
static void load_rh_marked_v6();
static int
static int handle_unvisited_nodes_v6();
#ifdef DEBUG
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
* code in the kernel.
*/
/*
* 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.
*/
/* Mask comparison: is IPv6 addr a, and'ed with mask m, equal to addr b? */
#define V6_MASK_EQ(a, m, b) \
/*
* 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.
*/
/*
* 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.
*/
int debugl = 0;
int delay_poll_flag = 0;
void *tp_tree;
\
} \
}
/* 128 privs * (24 bytes + 1 deliminator)= 3200 bytes + 1200 cushion */
int
{
/* set the locale for only the messages system (all else is clean) */
#ifndef TEXT_DOMAIN /* Should be defined by cc -D */
#endif
(void) textdomain(TEXT_DOMAIN);
if (getzoneid() != GLOBAL_ZONEID) {
exit(-1);
}
exit(-1);
}
exit(-1);
}
/* parse command line options */
/*
* used within this process only.
*/
exit(-1);
}
/* catch the usual termination signals for graceful exit */
if (debugl == MAX_TND_DEBUG) {
getpid());
gettext("max level debugging! not forking\n"));
} else {
}
if (!delay_poll_flag) {
timer();
}
if (debugl != MAX_TND_DEBUG) {
}
(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
{
#ifdef DEBUG
#endif
}
/*
* we use this where exact match is needed.
*/
static int
{
#ifdef DEBUG
#endif
}
/*
* 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
{
(void) rh_index_to_mask_v6(i, &tmpmask);
}
/*
* we use this where v6 exact match is needed.
*/
static int
{
}
static int
{
unsigned char *bp;
}
/*
* Convert length for a mask to the mask.
*/
static in_addr_t
{
if (masklen == 0)
return (0);
}
/*
* Convert length for a mask to the mask.
* Returns the argument bitmask.
*/
static in6_addr_t *
{
while (masklen > 32) {
*ptr++ = 0xffffffffU;
masklen -= 32;
}
return (bitmask);
}
static void
int argc;
char **argv;
{
extern char *optarg;
int c;
switch (c) {
case 'd':
} else {
usage();
exit(1);
}
break;
case 'f':
break;
case 'p':
if (poll_interval == 0)
usage();
} else {
usage();
}
break;
case 'n':
delay_poll_flag = 1;
break;
case '?':
usage();
}
}
static int
int debug_level;
{
if (debug_level > MAX_TND_DEBUG) {
gettext("invalid debug level: %d, not changed!\n"),
}
cprint("invalid debug level: %d, not changed!\n",
return (-1);
}
return (0);
}
static FILE *
char *logfile;
{
logfile);
exit(-1);
}
return (fp);
}
static void
{
(void) close(0);
(void) close(1);
(void) close(2);
case (pid_t)-1:
}
break;
case 0:
break;
default:
}
/*
* Suspend parent till child signals it. We catch the signal
* in order to return correct exit value.
*/
(void) pause();
exit(0);
}
(void) setsid();
(void) dup(0);
(void) dup(0);
}
static void
usage()
{
"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
{
return (0);
}
static int
{
}
/*
* Build tree of tp entries, tossing duplicates
*/
static int
{
int count = 0;
tsol_settpent(1);
if ((new = (struct tnd_tnrhtp_c *)
continue;
else
count++;
}
return (count);
}
/* load tp ents into kernel */
static void
load_tp()
{
}
static void
/* LINTED */
{
return;
gettext("load of remote-host template "
"%s into kernel cache failed\n"),
}
}
}
static void
{
}
}
/*
* name service sources, files, ldap etc.
*/
static int
{
int found_entry = 0;
int count = 0;
int newflag = 0;
int v6cnt = 0;
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.
*/
rhp->rh_template);
gettext("Unknown template name: %s\n"),
rhp->rh_template);
rhp->rh_template);
continue;
}
found_entry++; /* found a valid tnrhdb entry */
#ifdef DEBUG
#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.
*/
(void) rw_unlock(&cache_rwlp);
(void) rw_unlock(&entire_rwlp);
#ifdef DEBUG
#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.
*/
(void) rw_unlock(&cache_rwlp_v6);
(void) rw_unlock(&entire_rwlp_v6);
}
if (newflag)
count++;
}
/*
* 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
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.
*/
(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
{
int i, j, cnt = 0;
for (i = 0; i < TNRH_TABLE_HASH_SIZE; i++)
do {
/*
* Mark for deletion of both our cache
* entry and the kernel cache entry.
*/
cnt++;
}
/*
* 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++) {
/*
* Check if start node
*/
if (rhent == tnrh_entire_table[i][j]) {
prev = tnrh_entire_table[i][j] =
} else {
/* bypass the deleted node */
}
break;
else {
continue;
}
} else
}
}
}
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
{
int i, j, cnt = 0;
for (i = 0; i < TNRH_TABLE_HASH_SIZE; i++)
do {
/*
* Mark for deletion of both our cache entry
* and the kernel cache entry.
*/
cnt++;
}
/*
* 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++) {
/* Check if start node */
if (rhent == tnrh_entire_table_v6[i][j]) {
prev = tnrh_entire_table_v6[i][j] =
} else {
/* bypass the deleted node */
}
break;
else {
continue;
}
} else
}
}
}
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
{
unsigned char hash;
int i;
#ifdef DEBUG
#endif
if (tnrh_entire_table[i] == NULL) {
return (0);
}
}
#ifdef DEBUG
i, hash);
} else {
}
#endif
/* Check if this is a duplicate entry */
return (0);
if (duplflag)
/*
* Template is changed in the name service.
* Use the new template.
*/
ent->rh_template);
/*
* Check if this modified entry
* affects the cache table.
*/
return (rflag);
} else
return (0);
}
}
/* Not found. Add the entry */
sizeof (struct tnd_tnrhdb_c));
return (0);
if (duplflag)
/* linked list. Insert in the beginning */
#ifdef DEBUG
i, hash);
#endif
/* Check if the new entry affects the cache table */
#ifdef DEBUG
#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
{
unsigned char hash;
int i;
(void) rh_index_to_mask_v6(i, &tmpmask6);
tmpmask6);
if (tnrh_entire_table_v6[i] == NULL) {
return (0);
}
}
/* Check if this is a duplicate entry */
return (0);
if (duplflag)
/*
* Template is changed in the name service.
* Use the new template.
*/
ent->rh_template);
/*
* Check if this modified entry
* affects the cache table.
*/
return (rflag);
} else
return (0);
}
}
/* Not found. Add the entry */
return (0);
if (duplflag)
/* linked list. Insert in the beginning */
/* Check if the new entry affects the cache table */
return (rflag);
}
/*
* The array element i points to the hash table.
* Search the hash chain for the address.
*/
static struct tnd_tnrhdb_c *
{
unsigned char hash;
if (tnrh_entire_table[i] == NULL)
return (NULL);
#ifdef DEBUG
i, hash);
#endif
#ifdef DEBUG
#endif
return (rhent);
}
#ifdef DEBUG
#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 *
{
unsigned char hash;
if (tnrh_entire_table_v6[i] == NULL)
return (NULL);
return (rhent);
}
/* Not found */
return (NULL);
}
void
{
unsigned char hash;
/* Look if some other thread already added this entry */
return;
#ifdef DEBUG
#endif
return;
#ifdef DEBUG
#endif
/* Add to the chain */
} else {
/* Add in the beginning */
}
}
static tnrh_tlb_t *
{
unsigned char hash;
break;
}
return (tlbt);
}
static void
{
unsigned char hash;
/* Look if some other thread already added this entry */
return;
sizeof (tnrh_tlb_ipv6_t))) == NULL)
return;
/* Add to the chain */
} else {
/* Add in the beginning */
}
}
static tnrh_tlb_ipv6_t *
{
unsigned char hash;
break;
}
return (tlbt);
}
/*
* will be a better match for an existing entry in the cache.
* will add cache if not already exists
*/
static int
{
int i;
int rflag = 0;
(void) rw_rdlock(&cache_rwlp);
(void) rw_unlock(&cache_rwlp);
(void) rw_rdlock(&entire_rwlp);
for (i = (IP_MASK_TABLE_SIZE - 1); i >= 0; i--) {
#ifdef DEBUG
#endif
if (tnrh_entire_table[i] == NULL)
continue;
tmpmask = rh_index_to_mask(i);
#ifdef DEBUG
"update_cache_table found i = %d\n", i);
i, tmpmask);
#endif
/* Add this result to the cache also */
(void) rw_wrlock(&cache_rwlp);
rflag++;
(void) rw_unlock(&cache_rwlp);
break;
} else {
#ifdef DEBUG
"rhtable_lookup return null !!");
#endif
}
}
(void) rw_unlock(&entire_rwlp);
}
return (rflag);
}
/*
* will be a better match for an existing entry in the cache.
*/
static int
{
int i;
int rflag = 0;
/* Look in the cache first */
(void) rw_rdlock(&cache_rwlp);
(void) rw_unlock(&cache_rwlp);
(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 *)
/* Add this result to the cache also */
(void) rw_wrlock(&cache_rwlp_v6);
rflag++;
(void) rw_unlock(&cache_rwlp_v6);
break;
}
}
(void) rw_unlock(&entire_rwlp_v6);
}
return (rflag);
}
/*
* Check if this prefix addr will be a better match
* for an existing entry.
*/
static int
{
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
{
int i;
int rflag = 0;
for (i = 0; i < TNRH_TABLE_HASH_SIZE; i++) {
tlbt = tnrh_cache_table[i];
/*
* 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.
*/
rflag ++;
}
}
}
}
#ifdef DEBUG
#endif
return (rflag);
}
/*
* Check if this prefix addr will be a better match
* for an existing entry.
*/
static int
{
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
{
int i;
int rflag = 0;
for (i = 0; i < TNRH_TABLE_HASH_SIZE; i++) {
tlbt = tnrh_cache_table_v6[i];
/*
* 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.
*/
rflag ++;
}
}
}
}
return (rflag);
}
/*
* depending on the reload flag by invoking tnrh().
* It will mark other entries as TNDB_NOOP
*/
static void
{
int i;
for (i = 0; i < TNRH_TABLE_HASH_SIZE; i++) {
/*
* We have to call tnrh() with tsol_rhent argument.
* Construct such a struct from the tlbt struct we have.
*/
#ifdef DEBUG
#endif
if (tlbt == tnrh_cache_table[i]) {
tnrh_cache_table[i] =
prev = tnrh_cache_table[i];
} else {
}
break;
else {
continue;
}
}
}
}
}
}
/* load marked rh ents into kernel */
static void
{
int i;
for (i = 0; i < TNRH_TABLE_HASH_SIZE; i++) {
/*
* We have to call tnrh() with tsol_rhent argument.
* Construct such a struct from the tlbt struct we have.
*/
if (tlbt == tnrh_cache_table_v6[i]) {
tnrh_cache_table_v6[i] =
prev = tnrh_cache_table_v6[i];
} else {
}
break;
else {
continue;
}
}
}
}
}
}
/*
*/
static void
{
#ifdef DEBUG
#endif
gettext("load of remote host database "
"%s into kernel cache failed\n"),
if (op == TNDB_DELETE)
gettext("delete of remote host database "
"%s from kernel cache failed\n"),
}
}
}
static void
timer()
{
poll_now();
(void) alarm(poll_interval);
}
#define max(a, b) ((a) > (b) ? (a) : (b))
static void
poll_now()
{
if (nss_get_tp() > 0) {
load_tp();
}
#ifdef DEBUG
#endif
if (nss_get_rh() > 0) {
gettime());
}
(void) rw_wrlock(&cache_rwlp);
/* This function will cleanup cache table */
(void) rw_unlock(&cache_rwlp);
(void) rw_wrlock(&cache_rwlp_v6);
/* This function will cleanup cache table */
(void) rw_unlock(&cache_rwlp_v6);
}
#ifdef DEBUG
}
#endif
}
static void
{
for (;;) {
(void) pause();
}
}
static void
{
}
exit(1);
}
static void
noop()
{
}
static char *
gettime()
{
char *fmt;
return (time_buf);
}
/*
* debugging routines
*/
#ifdef DEBUG
static void
{
}
static void
{
}
static void
{
int i;
for (i = 0; i < TNRH_TABLE_HASH_SIZE; i++) {
}
}
static void
{
int i;
for (i = 0; i < TNRH_TABLE_HASH_SIZE; i++) {
}
}
static void
{
sizeof (abuf)));
}
}
static void
{
}
static void
{
}
static void
{
}
/*
* Walk through all the entries in tnrh_entire_table[][]
* and execute the function passing the entry as argument.
*/
static void
{
int i, j;
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];
}
}
}
}
/*
* Walk through all the entries in tnrh_entire_table_v6[][]
* and execute the function passing the entry as argument.
*/
static void
{
int i, j;
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];
}
}
}
}
#endif /* DEBUG */