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