xref: /illumos-gate/usr/src/cmd/smbsrv/smbd/smbd_nicmon.c (revision 89a7715a55deca73d03076f5c24463717f0aaa91)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * This is the SMB NIC monitoring module.
30  */
31 #include <sys/types.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <signal.h>
37 #include <stdio.h>
38 #include <net/if.h>
39 #include <net/route.h>
40 #include <sys/sockio.h>
41 #include <sys/socket.h>
42 #include <netinet/in.h>
43 #include <arpa/inet.h>
44 #include <fcntl.h>
45 #include <pthread.h>
46 #include <syslog.h>
47 #include <smbsrv/libsmb.h>
48 #include <smbsrv/libsmbns.h>
49 
50 static pthread_t smb_nicmon_thread;
51 
52 static void smb_setup_rtsock(int, int *);
53 static int smb_nics_changed(int);
54 static void *smb_nicmonitor(void *);
55 static int smb_setup_eventpipe(int *, int *);
56 static void smb_process_nic_change();
57 
58 /* Use this to stop monitoring */
59 static int eventpipe_write = -1;
60 
61 /*
62  * Start the nic monitor thread.
63  */
64 int
65 smb_nicmon_start(void)
66 {
67 	int rc = 0;
68 
69 	smb_nic_build_info();
70 	rc = pthread_create(&smb_nicmon_thread, NULL, smb_nicmonitor, 0);
71 	if (rc != 0) {
72 		syslog(LOG_ERR, "smb_nicmonitor: "
73 		    "NIC monitoring failed to start: %s", strerror(errno));
74 		return (rc);
75 	}
76 	return (rc);
77 }
78 
79 /*
80  * Stop the nic monitor.
81  */
82 void
83 smb_nicmon_stop(void)
84 {
85 	uchar_t buf = 1;
86 
87 	if (eventpipe_write < 0)
88 		return;
89 
90 	(void) write(eventpipe_write, &buf, sizeof (buf));
91 }
92 
93 /*
94  * Setup routing socket for getting RTM messages.
95  */
96 static void
97 smb_setup_rtsock(int af, int *s)
98 {
99 	int flags;
100 
101 	*s = socket(PF_ROUTE, SOCK_RAW, af);
102 	if (*s == -1) {
103 		syslog(LOG_ERR, "smb_setup_rtsock: failed to "
104 		    "create routing socket");
105 		return;
106 	}
107 	if ((flags = fcntl(*s, F_GETFL, 0)) < 0) {
108 		syslog(LOG_ERR, "smb_setup_rtsock: failed to fcntl F_GETFL");
109 		(void) close(*s);
110 		*s = -1;
111 		return;
112 	}
113 	if ((fcntl(*s, F_SETFL, flags | O_NONBLOCK)) < 0) {
114 		syslog(LOG_ERR, "smb_setup_rtsock: failed to fcntl F_SETFL");
115 		(void) close(*s);
116 		*s = -1;
117 		return;
118 	}
119 }
120 
121 static int
122 smb_nics_changed(int sock)
123 {
124 	int	nbytes;
125 	int64_t msg[2048 / 8];
126 	struct rt_msghdr *rtm;
127 	int need_if_scan = 0;
128 
129 	/* Read as many messages as possible and try to empty the sockets */
130 	for (;;) {
131 		nbytes = read(sock, msg, sizeof (msg));
132 		if (nbytes <= 0) {
133 			break;
134 		}
135 		rtm = (struct rt_msghdr *)msg;
136 		if (rtm->rtm_version != RTM_VERSION) {
137 			continue;
138 		}
139 		if (nbytes < rtm->rtm_msglen) {
140 			syslog(LOG_DEBUG, "smb_nicmonitor: short read: %d "
141 			    "of %d", nbytes, rtm->rtm_msglen);
142 			continue;
143 		}
144 
145 		switch (rtm->rtm_type) {
146 		case RTM_NEWADDR:
147 		case RTM_DELADDR:
148 		case RTM_IFINFO:
149 			need_if_scan = 1;
150 			break;
151 		default:
152 			break;
153 		}
154 	}
155 
156 	return (need_if_scan);
157 }
158 
159 /*
160  * Create pipe for signal delivery and set up signal handlers.
161  */
162 static int
163 smb_setup_eventpipe(int *read_pipe, int *write_pipe)
164 {
165 	int fds[2];
166 
167 	if ((pipe(fds)) < 0) {
168 		syslog(LOG_ERR, "smb_nicmonitor: failed to open pipe");
169 		return (1);
170 	}
171 	*read_pipe = fds[0];
172 	*write_pipe = fds[1];
173 	return (0);
174 }
175 
176 /*
177  * Call this to do stuff after ifs changed.
178  */
179 static void
180 smb_process_nic_change()
181 {
182 	int ddns_enabled;
183 
184 	smb_config_rdlock();
185 	ddns_enabled = smb_config_getyorn(SMB_CI_DYNDNS_ENABLE);
186 	smb_config_unlock();
187 
188 	/* Clear rev zone before creating if list */
189 	if (ddns_enabled) {
190 		if (dyndns_clear_rev_zone() != 0) {
191 			syslog(LOG_ERR, "smb_nicmonitor: "
192 			    "failed to clear DNS reverse lookup zone");
193 		}
194 	}
195 
196 	/* re-initialize NIC table */
197 	smb_nic_build_info();
198 
199 	smb_netbios_name_reconfig();
200 
201 	if (ddns_enabled) {
202 		if (dyndns_update() != 0) {
203 			syslog(LOG_ERR, "smb_nicmonitor: "
204 			    "failed to update dynamic DNS");
205 		}
206 	}
207 }
208 
209 /*ARGSUSED*/
210 static void *
211 smb_nicmonitor(void *args)
212 {
213 	struct pollfd pollfds[2];
214 	int pollfd_num = 2;
215 	int i, nic_changed;
216 	/* AF_INET routing socket add AF_INET6 when we support IPv6 */
217 	static int rtsock_v4;
218 	static int eventpipe_read = -1;
219 
220 	/*
221 	 * Create the global routing socket.  We use this to
222 	 * monitor changes in NIC interfaces. We are only interested
223 	 * in new inerface addition/deletion and change in UP/DOWN status.
224 	 */
225 	smb_setup_rtsock(AF_INET, &rtsock_v4);
226 	if (rtsock_v4 == -1) {
227 		syslog(LOG_ERR, "smb_nicmonitor: cannot open routing socket");
228 		return (NULL);
229 	}
230 	if (smb_setup_eventpipe(&eventpipe_read, &eventpipe_write) != 0) {
231 		syslog(LOG_ERR, "smb_nicmonitor: cannot open event pipes");
232 		return (NULL);
233 	}
234 
235 	/*
236 	 * Keep listening for activity on any of the sockets.
237 	 */
238 	for (;;) {
239 		nic_changed = 0;
240 		pollfds[0].fd = rtsock_v4;
241 		pollfds[0].events = POLLIN;
242 		pollfds[1].fd = eventpipe_read;
243 		pollfds[1].events = POLLIN;
244 		if (poll(pollfds, pollfd_num, -1) < 0) {
245 			if (errno == EINTR)
246 				continue;
247 			syslog(LOG_ERR, "smb_nicmonitor: "
248 			    "poll failed with errno %d", errno);
249 			break;
250 		}
251 		for (i = 0; i < pollfd_num; i++) {
252 			if ((pollfds[i].fd < 0) ||
253 			    !(pollfds[i].revents & POLLIN))
254 				continue;
255 			if (pollfds[i].fd == rtsock_v4)
256 				nic_changed = smb_nics_changed(rtsock_v4);
257 			if (pollfds[i].fd == eventpipe_read)
258 				goto done;
259 		}
260 
261 		/*
262 		 * If anything changed do refresh our
263 		 * nic list and other configs.
264 		 */
265 		if (nic_changed)
266 			smb_process_nic_change();
267 	}
268 done:
269 	/* Close sockets */
270 	(void) close(rtsock_v4);
271 	(void) close(eventpipe_read);
272 	(void) close(eventpipe_write);
273 	eventpipe_write = -1;
274 	return (NULL);
275 }
276