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_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 	boolean_t ddns_enabled;
183 
184 	ddns_enabled = smb_config_getbool(SMB_CI_DYNDNS_ENABLE);
185 
186 	/* Clear rev zone before creating if list */
187 	if (ddns_enabled) {
188 		if (dyndns_clear_rev_zone() != 0) {
189 			syslog(LOG_ERR, "smb_nicmonitor: "
190 			    "failed to clear DNS reverse lookup zone");
191 		}
192 	}
193 
194 	/* re-initialize NIC table */
195 	smb_nic_build_info();
196 
197 	smb_netbios_name_reconfig();
198 
199 	if (ddns_enabled) {
200 		if (dyndns_update() != 0) {
201 			syslog(LOG_ERR, "smb_nicmonitor: "
202 			    "failed to update dynamic DNS");
203 		}
204 	}
205 }
206 
207 /*ARGSUSED*/
208 static void *
209 smb_nicmonitor(void *args)
210 {
211 	struct pollfd pollfds[2];
212 	int pollfd_num = 2;
213 	int i, nic_changed;
214 	/* AF_INET routing socket add AF_INET6 when we support IPv6 */
215 	static int rtsock_v4;
216 	static int eventpipe_read = -1;
217 
218 	/*
219 	 * Create the global routing socket.  We use this to
220 	 * monitor changes in NIC interfaces. We are only interested
221 	 * in new inerface addition/deletion and change in UP/DOWN status.
222 	 */
223 	smb_setup_rtsock(AF_INET, &rtsock_v4);
224 	if (rtsock_v4 == -1) {
225 		syslog(LOG_ERR, "smb_nicmonitor: cannot open routing socket");
226 		return (NULL);
227 	}
228 	if (smb_setup_eventpipe(&eventpipe_read, &eventpipe_write) != 0) {
229 		syslog(LOG_ERR, "smb_nicmonitor: cannot open event pipes");
230 		return (NULL);
231 	}
232 
233 	/*
234 	 * Keep listening for activity on any of the sockets.
235 	 */
236 	for (;;) {
237 		nic_changed = 0;
238 		pollfds[0].fd = rtsock_v4;
239 		pollfds[0].events = POLLIN;
240 		pollfds[1].fd = eventpipe_read;
241 		pollfds[1].events = POLLIN;
242 		if (poll(pollfds, pollfd_num, -1) < 0) {
243 			if (errno == EINTR)
244 				continue;
245 			syslog(LOG_ERR, "smb_nicmonitor: "
246 			    "poll failed with errno %d", errno);
247 			break;
248 		}
249 		for (i = 0; i < pollfd_num; i++) {
250 			if ((pollfds[i].fd < 0) ||
251 			    !(pollfds[i].revents & POLLIN))
252 				continue;
253 			if (pollfds[i].fd == rtsock_v4)
254 				nic_changed = smb_nics_changed(rtsock_v4);
255 			if (pollfds[i].fd == eventpipe_read)
256 				goto done;
257 		}
258 
259 		/*
260 		 * If anything changed do refresh our
261 		 * nic list and other configs.
262 		 */
263 		if (nic_changed)
264 			smb_process_nic_change();
265 	}
266 done:
267 	/* Close sockets */
268 	(void) close(rtsock_v4);
269 	(void) close(eventpipe_read);
270 	(void) close(eventpipe_write);
271 	eventpipe_write = -1;
272 	return (NULL);
273 }
274