xref: /illumos-gate/usr/src/lib/smbsrv/libsmb/common/smb_nic.c (revision 7f667e74610492ddbce8ce60f52ece95d2401949)
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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"@(#)smb_nic.c	1.6	08/07/24 SMI"
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <syslog.h>
31 #include <libintl.h>
32 #include <strings.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <synch.h>
36 #include <stropts.h>
37 #include <errno.h>
38 #include <pthread.h>
39 
40 #include <inet/ip.h>
41 #include <net/if.h>
42 #include <netinet/in.h>
43 #include <netdb.h>
44 #include <net/route.h>
45 #include <arpa/inet.h>
46 
47 #include <sys/socket.h>
48 #include <sys/sockio.h>
49 #include <sys/systeminfo.h>
50 
51 #include <smbsrv/libsmb.h>
52 
53 #define	SMB_NIC_DB_NAME		"/var/smb/smbhosts.db"
54 #define	SMB_NIC_DB_TIMEOUT	3000		/* in millisecond */
55 #define	SMB_NIC_DB_VERMAJOR	1
56 #define	SMB_NIC_DB_VERMINOR	0
57 #define	SMB_NIC_DB_MAGIC	0x484F5354	/* HOST */
58 
59 #define	SMB_NIC_DB_ORD		1		/* open read-only */
60 #define	SMB_NIC_DB_ORW		2		/* open read/write */
61 
62 #define	SMB_NIC_DB_SQL \
63 	"CREATE TABLE db_info ("				\
64 	"	ver_major INTEGER,"				\
65 	"	ver_minor INTEGER,"				\
66 	"	magic     INTEGER"				\
67 	");"							\
68 	""							\
69 	"CREATE TABLE hosts ("					\
70 	"	hostname  TEXT PRIMARY KEY,"			\
71 	"	comment   TEXT,"				\
72 	"	ifnames   TEXT"					\
73 	");"
74 
75 #define	SMB_NIC_HTBL_NCOL	3
76 #define	SMB_NIC_HTBL_HOST	0
77 #define	SMB_NIC_HTBL_CMNT	1
78 #define	SMB_NIC_HTBL_IFS	2
79 
80 #define	NULL_MSGCHK(msg)	((msg) ? (msg) : "NULL")
81 
82 #define	SMB_NIC_MAXIFS		256
83 #define	SMB_NIC_MAXEXCLLIST_LEN	512
84 
85 typedef struct smb_hostifs {
86 	list_node_t if_lnd;
87 	char if_host[MAXHOSTNAMELEN];
88 	char if_cmnt[SMB_PI_MAX_COMMENT];
89 	char *if_names[SMB_NIC_MAXIFS];
90 	int if_num;
91 } smb_hostifs_t;
92 
93 typedef struct smb_hosts {
94 	list_t h_list;
95 	int h_num;
96 	int h_ifnum;
97 } smb_hosts_t;
98 
99 typedef struct {
100 	smb_nic_t	*nl_nics;
101 	int		nl_cnt;		/* number of smb_nic_t structures */
102 	int		nl_hcnt;	/* number of host names */
103 	long		nl_seqnum;	/* a random sequence number */
104 	rwlock_t	nl_rwl;
105 } smb_niclist_t;
106 
107 static int smb_nic_list_create(void);
108 static void smb_nic_list_destroy(void);
109 
110 static int smb_nic_hlist_create(smb_hosts_t *);
111 static void smb_nic_hlist_destroy(smb_hosts_t *);
112 static int smb_nic_hlist_dbget(smb_hosts_t *);
113 static int smb_nic_hlist_sysget(smb_hosts_t *);
114 
115 static void smb_nic_iflist_destroy(smb_hostifs_t *);
116 static smb_hostifs_t *smb_nic_iflist_decode(const char **);
117 
118 static int smb_nic_dbcreate(void);
119 static sqlite *smb_nic_dbopen(int);
120 static void smb_nic_dbclose(sqlite *);
121 static boolean_t smb_nic_dbexists(void);
122 static boolean_t smb_nic_dbvalidate(void);
123 static int smb_nic_dbaddhost(const char *, const char *, char *);
124 static int smb_nic_dbdelhost(const char *);
125 static int smb_nic_dbsetinfo(sqlite *);
126 
127 static int smb_nic_getinfo(char *, smb_nic_t *, int);
128 static boolean_t smb_nic_nbt_exclude(const smb_nic_t *, const char **, int);
129 static int smb_nic_nbt_get_exclude_list(char *, char **, int);
130 
131 static void smb_close_sockets(int, int);
132 static boolean_t smb_duplicate_nic(smb_hostifs_t *iflist, struct lifreq *lifrp);
133 
134 /* This is the list we will monitor */
135 static smb_niclist_t smb_niclist;
136 
137 /*
138  * smb_nic_init
139  *
140  * Initializes the interface list.
141  */
142 int
143 smb_nic_init(void)
144 {
145 	int rc;
146 
147 	(void) rw_wrlock(&smb_niclist.nl_rwl);
148 	smb_nic_list_destroy();
149 	rc = smb_nic_list_create();
150 	(void) rw_unlock(&smb_niclist.nl_rwl);
151 
152 	return (rc);
153 }
154 
155 /*
156  * smb_nic_fini
157  *
158  * Destroys the interface list.
159  */
160 void
161 smb_nic_fini(void)
162 {
163 	(void) rw_wrlock(&smb_niclist.nl_rwl);
164 	smb_nic_list_destroy();
165 	(void) rw_unlock(&smb_niclist.nl_rwl);
166 }
167 
168 /*
169  * smb_nic_getnum
170  *
171  * Gets the number of interfaces for the specified host.
172  * if host is NULL then total number of interfaces
173  * is returned. It's assumed that given name is a NetBIOS
174  * encoded name.
175  */
176 int
177 smb_nic_getnum(char *nb_hostname)
178 {
179 	int n = 0, i;
180 
181 	(void) rw_rdlock(&smb_niclist.nl_rwl);
182 
183 	if (nb_hostname != NULL) {
184 		for (i = 0; i < smb_niclist.nl_cnt; i++) {
185 			/* ignore the suffix */
186 			if (strncasecmp(smb_niclist.nl_nics[i].nic_nbname,
187 			    nb_hostname, NETBIOS_NAME_SZ - 1) == 0)
188 				n++;
189 		}
190 	} else {
191 		n = smb_niclist.nl_cnt;
192 	}
193 
194 	(void) rw_unlock(&smb_niclist.nl_rwl);
195 
196 	return (n);
197 }
198 
199 /*
200  * smb_nic_getfirst
201  *
202  * Returns the first NIC in the interface list and
203  * initializes the given iterator. To get the rest of
204  * NICs smb_nic_getnext() must be called.
205  *
206  * Returns 0 upon success and -1 if there's no interface
207  * available or if 'ni' is NULL.
208  */
209 int
210 smb_nic_getfirst(smb_niciter_t *ni)
211 {
212 	int rc = 0;
213 
214 	if (ni == NULL)
215 		return (-1);
216 
217 	(void) rw_rdlock(&smb_niclist.nl_rwl);
218 
219 	if (smb_niclist.nl_cnt > 0) {
220 		ni->ni_nic = smb_niclist.nl_nics[0];
221 		ni->ni_cookie = 1;
222 		ni->ni_seqnum = smb_niclist.nl_seqnum;
223 	} else {
224 		rc = -1;
225 	}
226 
227 	(void) rw_unlock(&smb_niclist.nl_rwl);
228 
229 	return (rc);
230 }
231 
232 /*
233  * smb_nic_getnext
234  *
235  * Returns the next NIC information based on the passed
236  * iterator (ni). The iterator must have previously been
237  * initialized by calling smb_nic_getfirst().
238  *
239  * Returns 0 upon successfully finding the specified NIC.
240  * Returns -1 if:
241  * 	- the specified iterator is invalid
242  * 	- reaches the end of the NIC list
243  * 	- sequence number in the iterator is different from
244  *	  the sequence number in the NIC list which means
245  *	  the list has been changed between getfirst/getnext
246  *	  calls.
247  */
248 int
249 smb_nic_getnext(smb_niciter_t *ni)
250 {
251 	int rc = 0;
252 
253 	if ((ni == NULL) || (ni->ni_cookie < 1))
254 		return (-1);
255 
256 	(void) rw_rdlock(&smb_niclist.nl_rwl);
257 
258 	if ((smb_niclist.nl_cnt > ni->ni_cookie) &&
259 	    (smb_niclist.nl_seqnum == ni->ni_seqnum)) {
260 		ni->ni_nic = smb_niclist.nl_nics[ni->ni_cookie];
261 		ni->ni_cookie++;
262 	} else {
263 		rc = -1;
264 	}
265 
266 	(void) rw_unlock(&smb_niclist.nl_rwl);
267 
268 	return (rc);
269 }
270 
271 boolean_t
272 smb_nic_exists(smb_inaddr_t *ipaddr, boolean_t use_mask)
273 {
274 	smb_nic_t *cfg;
275 	uint32_t mask = 0;
276 	int i;
277 
278 	(void) rw_rdlock(&smb_niclist.nl_rwl);
279 
280 	for (i = 0; i < smb_niclist.nl_cnt; i++) {
281 		cfg = &smb_niclist.nl_nics[i];
282 		if (use_mask)
283 			mask = cfg->nic_mask;
284 		if (smb_inet_equal(ipaddr, &cfg->nic_ip, mask)) {
285 			(void) rw_unlock(&smb_niclist.nl_rwl);
286 			return (B_TRUE);
287 		}
288 	}
289 	(void) rw_unlock(&smb_niclist.nl_rwl);
290 	return (B_FALSE);
291 }
292 
293 /*
294  * smb_nic_addhost
295  *
296  * Adds an association between the given host and the specified interface
297  * list (if_names). This function can be called as many times as needed,
298  * the associations will be stored in /var/smb/smbhosts.db, which is sqlite
299  * database. If this file exists and it's not empty NIC list is generated
300  * based on the information stored in this file.
301  *
302  * host: actual system's name (not Netbios name)
303  * cmnt: an optional description for the CIFS server running on
304  *       the specified host. Can be NULL.
305  * if_num: number of interface names in if_names arg
306  * if_names: array of interface names in string format
307  *
308  * Returns 0 upon success and -1 when fails
309  */
310 int
311 smb_nic_addhost(const char *host, const char *cmnt,
312     int if_num, const char **if_names)
313 {
314 	char *if_list;
315 	char *ifname;
316 	int buflen = 0;
317 	int rc, i;
318 
319 	if ((host == NULL) || (if_num <= 0) || (if_names == NULL))
320 		return (-1);
321 
322 	if (!smb_nic_dbexists() || !smb_nic_dbvalidate()) {
323 		if (smb_nic_dbcreate() != SQLITE_OK)
324 			return (-1);
325 	}
326 
327 	ifname = (char *)if_names;
328 	for (i = 0; i < if_num; i++, ifname++) {
329 		if ((ifname == NULL) || (*ifname == '\0'))
330 			return (-1);
331 		buflen += strlen(ifname) + 1;
332 	}
333 
334 	if ((if_list = malloc(buflen)) == NULL)
335 		return (-1);
336 
337 	ifname = if_list;
338 	for (i = 0; i < if_num - 1; i++)
339 		ifname += snprintf(ifname, buflen, "%s,", if_names[i]);
340 
341 	(void) snprintf(ifname, buflen, "%s", if_names[i]);
342 
343 	rc = smb_nic_dbaddhost(host, cmnt, if_list);
344 	free(if_list);
345 
346 	return ((rc == SQLITE_OK) ? 0 : -1);
347 }
348 
349 /*
350  * smb_nic_delhost
351  *
352  * Removes the stored interface association for the specified host
353  */
354 int
355 smb_nic_delhost(const char *host)
356 {
357 	if ((host == NULL) || (*host == '\0'))
358 		return (-1);
359 
360 	if (!smb_nic_dbexists())
361 		return (0);
362 
363 	if (!smb_nic_dbvalidate()) {
364 		(void) unlink(SMB_NIC_DB_NAME);
365 		return (0);
366 	}
367 
368 	if (smb_nic_dbdelhost(host) != SQLITE_OK)
369 		return (-1);
370 
371 	return (0);
372 }
373 
374 /*
375  * smb_nic_list_create
376  *
377  * Creates a NIC list either based on /var/smb/smbhosts.db or
378  * by getting the information from OS.
379  *
380  * Note that the caller of this function should grab the
381  * list lock.
382  */
383 static int
384 smb_nic_list_create(void)
385 {
386 	smb_hosts_t hlist;
387 	smb_hostifs_t *iflist;
388 	smb_nic_t *nc;
389 	char *ifname;
390 	char excludestr[SMB_NIC_MAXEXCLLIST_LEN];
391 	char *exclude[SMB_PI_MAX_NETWORKS];
392 	int nexclude = 0;
393 	int i;
394 
395 	if (smb_nic_hlist_create(&hlist) < 0)
396 		return (-1);
397 
398 	smb_niclist.nl_cnt = 0;
399 	smb_niclist.nl_seqnum = random();
400 	smb_niclist.nl_hcnt = hlist.h_num;
401 
402 	smb_niclist.nl_nics = calloc(hlist.h_ifnum, sizeof (smb_nic_t));
403 	if (smb_niclist.nl_nics == NULL) {
404 		smb_nic_hlist_destroy(&hlist);
405 		return (-1);
406 	}
407 
408 	*excludestr = '\0';
409 	(void) smb_config_getstr(SMB_CI_WINS_EXCL,
410 	    excludestr, sizeof (excludestr));
411 
412 	nexclude = smb_nic_nbt_get_exclude_list(excludestr,
413 	    exclude, SMB_PI_MAX_NETWORKS);
414 
415 	nc = smb_niclist.nl_nics;
416 	iflist = list_head(&hlist.h_list);
417 
418 	do {
419 		for (i = 0; i < iflist->if_num; i++) {
420 			ifname = iflist->if_names[i];
421 			if (smb_nic_getinfo(ifname, nc, AF_INET) < 0) {
422 				if (smb_nic_getinfo(ifname, nc,
423 				    AF_INET6) < 0) {
424 					continue;
425 				}
426 			}
427 
428 			(void) strlcpy(nc->nic_host, iflist->if_host,
429 			    sizeof (nc->nic_host));
430 			(void) strlcpy(nc->nic_cmnt, iflist->if_cmnt,
431 			    sizeof (nc->nic_cmnt));
432 
433 			smb_tonetbiosname(nc->nic_host, nc->nic_nbname, 0x00);
434 
435 			if (strchr(ifname, ':'))
436 				nc->nic_smbflags |= SMB_NICF_ALIAS;
437 
438 			if (smb_nic_nbt_exclude(nc,
439 			    (const char **)exclude, nexclude))
440 				nc->nic_smbflags |= SMB_NICF_NBEXCL;
441 
442 			smb_niclist.nl_cnt++;
443 			nc++;
444 		}
445 	} while	((iflist = list_next(&hlist.h_list, iflist)) != NULL);
446 
447 	smb_nic_hlist_destroy(&hlist);
448 
449 	return (0);
450 }
451 
452 static void
453 smb_nic_list_destroy(void)
454 {
455 	free(smb_niclist.nl_nics);
456 	smb_niclist.nl_nics = NULL;
457 	smb_niclist.nl_cnt = 0;
458 }
459 
460 static int
461 smb_nic_getinfo(char *interface, smb_nic_t *nc, int family)
462 {
463 	struct lifreq lifrr;
464 	int s;
465 	boolean_t isv6;
466 	struct sockaddr_in6 *sin6;
467 	struct sockaddr_in *sin;
468 
469 	if ((s = socket(family, SOCK_DGRAM, IPPROTO_IP)) < 0) {
470 		return (-1);
471 	}
472 
473 	(void) strlcpy(lifrr.lifr_name, interface, sizeof (lifrr.lifr_name));
474 	if (ioctl(s, SIOCGLIFADDR, &lifrr) < 0) {
475 		(void) close(s);
476 		return (-1);
477 	}
478 	isv6 = (lifrr.lifr_addr.ss_family == AF_INET6);
479 	if (isv6) {
480 		sin6 = (struct sockaddr_in6 *)(&lifrr.lifr_addr);
481 		nc->nic_ip.a_ipv6 = sin6->sin6_addr;
482 		nc->nic_ip.a_family = AF_INET6;
483 	} else {
484 		sin = (struct sockaddr_in *)(&lifrr.lifr_addr);
485 		nc->nic_ip.a_ipv4 = (in_addr_t)(sin->sin_addr.s_addr);
486 		nc->nic_ip.a_family = AF_INET;
487 	}
488 	if (smb_inet_iszero(&nc->nic_ip)) {
489 		(void) close(s);
490 		return (-1);
491 	}
492 	/* there is no broadcast or netmask for v6 */
493 	if (!isv6) {
494 		if (ioctl(s, SIOCGLIFBRDADDR, &lifrr) < 0) {
495 			(void) close(s);
496 			return (-1);
497 		}
498 		sin = (struct sockaddr_in *)&lifrr.lifr_broadaddr;
499 		nc->nic_bcast = (uint32_t)sin->sin_addr.s_addr;
500 
501 		if (ioctl(s, SIOCGLIFNETMASK, &lifrr) < 0) {
502 			(void) close(s);
503 			return (-1);
504 		}
505 		sin = (struct sockaddr_in *)&lifrr.lifr_addr;
506 		nc->nic_mask = (uint32_t)sin->sin_addr.s_addr;
507 	}
508 	if (ioctl(s, SIOCGLIFFLAGS, &lifrr) < 0) {
509 		(void) close(s);
510 		return (-1);
511 	}
512 	nc->nic_sysflags = lifrr.lifr_flags;
513 
514 	(void) strlcpy(nc->nic_ifname, interface, sizeof (nc->nic_ifname));
515 
516 	(void) close(s);
517 	return (0);
518 }
519 
520 /*
521  * smb_nic_hlist_create
522  *
523  * Creates a list of hosts and their associated interfaces.
524  * If host database exists the information is retrieved from
525  * the database, otherwise it's retrieved from OS.
526  *
527  * The allocated memories for the returned list should be freed
528  * by calling smb_nic_hlist_destroy()
529  */
530 static int
531 smb_nic_hlist_create(smb_hosts_t *hlist)
532 {
533 	int rc;
534 
535 	list_create(&hlist->h_list, sizeof (smb_hostifs_t),
536 	    offsetof(smb_hostifs_t, if_lnd));
537 	hlist->h_num = 0;
538 	hlist->h_ifnum = 0;
539 
540 	if (smb_nic_dbexists() && smb_nic_dbvalidate()) {
541 		rc = smb_nic_hlist_dbget(hlist);
542 		errno = EBADF;
543 	} else {
544 		rc = smb_nic_hlist_sysget(hlist);
545 	}
546 
547 	if (rc != 0)
548 		smb_nic_hlist_destroy(hlist);
549 
550 	return (rc);
551 }
552 
553 static void
554 smb_nic_hlist_destroy(smb_hosts_t *hlist)
555 {
556 	smb_hostifs_t *iflist;
557 
558 	if (hlist == NULL)
559 		return;
560 
561 	while ((iflist = list_head(&hlist->h_list)) != NULL) {
562 		list_remove(&hlist->h_list, iflist);
563 		smb_nic_iflist_destroy(iflist);
564 	}
565 
566 	list_destroy(&hlist->h_list);
567 }
568 
569 static void
570 smb_close_sockets(int s4, int s6)
571 {
572 	if (s4)
573 		(void) close(s4);
574 	if (s6)
575 		(void) close(s6);
576 }
577 
578 /*
579  * smb_nic_hlist_sysget
580  *
581  * Get the list of currently plumbed and up interface names. The loopback (lo0)
582  * port is ignored
583  */
584 static int
585 smb_nic_hlist_sysget(smb_hosts_t *hlist)
586 {
587 	smb_hostifs_t *iflist;
588 	struct lifconf lifc;
589 	struct lifreq lifrl;
590 	struct lifreq *lifrp;
591 	char *ifname;
592 	int ifnum;
593 	int i;
594 	int s4, s6;
595 	struct lifnum lifn;
596 
597 	iflist = malloc(sizeof (smb_hostifs_t));
598 	if (iflist == NULL)
599 		return (-1);
600 
601 	bzero(iflist, sizeof (smb_hostifs_t));
602 
603 	if (smb_gethostname(iflist->if_host, sizeof (iflist->if_host), 0) < 0) {
604 		free(iflist);
605 		return (-1);
606 	}
607 
608 	(void) smb_config_getstr(SMB_CI_SYS_CMNT, iflist->if_cmnt,
609 	    sizeof (iflist->if_cmnt));
610 
611 	if ((s4 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
612 		free(iflist);
613 		return (-1);
614 	}
615 	s6 = socket(AF_INET6, SOCK_DGRAM, 0);
616 
617 	lifn.lifn_family = AF_UNSPEC;
618 	lifn.lifn_flags = 0;
619 	if (ioctl(s4, SIOCGLIFNUM, (char *)&lifn) < 0) {
620 		smb_close_sockets(s4, s6);
621 		free(iflist);
622 		syslog(LOG_ERR, "hlist_sysget: SIOCGLIFNUM errno=%d", errno);
623 		return (-1);
624 	}
625 
626 	lifc.lifc_len = lifn.lifn_count * sizeof (struct lifreq);
627 	lifc.lifc_buf = malloc(lifc.lifc_len);
628 	if (lifc.lifc_buf == NULL) {
629 		smb_close_sockets(s4, s6);
630 		free(iflist);
631 		return (-1);
632 	}
633 	bzero(lifc.lifc_buf, lifc.lifc_len);
634 	lifc.lifc_family = AF_UNSPEC;
635 	lifc.lifc_flags = 0;
636 
637 	if (ioctl(s4, SIOCGLIFCONF, (char *)&lifc) < 0) {
638 		smb_close_sockets(s4, s6);
639 		free(iflist);
640 		free(lifc.lifc_buf);
641 		return (-1);
642 	}
643 
644 	lifrp = lifc.lifc_req;
645 	ifnum = lifc.lifc_len / sizeof (struct lifreq);
646 	hlist->h_num = 0;
647 	for (i = 0; i < ifnum; i++, lifrp++) {
648 
649 		if ((iflist->if_num > 0) && smb_duplicate_nic(iflist, lifrp))
650 			continue;
651 		/*
652 		 * Get the flags so that we can skip the loopback interface
653 		 */
654 		(void) memset(&lifrl, 0, sizeof (lifrl));
655 		(void) strlcpy(lifrl.lifr_name, lifrp->lifr_name,
656 		    sizeof (lifrl.lifr_name));
657 
658 		if (ioctl(s4, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0) {
659 			if ((s6 < 0) ||
660 			    (ioctl(s6, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0)) {
661 				smb_close_sockets(s4, s6);
662 				free(lifc.lifc_buf);
663 				smb_nic_iflist_destroy(iflist);
664 				return (-1);
665 			}
666 		}
667 		if (lifrl.lifr_flags & IFF_LOOPBACK) {
668 			continue;
669 		}
670 
671 		if ((lifrl.lifr_flags & IFF_UP) == 0) {
672 			continue;
673 		}
674 		ifname = strdup(lifrp->lifr_name);
675 		if (ifname == NULL) {
676 			smb_close_sockets(s4, s6);
677 			free(lifc.lifc_buf);
678 			smb_nic_iflist_destroy(iflist);
679 			return (-1);
680 		}
681 		iflist->if_names[iflist->if_num++] = ifname;
682 	}
683 	hlist->h_ifnum = iflist->if_num;
684 	hlist->h_num = 1;
685 	smb_close_sockets(s4, s6);
686 	free(lifc.lifc_buf);
687 	list_insert_tail(&hlist->h_list, iflist);
688 
689 	return (0);
690 }
691 
692 static boolean_t
693 smb_duplicate_nic(smb_hostifs_t *iflist, struct lifreq *lifrp)
694 {
695 	int j;
696 	/*
697 	 * throw out duplicate names
698 	 */
699 	for (j = 0; j < iflist->if_num; j++) {
700 		if (strcmp(iflist->if_names[j],
701 		    lifrp->lifr_name) == 0)
702 			return (B_TRUE);
703 	}
704 	return (B_FALSE);
705 }
706 
707 static int
708 smb_nic_hlist_dbget(smb_hosts_t *hlist)
709 {
710 	smb_hostifs_t *iflist;
711 	sqlite *db;
712 	sqlite_vm *vm;
713 	boolean_t error = B_FALSE;
714 	const char **values;
715 	char *sql;
716 	char *errmsg = NULL;
717 	int ncol, rc;
718 
719 	sql = sqlite_mprintf("SELECT * FROM hosts");
720 	if (sql == NULL)
721 		return (-1);
722 
723 	db = smb_nic_dbopen(SMB_NIC_DB_ORD);
724 	if (db == NULL) {
725 		sqlite_freemem(sql);
726 		return (-1);
727 	}
728 
729 	rc = sqlite_compile(db, sql, NULL, &vm, &errmsg);
730 	sqlite_freemem(sql);
731 
732 	if (rc != SQLITE_OK) {
733 		smb_nic_dbclose(db);
734 		syslog(LOG_DEBUG, "smb_nic_hlist_dbget: failed to create"
735 		    " VM (%s)", NULL_MSGCHK(errmsg));
736 		return (-1);
737 	}
738 
739 	do {
740 		rc = sqlite_step(vm, &ncol, &values, NULL);
741 		if (rc == SQLITE_ROW) {
742 			if (ncol != SMB_NIC_HTBL_NCOL) {
743 				error = B_TRUE;
744 				break;
745 			}
746 
747 			if ((iflist = smb_nic_iflist_decode(values)) == NULL) {
748 				error = B_TRUE;
749 				break;
750 			}
751 
752 			list_insert_tail(&hlist->h_list, iflist);
753 			hlist->h_num++;
754 			hlist->h_ifnum += iflist->if_num;
755 		}
756 	} while (rc == SQLITE_ROW);
757 
758 	if (rc != SQLITE_DONE)
759 		error = B_TRUE;
760 
761 	rc = sqlite_finalize(vm, &errmsg);
762 	if (rc != SQLITE_OK) {
763 		syslog(LOG_DEBUG, "smb_nic_hlist_dbget: failed to destroy"
764 		    "VM (%s)", NULL_MSGCHK(errmsg));
765 		error = B_TRUE;
766 	}
767 
768 	smb_nic_dbclose(db);
769 
770 	return ((error) ? -1 : 0);
771 }
772 
773 static smb_hostifs_t *
774 smb_nic_iflist_decode(const char **values)
775 {
776 	smb_hostifs_t *iflist;
777 	char *host;
778 	char *cmnt;
779 	char *ifnames;
780 	char *lasts;
781 	char *ifname;
782 	int if_num = 0;
783 
784 	host = (char *)values[SMB_NIC_HTBL_HOST];
785 	cmnt = (char *)values[SMB_NIC_HTBL_CMNT];
786 	ifnames = (char *)values[SMB_NIC_HTBL_IFS];
787 
788 	if ((host == NULL) || (ifnames == NULL))
789 		return (NULL);
790 
791 	iflist = malloc(sizeof (smb_hostifs_t));
792 	if (iflist == NULL)
793 		return (NULL);
794 
795 	bzero(iflist, sizeof (smb_hostifs_t));
796 
797 	(void) strlcpy(iflist->if_host, host, sizeof (iflist->if_host));
798 	(void) strlcpy(iflist->if_cmnt, (cmnt) ? cmnt : "",
799 	    sizeof (iflist->if_cmnt));
800 
801 	if ((ifname = strtok_r(ifnames, ",", &lasts)) == NULL)
802 		return (NULL);
803 
804 	iflist->if_names[if_num++] = strdup(ifname);
805 
806 	while ((ifname = strtok_r(NULL, ",", &lasts)) != NULL)
807 		iflist->if_names[if_num++] = strdup(ifname);
808 
809 	iflist->if_num = if_num;
810 
811 	for (if_num = 0; if_num < iflist->if_num; if_num++) {
812 		if (iflist->if_names[if_num] == NULL) {
813 			smb_nic_iflist_destroy(iflist);
814 			return (NULL);
815 		}
816 	}
817 
818 	return (iflist);
819 }
820 
821 /*
822  * smb_nic_iflist_destroy
823  *
824  * Frees allocated memory for the given IF names lists.
825  */
826 static void
827 smb_nic_iflist_destroy(smb_hostifs_t *iflist)
828 {
829 	int i;
830 
831 	if (iflist == NULL)
832 		return;
833 
834 	for (i = 0; i < iflist->if_num; i++)
835 		free(iflist->if_names[i]);
836 
837 	free(iflist);
838 }
839 
840 /*
841  * Functions to manage host/interface database
842  *
843  * Each entry in the hosts table associates a hostname with a
844  * list of interface names. The host/interface association could
845  * be added by calling smb_nic_addhost() and could be removed by
846  * calling smb_nic_delhost(). If the database exists and it contains
847  * valid information then the inteface list wouldn't be obtained
848  * from system using ioctl.
849  */
850 
851 /*
852  * smb_nic_dbcreate
853  *
854  * Creates the host database based on the defined SQL statement.
855  * It also initializes db_info table.
856  */
857 static int
858 smb_nic_dbcreate(void)
859 {
860 	sqlite *db = NULL;
861 	char *errmsg = NULL;
862 	int rc;
863 
864 	(void) unlink(SMB_NIC_DB_NAME);
865 
866 	db = sqlite_open(SMB_NIC_DB_NAME, 0600, &errmsg);
867 	if (db == NULL) {
868 		syslog(LOG_DEBUG, "failed to create host database (%s)",
869 		    NULL_MSGCHK(errmsg));
870 		sqlite_freemem(errmsg);
871 		return (SQLITE_CANTOPEN);
872 	}
873 
874 	sqlite_busy_timeout(db, SMB_NIC_DB_TIMEOUT);
875 	rc = sqlite_exec(db, "BEGIN TRANSACTION", NULL, NULL, &errmsg);
876 	if (rc != SQLITE_OK) {
877 		syslog(LOG_DEBUG, "failed to begin database transaction (%s)",
878 		    NULL_MSGCHK(errmsg));
879 		sqlite_freemem(errmsg);
880 		sqlite_close(db);
881 		return (rc);
882 	}
883 
884 	if (sqlite_exec(db, SMB_NIC_DB_SQL, NULL, NULL, &errmsg) == SQLITE_OK) {
885 		rc = sqlite_exec(db, "COMMIT TRANSACTION", NULL, NULL,
886 		    &errmsg);
887 		if (rc == SQLITE_OK)
888 			rc = smb_nic_dbsetinfo(db);
889 		if (rc != SQLITE_OK)
890 			rc = sqlite_exec(db, "ROLLBACK TRANSACTION", NULL, NULL,
891 			    &errmsg);
892 	} else {
893 		syslog(LOG_ERR, "failed to initialize host database (%s)",
894 		    errmsg);
895 		sqlite_freemem(errmsg);
896 		rc = sqlite_exec(db, "ROLLBACK TRANSACTION", NULL, NULL,
897 		    &errmsg);
898 	}
899 
900 	if (rc != SQLITE_OK) {
901 		/* this is bad - database may be left in a locked state */
902 		syslog(LOG_DEBUG, "failed to close a transaction (%s)",
903 		    NULL_MSGCHK(errmsg));
904 		sqlite_freemem(errmsg);
905 	}
906 
907 	(void) sqlite_close(db);
908 	return (rc);
909 }
910 
911 /*
912  * smb_nic_dbopen
913  *
914  * Opens host database with the given mode.
915  */
916 static sqlite *
917 smb_nic_dbopen(int mode)
918 {
919 	sqlite *db;
920 	char *errmsg = NULL;
921 
922 	db = sqlite_open(SMB_NIC_DB_NAME, mode, &errmsg);
923 	if (db == NULL) {
924 		syslog(LOG_ERR, "failed to open group database (%s)",
925 		    NULL_MSGCHK(errmsg));
926 		sqlite_freemem(errmsg);
927 	}
928 
929 	return (db);
930 }
931 
932 /*
933  * smb_nic_dbclose
934  *
935  * Closes the given database handle
936  */
937 static void
938 smb_nic_dbclose(sqlite *db)
939 {
940 	if (db) {
941 		sqlite_close(db);
942 	}
943 }
944 
945 static boolean_t
946 smb_nic_dbexists(void)
947 {
948 	return (access(SMB_NIC_DB_NAME, F_OK) == 0);
949 }
950 
951 static boolean_t
952 smb_nic_dbvalidate(void)
953 {
954 	sqlite *db;
955 	char *errmsg = NULL;
956 	char *sql;
957 	char **result;
958 	int nrow, ncol;
959 	boolean_t check = B_TRUE;
960 	int rc;
961 
962 	sql = sqlite_mprintf("SELECT * FROM db_info");
963 	if (sql == NULL)
964 		return (B_FALSE);
965 
966 	db = smb_nic_dbopen(SMB_NIC_DB_ORW);
967 	if (db == NULL) {
968 		sqlite_freemem(sql);
969 		return (B_FALSE);
970 	}
971 
972 	rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
973 	sqlite_freemem(sql);
974 
975 	if (rc != SQLITE_OK) {
976 		syslog(LOG_DEBUG, "smb_nic_dbvalidate: failed to get db_info"
977 		    " (%s)", NULL_MSGCHK(errmsg));
978 		sqlite_freemem(errmsg);
979 		smb_nic_dbclose(db);
980 		return (B_FALSE);
981 	}
982 
983 	if (nrow != 1 || ncol != 3) {
984 		syslog(LOG_DEBUG, "smb_nic_dbvalidate: bad db_info table");
985 		sqlite_free_table(result);
986 		smb_nic_dbclose(db);
987 		return (B_FALSE);
988 	}
989 
990 	if ((atoi(result[3]) != SMB_NIC_DB_VERMAJOR) ||
991 	    (atoi(result[4]) != SMB_NIC_DB_VERMINOR) ||
992 	    (atoi(result[5]) != SMB_NIC_DB_MAGIC)) {
993 		syslog(LOG_DEBUG, "smb_nic_dbvalidate: bad db_info content");
994 		sqlite_free_table(result);
995 		smb_nic_dbclose(db);
996 		return (B_FALSE);
997 	}
998 	sqlite_free_table(result);
999 
1000 	sql = sqlite_mprintf("SELECT hostname FROM hosts");
1001 	if (sql == NULL) {
1002 		smb_nic_dbclose(db);
1003 		return (B_FALSE);
1004 	}
1005 
1006 	rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
1007 	sqlite_freemem(sql);
1008 
1009 	if (rc != SQLITE_OK) {
1010 		syslog(LOG_DEBUG, "smb_nic_dbvalidate: failed to count (%s)",
1011 		    NULL_MSGCHK(errmsg));
1012 		sqlite_freemem(errmsg);
1013 		smb_nic_dbclose(db);
1014 		return (B_FALSE);
1015 	}
1016 
1017 	sqlite_free_table(result);
1018 
1019 	if (nrow == 0)
1020 		/* No hosts in the database */
1021 		check = B_FALSE;
1022 
1023 	smb_nic_dbclose(db);
1024 	return (check);
1025 }
1026 
1027 static int
1028 smb_nic_dbaddhost(const char *host, const char *cmnt, char *if_list)
1029 {
1030 	sqlite *db;
1031 	char *sql;
1032 	char *errmsg;
1033 	int rc;
1034 
1035 	sql = sqlite_mprintf("REPLACE INTO hosts (hostname, comment, ifnames)"
1036 	    "VALUES ('%s', '%q', '%s')", host, (cmnt) ? cmnt : "", if_list);
1037 	if (sql == NULL)
1038 		return (SQLITE_NOMEM);
1039 
1040 	db = smb_nic_dbopen(SMB_NIC_DB_ORW);
1041 	if (db == NULL) {
1042 		sqlite_freemem(sql);
1043 		return (SQLITE_CANTOPEN);
1044 	}
1045 
1046 	rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
1047 	sqlite_freemem(sql);
1048 	smb_nic_dbclose(db);
1049 
1050 	if (rc != SQLITE_OK) {
1051 		syslog(LOG_DEBUG, "smb_nic_dbaddhost: failed to insert %s (%s)",
1052 		    host, NULL_MSGCHK(errmsg));
1053 		sqlite_freemem(errmsg);
1054 	}
1055 
1056 	return (rc);
1057 }
1058 
1059 static int
1060 smb_nic_dbdelhost(const char *host)
1061 {
1062 	sqlite *db;
1063 	char *sql;
1064 	char *errmsg;
1065 	int rc;
1066 
1067 	sql = sqlite_mprintf("DELETE FROM hosts WHERE hostname = '%s'", host);
1068 	if (sql == NULL)
1069 		return (SQLITE_NOMEM);
1070 
1071 	db = smb_nic_dbopen(SMB_NIC_DB_ORW);
1072 	if (db == NULL) {
1073 		sqlite_freemem(sql);
1074 		return (SQLITE_CANTOPEN);
1075 	}
1076 
1077 	rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
1078 	sqlite_freemem(sql);
1079 	smb_nic_dbclose(db);
1080 
1081 	if (rc != SQLITE_OK) {
1082 		syslog(LOG_DEBUG, "smb_nic_dbdelhost: failed to delete %s (%s)",
1083 		    host, NULL_MSGCHK(errmsg));
1084 		sqlite_freemem(errmsg);
1085 	}
1086 
1087 	return (rc);
1088 }
1089 
1090 /*
1091  * smb_nic_dbsetinfo
1092  *
1093  * Initializes the db_info table upon database creation.
1094  */
1095 static int
1096 smb_nic_dbsetinfo(sqlite *db)
1097 {
1098 	char *errmsg = NULL;
1099 	char *sql;
1100 	int rc;
1101 
1102 	sql = sqlite_mprintf("INSERT INTO db_info (ver_major, ver_minor,"
1103 	    " magic) VALUES (%d, %d, %d)", SMB_NIC_DB_VERMAJOR,
1104 	    SMB_NIC_DB_VERMINOR, SMB_NIC_DB_MAGIC);
1105 
1106 	if (sql == NULL)
1107 		return (SQLITE_NOMEM);
1108 
1109 	rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
1110 	sqlite_freemem(sql);
1111 	if (rc != SQLITE_OK) {
1112 		syslog(LOG_DEBUG, "smb_nic_dbsetinfo: failed to insert database"
1113 		    " information (%s)", NULL_MSGCHK(errmsg));
1114 		sqlite_freemem(errmsg);
1115 	}
1116 
1117 	return (rc);
1118 }
1119 
1120 /*
1121  * smb_nic_nbt_get_exclude_list
1122  *
1123  * Construct an array containing list of i/f names on which NetBIOS traffic is
1124  * to be disabled, from a string containing a list of comma separated i/f names.
1125  *
1126  * Returns the number of i/f on which NetBIOS traffic is to be disabled.
1127  */
1128 static int
1129 smb_nic_nbt_get_exclude_list(char *excludestr, char **iflist, int max_nifs)
1130 {
1131 	int n = 0;
1132 	char *entry;
1133 
1134 	bzero(iflist, SMB_PI_MAX_NETWORKS * sizeof (char *));
1135 
1136 	(void) trim_whitespace(excludestr);
1137 	(void) strcanon(excludestr, ",");
1138 
1139 	if (*excludestr == '\0')
1140 		return (0);
1141 
1142 	while (((iflist[n] = strsep(&excludestr, ",")) != NULL) &&
1143 	    (n < max_nifs)) {
1144 		entry = iflist[n];
1145 		if (*entry == '\0')
1146 			continue;
1147 		n++;
1148 	}
1149 
1150 	return (n);
1151 }
1152 
1153 /*
1154  * smb_nic_nbt_exclude
1155  *
1156  * Check to see if the given interface name should send NetBIOS traffic or not.
1157  *
1158  * Returns TRUE if NetBIOS traffic is disabled on an interface name.
1159  * Returns FALSE otherwise.
1160  */
1161 static boolean_t
1162 smb_nic_nbt_exclude(const smb_nic_t *nc, const char **exclude_list,
1163     int nexclude)
1164 {
1165 	char buf[INET6_ADDRSTRLEN];
1166 	const char *ifname = nc->nic_ifname;
1167 	int i;
1168 
1169 	if (inet_ntop(AF_INET, &nc->nic_ip, buf, INET6_ADDRSTRLEN) == NULL)
1170 		buf[0] = '\0';
1171 
1172 	for (i = 0; i < nexclude; i++) {
1173 		if (strcmp(ifname, exclude_list[i]) == 0)
1174 			return (B_TRUE);
1175 
1176 		if ((buf[0] != '\0') && (strcmp(buf, exclude_list[i]) == 0))
1177 			return (B_TRUE);
1178 	}
1179 
1180 	return (B_FALSE);
1181 }
1182