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