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