xref: /illumos-gate/usr/src/cmd/tsol/tnd/tnd.c (revision fc80c0dfb0c877aee828d778ea32b77fcf7b1ef4)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  *  Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  *  Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <time.h>
31 #include <unistd.h>
32 #include <stdio.h>
33 #include <sys/fcntl.h>
34 #include <sys/stat.h>
35 #include <fcntl.h>
36 #include <locale.h>
37 #include <langinfo.h>
38 #include <search.h>
39 #include <tsol/label.h>
40 #include <errno.h>
41 #include <sys/tsol/tndb.h>
42 #include <sys/socket.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
45 #include <netdb.h>
46 #include <signal.h>
47 #include <sys/signal.h>
48 #include <string.h>
49 #include <stdlib.h>
50 #include <unistd.h>
51 #include <stdarg.h>
52 #include <syslog.h>
53 #include <ctype.h>
54 #include <pwd.h>
55 #include <grp.h>
56 #include <door.h>
57 #include <synch.h>
58 #include <sys/tsol/tsyscall.h>
59 #include <nss_dbdefs.h>
60 #include <libtsnet.h>
61 #include <zone.h>
62 
63 #include "tnd.h"
64 
65 static FILE *tnlog_open(char *);
66 static void usage();
67 static void parse_opts(int, char **);
68 static int check_debugl(int);
69 static void load_tp();
70 static void load_tp_entry();
71 static void tnd_serve();
72 static void detachfromtty();
73 static void terminate();
74 static void noop();
75 static char *gettime();
76 static int isnumber(char *);
77 static void poll_now();
78 static int nss_get_tp();
79 static int nss_get_rh();
80 static void timer();
81 static void load_rh_marked();
82 static int rhtable_search_and_update(struct tsol_rhent *ent, int duplflag);
83 static int is_better_match(in_addr_t newaddr, int indx, tnrh_tlb_t *tlbt);
84 static int walk_cache_table(in_addr_t newaddr, char *name,
85     int indx, tnd_tnrhdb_t *src);
86 static tnrh_tlb_t *lookup_cache_table(in_addr_t addr);
87 static int update_cache_table(tsol_rhent_t *ent, tnd_tnrhdb_t *src);
88 static void update_rh_entry(int op, struct tsol_rhent *rhentp);
89 static int handle_unvisited_nodes();
90 static in_addr_t rh_index_to_mask(uint_t masklen);
91 static tnrh_tlb_ipv6_t *lookup_cache_table_v6(in6_addr_t addr);
92 static in6_addr_t *rh_index_to_mask_v6(uint_t masklen, in6_addr_t *bitmask);
93 static void load_rh_marked_v6();
94 static int
95     rhtable_search_and_update_v6(struct tsol_rhent *ent, int duplflag);
96 static int walk_cache_table_v6(in6_addr_t newaddr, char *name,
97     int indx, tnd_tnrhdb_t *src);
98 static int update_cache_table_v6(tsol_rhent_t *ent, tnd_tnrhdb_t *src);
99 static int handle_unvisited_nodes_v6();
100 
101 #ifdef DEBUG
102 static void print_entry(tsol_rhent_t *ent, int af);
103 static void print_tlbt(tnrh_tlb_t *tlbt);
104 static void rhtable_print();
105 static void cachetable_print();
106 static void rhtable_walk(void (*action)());
107 static void cachetable_print_v6();
108 static void rhtable_print_v6();
109 static void rhtable_walk_v6(void (*action)());
110 #endif /* DEBUG */
111 
112 /*
113  * The following constants and structures and the functions
114  * that operate on them are similar to the ip_ire.c and ip6_ire.c
115  * code in the kernel.
116  */
117 #define	TNRH_TABLE_HASH_SIZE 256
118 #define	IP_ABITS 32
119 #define	IP_MASK_TABLE_SIZE (IP_ABITS + 1)
120 #define	RH_HOST_MASK (in_addr_t)0xffffffffU
121 
122 #define	IPV6_ABITS 128
123 #define	IPV6_MASK_TABLE_SIZE (IPV6_ABITS + 1)
124 #define	s6_addr8 _S6_un._S6_u8
125 #define	s6_addr32 _S6_un._S6_u32
126 
127 /*
128  * Exclusive-or the 6 bytes that are likely to contain the MAC
129  * address. Assumes table_size does not exceed 256.
130  * Assumes EUI-64 format for good hashing.
131  */
132 #define	TNRH_ADDR_HASH_V6(addr)				\
133 	(((addr).s6_addr8[8] ^ (addr).s6_addr8[9] ^	\
134 	(addr).s6_addr8[10] ^ (addr).s6_addr8[13] ^	\
135 	(addr).s6_addr8[14] ^ (addr).s6_addr8[15]) % TNRH_TABLE_HASH_SIZE)
136 
137 #define	TNRH_ADDR_MASK_HASH_V6(addr, mask)	\
138 	((((addr).s6_addr8[8] & (mask).s6_addr8[8]) ^	\
139 	((addr).s6_addr8[9] & (mask).s6_addr8[9]) ^	\
140 	((addr).s6_addr8[10] & (mask).s6_addr8[10]) ^	\
141 	((addr).s6_addr8[13] & (mask).s6_addr8[13]) ^	\
142 	((addr).s6_addr8[14] & (mask).s6_addr8[14]) ^	\
143 	((addr).s6_addr8[15] & (mask).s6_addr8[15])) % TNRH_TABLE_HASH_SIZE)
144 
145 /* Mask comparison: is IPv6 addr a, and'ed with mask m, equal to addr b? */
146 #define	V6_MASK_EQ(a, m, b)	\
147 	((((a).s6_addr32[0] & (m).s6_addr32[0]) == (b).s6_addr32[0]) && \
148 	(((a).s6_addr32[1] & (m).s6_addr32[1]) == (b).s6_addr32[1]) &&  \
149 	(((a).s6_addr32[2] & (m).s6_addr32[2]) == (b).s6_addr32[2]) &&  \
150 	(((a).s6_addr32[3] & (m).s6_addr32[3]) == (b).s6_addr32[3]))
151 
152 
153 const in6_addr_t ipv6_all_zeros = { 0, 0, 0, 0 };
154 
155 /*
156  * This is a table of hash tables to keep
157  * all the name service entries. We don't have
158  * a separate hash bucket structure, instead mantain
159  * a pointer to the hash chain.
160  */
161 tnd_tnrhdb_t **tnrh_entire_table[IP_MASK_TABLE_SIZE];
162 tnd_tnrhdb_t **tnrh_entire_table_v6[IPV6_MASK_TABLE_SIZE];
163 
164 /* reader/writer lock for tnrh_entire_table */
165 rwlock_t entire_rwlp;
166 rwlock_t entire_rwlp_v6;
167 
168 
169 /*
170  * This is a hash table which keeps fully resolved
171  * tnrhdb entries <IP address, Host type>. We don't have
172  * a separate hash bucket structure, instead
173  * mantain a pointer to the hash chain.
174  */
175 tnrh_tlb_t *tnrh_cache_table[TNRH_TABLE_HASH_SIZE];
176 tnrh_tlb_ipv6_t *tnrh_cache_table_v6[TNRH_TABLE_HASH_SIZE];
177 
178 /* reader/writer lock for tnrh_cache_table */
179 rwlock_t cache_rwlp;
180 rwlock_t cache_rwlp_v6;
181 
182 FILE	 *logf;
183 int	 debugl = 0;
184 int	 poll_interval = TND_DEF_POLL_TIME;
185 int	 delay_poll_flag = 0;
186 
187 void	*tp_tree;
188 
189 #define	_SZ_TIME_BUF 100
190 char time_buf[_SZ_TIME_BUF];
191 
192 #define	cprint(s, param) { \
193 		register FILE *consl; \
194 \
195 		if ((consl = fopen("/dev/msglog", "w")) != NULL) { \
196 		    setbuf(consl, NULL); \
197 		    (void) fprintf(consl, "tnd: "); \
198 		    (void) fprintf(consl, s, param); \
199 		    (void) fclose(consl); \
200 			} \
201 	    }
202 
203 #define	RHENT_BUF_SIZE 300
204 #define	TPENT_BUF_SIZE 2000
205 
206 /* 128 privs * (24 bytes + 1 deliminator)= 3200 bytes + 1200 cushion */
207 #define	STRING_PRIVS_SIZE 4800
208 #define	ID_ENT_SIZE 500
209 
210 int
211 main(int argc, char **argv)
212 {
213 
214 
215 	const ucred_t	*uc = NULL;
216 	const priv_set_t	*pset;
217 	struct sigaction act;
218 
219 	/* set the locale for only the messages system (all else is clean) */
220 	(void) setlocale(LC_ALL, "");
221 #ifndef TEXT_DOMAIN			/* Should be defined by cc -D */
222 #define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it weren't */
223 #endif
224 	(void) textdomain(TEXT_DOMAIN);
225 
226 	if (getzoneid() != GLOBAL_ZONEID) {
227 		syslog(LOG_ERR,	"can not run tnd from a local zone");
228 		exit(-1);
229 	}
230 
231 
232 	if (((uc = ucred_get(getpid())) == NULL) ||
233 	    ((pset = ucred_getprivset(uc, PRIV_EFFECTIVE)) == NULL)) {
234 		syslog(LOG_ERR,	"don't have privilege set");
235 		exit(-1);
236 	}
237 
238 	if (!priv_ismember(pset, PRIV_SYS_NET_CONFIG)) {
239 		syslog(LOG_ERR,	"don't have privilege to run tnd");
240 		exit(-1);
241 	}
242 
243 
244 	/* parse command line options */
245 	(void) parse_opts(argc, argv);
246 
247 	/*
248 	 * Initialize reader/writer locks. To be
249 	 * used within this process only.
250 	 */
251 	if ((rwlock_init(&entire_rwlp, USYNC_THREAD, 0) != 0) ||
252 	    (rwlock_init(&entire_rwlp_v6, USYNC_THREAD, 0) != 0) ||
253 	    (rwlock_init(&cache_rwlp, USYNC_THREAD, 0) != 0) ||
254 	    (rwlock_init(&cache_rwlp_v6, USYNC_THREAD, 0) != 0)) {
255 		syslog(LOG_ERR, "cannot initialize lock");
256 		exit(-1);
257 	}
258 
259 	/* catch the usual termination signals for graceful exit */
260 	(void) sigset(SIGINT, terminate);
261 	(void) sigset(SIGTERM, terminate);
262 	(void) sigset(SIGQUIT, terminate);
263 	(void) sigset(SIGUSR1, noop);
264 
265 	act.sa_handler = timer;
266 	act.sa_flags = SA_RESTART;
267 	(void *) sigemptyset(&act.sa_mask);
268 	(void *) sigaddset(&act.sa_mask, SIGALRM);
269 	(void *) sigaddset(&act.sa_mask, SIGHUP);
270 	(void *) sigaction(SIGALRM, &act, NULL);
271 	(void *) sigaction(SIGHUP, &act, NULL);
272 
273 	if (debugl == MAX_TND_DEBUG) {
274 		(void) fprintf(logf, "%s : ", gettime());
275 		(void) fprintf(logf, gettext("tnd started. pid= %d\n"),
276 		    getpid());
277 		(void) fprintf(logf, "%s : ", gettime());
278 		(void) fprintf(logf,
279 		    gettext("max level debugging! not forking\n"));
280 		(void) fflush(logf);
281 	} else {
282 		detachfromtty();
283 	}
284 
285 	if (!delay_poll_flag) {
286 		(void) sigprocmask(SIG_BLOCK, &act.sa_mask, NULL);
287 		timer();
288 		(void) sigprocmask(SIG_UNBLOCK, &act.sa_mask, NULL);
289 	}
290 
291 	if (debugl != MAX_TND_DEBUG) {
292 		(void) sigsend(P_PID, getppid(), SIGUSR1);
293 	}
294 
295 	(void) tnd_serve();
296 
297 	/* NOT REACHED */
298 	return (0);
299 }
300 
301 
302 /*
303  * Compare addresses after masking off unneeded bits.
304  * We do this to handle addresses where prefix_len is
305  * less than the bit length.
306  */
307 static int
308 rhaddr_compar_mask(struct sockaddr_in *tp1, struct tnd_tnrhdb_c *tp2, int i)
309 {
310 	struct sockaddr_in *saddrp;
311 	in_addr_t tmpmask = rh_index_to_mask(i);
312 
313 	saddrp = (struct sockaddr_in *)(&tp2->rh_ent.rh_address.ip_addr_v4);
314 
315 #ifdef DEBUG
316 	(void) fprintf(logf, gettext("rhaddr_compar_mask mask = 0x%4x, \
317 	    tp1 = 0x%4x, tp2 = 0x%4x\n"), tmpmask, (tp1->sin_addr),
318 	    (saddrp->sin_addr.s_addr & tmpmask));
319 	(void) fprintf(logf, gettext("rhaddr_compar_mask return = %d\n"),
320 	    (tp1->sin_addr.s_addr == (saddrp->sin_addr.s_addr & tmpmask)));
321 #endif
322 	return (tp1->sin_addr.s_addr == (saddrp->sin_addr.s_addr & tmpmask));
323 }
324 
325 
326 /*
327  * we use this where exact match is needed.
328  */
329 static int
330 rhaddr_compar(struct sockaddr_in *tp1, struct tnd_tnrhdb_c *tp2)
331 {
332 	struct sockaddr_in *saddrp;
333 
334 	saddrp = (struct sockaddr_in *)(&tp2->rh_ent.rh_address.ip_addr_v4);
335 
336 #ifdef DEBUG
337 	(void) fprintf(logf, gettext("\t tp1 saddrp IP : %s %s\n"),
338 	    inet_ntoa(tp1->sin_addr), inet_ntoa(saddrp->sin_addr));
339 #endif
340 
341 	return (tp1->sin_addr.s_addr == saddrp->sin_addr.s_addr);
342 }
343 
344 /*
345  * Compare v6 addresses after masking off unneeded bits.
346  * We do this to handle addresses where prefix_len is
347  * less than the bit length.
348  */
349 static int
350 rhaddr_compar_mask_v6(struct sockaddr_in6 *tp1, struct tnd_tnrhdb_c *tp2, int i)
351 {
352 	struct sockaddr_in6 *saddrp;
353 	in6_addr_t tmpmask;
354 
355 	(void) rh_index_to_mask_v6(i, &tmpmask);
356 	saddrp = (struct sockaddr_in6 *)(&tp2->rh_ent.rh_address.ip_addr_v6);
357 	return (V6_MASK_EQ(tp1->sin6_addr, tmpmask, saddrp->sin6_addr));
358 }
359 
360 /*
361  * we use this where v6 exact match is needed.
362  */
363 static int
364 rhaddr_compar_v6(struct sockaddr_in6 *tp1, struct tnd_tnrhdb_c *tp2)
365 {
366 	struct sockaddr_in6 *saddrp;
367 
368 	saddrp = (struct sockaddr_in6 *)(&tp2->rh_ent.rh_address.ip_addr_v6);
369 	return (IN6_ARE_ADDR_EQUAL(&tp1->sin6_addr, &saddrp->sin6_addr));
370 }
371 
372 static int
373 get_hashvalue(in_addr_t addr)
374 {
375 	unsigned char *bp;
376 
377 	bp = (unsigned char *) &addr;
378 	return ((bp[0] ^ bp[1] ^ bp[2] ^ bp[3]) % TNRH_TABLE_HASH_SIZE);
379 }
380 
381 /*
382  * Convert length for a mask to the mask.
383  */
384 static in_addr_t
385 rh_index_to_mask(uint_t masklen)
386 {
387 	if (masklen == 0)
388 		return (0);
389 	return (htonl(RH_HOST_MASK << (IP_ABITS - masklen)));
390 }
391 
392 /*
393  * Convert length for a mask to the mask.
394  * Returns the argument bitmask.
395  */
396 static in6_addr_t *
397 rh_index_to_mask_v6(uint_t masklen, in6_addr_t *bitmask)
398 {
399 	uint32_t *ptr;
400 
401 	*bitmask = ipv6_all_zeros;
402 
403 	ptr = (uint32_t *)bitmask;
404 	while (masklen > 32) {
405 		*ptr++ = 0xffffffffU;
406 		masklen -= 32;
407 	}
408 	*ptr = htonl(0xffffffffU << (32 - masklen));
409 	return (bitmask);
410 }
411 
412 
413 static void
414 parse_opts(argc, argv)
415 	int argc;
416 	char **argv;
417 {
418 	char *logfile = TNDLOG;
419 	extern char *optarg;
420 	int c;
421 
422 	while ((c = getopt(argc, argv, "d:f:p:n")) != EOF)
423 	    switch (c) {
424 	    case 'd':
425 		if (isnumber(optarg)) {
426 		    debugl = atoi(optarg);
427 		    if (check_debugl(debugl) == -1)
428 		    debugl = 1; /* default to 1 */
429 		} else {
430 		    usage();
431 		    exit(1);
432 		}
433 		break;
434 	    case 'f':
435 		logfile = optarg;
436 		break;
437 	    case 'p':
438 		if (isnumber(optarg)) {
439 		    poll_interval = atoi(optarg);
440 		    if (poll_interval == 0)
441 			usage();
442 		} else {
443 		    usage();
444 		}
445 		break;
446 	    case 'n':
447 		delay_poll_flag = 1;
448 		break;
449 	    case '?':
450 		usage();
451 	    }
452 
453 	logf = tnlog_open(logfile);
454 }
455 
456 static int
457 check_debugl(debug_level)
458 	int debug_level;
459 {
460 	if (debug_level > MAX_TND_DEBUG) {
461 	    if ((debugl > 0) && (logf != NULL)) {
462 		(void) fprintf(logf, "%s : ", gettime());
463 		(void) fprintf(logf,
464 		    gettext("invalid debug level: %d, not changed!\n"),
465 			debug_level);
466 		(void) fflush(logf);
467 	    }
468 	    cprint("invalid debug level: %d, not changed!\n",
469 		debug_level);
470 	    return (-1);
471 	}
472 	return (0);
473 }
474 
475 static FILE *
476 tnlog_open(logfile)
477 	char *logfile;
478 {
479 	FILE *fp;
480 
481 	if ((fp = fopen(logfile, "a")) == NULL) {
482 		syslog(LOG_ERR, "unable to open logfile %s",
483 			logfile);
484 		exit(-1);
485 	}
486 	(void) fprintf(fp, "%s : ", gettime());
487 	(void) fprintf(fp, gettext("tnd starting\n"));
488 
489 	return (fp);
490 }
491 
492 static void
493 detachfromtty()
494 {
495 	pid_t tnd_pid;
496 
497 	(void) close(0);
498 	(void) close(1);
499 	(void) close(2);
500 	switch (tnd_pid = fork()) {
501 	case (pid_t)-1:
502 		if (debugl && (logf != NULL)) {
503 			(void) fprintf(logf, "%s : ", gettime());
504 			(void) fprintf(logf,
505 			    gettext("fork() failed: %s\n"), strerror(errno));
506 			(void) fflush(logf);
507 		}
508 		cprint("fork() failed: %s\n", strerror(errno));
509 		break;
510 	case 0:
511 		break;
512 	default:
513 		if (debugl && (logf != NULL)) {
514 			(void) fprintf(logf, "%s : ", gettime());
515 			(void) fprintf(logf,
516 			    gettext("tnd started. pid= %d\n"), tnd_pid);
517 			(void) fflush(logf);
518 		}
519 		/*
520 		 * Suspend parent till child signals it. We catch the signal
521 		 * in order to return correct exit value.
522 		 */
523 
524 		(void) pause();
525 		exit(0);
526 	}
527 	(void) setsid();
528 	(void) open("/dev/null", O_RDWR, 0);
529 	(void) dup(0);
530 	(void) dup(0);
531 }
532 
533 static void
534 usage()
535 {
536 	(void) fprintf(stderr, gettext(
537 	    "Usage:\n\ttnd [-d debug-level][-f debug-file]"
538 	    "[-p poll-interval]\n"));
539 
540 	exit(1);
541 }
542 
543 static int
544 isnumber(s)
545 char *s;
546 {
547 	register int c;
548 
549 	/* LINTED */
550 	while (c = *s++)
551 		if (!isdigit(c))
552 			return (0);
553 	return (1);
554 }
555 
556 
557 /*
558  * match any entry in any tree
559  *	used in tree removal
560  */
561 /* ARGSUSED */
562 static int
563 any_compar(const void *v1, const void *v2)
564 {
565 	return (0);
566 }
567 
568 static int
569 tp_compar(const void *v1, const void *v2)
570 {
571 	struct tnd_tnrhtp_c	*tp1 = (struct tnd_tnrhtp_c *)v1;
572 	struct tnd_tnrhtp_c	*tp2 = (struct tnd_tnrhtp_c *)v2;
573 	return (strcmp(tp1->tp_ent.name, tp2->tp_ent.name));
574 }
575 
576 /*
577  * Build tree of tp entries, tossing duplicates
578  */
579 static int
580 nss_get_tp()
581 {
582 	tsol_tpent_t tp; /* to store result */
583 	tsol_tpent_t *tpp;
584 	struct tnd_tnrhtp_c *new, **old;
585 	int count = 0;
586 
587 	tpp = &tp;
588 
589 	tsol_settpent(1);
590 
591 	while ((tpp = (tsol_tpent_t *)tsol_gettpent()) != NULL) {
592 		if ((new = (struct tnd_tnrhtp_c *)
593 		    calloc(1, sizeof (struct tnd_tnrhtp_c))) == NULL)
594 			continue;
595 		(void) memcpy(&new->tp_ent, tpp, sizeof (tp));
596 		old = (struct tnd_tnrhtp_c **)tsearch(new, &tp_tree, tp_compar);
597 		if (*old != new)
598 			free(new);
599 		else
600 			count++;
601 	}
602 	tsol_endtpent();
603 
604 	return (count);
605 }
606 
607 /* load tp ents into kernel */
608 static void
609 load_tp()
610 {
611 	twalk(tp_tree, load_tp_entry);
612 }
613 
614 
615 static void
616 /* LINTED */
617 load_tp_entry(struct tnd_tnrhtp_c **tppp, VISIT visit, int level)
618 {
619 	struct tnd_tnrhtp_c *tpp;
620 
621 	if (!(visit == postorder || visit == leaf))
622 		return;
623 
624 	tpp = *tppp;
625 	if (tnrhtp(TNDB_LOAD, &tpp->tp_ent)) {
626 		if (debugl && (logf != NULL)) {
627 			(void) fprintf(logf, "%s : ", gettime());
628 			(void) fprintf(logf, gettext("tnrhtp() failed 0: %s\n"),
629 			    strerror(errno));
630 			(void) fprintf(logf,
631 			    gettext("load of remote-host template "
632 			    "%s into kernel cache failed\n"),
633 			    tpp->tp_ent.name);
634 			(void) fflush(logf);
635 		}
636 		cprint("tnrhtp() failed here 1: %s\n", strerror(errno));
637 	}
638 }
639 
640 static void
641 tp_flush_cache()
642 {
643 	struct tnd_tnrhtp_c	dummy;
644 	struct tnd_tnrhtp_c	*tp;
645 
646 	while (tp = tfind(&dummy, tp_tree, any_compar)) {
647 		(void) tdelete(tp, &tp_tree, tp_compar);
648 		free(tp);
649 	}
650 }
651 
652 /*
653  * Build/update the table of rh entries from the
654  * name service sources, files, ldap etc.
655  */
656 static int
657 nss_get_rh()
658 {
659 	int found_entry = 0;
660 	int count = 0;
661 	int newflag = 0;
662 	struct tsol_rhent rh; /* to store result */
663 	struct tsol_rhent *rhp;
664 	tsol_tpent_t tp;
665 	sa_family_t af;
666 	int v6cnt = 0;
667 
668 	rhp = &rh;
669 
670 	tsol_setrhent(1);
671 	while ((rhp = (struct tsol_rhent *)
672 	    tsol_getrhent()) != NULL) {
673 		/*
674 		 * Check if this is a known template name
675 		 * Entries with missing template in kernel will be logged
676 		 * and not added to cache.
677 		 */
678 
679 		(void) fprintf(logf, gettext("getrhent template name: %s\n"),
680 		    rhp->rh_template);
681 
682 		(void) strncpy(tp.name, rhp->rh_template, TNTNAMSIZ - 1);
683 		if (tnrhtp(TNDB_GET, &tp) != 0) {
684 			if (debugl && (logf != NULL))
685 				(void) fprintf(logf,
686 				    gettext("Unknown template name: %s\n"),
687 				    rhp->rh_template);
688 			cprint(gettext("Unknown template name: %s\n"),
689 			    rhp->rh_template);
690 			continue;
691 		}
692 		found_entry++;		/* found a valid tnrhdb entry */
693 		af = rhp->rh_address.ta_family;
694 
695 		if (af == AF_INET) {
696 #ifdef DEBUG
697 			(void) fprintf(logf, gettext("nss_get_rh() v4\n"));
698 #endif
699 			(void) rw_wrlock(&entire_rwlp);
700 			(void) rw_wrlock(&cache_rwlp);
701 
702 			/*
703 			 * Both cache table and entire table can be modified
704 			 * by this function. So, get both locks.
705 			 */
706 			newflag = rhtable_search_and_update(rhp, 1);
707 
708 			(void) rw_unlock(&cache_rwlp);
709 			(void) rw_unlock(&entire_rwlp);
710 		} else if (af == AF_INET6) {
711 #ifdef DEBUG
712 			(void) fprintf(logf, gettext("nss_get_rh() v6\n"));
713 #endif
714 			v6cnt++;
715 			(void) rw_wrlock(&entire_rwlp_v6);
716 			(void) rw_wrlock(&cache_rwlp_v6);
717 
718 			/*
719 			 * Both cache table and entire table can be modified
720 			 * by this function. So, get both locks.
721 			 */
722 			newflag = rhtable_search_and_update_v6(rhp, 1);
723 
724 			(void) rw_unlock(&cache_rwlp_v6);
725 			(void) rw_unlock(&entire_rwlp_v6);
726 		}
727 		if (newflag)
728 			count++;
729 	}
730 	tsol_endrhent();
731 
732 	/*
733 	 * If the first tsol_getrhent() failed, we bail out and
734 	 * try again at the next poll interval, just in case the
735 	 * name service was not reachable the first time.
736 	 */
737 	if (!found_entry) {
738 #ifdef	DEBUG
739 		if (logf != NULL)
740 			(void) fprintf(logf,
741 			    gettext("Unable to contact ldap server?\n"));
742 #endif
743 		return (count);
744 	}
745 
746 	(void) rw_wrlock(&entire_rwlp);
747 	(void) rw_wrlock(&cache_rwlp);
748 	/*
749 	 * Handle deletions in the name service entries
750 	 * Both cache table and entire table can be modified
751 	 * by this function. So, get both locks.
752 	 */
753 	count += handle_unvisited_nodes();
754 
755 	(void) rw_unlock(&cache_rwlp);
756 	(void) rw_unlock(&entire_rwlp);
757 
758 	if (v6cnt > 0) {
759 		(void) rw_wrlock(&entire_rwlp_v6);
760 		(void) rw_wrlock(&cache_rwlp_v6);
761 		/*
762 		 * Handle deletions in the name service entries
763 		 * Both cache table and entire table can be modified
764 		 * by this function. So, get both locks.
765 		 */
766 		count += handle_unvisited_nodes_v6();
767 
768 		(void) rw_unlock(&cache_rwlp_v6);
769 		(void) rw_unlock(&entire_rwlp_v6);
770 	}
771 
772 	return (count);
773 }
774 
775 /*
776  * Check if any deletions in  the name service tables
777  * affect the cache entries. We need to do this
778  * in order to not flush the entrie kernel tnrhdb
779  * cache every time we poll the name services.
780  */
781 static int
782 handle_unvisited_nodes()
783 {
784 	int i, j, cnt = 0;
785 	tnrh_tlb_t *tlbt;
786 	tnd_tnrhdb_t *rhent, *prev;
787 
788 	for (i = 0; i < TNRH_TABLE_HASH_SIZE; i++)
789 		if ((tlbt = tnrh_cache_table[i]) != NULL)
790 			do {
791 				if (tlbt->src->visited == 0) {
792 					/*
793 					 * Mark for deletion of both our cache
794 					 * entry and the kernel cache entry.
795 					 */
796 					tlbt->reload = TNDB_DELETE;
797 					cnt++;
798 				}
799 
800 				tlbt = tlbt->next;
801 			} while (tlbt != NULL);
802 
803 	/*
804 	 * Remove any unvisited nodes. This can
805 	 * happen if they are not in use by any cache entry. Then,
806 	 * mark all nodes in entire_table, un-visited, for next iteration.
807 	 */
808 
809 	for (i = 0; i <= IP_ABITS; i++) {
810 		if (tnrh_entire_table[i] == NULL)
811 			continue;
812 
813 		for (j = 0; j < TNRH_TABLE_HASH_SIZE; j++) {
814 			prev = rhent = tnrh_entire_table[i][j];
815 
816 			while (rhent != NULL) {
817 				if (rhent->visited == 0) {
818 					/*
819 					 * Check if start node
820 					 */
821 					if (rhent == tnrh_entire_table[i][j]) {
822 						prev = tnrh_entire_table[i][j] =
823 						    rhent->rh_next;
824 					} else {
825 						/* bypass the deleted node */
826 						prev->rh_next = rhent->rh_next;
827 						prev = prev->rh_next;
828 					}
829 
830 					free(rhent);
831 
832 					if (prev == NULL)
833 						break;
834 					else {
835 						rhent = prev;
836 						continue;
837 					}
838 				} else
839 					rhent->visited = 0;
840 
841 				prev = rhent;
842 				rhent = rhent->rh_next;
843 			}
844 		}
845 	}
846 
847 	return (cnt);
848 }
849 
850 /*
851  * Check if any deletions in  the name service tables
852  * affect the cache entries. We need to do this
853  * in order to not flush the entrie kernel tnrhdb
854  * cache every time we poll the name services.
855  */
856 static int
857 handle_unvisited_nodes_v6()
858 {
859 	int i, j, cnt = 0;
860 	tnrh_tlb_ipv6_t *tlbt;
861 	tnd_tnrhdb_t *rhent, *prev;
862 
863 	for (i = 0; i < TNRH_TABLE_HASH_SIZE; i++)
864 	if ((tlbt = tnrh_cache_table_v6[i]) != NULL)
865 	do {
866 		if (tlbt->src->visited == 0) {
867 			/*
868 			 * Mark for deletion of both our cache entry
869 			 * and the kernel cache entry.
870 			 */
871 			tlbt->reload = TNDB_DELETE;
872 			cnt++;
873 		}
874 
875 		tlbt = tlbt->next;
876 	} while (tlbt != NULL);
877 
878 	/*
879 	 * Remove any unvisited nodes. This can
880 	 * happen if they are not in use by any cache entry. Then,
881 	 * mark all nodes in entire_table, un-visited, for next iteration.
882 	 */
883 
884 	for (i = 0; i <= IPV6_ABITS; i++) {
885 	if (tnrh_entire_table_v6[i] == NULL)
886 		continue;
887 
888 	for (j = 0; j < TNRH_TABLE_HASH_SIZE; j++) {
889 		prev = rhent = tnrh_entire_table_v6[i][j];
890 
891 		while (rhent != NULL) {
892 		if (rhent->visited == 0) {	/* delete the node */
893 			/* Check if start node */
894 			if (rhent == tnrh_entire_table_v6[i][j]) {
895 				prev = tnrh_entire_table_v6[i][j] =
896 				    rhent->rh_next;
897 			} else {
898 				/* bypass the deleted node */
899 				prev->rh_next = rhent->rh_next;
900 				prev = prev->rh_next;
901 			}
902 
903 			free(rhent);
904 			if (prev == NULL)
905 				break;
906 			else {
907 				rhent = prev;
908 				continue;
909 			}
910 		} else
911 			rhent->visited = 0;
912 
913 		prev = rhent;
914 		rhent = rhent->rh_next;
915 		}
916 	}
917 	}
918 
919 	return (cnt);
920 }
921 
922 
923 /*
924  * Search the hash chain for the address. If not found,
925  * add the entry to the hash table. If necessary,
926  * construct the hash table.
927  * If the rh entry is in table, we may update its template name
928  */
929 static int
930 rhtable_search_and_update(struct tsol_rhent *ent, int duplflag)
931 {
932 	struct sockaddr_in *saddrp;
933 	unsigned char hash;
934 	tnd_tnrhdb_t *rhent;
935 	int i;
936 	int rflag = 1;
937 
938 	struct tnd_tnrhdb_c *new;
939 
940 	saddrp = (struct sockaddr_in *)&ent->rh_address.ip_addr_v4;
941 	hash = (unsigned char) get_hashvalue(saddrp->sin_addr.s_addr);
942 	i = ent->rh_prefix;
943 
944 #ifdef DEBUG
945 	(void) fprintf(logf, gettext("\trhtable_search_and_update IP address:\
946 		%s\n"), inet_ntoa(saddrp->sin_addr));
947 #endif
948 
949 	if (tnrh_entire_table[i] == NULL) {
950 		if ((tnrh_entire_table[i] = (tnd_tnrhdb_t **)calloc(
951 		    TNRH_TABLE_HASH_SIZE, sizeof (tnd_tnrhdb_t *))) == NULL) {
952 			return (0);
953 		}
954 	}
955 
956 	rhent = tnrh_entire_table[i][hash];
957 #ifdef DEBUG
958 	(void) fprintf(logf, gettext("\tsearch_and_update i = %d hash = %d\n"),
959 	    i, hash);
960 	if (rhent != NULL) {
961 		(void) fprintf(logf, gettext("\trhent visited  = %d\n"),
962 		    rhent->visited);
963 		print_entry(&rhent->rh_ent, AF_INET);
964 	} else {
965 		(void) fprintf(logf, gettext("\tsearch_and_update null\n"));
966 	}
967 #endif
968 	while (rhent != NULL) {
969 		if (rhaddr_compar(saddrp, rhent) == 1) {
970 			/* Check if this is a duplicate entry */
971 			if ((rhent->visited == 1) && duplflag)
972 				return (0);
973 
974 			if (duplflag)
975 				rhent->visited = 1;
976 
977 			if (strcmp(ent->rh_template,
978 			    rhent->rh_ent.rh_template) != 0) {
979 				/*
980 				 * Template is changed in the name service.
981 				 * Use the new template.
982 				 */
983 				(void) strcpy(rhent->rh_ent.rh_template,
984 				    ent->rh_template);
985 				/*
986 				 * Check if this modified entry
987 				 * affects the cache table.
988 				 */
989 				rflag = update_cache_table(ent, rhent);
990 				return (rflag);
991 			} else
992 				return (0);
993 		}
994 		rhent = rhent->rh_next;
995 	}
996 
997 	/* Not found. Add the entry */
998 	new = (struct tnd_tnrhdb_c *)calloc(1,
999 	    sizeof (struct tnd_tnrhdb_c));
1000 	if (new == NULL)
1001 		return (0);
1002 	(void) memcpy(&new->rh_ent, ent, sizeof (struct tsol_rhent));
1003 	if (duplflag)
1004 		new->visited = 1;	/* Mark all new nodes visited */
1005 
1006 	/* linked list. Insert in the beginning */
1007 	new->rh_next = tnrh_entire_table[i][hash];
1008 	tnrh_entire_table[i][hash] = new;
1009 #ifdef DEBUG
1010 	(void) fprintf(logf, gettext("rhtable added i = %d, hash = %d\n"),
1011 	    i, hash);
1012 #endif
1013 
1014 	/* Check if the new entry affects the cache table */
1015 	rflag = update_cache_table(ent, new);
1016 
1017 #ifdef DEBUG
1018 	(void) fprintf(logf, gettext("search_and_update rflag=%d\n"), rflag);
1019 #endif
1020 	return (rflag);
1021 }
1022 
1023 /*
1024  * Search the hash chain for the address. If not found,
1025  * add the entry to the hash table. If necessary,
1026  * construct the hash table.
1027  */
1028 static int
1029 rhtable_search_and_update_v6(struct tsol_rhent *ent, int duplflag)
1030 {
1031 	struct sockaddr_in6 *saddrp;
1032 	unsigned char hash;
1033 	tnd_tnrhdb_t *rhent;
1034 	int i;
1035 	int rflag = 1;
1036 
1037 	struct tnd_tnrhdb_c *new;
1038 	in6_addr_t tmpmask6;
1039 
1040 	saddrp = (struct sockaddr_in6 *)&ent->rh_address.ip_addr_v6;
1041 	i = ent->rh_prefix;
1042 	(void) rh_index_to_mask_v6(i, &tmpmask6);
1043 	hash = (unsigned char) TNRH_ADDR_MASK_HASH_V6(saddrp->sin6_addr,
1044 	    tmpmask6);
1045 
1046 	if (tnrh_entire_table_v6[i] == NULL) {
1047 		if ((tnrh_entire_table_v6[i] = (tnd_tnrhdb_t **)calloc(
1048 		    TNRH_TABLE_HASH_SIZE, sizeof (tnd_tnrhdb_t *))) == NULL) {
1049 			return (0);
1050 		}
1051 	}
1052 
1053 	rhent = tnrh_entire_table_v6[i][hash];
1054 	while (rhent != NULL) {
1055 		if (rhaddr_compar_v6(saddrp, rhent) == 1) {
1056 			/* Check if this is a duplicate entry */
1057 			if ((rhent->visited == 1) && duplflag)
1058 				return (0);
1059 
1060 			if (duplflag)
1061 				rhent->visited = 1;
1062 
1063 			if (strcmp(ent->rh_template,
1064 			    rhent->rh_ent.rh_template) != 0) {
1065 				/*
1066 				 * Template is changed in the name service.
1067 				 * Use the new template.
1068 				 */
1069 				(void) strcpy(rhent->rh_ent.rh_template,
1070 				    ent->rh_template);
1071 				/*
1072 				 * Check if this modified entry
1073 				 * affects the cache table.
1074 				 */
1075 				rflag = update_cache_table_v6(ent, rhent);
1076 				return (rflag);
1077 			} else
1078 				return (0);
1079 		}
1080 		rhent = rhent->rh_next;
1081 	}
1082 
1083 	/* Not found. Add the entry */
1084 	new = (struct tnd_tnrhdb_c *)calloc(1, sizeof (struct tnd_tnrhdb_c));
1085 	if (new == NULL)
1086 		return (0);
1087 	(void) memcpy(&new->rh_ent, ent, sizeof (struct tsol_rhent));
1088 	if (duplflag)
1089 		new->visited = 1;	/* Mark all new nodes visited */
1090 
1091 	/* linked list. Insert in the beginning */
1092 	new->rh_next = tnrh_entire_table_v6[i][hash];
1093 	tnrh_entire_table_v6[i][hash] = new;
1094 
1095 	/* Check if the new entry affects the cache table */
1096 	rflag = update_cache_table_v6(ent, new);
1097 
1098 	return (rflag);
1099 }
1100 
1101 /*
1102  * The array element i points to the hash table.
1103  * Search the hash chain for the address.
1104  */
1105 static struct tnd_tnrhdb_c *
1106 rhtable_lookup(struct sockaddr_in *saddrp, int i)
1107 {
1108 	unsigned char hash;
1109 	tnd_tnrhdb_t *rhent;
1110 
1111 	if (tnrh_entire_table[i] == NULL)
1112 		return (NULL);
1113 
1114 	hash = (unsigned char) get_hashvalue(saddrp->sin_addr.s_addr);
1115 	rhent = tnrh_entire_table[i][hash];
1116 
1117 #ifdef DEBUG
1118 	(void) fprintf(logf, gettext("rhtable_lookup i = %d, hash = %d\n"),
1119 	    i, hash);
1120 #endif
1121 
1122 	while (rhent != NULL) {
1123 #ifdef DEBUG
1124 	struct sockaddr_in *saddrp2;
1125 	saddrp2 = (struct sockaddr_in *)(&rhent->rh_ent.rh_address.ip_addr_v4);
1126 	(void) fprintf(logf, gettext("rhtable_lookup addr = %s, tmpl = %s\n"),
1127 	    inet_ntoa(saddrp2->sin_addr), rhent->rh_ent.rh_template);
1128 #endif
1129 		if (rhaddr_compar_mask(saddrp, rhent, i) == 1)
1130 			return (rhent);
1131 		rhent = rhent->rh_next;
1132 	}
1133 
1134 #ifdef DEBUG
1135 	(void) fprintf(logf, gettext("\trhtable_lookup failed\n"));
1136 #endif
1137 
1138 	/* Not found */
1139 	return (NULL);
1140 }
1141 
1142 /*
1143  * The array element i points to the hash table.
1144  * Search the hash chain for the address.
1145  */
1146 static struct tnd_tnrhdb_c *
1147 rhtable_lookup_v6(struct sockaddr_in6 *saddrp, in6_addr_t mask, int i)
1148 {
1149 	unsigned char hash;
1150 	tnd_tnrhdb_t *rhent;
1151 
1152 	if (tnrh_entire_table_v6[i] == NULL)
1153 		return (NULL);
1154 
1155 	hash = (unsigned char) TNRH_ADDR_MASK_HASH_V6(saddrp->sin6_addr, mask);
1156 	rhent = tnrh_entire_table_v6[i][hash];
1157 
1158 	while (rhent != NULL) {
1159 		if (rhaddr_compar_mask_v6(saddrp, rhent, i) == 1)
1160 			return (rhent);
1161 		rhent = rhent->rh_next;
1162 	}
1163 
1164 	/* Not found */
1165 	return (NULL);
1166 }
1167 
1168 void
1169 add_cache_entry(in_addr_t addr, char *name, int indx,
1170     tnd_tnrhdb_t *src)
1171 {
1172 	unsigned char hash;
1173 	tnrh_tlb_t *tlbt;
1174 
1175 	hash = (unsigned char) get_hashvalue(addr);
1176 
1177 	/* Look if some other thread already added this entry */
1178 	if (lookup_cache_table(addr) != NULL)
1179 		return;
1180 #ifdef DEBUG
1181 	(void) fprintf(logf, gettext("\tenter add_cache_entry\n"));
1182 #endif
1183 	if ((tlbt = (tnrh_tlb_t *)calloc(1, sizeof (tnrh_tlb_t))) == NULL)
1184 		return;
1185 	tlbt->addr = addr;
1186 	(void) strncpy(tlbt->template_name, name, TNTNAMSIZ-1);
1187 	tlbt->masklen_used = indx;
1188 	tlbt->reload = TNDB_LOAD;
1189 	tlbt->src = src;
1190 
1191 #ifdef DEBUG
1192 	(void) fprintf(logf, gettext("adding cache entry\n"));
1193 	print_tlbt(tlbt);
1194 #endif
1195 	/* Add to the chain */
1196 	if (tnrh_cache_table[hash] == NULL) {
1197 		tnrh_cache_table[hash] = tlbt;
1198 	} else {
1199 		/* Add in the beginning */
1200 		tlbt->next = tnrh_cache_table[hash];
1201 		tnrh_cache_table[hash] = tlbt;
1202 	}
1203 }
1204 
1205 static tnrh_tlb_t *
1206 lookup_cache_table(in_addr_t addr)
1207 {
1208 	tnrh_tlb_t *tlbt = NULL;
1209 	unsigned char hash;
1210 
1211 	hash = (unsigned char) get_hashvalue(addr);
1212 	tlbt = tnrh_cache_table[hash];
1213 	while (tlbt != NULL) {
1214 		if (addr == tlbt->addr)
1215 			break;
1216 		tlbt = tlbt->next;
1217 	}
1218 	return (tlbt);
1219 }
1220 
1221 static void
1222 add_cache_entry_v6(in6_addr_t addr, char *name, int indx,
1223 				tnd_tnrhdb_t *src)
1224 {
1225 	unsigned char hash;
1226 	tnrh_tlb_ipv6_t *tlbt;
1227 
1228 	hash = (unsigned char) TNRH_ADDR_HASH_V6(addr);
1229 
1230 	/* Look if some other thread already added this entry */
1231 	if (lookup_cache_table_v6(addr) != NULL)
1232 		return;
1233 
1234 	if ((tlbt = (tnrh_tlb_ipv6_t *)calloc(1,
1235 	    sizeof (tnrh_tlb_ipv6_t))) == NULL)
1236 		return;
1237 	(void) memcpy(&tlbt->addr, &addr, sizeof (in6_addr_t));
1238 	(void) strncpy(tlbt->template_name, name, TNTNAMSIZ-1);
1239 	tlbt->masklen_used = indx;
1240 	tlbt->reload = TNDB_LOAD;
1241 	tlbt->src = src;
1242 
1243 	/* Add to the chain */
1244 	if (tnrh_cache_table_v6[hash] == NULL) {
1245 		tnrh_cache_table_v6[hash] = tlbt;
1246 	} else {
1247 		/* Add in the beginning */
1248 		tlbt->next = tnrh_cache_table_v6[hash];
1249 		tnrh_cache_table_v6[hash] = tlbt;
1250 	}
1251 }
1252 
1253 static tnrh_tlb_ipv6_t *
1254 lookup_cache_table_v6(in6_addr_t addr)
1255 {
1256 	tnrh_tlb_ipv6_t *tlbt = NULL;
1257 	unsigned char hash;
1258 
1259 	hash = (unsigned char) TNRH_ADDR_HASH_V6(addr);
1260 	tlbt = tnrh_cache_table_v6[hash];
1261 	while (tlbt != NULL) {
1262 		if (IN6_ARE_ADDR_EQUAL(&addr, &tlbt->addr))
1263 			break;
1264 		tlbt = tlbt->next;
1265 	}
1266 	return (tlbt);
1267 }
1268 
1269 
1270 /*
1271  * Walk the cache table and check if this IP address/address prefix
1272  * will be a better match for an existing entry in the cache.
1273  * will add cache if not already exists
1274  */
1275 static int
1276 update_cache_table(tsol_rhent_t *ent, tnd_tnrhdb_t *src)
1277 {
1278 	int i;
1279 	char result[TNTNAMSIZ];
1280 	in_addr_t tmpmask;
1281 	in_addr_t addr;
1282 	struct sockaddr_in *saddrp;
1283 	tnrh_tlb_t *tlbt;
1284 	struct tnd_tnrhdb_c	*rhp;
1285 	int rflag = 0;
1286 
1287 	saddrp = (struct sockaddr_in *)&ent->rh_address.ip_addr_v4;
1288 	addr = saddrp->sin_addr.s_addr;
1289 
1290 	(void) rw_rdlock(&cache_rwlp);
1291 	tlbt = lookup_cache_table(addr);
1292 	(void) rw_unlock(&cache_rwlp);
1293 
1294 	if (tlbt == NULL) {
1295 		(void) rw_rdlock(&entire_rwlp);
1296 		for (i = (IP_MASK_TABLE_SIZE - 1); i >= 0; i--) {
1297 #ifdef DEBUG
1298 			(void) fprintf(logf, "update_cache_table i = %d\n", i);
1299 #endif
1300 			if (tnrh_entire_table[i] == NULL)
1301 				continue;
1302 
1303 			tmpmask = rh_index_to_mask(i);
1304 			saddrp->sin_addr.s_addr &= tmpmask;
1305 #ifdef DEBUG
1306 			(void) fprintf(logf,
1307 			    "update_cache_table found i = %d\n", i);
1308 			(void) fprintf(logf, "\ti = %d, tmpmask = 0x%4x\n",
1309 			    i, tmpmask);
1310 #endif
1311 			rhp = (struct tnd_tnrhdb_c *)rhtable_lookup(saddrp, i);
1312 			if (rhp != NULL) {
1313 				(void) strcpy(result, rhp->rh_ent.rh_template);
1314 				/* Add this result to the cache also */
1315 				(void) rw_wrlock(&cache_rwlp);
1316 				add_cache_entry(addr, result, i, rhp);
1317 				rflag++;
1318 				(void) rw_unlock(&cache_rwlp);
1319 				break;
1320 			} else {
1321 #ifdef DEBUG
1322 				(void) fprintf(logf,
1323 				    "rhtable_lookup return null !!");
1324 #endif
1325 			}
1326 		}
1327 		(void) rw_unlock(&entire_rwlp);
1328 	}
1329 
1330 	rflag += walk_cache_table(addr, ent->rh_template, ent->rh_prefix, src);
1331 	return (rflag);
1332 }
1333 
1334 /*
1335  * Walk the cache table and check if this IP address/address prefix
1336  * will be a better match for an existing entry in the cache.
1337  */
1338 static int
1339 update_cache_table_v6(tsol_rhent_t *ent, tnd_tnrhdb_t *src)
1340 {
1341 	int i;
1342 	char result[TNTNAMSIZ];
1343 	in6_addr_t addr;
1344 	struct sockaddr_in6 *saddrp;
1345 	tnrh_tlb_ipv6_t *tlbt;
1346 	struct tnd_tnrhdb_c	*rhp;
1347 	in6_addr_t tmpmask6;
1348 	int rflag = 0;
1349 
1350 	saddrp = (struct sockaddr_in6 *)&ent->rh_address.ip_addr_v6;
1351 	(void) memcpy(&addr, &saddrp->sin6_addr, sizeof (in6_addr_t));
1352 
1353 	/* Look in the cache first */
1354 	(void) rw_rdlock(&cache_rwlp);
1355 	tlbt = lookup_cache_table_v6(addr);
1356 	(void) rw_unlock(&cache_rwlp);
1357 
1358 
1359 	if (tlbt == NULL) {
1360 		(void) rw_rdlock(&entire_rwlp_v6);
1361 		for (i = (IPV6_MASK_TABLE_SIZE - 1); i >= 0; i--) {
1362 			if (tnrh_entire_table_v6[i] == NULL)
1363 				continue;
1364 			(void) rh_index_to_mask_v6(i, &tmpmask6);
1365 			rhp = (struct tnd_tnrhdb_c *)
1366 			    rhtable_lookup_v6(saddrp, tmpmask6, i);
1367 			if (rhp != NULL) {
1368 				(void) strcpy(result, rhp->rh_ent.rh_template);
1369 				/* Add this result to the cache also */
1370 				(void) rw_wrlock(&cache_rwlp_v6);
1371 				add_cache_entry_v6(addr, result, i, rhp);
1372 				rflag++;
1373 				(void) rw_unlock(&cache_rwlp_v6);
1374 				break;
1375 			}
1376 		}
1377 		(void) rw_unlock(&entire_rwlp_v6);
1378 	}
1379 
1380 	rflag += walk_cache_table_v6(addr, ent->rh_template,
1381 	    ent->rh_prefix, src);
1382 	return (rflag);
1383 }
1384 
1385 
1386 /*
1387  * Check if this prefix addr will be a better match
1388  * for an existing entry.
1389  */
1390 static int
1391 is_better_match(in_addr_t newaddr, int indx, tnrh_tlb_t *tlbt)
1392 {
1393 	if (tlbt->masklen_used <= indx) {
1394 		in_addr_t tmpmask = rh_index_to_mask(indx);
1395 
1396 		if ((newaddr) == (tlbt->addr & tmpmask))
1397 			return (1);
1398 	}
1399 
1400 	return (0);
1401 }
1402 
1403 /*
1404  * Walk the cache table and update entries if needed.
1405  * Mark entries for reload to kernel, if somehow their
1406  * template changed.
1407  * why is_better_match() is called???
1408  */
1409 static int
1410 walk_cache_table(in_addr_t newaddr, char *name, int indx, tnd_tnrhdb_t *src)
1411 {
1412 	int i;
1413 	tnrh_tlb_t *tlbt;
1414 	int rflag = 0;
1415 
1416 	for (i = 0; i < TNRH_TABLE_HASH_SIZE; i++) {
1417 		tlbt = tnrh_cache_table[i];
1418 
1419 		while (tlbt != NULL) {
1420 			if (is_better_match(newaddr, indx, tlbt)) {
1421 				tlbt->masklen_used = indx;
1422 				tlbt->src = src;
1423 				/*
1424 				 * Reload to the kernel only if the
1425 				 * host type changed. There is no need
1426 				 * to load, if only the mask used has changed,
1427 				 * since the kernel does not need that
1428 				 * information.
1429 				 */
1430 				if (strcmp(name, tlbt->template_name) != 0) {
1431 					(void) strncpy(tlbt->template_name,
1432 					    name, TNTNAMSIZ-1);
1433 					tlbt->reload = TNDB_LOAD;
1434 					rflag ++;
1435 				}
1436 			}
1437 
1438 			tlbt = tlbt->next;
1439 		}
1440 	}
1441 #ifdef DEBUG
1442 	(void) fprintf(logf, gettext("walk_cache_table rflag=%d\n"), rflag);
1443 #endif
1444 	return (rflag);
1445 }
1446 
1447 /*
1448  * Check if this prefix addr will be a better match
1449  * for an existing entry.
1450  */
1451 static int
1452 is_better_match_v6(in6_addr_t newaddr, int indx, tnrh_tlb_ipv6_t *tlbt)
1453 {
1454 	in6_addr_t tmpmask;
1455 
1456 	if (tlbt->masklen_used <= indx) {
1457 		(void) rh_index_to_mask_v6(indx, &tmpmask);
1458 
1459 		if (V6_MASK_EQ(newaddr, tmpmask, tlbt->addr))
1460 			return (1);
1461 	}
1462 
1463 	return (0);
1464 }
1465 
1466 
1467 /*
1468  * Walk the cache table and update entries if needed.
1469  * Mark entries for reload to kernel, if somehow their
1470  * template changed.
1471  */
1472 static int
1473 walk_cache_table_v6(in6_addr_t newaddr, char *name, int indx, tnd_tnrhdb_t *src)
1474 {
1475 	int i;
1476 	tnrh_tlb_ipv6_t *tlbt;
1477 	int rflag = 0;
1478 
1479 	for (i = 0; i < TNRH_TABLE_HASH_SIZE; i++) {
1480 		tlbt = tnrh_cache_table_v6[i];
1481 
1482 		while (tlbt != NULL) {
1483 			if (is_better_match_v6(newaddr, indx, tlbt)) {
1484 				tlbt->masklen_used = indx;
1485 				tlbt->src = src;
1486 				/*
1487 				 * Reload to the kernel only if the
1488 				 * host type changed. There is no need
1489 				 * to load, if only the mask used has changed,
1490 				 * since the kernel does not need that
1491 				 * information.
1492 				 */
1493 				if (strcmp(name, tlbt->template_name) != 0) {
1494 					(void) strncpy(tlbt->template_name,
1495 					    name, TNTNAMSIZ-1);
1496 					tlbt->reload = TNDB_LOAD;
1497 					rflag ++;
1498 				}
1499 			}
1500 
1501 			tlbt = tlbt->next;
1502 		}
1503 	}
1504 
1505 	return (rflag);
1506 }
1507 
1508 /*
1509  * load/delete marked rh ents into kernel
1510  * depending on the reload flag by invoking tnrh().
1511  * It will mark other entries as TNDB_NOOP
1512  */
1513 static void
1514 load_rh_marked()
1515 {
1516 	int i;
1517 	tnrh_tlb_t *tlbt, *prev;
1518 	struct tsol_rhent rhentp;
1519 
1520 	(void) memset((char *)&rhentp, '\0', sizeof (rhentp));
1521 
1522 	for (i = 0; i < TNRH_TABLE_HASH_SIZE; i++) {
1523 
1524 		prev = tlbt = tnrh_cache_table[i];
1525 
1526 		while (tlbt != NULL) {
1527 			if ((tlbt->reload == TNDB_LOAD) ||
1528 			    (tlbt->reload == TNDB_DELETE)) {
1529 			/*
1530 			 * We have to call tnrh() with tsol_rhent argument.
1531 			 * Construct such a struct from the tlbt struct we have.
1532 			 */
1533 				rhentp.rh_address.ip_addr_v4.sin_addr.s_addr =
1534 				    tlbt->addr;
1535 				rhentp.rh_address.ip_addr_v4.sin_family =
1536 				    AF_INET;
1537 				rhentp.rh_prefix = tlbt->masklen_used;
1538 				(void) strcpy(rhentp.rh_template,
1539 				    tlbt->template_name);
1540 
1541 #ifdef DEBUG
1542 				(void) fprintf(logf, "load op =%d\n",
1543 				    tlbt->reload);
1544 				print_tlbt(tlbt);
1545 #endif
1546 				update_rh_entry(tlbt->reload, &rhentp);
1547 
1548 				if (tlbt->reload == TNDB_DELETE) {
1549 					if (tlbt == tnrh_cache_table[i]) {
1550 						tnrh_cache_table[i] =
1551 						    tlbt->next;
1552 						prev = tnrh_cache_table[i];
1553 					} else {
1554 						prev->next = tlbt->next;
1555 						prev = prev->next;
1556 					}
1557 
1558 					free(tlbt);
1559 					if (prev == NULL)
1560 						break;
1561 					else {
1562 						tlbt = prev;
1563 						continue;
1564 					}
1565 				}
1566 				tlbt->reload = TNDB_NOOP;
1567 			}
1568 
1569 			prev = tlbt;
1570 			tlbt = tlbt->next;
1571 		}
1572 	}
1573 
1574 }
1575 
1576 /* load marked rh ents into kernel */
1577 static void
1578 load_rh_marked_v6()
1579 {
1580 	int i;
1581 	tnrh_tlb_ipv6_t *tlbt, *prev;
1582 	struct tsol_rhent rhentp;
1583 
1584 	(void) memset((char *)&rhentp, '\0', sizeof (rhentp));
1585 
1586 	for (i = 0; i < TNRH_TABLE_HASH_SIZE; i++) {
1587 		prev = tlbt = tnrh_cache_table_v6[i];
1588 
1589 		while (tlbt != NULL) {
1590 		if ((tlbt->reload == TNDB_LOAD) ||
1591 		    (tlbt->reload == TNDB_DELETE)) {
1592 			/*
1593 			 * We have to call tnrh() with tsol_rhent argument.
1594 			 * Construct such a struct from the tlbt struct we have.
1595 			 */
1596 			(void) memcpy(&rhentp.rh_address.ip_addr_v6.sin6_addr,
1597 			    &tlbt->addr, sizeof (in6_addr_t));
1598 			rhentp.rh_address.ip_addr_v6.sin6_family = AF_INET6;
1599 			rhentp.rh_prefix = tlbt->masklen_used;
1600 			(void) strcpy(rhentp.rh_template, tlbt->template_name);
1601 
1602 			update_rh_entry(tlbt->reload, &rhentp);
1603 
1604 			if (tlbt->reload == TNDB_DELETE) {
1605 				if (tlbt == tnrh_cache_table_v6[i]) {
1606 					tnrh_cache_table_v6[i] =
1607 					    tlbt->next;
1608 					prev = tnrh_cache_table_v6[i];
1609 				} else {
1610 					prev->next = tlbt->next;
1611 					prev = prev->next;
1612 				}
1613 
1614 				free(tlbt);
1615 				if (prev == NULL)
1616 					break;
1617 				else {
1618 					tlbt = prev;
1619 					continue;
1620 				}
1621 			}
1622 			tlbt->reload = TNDB_NOOP;
1623 		}
1624 
1625 		prev = tlbt;
1626 		tlbt = tlbt->next;
1627 	}
1628 	}
1629 
1630 }
1631 
1632 /*
1633  * Does the real load/delete for the entry depending on op code.
1634  */
1635 
1636 static void
1637 update_rh_entry(int op, struct tsol_rhent *rhentp)
1638 {
1639 #ifdef DEBUG
1640 	(void) fprintf(logf, gettext("\t###update_rh_entry op = %d\n"), op);
1641 	print_entry(rhentp, AF_INET);
1642 #endif
1643 	if (tnrh(op, rhentp) != 0) {
1644 		if (debugl && (logf != NULL)) {
1645 			(void) fprintf(logf, "%s : ", gettime());
1646 			(void) fprintf(logf, gettext("tnrh() failed: %s\n"),
1647 			    strerror(errno));
1648 			if (op == TNDB_LOAD)
1649 			(void) fprintf(logf,
1650 			    gettext("load of remote host database "
1651 			    "%s into kernel cache failed\n"),
1652 			    rhentp->rh_template);
1653 			if (op == TNDB_DELETE)
1654 			(void) fprintf(logf,
1655 			    gettext("delete of remote host database "
1656 			    "%s from kernel cache failed\n"),
1657 			    rhentp->rh_template);
1658 			(void) fflush(logf);
1659 		}
1660 		cprint("tnrh() failed..: %s\n", strerror(errno));
1661 	}
1662 }
1663 
1664 static void
1665 timer()
1666 {
1667 	poll_now();
1668 	(void) alarm(poll_interval);
1669 }
1670 
1671 #define	max(a, b)	((a) > (b) ? (a) : (b))
1672 
1673 static void
1674 poll_now()
1675 {
1676 
1677 	(void) fprintf(logf, "enter poll_now at %s \n", gettime());
1678 	(void) fflush(logf);
1679 
1680 	if (nss_get_tp() > 0) {
1681 		load_tp();
1682 		tp_flush_cache();
1683 	}
1684 
1685 #ifdef DEBUG
1686 	(void) fprintf(logf, "now search for tnrhdb update %s \n", gettime());
1687 #endif
1688 
1689 	if (nss_get_rh() > 0) {
1690 		if (logf != NULL) {
1691 			(void) fprintf(logf, "tnrhdb needs update %s \n",
1692 			    gettime());
1693 		}
1694 
1695 		(void) rw_wrlock(&cache_rwlp);
1696 		/* This function will cleanup cache table */
1697 		load_rh_marked();
1698 		(void) rw_unlock(&cache_rwlp);
1699 
1700 		(void) rw_wrlock(&cache_rwlp_v6);
1701 		/* This function will cleanup cache table */
1702 		load_rh_marked_v6();
1703 		(void) rw_unlock(&cache_rwlp_v6);
1704 	}
1705 
1706 #ifdef DEBUG
1707 	if (logf != NULL) {
1708 		cachetable_print();
1709 		cachetable_print_v6();
1710 
1711 		(void) fprintf(logf, "rh table begin\n");
1712 		rhtable_print();
1713 		rhtable_print_v6();
1714 		(void) fprintf(logf, "rh table end \n");
1715 		(void) fprintf(logf, "-------------------------\n\n");
1716 		(void) fflush(logf);
1717 	}
1718 #endif
1719 }
1720 
1721 static void
1722 tnd_serve()
1723 {
1724 	for (;;) {
1725 		(void) pause();
1726 	}
1727 }
1728 
1729 static void
1730 terminate()
1731 {
1732 	if (debugl && (logf != NULL)) {
1733 		(void) fprintf(logf, "%s : ", gettime());
1734 		(void) fprintf(logf, gettext("tnd terminating on signal.\n"));
1735 		(void) fflush(logf);
1736 	}
1737 	exit(1);
1738 }
1739 
1740 static void
1741 noop()
1742 {
1743 }
1744 
1745 static char *
1746 gettime()
1747 {
1748 	time_t now;
1749 	struct tm *tp, tm;
1750 	char *fmt;
1751 
1752 	(void) time(&now);
1753 	tp = localtime(&now);
1754 	(void) memcpy(&tm, tp, sizeof (struct tm));
1755 	fmt = nl_langinfo(_DATE_FMT);
1756 
1757 	(void) strftime(time_buf, _SZ_TIME_BUF, fmt, &tm);
1758 
1759 	return (time_buf);
1760 }
1761 /*
1762  * debugging routines
1763  */
1764 
1765 
1766 #ifdef DEBUG
1767 static void
1768 print_cache_entry(tnrh_tlb_t *tlbt)
1769 {
1770 	struct in_addr addr;
1771 
1772 	addr.s_addr = tlbt->addr;
1773 	(void) fprintf(logf, "\tIP address: %s", inet_ntoa(addr));
1774 	(void) fprintf(logf, "\tTemplate name: %s", tlbt->template_name);
1775 	(void) fprintf(logf, "\tMask length used: %d\n", tlbt->masklen_used);
1776 }
1777 
1778 static void
1779 print_cache_entry_v6(tnrh_tlb_ipv6_t *tlbt)
1780 {
1781 	char abuf[INET6_ADDRSTRLEN];
1782 
1783 	(void) fprintf(logf, "\tIP address: %s",
1784 	    inet_ntop(AF_INET6, &tlbt->addr, abuf, sizeof (abuf)));
1785 	(void) fprintf(logf, "\tTemplate name: %s", tlbt->template_name);
1786 	(void) fprintf(logf, "\tMask length used: %d\n", tlbt->masklen_used);
1787 }
1788 
1789 static void
1790 cachetable_print()
1791 {
1792 	int i;
1793 	tnrh_tlb_t *tlbt;
1794 
1795 	(void) fprintf(logf, "-------------------------\n");
1796 	(void) fprintf(logf, "Cache table begin\n");
1797 
1798 	for (i = 0; i < TNRH_TABLE_HASH_SIZE; i++) {
1799 		if ((tlbt = tnrh_cache_table[i]) != NULL)
1800 			print_cache_entry(tlbt);
1801 	}
1802 
1803 	(void) fprintf(logf, "Cache table end \n");
1804 	(void) fprintf(logf, "-------------------------\n\n");
1805 }
1806 
1807 static void
1808 cachetable_print_v6()
1809 {
1810 	int i;
1811 	tnrh_tlb_ipv6_t *tlbt;
1812 
1813 	(void) fprintf(logf, "-------------------------\n");
1814 	(void) fprintf(logf, "Cache table begin\n");
1815 
1816 	for (i = 0; i < TNRH_TABLE_HASH_SIZE; i++) {
1817 		if ((tlbt = tnrh_cache_table_v6[i]) != NULL)
1818 			print_cache_entry_v6(tlbt);
1819 	}
1820 
1821 	(void) fprintf(logf, "Cache table end \n");
1822 	(void) fprintf(logf, "-------------------------\n\n");
1823 }
1824 
1825 
1826 static void
1827 print_entry(tsol_rhent_t *ent, int af)
1828 {
1829 	struct sockaddr_in *saddrp;
1830 	struct sockaddr_in6 *saddrp6;
1831 	char abuf[INET6_ADDRSTRLEN];
1832 
1833 	if (af == AF_INET) {
1834 		saddrp = (struct sockaddr_in *)&ent->rh_address.ip_addr_v4;
1835 		(void) fprintf(logf, gettext("\tIP address: %s"),
1836 		    inet_ntoa(saddrp->sin_addr));
1837 	} else if (af == AF_INET6) {
1838 		saddrp6 = (struct sockaddr_in6 *)&ent->rh_address.ip_addr_v6;
1839 		(void) fprintf(logf, gettext("\tIP address: %s"),
1840 		    inet_ntop(AF_INET6, &saddrp6->sin6_addr, abuf,
1841 		    sizeof (abuf)));
1842 	}
1843 
1844 	(void) fprintf(logf,
1845 	    gettext("\tTemplate name: %s"), ent->rh_template);
1846 	(void) fprintf(logf, gettext("\tprefix_len: %d\n"), ent->rh_prefix);
1847 	(void) fflush(logf);
1848 }
1849 
1850 static void
1851 print_tlbt(tnrh_tlb_t *tlbt)
1852 {
1853 	(void) fprintf(logf, "tlbt addr = 0x%4x name = %s \
1854 	    mask = %u, reload = %d\n", tlbt->addr, tlbt->template_name,
1855 	    tlbt->masklen_used, tlbt->reload);
1856 }
1857 
1858 static void
1859 rhtable_print()
1860 {
1861 	rhtable_walk(print_entry);
1862 	(void) fprintf(logf, "-----------------------------\n\n");
1863 }
1864 
1865 static void
1866 rhtable_print_v6()
1867 {
1868 	rhtable_walk_v6(print_entry);
1869 	(void) fprintf(logf, "-----------------------------\n\n");
1870 }
1871 
1872 /*
1873  * Walk through all the entries in tnrh_entire_table[][]
1874  * and execute the function passing the entry as argument.
1875  */
1876 static void
1877 rhtable_walk(void (*action)())
1878 {
1879 	int i, j;
1880 	tnd_tnrhdb_t *rhent;
1881 
1882 	for (i = 0; i <= IP_ABITS; i++) {
1883 		if (tnrh_entire_table[i] == NULL)
1884 			continue;
1885 
1886 		for (j = 0; j < TNRH_TABLE_HASH_SIZE; j++) {
1887 			rhent = tnrh_entire_table[i][j];
1888 
1889 			while (rhent != NULL) {
1890 				action(&rhent->rh_ent, AF_INET);
1891 				rhent = rhent->rh_next;
1892 			}
1893 		}
1894 	}
1895 }
1896 
1897 /*
1898  * Walk through all the entries in tnrh_entire_table_v6[][]
1899  * and execute the function passing the entry as argument.
1900  */
1901 static void
1902 rhtable_walk_v6(void (*action)())
1903 {
1904 	int i, j;
1905 	tnd_tnrhdb_t *rhent;
1906 
1907 	for (i = 0; i <= IPV6_ABITS; i++) {
1908 		if (tnrh_entire_table_v6[i] == NULL)
1909 			continue;
1910 
1911 		for (j = 0; j < TNRH_TABLE_HASH_SIZE; j++) {
1912 			rhent = tnrh_entire_table_v6[i][j];
1913 
1914 			while (rhent != NULL) {
1915 				action(&rhent->rh_ent, AF_INET6);
1916 				rhent = rhent->rh_next;
1917 			}
1918 		}
1919 	}
1920 }
1921 #endif /* DEBUG */
1922