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