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