xref: /illumos-gate/usr/src/cmd/smbsrv/smbd/smbd_nicmon.c (revision f998c95e3b7029fe5f7542e115f7474ddb8024d7)
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 2008 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_nicmon_setup_rtsock(int, int *);
53 static int smb_nicmon_needscan(int);
54 static void *smb_nicmon_daemon(void *);
55 static int smb_nicmon_setup_eventpipe(int *, int *);
56 
57 /* Use this to stop monitoring */
58 static int eventpipe_write = -1;
59 
60 /*
61  * Start the nic monitor thread.
62  */
63 int
64 smb_nicmon_start(void)
65 {
66 	int rc = 0;
67 
68 	if ((rc = smb_nic_init()) != 0) {
69 		syslog(LOG_ERR, "NIC monitor failed to initialize (%s)",
70 		    strerror(errno));
71 		return (rc);
72 	}
73 
74 	rc = pthread_create(&smb_nicmon_thread, NULL, smb_nicmon_daemon, 0);
75 	if (rc != 0) {
76 		syslog(LOG_ERR, "NIC monitor failed to start (%s)",
77 		    strerror(errno));
78 		return (rc);
79 	}
80 
81 	return (rc);
82 }
83 
84 /*
85  * Stop the nic monitor.
86  */
87 void
88 smb_nicmon_stop(void)
89 {
90 	uchar_t buf = 1;
91 
92 	if (eventpipe_write < 0)
93 		return;
94 
95 	(void) write(eventpipe_write, &buf, sizeof (buf));
96 	smb_nic_fini();
97 }
98 
99 /*
100  * Call this to do stuff after ifs changed.
101  */
102 void
103 smb_nicmon_reconfig(void)
104 {
105 	char fqdn[MAXHOSTNAMELEN];
106 	boolean_t ddns_enabled;
107 
108 	ddns_enabled = smb_config_getbool(SMB_CI_DYNDNS_ENABLE);
109 	(void) smb_getfqdomainname(fqdn, MAXHOSTNAMELEN);
110 
111 	/* Clear rev zone before creating if list */
112 	if (ddns_enabled) {
113 		if (*fqdn != '\0' && dyndns_clear_rev_zone(fqdn) != 0) {
114 			syslog(LOG_ERR, "smb_nicmon_daemon: "
115 			    "failed to clear DNS reverse lookup zone");
116 		}
117 	}
118 
119 	/* re-initialize NIC table */
120 	if (smb_nic_init() != 0)
121 		syslog(LOG_ERR, "smb_nicmon_daemon: "
122 		    "failed to get NIC information");
123 
124 	smb_netbios_name_reconfig();
125 	smb_browser_reconfig();
126 
127 	if (ddns_enabled) {
128 		if (*fqdn != '\0' && dyndns_update(fqdn, B_FALSE) != 0) {
129 			syslog(LOG_ERR, "smb_nicmon_daemon: "
130 			    "failed to update dynamic DNS");
131 		}
132 	}
133 }
134 
135 /*
136  * Setup routing socket for getting RTM messages.
137  */
138 static void
139 smb_nicmon_setup_rtsock(int af, int *s)
140 {
141 	int flags;
142 
143 	*s = socket(PF_ROUTE, SOCK_RAW, af);
144 	if (*s == -1) {
145 		syslog(LOG_ERR, "smb_nicmon_daemon: failed to "
146 		    "create routing socket");
147 		return;
148 	}
149 	if ((flags = fcntl(*s, F_GETFL, 0)) < 0) {
150 		syslog(LOG_ERR, "smb_nicmon_daemon: "
151 		    "failed to fcntl F_GETFL");
152 		(void) close(*s);
153 		*s = -1;
154 		return;
155 	}
156 	if ((fcntl(*s, F_SETFL, flags | O_NONBLOCK)) < 0) {
157 		syslog(LOG_ERR, "smb_nicmon_daemon: "
158 		    "failed to fcntl F_SETFL");
159 		(void) close(*s);
160 		*s = -1;
161 		return;
162 	}
163 }
164 
165 static int
166 smb_nicmon_needscan(int sock)
167 {
168 	int	nbytes;
169 	int64_t msg[2048 / 8];
170 	struct rt_msghdr *rtm;
171 	int need_if_scan = 0;
172 
173 	/* Read as many messages as possible and try to empty the sockets */
174 	for (;;) {
175 		nbytes = read(sock, msg, sizeof (msg));
176 		if (nbytes <= 0) {
177 			break;
178 		}
179 		rtm = (struct rt_msghdr *)msg;
180 		if (rtm->rtm_version != RTM_VERSION) {
181 			continue;
182 		}
183 		if (nbytes < rtm->rtm_msglen) {
184 			syslog(LOG_DEBUG, "smb_nicmon_daemon: short read: %d "
185 			    "of %d", nbytes, rtm->rtm_msglen);
186 			continue;
187 		}
188 
189 		switch (rtm->rtm_type) {
190 		case RTM_NEWADDR:
191 		case RTM_DELADDR:
192 		case RTM_IFINFO:
193 			need_if_scan = 1;
194 			break;
195 		default:
196 			break;
197 		}
198 	}
199 
200 	return (need_if_scan);
201 }
202 
203 /*
204  * Create pipe for signal delivery and set up signal handlers.
205  */
206 static int
207 smb_nicmon_setup_eventpipe(int *read_pipe, int *write_pipe)
208 {
209 	int fds[2];
210 
211 	if ((pipe(fds)) < 0) {
212 		syslog(LOG_ERR, "smb_nicmon_daemon: failed to open pipe");
213 		return (1);
214 	}
215 	*read_pipe = fds[0];
216 	*write_pipe = fds[1];
217 	return (0);
218 }
219 
220 /*ARGSUSED*/
221 static void *
222 smb_nicmon_daemon(void *args)
223 {
224 	struct pollfd pollfds[2];
225 	int pollfd_num = 2;
226 	int i, nic_changed;
227 	/* AF_INET routing socket add AF_INET6 when we support IPv6 */
228 	static int rtsock_v4;
229 	static int eventpipe_read = -1;
230 
231 	/*
232 	 * Create the global routing socket.  We use this to
233 	 * monitor changes in NIC interfaces. We are only interested
234 	 * in new inerface addition/deletion and change in UP/DOWN status.
235 	 */
236 	smb_nicmon_setup_rtsock(AF_INET, &rtsock_v4);
237 	if (rtsock_v4 == -1) {
238 		syslog(LOG_ERR, "smb_nicmon_daemon: "
239 		    "cannot open routing socket");
240 		return (NULL);
241 	}
242 
243 	if (smb_nicmon_setup_eventpipe(&eventpipe_read, &eventpipe_write)
244 	    != 0) {
245 		syslog(LOG_ERR, "smb_nicmon_daemon: cannot open event pipes");
246 		return (NULL);
247 	}
248 
249 	/*
250 	 * Keep listening for activity on any of the sockets.
251 	 */
252 	for (;;) {
253 		nic_changed = 0;
254 		pollfds[0].fd = rtsock_v4;
255 		pollfds[0].events = POLLIN;
256 		pollfds[1].fd = eventpipe_read;
257 		pollfds[1].events = POLLIN;
258 		if (poll(pollfds, pollfd_num, -1) < 0) {
259 			if (errno == EINTR)
260 				continue;
261 			syslog(LOG_ERR, "smb_nicmon_daemon: "
262 			    "poll failed with errno %d", errno);
263 			break;
264 		}
265 		for (i = 0; i < pollfd_num; i++) {
266 			if ((pollfds[i].fd < 0) ||
267 			    !(pollfds[i].revents & POLLIN))
268 				continue;
269 			if (pollfds[i].fd == rtsock_v4)
270 				nic_changed = smb_nicmon_needscan(rtsock_v4);
271 			if (pollfds[i].fd == eventpipe_read)
272 				goto done;
273 		}
274 
275 		/*
276 		 * If anything changed do refresh our
277 		 * nic list and other configs.
278 		 */
279 		if (nic_changed)
280 			smb_nicmon_reconfig();
281 	}
282 done:
283 	/* Close sockets */
284 	(void) close(rtsock_v4);
285 	(void) close(eventpipe_read);
286 	(void) close(eventpipe_write);
287 	eventpipe_write = -1;
288 	return (NULL);
289 }
290