1148c5f43SAlan Wright /*
2148c5f43SAlan Wright  * CDDL HEADER START
3148c5f43SAlan Wright  *
4148c5f43SAlan Wright  * The contents of this file are subject to the terms of the
5148c5f43SAlan Wright  * Common Development and Distribution License (the "License").
6148c5f43SAlan Wright  * You may not use this file except in compliance with the License.
7148c5f43SAlan Wright  *
8148c5f43SAlan Wright  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9148c5f43SAlan Wright  * or http://www.opensolaris.org/os/licensing.
10148c5f43SAlan Wright  * See the License for the specific language governing permissions
11148c5f43SAlan Wright  * and limitations under the License.
12148c5f43SAlan Wright  *
13148c5f43SAlan Wright  * When distributing Covered Code, include this CDDL HEADER in each
14148c5f43SAlan Wright  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15148c5f43SAlan Wright  * If applicable, add the following below this CDDL HEADER, with the
16148c5f43SAlan Wright  * fields enclosed by brackets "[]" replaced with your own identifying
17148c5f43SAlan Wright  * information: Portions Copyright [yyyy] [name of copyright owner]
18148c5f43SAlan Wright  *
19148c5f43SAlan Wright  * CDDL HEADER END
20148c5f43SAlan Wright  */
21148c5f43SAlan Wright /*
22148c5f43SAlan Wright  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23148c5f43SAlan Wright  */
24148c5f43SAlan Wright 
25148c5f43SAlan Wright /*
26148c5f43SAlan Wright  * smbd NIC monitor.
27148c5f43SAlan Wright  */
28148c5f43SAlan Wright 
29148c5f43SAlan Wright #include <sys/types.h>
30148c5f43SAlan Wright #include <stdlib.h>
31148c5f43SAlan Wright #include <errno.h>
32148c5f43SAlan Wright #include <string.h>
33148c5f43SAlan Wright #include <unistd.h>
34148c5f43SAlan Wright #include <signal.h>
35148c5f43SAlan Wright #include <stdio.h>
36148c5f43SAlan Wright #include <net/if.h>
37148c5f43SAlan Wright #include <net/route.h>
38148c5f43SAlan Wright #include <sys/sockio.h>
39148c5f43SAlan Wright #include <sys/socket.h>
40148c5f43SAlan Wright #include <netinet/in.h>
41148c5f43SAlan Wright #include <arpa/inet.h>
42148c5f43SAlan Wright #include <fcntl.h>
43148c5f43SAlan Wright #include <pthread.h>
44148c5f43SAlan Wright #include <syslog.h>
45148c5f43SAlan Wright #include <smbsrv/libsmb.h>
46148c5f43SAlan Wright #include "smbd.h"
47148c5f43SAlan Wright 
48148c5f43SAlan Wright #define	SMBD_NICMON_ENABLE	"nicmon_enable"
49148c5f43SAlan Wright #define	SMBD_NICMON_THROTTLE	100
50148c5f43SAlan Wright #define	SMBD_NICMON_DEBOUNCE	2
51148c5f43SAlan Wright 
52148c5f43SAlan Wright extern smbd_t smbd;
53148c5f43SAlan Wright 
54148c5f43SAlan Wright static boolean_t smbd_nicmon_enabled = B_TRUE;
55148c5f43SAlan Wright 
56148c5f43SAlan Wright /* Use this to stop monitoring */
57148c5f43SAlan Wright static int eventpipe_write = -1;
58148c5f43SAlan Wright 
59148c5f43SAlan Wright /* Use this to refresh service instance */
60148c5f43SAlan Wright static char *smbd_nicmon_caller_fmri = NULL;
61148c5f43SAlan Wright 
62148c5f43SAlan Wright static void smbd_nicmon_run_check(void);
63148c5f43SAlan Wright static int smbd_nicmon_setup_rtsock(int);
64148c5f43SAlan Wright static int smbd_nicmon_needscan(int);
65148c5f43SAlan Wright static int smbd_nicmon_setup_eventpipe(int *, int *);
66148c5f43SAlan Wright static void *smbd_nicmon_daemon(void *);
67148c5f43SAlan Wright 
68148c5f43SAlan Wright /*
69148c5f43SAlan Wright  * Start the nic monitor thread.
70148c5f43SAlan Wright  */
71148c5f43SAlan Wright int
smbd_nicmon_start(const char * svc_fmri)72148c5f43SAlan Wright smbd_nicmon_start(const char *svc_fmri)
73148c5f43SAlan Wright {
74148c5f43SAlan Wright 	pthread_t	smbd_nicmon_tid;
75148c5f43SAlan Wright 	int		rc;
76148c5f43SAlan Wright 
77148c5f43SAlan Wright 	if (smb_nic_init() != SMB_NIC_SUCCESS)
78148c5f43SAlan Wright 		return (-1);
79148c5f43SAlan Wright 
80148c5f43SAlan Wright 	rc = pthread_create(&smbd_nicmon_tid, NULL, smbd_nicmon_daemon, NULL);
81148c5f43SAlan Wright 	if (rc != 0)
82148c5f43SAlan Wright 		return (-1);
83148c5f43SAlan Wright 
84148c5f43SAlan Wright 	if (svc_fmri)
85148c5f43SAlan Wright 		smbd_nicmon_caller_fmri = (char *)svc_fmri;
86148c5f43SAlan Wright 
87148c5f43SAlan Wright 	smbd_nicmon_run_check();
88148c5f43SAlan Wright 	return (0);
89148c5f43SAlan Wright }
90148c5f43SAlan Wright 
91148c5f43SAlan Wright void
smbd_nicmon_stop(void)92148c5f43SAlan Wright smbd_nicmon_stop(void)
93148c5f43SAlan Wright {
94148c5f43SAlan Wright 	uchar_t buf = 1;
95148c5f43SAlan Wright 
96148c5f43SAlan Wright 	if (eventpipe_write < 0)
97148c5f43SAlan Wright 		return;
98148c5f43SAlan Wright 
99148c5f43SAlan Wright 	(void) write(eventpipe_write, &buf, sizeof (buf));
100148c5f43SAlan Wright 	smbd_nicmon_caller_fmri = NULL;
101148c5f43SAlan Wright 	smb_nic_fini();
102148c5f43SAlan Wright }
103148c5f43SAlan Wright 
104148c5f43SAlan Wright int
smbd_nicmon_refresh(void)105148c5f43SAlan Wright smbd_nicmon_refresh(void)
106148c5f43SAlan Wright {
107148c5f43SAlan Wright 	if (smb_nic_init() != SMB_NIC_SUCCESS)
108148c5f43SAlan Wright 		return (-1);
109148c5f43SAlan Wright 
110148c5f43SAlan Wright 	smbd_nicmon_run_check();
111148c5f43SAlan Wright 	return (0);
112148c5f43SAlan Wright }
113148c5f43SAlan Wright 
114148c5f43SAlan Wright /*
115148c5f43SAlan Wright  * The monitor is enabled unless it is explicitly
116148c5f43SAlan Wright  * disabled by setting smbd/nicmon_enable to false.
117148c5f43SAlan Wright  * smbd/nicmon_enable is not defined by default.
118148c5f43SAlan Wright  */
119148c5f43SAlan Wright static void
smbd_nicmon_run_check(void)120148c5f43SAlan Wright smbd_nicmon_run_check(void)
121148c5f43SAlan Wright {
122148c5f43SAlan Wright 	smb_scfhandle_t	*hd;
123148c5f43SAlan Wright 	uint8_t		status;
124148c5f43SAlan Wright 	int		rc;
125148c5f43SAlan Wright 
126148c5f43SAlan Wright 	smbd_nicmon_enabled = B_TRUE;
127148c5f43SAlan Wright 
128148c5f43SAlan Wright 	if ((hd = smb_smf_scf_init(SMBD_FMRI_PREFIX)) == NULL) {
129*b819cea2SGordon Ross 		syslog(LOG_DEBUG,
130148c5f43SAlan Wright 		    "smbd_nicmon: smb_smf_scf_init failed");
131148c5f43SAlan Wright 		return;
132148c5f43SAlan Wright 	}
133148c5f43SAlan Wright 
134148c5f43SAlan Wright 	rc = smb_smf_create_service_pgroup(hd, SMBD_PG_NAME);
135148c5f43SAlan Wright 	if (rc != SMBD_SMF_OK) {
136148c5f43SAlan Wright 		smb_smf_scf_fini(hd);
137*b819cea2SGordon Ross 		syslog(LOG_DEBUG,
138148c5f43SAlan Wright 		    "smbd_nicmon: smb_smf_create_service_pgroup failed");
139148c5f43SAlan Wright 		return;
140148c5f43SAlan Wright 	}
141148c5f43SAlan Wright 
142148c5f43SAlan Wright 	rc = smb_smf_get_boolean_property(hd, SMBD_NICMON_ENABLE, &status);
143148c5f43SAlan Wright 	if (rc == SMBD_SMF_OK && status == 0)
144148c5f43SAlan Wright 		smbd_nicmon_enabled = B_FALSE;
145148c5f43SAlan Wright 
146148c5f43SAlan Wright 	smb_smf_scf_fini(hd);
147148c5f43SAlan Wright }
148148c5f43SAlan Wright 
149148c5f43SAlan Wright /*
150148c5f43SAlan Wright  * Setup routing socket for getting RTM messages.
151148c5f43SAlan Wright  */
152148c5f43SAlan Wright static int
smbd_nicmon_setup_rtsock(int af)153148c5f43SAlan Wright smbd_nicmon_setup_rtsock(int af)
154148c5f43SAlan Wright {
155148c5f43SAlan Wright 	int sd;
156148c5f43SAlan Wright 	int flags;
157148c5f43SAlan Wright 
158148c5f43SAlan Wright 	if ((sd = socket(PF_ROUTE, SOCK_RAW, af)) == -1) {
159*b819cea2SGordon Ross 		syslog(LOG_ERR,
160148c5f43SAlan Wright 		    "smbd_nicmon: routing socket failed: %d", errno);
161148c5f43SAlan Wright 		return (-1);
162148c5f43SAlan Wright 	}
163148c5f43SAlan Wright 
164148c5f43SAlan Wright 	if ((flags = fcntl(sd, F_GETFL, 0)) < 0) {
165*b819cea2SGordon Ross 		syslog(LOG_ERR,
166148c5f43SAlan Wright 		    "smbd_nicmon: fcntl F_GETFL failed: %d", errno);
167148c5f43SAlan Wright 		(void) close(sd);
168148c5f43SAlan Wright 		return (-1);
169148c5f43SAlan Wright 	}
170148c5f43SAlan Wright 
171148c5f43SAlan Wright 	if ((fcntl(sd, F_SETFL, flags | O_NONBLOCK)) < 0) {
172*b819cea2SGordon Ross 		syslog(LOG_ERR,
173148c5f43SAlan Wright 		    "smbd_nicmon: fcntl F_SETFL failed: %d", errno);
174148c5f43SAlan Wright 		(void) close(sd);
175148c5f43SAlan Wright 		return (-1);
176148c5f43SAlan Wright 	}
177148c5f43SAlan Wright 
178148c5f43SAlan Wright 	return (sd);
179148c5f43SAlan Wright }
180148c5f43SAlan Wright 
181148c5f43SAlan Wright static int
smbd_nicmon_needscan(int sock)182148c5f43SAlan Wright smbd_nicmon_needscan(int sock)
183148c5f43SAlan Wright {
184148c5f43SAlan Wright 	static uint32_t		throttle;
185148c5f43SAlan Wright 	struct rt_msghdr	*rtm;
186148c5f43SAlan Wright 	int64_t			msg[2048 / 8];
187148c5f43SAlan Wright 	int			need_if_scan = 0;
188148c5f43SAlan Wright 	int			nbytes;
189148c5f43SAlan Wright 
190148c5f43SAlan Wright 	/* Read as many messages as possible and try to empty the sockets */
191148c5f43SAlan Wright 	for (;;) {
192148c5f43SAlan Wright 		nbytes = read(sock, msg, sizeof (msg));
193148c5f43SAlan Wright 		if (nbytes <= 0)
194148c5f43SAlan Wright 			break;
195148c5f43SAlan Wright 
196148c5f43SAlan Wright 		rtm = (struct rt_msghdr *)msg;
197148c5f43SAlan Wright 		if (rtm->rtm_version != RTM_VERSION)
198148c5f43SAlan Wright 			continue;
199148c5f43SAlan Wright 
200148c5f43SAlan Wright 		if (nbytes < rtm->rtm_msglen) {
201148c5f43SAlan Wright 			if ((throttle % SMBD_NICMON_THROTTLE) == 0) {
202*b819cea2SGordon Ross 				syslog(LOG_DEBUG,
203148c5f43SAlan Wright 				    "smbd_nicmon: short read: %d of %d",
204148c5f43SAlan Wright 				    nbytes, rtm->rtm_msglen);
205148c5f43SAlan Wright 			}
206148c5f43SAlan Wright 			++throttle;
207148c5f43SAlan Wright 			continue;
208148c5f43SAlan Wright 		}
209148c5f43SAlan Wright 
210148c5f43SAlan Wright 		switch (rtm->rtm_type) {
211148c5f43SAlan Wright 		case RTM_NEWADDR:
212148c5f43SAlan Wright 		case RTM_DELADDR:
213148c5f43SAlan Wright 		case RTM_IFINFO:
214148c5f43SAlan Wright 			need_if_scan = 1;
215148c5f43SAlan Wright 			break;
216148c5f43SAlan Wright 		default:
217148c5f43SAlan Wright 			break;
218148c5f43SAlan Wright 		}
219148c5f43SAlan Wright 	}
220148c5f43SAlan Wright 
221148c5f43SAlan Wright 	return (need_if_scan);
222148c5f43SAlan Wright }
223148c5f43SAlan Wright 
224148c5f43SAlan Wright /*
225148c5f43SAlan Wright  * Create pipe for signal delivery and set up signal handlers.
226148c5f43SAlan Wright  */
227148c5f43SAlan Wright static int
smbd_nicmon_setup_eventpipe(int * read_pipe,int * write_pipe)228148c5f43SAlan Wright smbd_nicmon_setup_eventpipe(int *read_pipe, int *write_pipe)
229148c5f43SAlan Wright {
230148c5f43SAlan Wright 	int fds[2];
231148c5f43SAlan Wright 
232148c5f43SAlan Wright 	if ((pipe(fds)) < 0) {
233*b819cea2SGordon Ross 		syslog(LOG_ERR,
234148c5f43SAlan Wright 		    "smbd_nicmon: event pipe failed: %d", errno);
235148c5f43SAlan Wright 		return (-1);
236148c5f43SAlan Wright 	}
237148c5f43SAlan Wright 
238148c5f43SAlan Wright 	*read_pipe = fds[0];
239148c5f43SAlan Wright 	*write_pipe = fds[1];
240148c5f43SAlan Wright 	return (0);
241148c5f43SAlan Wright }
242148c5f43SAlan Wright 
243148c5f43SAlan Wright /*
244148c5f43SAlan Wright  * Create the global routing socket to monitor changes in NIC interfaces.
245148c5f43SAlan Wright  * We are only interested in new inerface addition/deletion and changes
246148c5f43SAlan Wright  * in UP/DOWN status.
247148c5f43SAlan Wright  *
248148c5f43SAlan Wright  * Note: only supports AF_INET routing socket.  Need to add AF_INET6 to
249148c5f43SAlan Wright  * support IPv6.
250148c5f43SAlan Wright  */
251148c5f43SAlan Wright /*ARGSUSED*/
252148c5f43SAlan Wright static void *
smbd_nicmon_daemon(void * arg)253148c5f43SAlan Wright smbd_nicmon_daemon(void *arg)
254148c5f43SAlan Wright {
255148c5f43SAlan Wright 	static uint32_t	throttle;
256148c5f43SAlan Wright 	static int	rtsock_v4;
257148c5f43SAlan Wright 	static int	eventpipe_read = -1;
258148c5f43SAlan Wright 	struct pollfd	pollfds[2];
259148c5f43SAlan Wright 	int		pollfd_num = 2;
260148c5f43SAlan Wright 	int		i, nic_changed;
261148c5f43SAlan Wright 	int		rc;
262148c5f43SAlan Wright 
263148c5f43SAlan Wright 	if ((rtsock_v4 = smbd_nicmon_setup_rtsock(AF_INET)) == -1)
264148c5f43SAlan Wright 		return (NULL);
265148c5f43SAlan Wright 
266148c5f43SAlan Wright 	rc = smbd_nicmon_setup_eventpipe(&eventpipe_read, &eventpipe_write);
267148c5f43SAlan Wright 	if (rc != 0)
268148c5f43SAlan Wright 		return (NULL);
269148c5f43SAlan Wright 
270148c5f43SAlan Wright 	/*
271148c5f43SAlan Wright 	 * Listen for activity on any of the sockets.
272148c5f43SAlan Wright 	 * The delay before checking the rtsock will hopefully
273148c5f43SAlan Wright 	 * smooth things out when there is a lot of activity.
274148c5f43SAlan Wright 	 */
275148c5f43SAlan Wright 	for (;;) {
276148c5f43SAlan Wright 		errno = 0;
277148c5f43SAlan Wright 		nic_changed = 0;
278148c5f43SAlan Wright 		pollfds[0].fd = rtsock_v4;
279148c5f43SAlan Wright 		pollfds[0].events = POLLIN;
280148c5f43SAlan Wright 		pollfds[1].fd = eventpipe_read;
281148c5f43SAlan Wright 		pollfds[1].events = POLLIN;
282148c5f43SAlan Wright 
283148c5f43SAlan Wright 		if (poll(pollfds, pollfd_num, -1) < 0) {
284148c5f43SAlan Wright 			if (errno == EINTR)
285148c5f43SAlan Wright 				continue;
286148c5f43SAlan Wright 			if ((throttle % SMBD_NICMON_THROTTLE) == 0)
287*b819cea2SGordon Ross 				syslog(LOG_DEBUG,
288148c5f43SAlan Wright 				    "smbd_nicmon: poll failed: %d", errno);
289148c5f43SAlan Wright 			++throttle;
290148c5f43SAlan Wright 			break;
291148c5f43SAlan Wright 		}
292148c5f43SAlan Wright 
293148c5f43SAlan Wright 		for (i = 0; i < pollfd_num; i++) {
294148c5f43SAlan Wright 			if ((pollfds[i].fd < 0) ||
295148c5f43SAlan Wright 			    !(pollfds[i].revents & POLLIN))
296148c5f43SAlan Wright 				continue;
297148c5f43SAlan Wright 			if (pollfds[i].fd == rtsock_v4) {
298148c5f43SAlan Wright 				(void) sleep(SMBD_NICMON_DEBOUNCE);
299148c5f43SAlan Wright 				nic_changed = smbd_nicmon_needscan(rtsock_v4);
300148c5f43SAlan Wright 			}
301148c5f43SAlan Wright 			if (pollfds[i].fd == eventpipe_read)
302148c5f43SAlan Wright 				goto done;
303148c5f43SAlan Wright 		}
304148c5f43SAlan Wright 
305148c5f43SAlan Wright 		/*
306148c5f43SAlan Wright 		 * If the monitor is enabled and something has changed,
307148c5f43SAlan Wright 		 * refresh the registered SMF service.
308148c5f43SAlan Wright 		 */
309148c5f43SAlan Wright 		if (smbd_nicmon_enabled && nic_changed &&
310148c5f43SAlan Wright 		    smbd_nicmon_caller_fmri) {
311148c5f43SAlan Wright 			if (smf_refresh_instance(smbd_nicmon_caller_fmri) != 0)
312*b819cea2SGordon Ross 				syslog(LOG_ERR,
313148c5f43SAlan Wright 				    "smbd_nicmon: %s refresh failed",
314148c5f43SAlan Wright 				    smbd_nicmon_caller_fmri);
315148c5f43SAlan Wright 		}
316148c5f43SAlan Wright 	}
317148c5f43SAlan Wright done:
318148c5f43SAlan Wright 	(void) close(rtsock_v4);
319148c5f43SAlan Wright 	(void) close(eventpipe_read);
320148c5f43SAlan Wright 	(void) close(eventpipe_write);
321148c5f43SAlan Wright 	eventpipe_write = -1;
322148c5f43SAlan Wright 	return (NULL);
323148c5f43SAlan Wright }
324