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 	boolean_t ddns_enabled;
106 
107 	ddns_enabled = smb_config_getbool(SMB_CI_DYNDNS_ENABLE);
108 
109 	/* Clear rev zone before creating if list */
110 	if (ddns_enabled) {
111 		if (dyndns_clear_rev_zone() != 0) {
112 			syslog(LOG_ERR, "smb_nicmon_daemon: "
113 			    "failed to clear DNS reverse lookup zone");
114 		}
115 	}
116 
117 	/* re-initialize NIC table */
118 	if (smb_nic_init() != 0)
119 		syslog(LOG_ERR, "smb_nicmon_daemon: "
120 		    "failed to get NIC information");
121 
122 	smb_netbios_name_reconfig();
123 	smb_browser_reconfig();
124 
125 	if (ddns_enabled) {
126 		if (dyndns_update() != 0) {
127 			syslog(LOG_ERR, "smb_nicmon_daemon: "
128 			    "failed to update dynamic DNS");
129 		}
130 	}
131 }
132 
133 /*
134  * Setup routing socket for getting RTM messages.
135  */
136 static void
137 smb_nicmon_setup_rtsock(int af, int *s)
138 {
139 	int flags;
140 
141 	*s = socket(PF_ROUTE, SOCK_RAW, af);
142 	if (*s == -1) {
143 		syslog(LOG_ERR, "smb_nicmon_daemon: failed to "
144 		    "create routing socket");
145 		return;
146 	}
147 	if ((flags = fcntl(*s, F_GETFL, 0)) < 0) {
148 		syslog(LOG_ERR, "smb_nicmon_daemon: "
149 		    "failed to fcntl F_GETFL");
150 		(void) close(*s);
151 		*s = -1;
152 		return;
153 	}
154 	if ((fcntl(*s, F_SETFL, flags | O_NONBLOCK)) < 0) {
155 		syslog(LOG_ERR, "smb_nicmon_daemon: "
156 		    "failed to fcntl F_SETFL");
157 		(void) close(*s);
158 		*s = -1;
159 		return;
160 	}
161 }
162 
163 static int
164 smb_nicmon_needscan(int sock)
165 {
166 	int	nbytes;
167 	int64_t msg[2048 / 8];
168 	struct rt_msghdr *rtm;
169 	int need_if_scan = 0;
170 
171 	/* Read as many messages as possible and try to empty the sockets */
172 	for (;;) {
173 		nbytes = read(sock, msg, sizeof (msg));
174 		if (nbytes <= 0) {
175 			break;
176 		}
177 		rtm = (struct rt_msghdr *)msg;
178 		if (rtm->rtm_version != RTM_VERSION) {
179 			continue;
180 		}
181 		if (nbytes < rtm->rtm_msglen) {
182 			syslog(LOG_DEBUG, "smb_nicmon_daemon: short read: %d "
183 			    "of %d", nbytes, rtm->rtm_msglen);
184 			continue;
185 		}
186 
187 		switch (rtm->rtm_type) {
188 		case RTM_NEWADDR:
189 		case RTM_DELADDR:
190 		case RTM_IFINFO:
191 			need_if_scan = 1;
192 			break;
193 		default:
194 			break;
195 		}
196 	}
197 
198 	return (need_if_scan);
199 }
200 
201 /*
202  * Create pipe for signal delivery and set up signal handlers.
203  */
204 static int
205 smb_nicmon_setup_eventpipe(int *read_pipe, int *write_pipe)
206 {
207 	int fds[2];
208 
209 	if ((pipe(fds)) < 0) {
210 		syslog(LOG_ERR, "smb_nicmon_daemon: failed to open pipe");
211 		return (1);
212 	}
213 	*read_pipe = fds[0];
214 	*write_pipe = fds[1];
215 	return (0);
216 }
217 
218 /*ARGSUSED*/
219 static void *
220 smb_nicmon_daemon(void *args)
221 {
222 	struct pollfd pollfds[2];
223 	int pollfd_num = 2;
224 	int i, nic_changed;
225 	/* AF_INET routing socket add AF_INET6 when we support IPv6 */
226 	static int rtsock_v4;
227 	static int eventpipe_read = -1;
228 
229 	/*
230 	 * Create the global routing socket.  We use this to
231 	 * monitor changes in NIC interfaces. We are only interested
232 	 * in new inerface addition/deletion and change in UP/DOWN status.
233 	 */
234 	smb_nicmon_setup_rtsock(AF_INET, &rtsock_v4);
235 	if (rtsock_v4 == -1) {
236 		syslog(LOG_ERR, "smb_nicmon_daemon: "
237 		    "cannot open routing socket");
238 		return (NULL);
239 	}
240 
241 	if (smb_nicmon_setup_eventpipe(&eventpipe_read, &eventpipe_write)
242 	    != 0) {
243 		syslog(LOG_ERR, "smb_nicmon_daemon: cannot open event pipes");
244 		return (NULL);
245 	}
246 
247 	/*
248 	 * Keep listening for activity on any of the sockets.
249 	 */
250 	for (;;) {
251 		nic_changed = 0;
252 		pollfds[0].fd = rtsock_v4;
253 		pollfds[0].events = POLLIN;
254 		pollfds[1].fd = eventpipe_read;
255 		pollfds[1].events = POLLIN;
256 		if (poll(pollfds, pollfd_num, -1) < 0) {
257 			if (errno == EINTR)
258 				continue;
259 			syslog(LOG_ERR, "smb_nicmon_daemon: "
260 			    "poll failed with errno %d", errno);
261 			break;
262 		}
263 		for (i = 0; i < pollfd_num; i++) {
264 			if ((pollfds[i].fd < 0) ||
265 			    !(pollfds[i].revents & POLLIN))
266 				continue;
267 			if (pollfds[i].fd == rtsock_v4)
268 				nic_changed = smb_nicmon_needscan(rtsock_v4);
269 			if (pollfds[i].fd == eventpipe_read)
270 				goto done;
271 		}
272 
273 		/*
274 		 * If anything changed do refresh our
275 		 * nic list and other configs.
276 		 */
277 		if (nic_changed)
278 			smb_nicmon_reconfig();
279 	}
280 done:
281 	/* Close sockets */
282 	(void) close(rtsock_v4);
283 	(void) close(eventpipe_read);
284 	(void) close(eventpipe_write);
285 	eventpipe_write = -1;
286 	return (NULL);
287 }
288