xref: /onnv-gate/usr/src/cmd/smbsrv/smbd/smbd_nicmon.c (revision 12508:edb7861a1533)
1*12508Samw@Sun.COM /*
2*12508Samw@Sun.COM  * CDDL HEADER START
3*12508Samw@Sun.COM  *
4*12508Samw@Sun.COM  * The contents of this file are subject to the terms of the
5*12508Samw@Sun.COM  * Common Development and Distribution License (the "License").
6*12508Samw@Sun.COM  * You may not use this file except in compliance with the License.
7*12508Samw@Sun.COM  *
8*12508Samw@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*12508Samw@Sun.COM  * or http://www.opensolaris.org/os/licensing.
10*12508Samw@Sun.COM  * See the License for the specific language governing permissions
11*12508Samw@Sun.COM  * and limitations under the License.
12*12508Samw@Sun.COM  *
13*12508Samw@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
14*12508Samw@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*12508Samw@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
16*12508Samw@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
17*12508Samw@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
18*12508Samw@Sun.COM  *
19*12508Samw@Sun.COM  * CDDL HEADER END
20*12508Samw@Sun.COM  */
21*12508Samw@Sun.COM /*
22*12508Samw@Sun.COM  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23*12508Samw@Sun.COM  */
24*12508Samw@Sun.COM 
25*12508Samw@Sun.COM /*
26*12508Samw@Sun.COM  * smbd NIC monitor.
27*12508Samw@Sun.COM  */
28*12508Samw@Sun.COM 
29*12508Samw@Sun.COM #include <sys/types.h>
30*12508Samw@Sun.COM #include <stdlib.h>
31*12508Samw@Sun.COM #include <errno.h>
32*12508Samw@Sun.COM #include <string.h>
33*12508Samw@Sun.COM #include <unistd.h>
34*12508Samw@Sun.COM #include <signal.h>
35*12508Samw@Sun.COM #include <stdio.h>
36*12508Samw@Sun.COM #include <net/if.h>
37*12508Samw@Sun.COM #include <net/route.h>
38*12508Samw@Sun.COM #include <sys/sockio.h>
39*12508Samw@Sun.COM #include <sys/socket.h>
40*12508Samw@Sun.COM #include <netinet/in.h>
41*12508Samw@Sun.COM #include <arpa/inet.h>
42*12508Samw@Sun.COM #include <fcntl.h>
43*12508Samw@Sun.COM #include <pthread.h>
44*12508Samw@Sun.COM #include <syslog.h>
45*12508Samw@Sun.COM #include <smbsrv/libsmb.h>
46*12508Samw@Sun.COM #include "smbd.h"
47*12508Samw@Sun.COM 
48*12508Samw@Sun.COM #define	SMBD_NICMON_ENABLE	"nicmon_enable"
49*12508Samw@Sun.COM #define	SMBD_NICMON_THROTTLE	100
50*12508Samw@Sun.COM #define	SMBD_NICMON_DEBOUNCE	2
51*12508Samw@Sun.COM 
52*12508Samw@Sun.COM extern smbd_t smbd;
53*12508Samw@Sun.COM 
54*12508Samw@Sun.COM static boolean_t smbd_nicmon_enabled = B_TRUE;
55*12508Samw@Sun.COM 
56*12508Samw@Sun.COM /* Use this to stop monitoring */
57*12508Samw@Sun.COM static int eventpipe_write = -1;
58*12508Samw@Sun.COM 
59*12508Samw@Sun.COM /* Use this to refresh service instance */
60*12508Samw@Sun.COM static char *smbd_nicmon_caller_fmri = NULL;
61*12508Samw@Sun.COM 
62*12508Samw@Sun.COM static void smbd_nicmon_run_check(void);
63*12508Samw@Sun.COM static int smbd_nicmon_setup_rtsock(int);
64*12508Samw@Sun.COM static int smbd_nicmon_needscan(int);
65*12508Samw@Sun.COM static int smbd_nicmon_setup_eventpipe(int *, int *);
66*12508Samw@Sun.COM static void *smbd_nicmon_daemon(void *);
67*12508Samw@Sun.COM 
68*12508Samw@Sun.COM /*
69*12508Samw@Sun.COM  * Start the nic monitor thread.
70*12508Samw@Sun.COM  */
71*12508Samw@Sun.COM int
smbd_nicmon_start(const char * svc_fmri)72*12508Samw@Sun.COM smbd_nicmon_start(const char *svc_fmri)
73*12508Samw@Sun.COM {
74*12508Samw@Sun.COM 	pthread_t	smbd_nicmon_tid;
75*12508Samw@Sun.COM 	int		rc;
76*12508Samw@Sun.COM 
77*12508Samw@Sun.COM 	if (smb_nic_init() != SMB_NIC_SUCCESS)
78*12508Samw@Sun.COM 		return (-1);
79*12508Samw@Sun.COM 
80*12508Samw@Sun.COM 	rc = pthread_create(&smbd_nicmon_tid, NULL, smbd_nicmon_daemon, NULL);
81*12508Samw@Sun.COM 	if (rc != 0)
82*12508Samw@Sun.COM 		return (-1);
83*12508Samw@Sun.COM 
84*12508Samw@Sun.COM 	if (svc_fmri)
85*12508Samw@Sun.COM 		smbd_nicmon_caller_fmri = (char *)svc_fmri;
86*12508Samw@Sun.COM 
87*12508Samw@Sun.COM 	smbd_nicmon_run_check();
88*12508Samw@Sun.COM 	return (0);
89*12508Samw@Sun.COM }
90*12508Samw@Sun.COM 
91*12508Samw@Sun.COM void
smbd_nicmon_stop(void)92*12508Samw@Sun.COM smbd_nicmon_stop(void)
93*12508Samw@Sun.COM {
94*12508Samw@Sun.COM 	uchar_t buf = 1;
95*12508Samw@Sun.COM 
96*12508Samw@Sun.COM 	if (eventpipe_write < 0)
97*12508Samw@Sun.COM 		return;
98*12508Samw@Sun.COM 
99*12508Samw@Sun.COM 	(void) write(eventpipe_write, &buf, sizeof (buf));
100*12508Samw@Sun.COM 	smbd_nicmon_caller_fmri = NULL;
101*12508Samw@Sun.COM 	smb_nic_fini();
102*12508Samw@Sun.COM }
103*12508Samw@Sun.COM 
104*12508Samw@Sun.COM int
smbd_nicmon_refresh(void)105*12508Samw@Sun.COM smbd_nicmon_refresh(void)
106*12508Samw@Sun.COM {
107*12508Samw@Sun.COM 	if (smb_nic_init() != SMB_NIC_SUCCESS)
108*12508Samw@Sun.COM 		return (-1);
109*12508Samw@Sun.COM 
110*12508Samw@Sun.COM 	smbd_nicmon_run_check();
111*12508Samw@Sun.COM 	return (0);
112*12508Samw@Sun.COM }
113*12508Samw@Sun.COM 
114*12508Samw@Sun.COM /*
115*12508Samw@Sun.COM  * The monitor is enabled unless it is explicitly
116*12508Samw@Sun.COM  * disabled by setting smbd/nicmon_enable to false.
117*12508Samw@Sun.COM  * smbd/nicmon_enable is not defined by default.
118*12508Samw@Sun.COM  */
119*12508Samw@Sun.COM static void
smbd_nicmon_run_check(void)120*12508Samw@Sun.COM smbd_nicmon_run_check(void)
121*12508Samw@Sun.COM {
122*12508Samw@Sun.COM 	smb_scfhandle_t	*hd;
123*12508Samw@Sun.COM 	uint8_t		status;
124*12508Samw@Sun.COM 	int		rc;
125*12508Samw@Sun.COM 
126*12508Samw@Sun.COM 	smbd_nicmon_enabled = B_TRUE;
127*12508Samw@Sun.COM 
128*12508Samw@Sun.COM 	if ((hd = smb_smf_scf_init(SMBD_FMRI_PREFIX)) == NULL) {
129*12508Samw@Sun.COM 		smb_log(smbd.s_loghd, LOG_DEBUG,
130*12508Samw@Sun.COM 		    "smbd_nicmon: smb_smf_scf_init failed");
131*12508Samw@Sun.COM 		return;
132*12508Samw@Sun.COM 	}
133*12508Samw@Sun.COM 
134*12508Samw@Sun.COM 	rc = smb_smf_create_service_pgroup(hd, SMBD_PG_NAME);
135*12508Samw@Sun.COM 	if (rc != SMBD_SMF_OK) {
136*12508Samw@Sun.COM 		smb_smf_scf_fini(hd);
137*12508Samw@Sun.COM 		smb_log(smbd.s_loghd, LOG_DEBUG,
138*12508Samw@Sun.COM 		    "smbd_nicmon: smb_smf_create_service_pgroup failed");
139*12508Samw@Sun.COM 		return;
140*12508Samw@Sun.COM 	}
141*12508Samw@Sun.COM 
142*12508Samw@Sun.COM 	rc = smb_smf_get_boolean_property(hd, SMBD_NICMON_ENABLE, &status);
143*12508Samw@Sun.COM 	if (rc == SMBD_SMF_OK && status == 0)
144*12508Samw@Sun.COM 		smbd_nicmon_enabled = B_FALSE;
145*12508Samw@Sun.COM 
146*12508Samw@Sun.COM 	smb_smf_scf_fini(hd);
147*12508Samw@Sun.COM }
148*12508Samw@Sun.COM 
149*12508Samw@Sun.COM /*
150*12508Samw@Sun.COM  * Setup routing socket for getting RTM messages.
151*12508Samw@Sun.COM  */
152*12508Samw@Sun.COM static int
smbd_nicmon_setup_rtsock(int af)153*12508Samw@Sun.COM smbd_nicmon_setup_rtsock(int af)
154*12508Samw@Sun.COM {
155*12508Samw@Sun.COM 	int sd;
156*12508Samw@Sun.COM 	int flags;
157*12508Samw@Sun.COM 
158*12508Samw@Sun.COM 	if ((sd = socket(PF_ROUTE, SOCK_RAW, af)) == -1) {
159*12508Samw@Sun.COM 		smb_log(smbd.s_loghd, LOG_ERR,
160*12508Samw@Sun.COM 		    "smbd_nicmon: routing socket failed: %d", errno);
161*12508Samw@Sun.COM 		return (-1);
162*12508Samw@Sun.COM 	}
163*12508Samw@Sun.COM 
164*12508Samw@Sun.COM 	if ((flags = fcntl(sd, F_GETFL, 0)) < 0) {
165*12508Samw@Sun.COM 		smb_log(smbd.s_loghd, LOG_ERR,
166*12508Samw@Sun.COM 		    "smbd_nicmon: fcntl F_GETFL failed: %d", errno);
167*12508Samw@Sun.COM 		(void) close(sd);
168*12508Samw@Sun.COM 		return (-1);
169*12508Samw@Sun.COM 	}
170*12508Samw@Sun.COM 
171*12508Samw@Sun.COM 	if ((fcntl(sd, F_SETFL, flags | O_NONBLOCK)) < 0) {
172*12508Samw@Sun.COM 		smb_log(smbd.s_loghd, LOG_ERR,
173*12508Samw@Sun.COM 		    "smbd_nicmon: fcntl F_SETFL failed: %d", errno);
174*12508Samw@Sun.COM 		(void) close(sd);
175*12508Samw@Sun.COM 		return (-1);
176*12508Samw@Sun.COM 	}
177*12508Samw@Sun.COM 
178*12508Samw@Sun.COM 	return (sd);
179*12508Samw@Sun.COM }
180*12508Samw@Sun.COM 
181*12508Samw@Sun.COM static int
smbd_nicmon_needscan(int sock)182*12508Samw@Sun.COM smbd_nicmon_needscan(int sock)
183*12508Samw@Sun.COM {
184*12508Samw@Sun.COM 	static uint32_t		throttle;
185*12508Samw@Sun.COM 	struct rt_msghdr	*rtm;
186*12508Samw@Sun.COM 	int64_t			msg[2048 / 8];
187*12508Samw@Sun.COM 	int			need_if_scan = 0;
188*12508Samw@Sun.COM 	int			nbytes;
189*12508Samw@Sun.COM 
190*12508Samw@Sun.COM 	/* Read as many messages as possible and try to empty the sockets */
191*12508Samw@Sun.COM 	for (;;) {
192*12508Samw@Sun.COM 		nbytes = read(sock, msg, sizeof (msg));
193*12508Samw@Sun.COM 		if (nbytes <= 0)
194*12508Samw@Sun.COM 			break;
195*12508Samw@Sun.COM 
196*12508Samw@Sun.COM 		rtm = (struct rt_msghdr *)msg;
197*12508Samw@Sun.COM 		if (rtm->rtm_version != RTM_VERSION)
198*12508Samw@Sun.COM 			continue;
199*12508Samw@Sun.COM 
200*12508Samw@Sun.COM 		if (nbytes < rtm->rtm_msglen) {
201*12508Samw@Sun.COM 			if ((throttle % SMBD_NICMON_THROTTLE) == 0) {
202*12508Samw@Sun.COM 				smb_log(smbd.s_loghd, LOG_DEBUG,
203*12508Samw@Sun.COM 				    "smbd_nicmon: short read: %d of %d",
204*12508Samw@Sun.COM 				    nbytes, rtm->rtm_msglen);
205*12508Samw@Sun.COM 			}
206*12508Samw@Sun.COM 			++throttle;
207*12508Samw@Sun.COM 			continue;
208*12508Samw@Sun.COM 		}
209*12508Samw@Sun.COM 
210*12508Samw@Sun.COM 		switch (rtm->rtm_type) {
211*12508Samw@Sun.COM 		case RTM_NEWADDR:
212*12508Samw@Sun.COM 		case RTM_DELADDR:
213*12508Samw@Sun.COM 		case RTM_IFINFO:
214*12508Samw@Sun.COM 			need_if_scan = 1;
215*12508Samw@Sun.COM 			break;
216*12508Samw@Sun.COM 		default:
217*12508Samw@Sun.COM 			break;
218*12508Samw@Sun.COM 		}
219*12508Samw@Sun.COM 	}
220*12508Samw@Sun.COM 
221*12508Samw@Sun.COM 	return (need_if_scan);
222*12508Samw@Sun.COM }
223*12508Samw@Sun.COM 
224*12508Samw@Sun.COM /*
225*12508Samw@Sun.COM  * Create pipe for signal delivery and set up signal handlers.
226*12508Samw@Sun.COM  */
227*12508Samw@Sun.COM static int
smbd_nicmon_setup_eventpipe(int * read_pipe,int * write_pipe)228*12508Samw@Sun.COM smbd_nicmon_setup_eventpipe(int *read_pipe, int *write_pipe)
229*12508Samw@Sun.COM {
230*12508Samw@Sun.COM 	int fds[2];
231*12508Samw@Sun.COM 
232*12508Samw@Sun.COM 	if ((pipe(fds)) < 0) {
233*12508Samw@Sun.COM 		smb_log(smbd.s_loghd, LOG_ERR,
234*12508Samw@Sun.COM 		    "smbd_nicmon: event pipe failed: %d", errno);
235*12508Samw@Sun.COM 		return (-1);
236*12508Samw@Sun.COM 	}
237*12508Samw@Sun.COM 
238*12508Samw@Sun.COM 	*read_pipe = fds[0];
239*12508Samw@Sun.COM 	*write_pipe = fds[1];
240*12508Samw@Sun.COM 	return (0);
241*12508Samw@Sun.COM }
242*12508Samw@Sun.COM 
243*12508Samw@Sun.COM /*
244*12508Samw@Sun.COM  * Create the global routing socket to monitor changes in NIC interfaces.
245*12508Samw@Sun.COM  * We are only interested in new inerface addition/deletion and changes
246*12508Samw@Sun.COM  * in UP/DOWN status.
247*12508Samw@Sun.COM  *
248*12508Samw@Sun.COM  * Note: only supports AF_INET routing socket.  Need to add AF_INET6 to
249*12508Samw@Sun.COM  * support IPv6.
250*12508Samw@Sun.COM  */
251*12508Samw@Sun.COM /*ARGSUSED*/
252*12508Samw@Sun.COM static void *
smbd_nicmon_daemon(void * arg)253*12508Samw@Sun.COM smbd_nicmon_daemon(void *arg)
254*12508Samw@Sun.COM {
255*12508Samw@Sun.COM 	static uint32_t	throttle;
256*12508Samw@Sun.COM 	static int	rtsock_v4;
257*12508Samw@Sun.COM 	static int	eventpipe_read = -1;
258*12508Samw@Sun.COM 	struct pollfd	pollfds[2];
259*12508Samw@Sun.COM 	int		pollfd_num = 2;
260*12508Samw@Sun.COM 	int		i, nic_changed;
261*12508Samw@Sun.COM 	int		rc;
262*12508Samw@Sun.COM 
263*12508Samw@Sun.COM 	if ((rtsock_v4 = smbd_nicmon_setup_rtsock(AF_INET)) == -1)
264*12508Samw@Sun.COM 		return (NULL);
265*12508Samw@Sun.COM 
266*12508Samw@Sun.COM 	rc = smbd_nicmon_setup_eventpipe(&eventpipe_read, &eventpipe_write);
267*12508Samw@Sun.COM 	if (rc != 0)
268*12508Samw@Sun.COM 		return (NULL);
269*12508Samw@Sun.COM 
270*12508Samw@Sun.COM 	/*
271*12508Samw@Sun.COM 	 * Listen for activity on any of the sockets.
272*12508Samw@Sun.COM 	 * The delay before checking the rtsock will hopefully
273*12508Samw@Sun.COM 	 * smooth things out when there is a lot of activity.
274*12508Samw@Sun.COM 	 */
275*12508Samw@Sun.COM 	for (;;) {
276*12508Samw@Sun.COM 		errno = 0;
277*12508Samw@Sun.COM 		nic_changed = 0;
278*12508Samw@Sun.COM 		pollfds[0].fd = rtsock_v4;
279*12508Samw@Sun.COM 		pollfds[0].events = POLLIN;
280*12508Samw@Sun.COM 		pollfds[1].fd = eventpipe_read;
281*12508Samw@Sun.COM 		pollfds[1].events = POLLIN;
282*12508Samw@Sun.COM 
283*12508Samw@Sun.COM 		if (poll(pollfds, pollfd_num, -1) < 0) {
284*12508Samw@Sun.COM 			if (errno == EINTR)
285*12508Samw@Sun.COM 				continue;
286*12508Samw@Sun.COM 			if ((throttle % SMBD_NICMON_THROTTLE) == 0)
287*12508Samw@Sun.COM 				smb_log(smbd.s_loghd, LOG_DEBUG,
288*12508Samw@Sun.COM 				    "smbd_nicmon: poll failed: %d", errno);
289*12508Samw@Sun.COM 			++throttle;
290*12508Samw@Sun.COM 			break;
291*12508Samw@Sun.COM 		}
292*12508Samw@Sun.COM 
293*12508Samw@Sun.COM 		for (i = 0; i < pollfd_num; i++) {
294*12508Samw@Sun.COM 			if ((pollfds[i].fd < 0) ||
295*12508Samw@Sun.COM 			    !(pollfds[i].revents & POLLIN))
296*12508Samw@Sun.COM 				continue;
297*12508Samw@Sun.COM 			if (pollfds[i].fd == rtsock_v4) {
298*12508Samw@Sun.COM 				(void) sleep(SMBD_NICMON_DEBOUNCE);
299*12508Samw@Sun.COM 				nic_changed = smbd_nicmon_needscan(rtsock_v4);
300*12508Samw@Sun.COM 			}
301*12508Samw@Sun.COM 			if (pollfds[i].fd == eventpipe_read)
302*12508Samw@Sun.COM 				goto done;
303*12508Samw@Sun.COM 		}
304*12508Samw@Sun.COM 
305*12508Samw@Sun.COM 		/*
306*12508Samw@Sun.COM 		 * If the monitor is enabled and something has changed,
307*12508Samw@Sun.COM 		 * refresh the registered SMF service.
308*12508Samw@Sun.COM 		 */
309*12508Samw@Sun.COM 		if (smbd_nicmon_enabled && nic_changed &&
310*12508Samw@Sun.COM 		    smbd_nicmon_caller_fmri) {
311*12508Samw@Sun.COM 			if (smf_refresh_instance(smbd_nicmon_caller_fmri) != 0)
312*12508Samw@Sun.COM 				smb_log(smbd.s_loghd, LOG_ERR,
313*12508Samw@Sun.COM 				    "smbd_nicmon: %s refresh failed",
314*12508Samw@Sun.COM 				    smbd_nicmon_caller_fmri);
315*12508Samw@Sun.COM 		}
316*12508Samw@Sun.COM 	}
317*12508Samw@Sun.COM done:
318*12508Samw@Sun.COM 	(void) close(rtsock_v4);
319*12508Samw@Sun.COM 	(void) close(eventpipe_read);
320*12508Samw@Sun.COM 	(void) close(eventpipe_write);
321*12508Samw@Sun.COM 	eventpipe_write = -1;
322*12508Samw@Sun.COM 	return (NULL);
323*12508Samw@Sun.COM }
324