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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 /* 26 * smbd NIC monitor. 27 */ 28 29 #include <sys/types.h> 30 #include <stdlib.h> 31 #include <errno.h> 32 #include <string.h> 33 #include <unistd.h> 34 #include <signal.h> 35 #include <stdio.h> 36 #include <net/if.h> 37 #include <net/route.h> 38 #include <sys/sockio.h> 39 #include <sys/socket.h> 40 #include <netinet/in.h> 41 #include <arpa/inet.h> 42 #include <fcntl.h> 43 #include <pthread.h> 44 #include <syslog.h> 45 #include <smbsrv/libsmb.h> 46 #include "smbd.h" 47 48 #define SMBD_NICMON_ENABLE "nicmon_enable" 49 #define SMBD_NICMON_THROTTLE 100 50 #define SMBD_NICMON_DEBOUNCE 2 51 52 extern smbd_t smbd; 53 54 static boolean_t smbd_nicmon_enabled = B_TRUE; 55 56 /* Use this to stop monitoring */ 57 static int eventpipe_write = -1; 58 59 /* Use this to refresh service instance */ 60 static char *smbd_nicmon_caller_fmri = NULL; 61 62 static void smbd_nicmon_run_check(void); 63 static int smbd_nicmon_setup_rtsock(int); 64 static int smbd_nicmon_needscan(int); 65 static int smbd_nicmon_setup_eventpipe(int *, int *); 66 static void *smbd_nicmon_daemon(void *); 67 68 /* 69 * Start the nic monitor thread. 70 */ 71 int 72 smbd_nicmon_start(const char *svc_fmri) 73 { 74 pthread_t smbd_nicmon_tid; 75 int rc; 76 77 if (smb_nic_init() != SMB_NIC_SUCCESS) 78 return (-1); 79 80 rc = pthread_create(&smbd_nicmon_tid, NULL, smbd_nicmon_daemon, NULL); 81 if (rc != 0) 82 return (-1); 83 84 if (svc_fmri) 85 smbd_nicmon_caller_fmri = (char *)svc_fmri; 86 87 smbd_nicmon_run_check(); 88 return (0); 89 } 90 91 void 92 smbd_nicmon_stop(void) 93 { 94 uchar_t buf = 1; 95 96 if (eventpipe_write < 0) 97 return; 98 99 (void) write(eventpipe_write, &buf, sizeof (buf)); 100 smbd_nicmon_caller_fmri = NULL; 101 smb_nic_fini(); 102 } 103 104 int 105 smbd_nicmon_refresh(void) 106 { 107 if (smb_nic_init() != SMB_NIC_SUCCESS) 108 return (-1); 109 110 smbd_nicmon_run_check(); 111 return (0); 112 } 113 114 /* 115 * The monitor is enabled unless it is explicitly 116 * disabled by setting smbd/nicmon_enable to false. 117 * smbd/nicmon_enable is not defined by default. 118 */ 119 static void 120 smbd_nicmon_run_check(void) 121 { 122 smb_scfhandle_t *hd; 123 uint8_t status; 124 int rc; 125 126 smbd_nicmon_enabled = B_TRUE; 127 128 if ((hd = smb_smf_scf_init(SMBD_FMRI_PREFIX)) == NULL) { 129 smb_log(smbd.s_loghd, LOG_DEBUG, 130 "smbd_nicmon: smb_smf_scf_init failed"); 131 return; 132 } 133 134 rc = smb_smf_create_service_pgroup(hd, SMBD_PG_NAME); 135 if (rc != SMBD_SMF_OK) { 136 smb_smf_scf_fini(hd); 137 smb_log(smbd.s_loghd, LOG_DEBUG, 138 "smbd_nicmon: smb_smf_create_service_pgroup failed"); 139 return; 140 } 141 142 rc = smb_smf_get_boolean_property(hd, SMBD_NICMON_ENABLE, &status); 143 if (rc == SMBD_SMF_OK && status == 0) 144 smbd_nicmon_enabled = B_FALSE; 145 146 smb_smf_scf_fini(hd); 147 } 148 149 /* 150 * Setup routing socket for getting RTM messages. 151 */ 152 static int 153 smbd_nicmon_setup_rtsock(int af) 154 { 155 int sd; 156 int flags; 157 158 if ((sd = socket(PF_ROUTE, SOCK_RAW, af)) == -1) { 159 smb_log(smbd.s_loghd, LOG_ERR, 160 "smbd_nicmon: routing socket failed: %d", errno); 161 return (-1); 162 } 163 164 if ((flags = fcntl(sd, F_GETFL, 0)) < 0) { 165 smb_log(smbd.s_loghd, LOG_ERR, 166 "smbd_nicmon: fcntl F_GETFL failed: %d", errno); 167 (void) close(sd); 168 return (-1); 169 } 170 171 if ((fcntl(sd, F_SETFL, flags | O_NONBLOCK)) < 0) { 172 smb_log(smbd.s_loghd, LOG_ERR, 173 "smbd_nicmon: fcntl F_SETFL failed: %d", errno); 174 (void) close(sd); 175 return (-1); 176 } 177 178 return (sd); 179 } 180 181 static int 182 smbd_nicmon_needscan(int sock) 183 { 184 static uint32_t throttle; 185 struct rt_msghdr *rtm; 186 int64_t msg[2048 / 8]; 187 int need_if_scan = 0; 188 int nbytes; 189 190 /* Read as many messages as possible and try to empty the sockets */ 191 for (;;) { 192 nbytes = read(sock, msg, sizeof (msg)); 193 if (nbytes <= 0) 194 break; 195 196 rtm = (struct rt_msghdr *)msg; 197 if (rtm->rtm_version != RTM_VERSION) 198 continue; 199 200 if (nbytes < rtm->rtm_msglen) { 201 if ((throttle % SMBD_NICMON_THROTTLE) == 0) { 202 smb_log(smbd.s_loghd, LOG_DEBUG, 203 "smbd_nicmon: short read: %d of %d", 204 nbytes, rtm->rtm_msglen); 205 } 206 ++throttle; 207 continue; 208 } 209 210 switch (rtm->rtm_type) { 211 case RTM_NEWADDR: 212 case RTM_DELADDR: 213 case RTM_IFINFO: 214 need_if_scan = 1; 215 break; 216 default: 217 break; 218 } 219 } 220 221 return (need_if_scan); 222 } 223 224 /* 225 * Create pipe for signal delivery and set up signal handlers. 226 */ 227 static int 228 smbd_nicmon_setup_eventpipe(int *read_pipe, int *write_pipe) 229 { 230 int fds[2]; 231 232 if ((pipe(fds)) < 0) { 233 smb_log(smbd.s_loghd, LOG_ERR, 234 "smbd_nicmon: event pipe failed: %d", errno); 235 return (-1); 236 } 237 238 *read_pipe = fds[0]; 239 *write_pipe = fds[1]; 240 return (0); 241 } 242 243 /* 244 * Create the global routing socket to monitor changes in NIC interfaces. 245 * We are only interested in new inerface addition/deletion and changes 246 * in UP/DOWN status. 247 * 248 * Note: only supports AF_INET routing socket. Need to add AF_INET6 to 249 * support IPv6. 250 */ 251 /*ARGSUSED*/ 252 static void * 253 smbd_nicmon_daemon(void *arg) 254 { 255 static uint32_t throttle; 256 static int rtsock_v4; 257 static int eventpipe_read = -1; 258 struct pollfd pollfds[2]; 259 int pollfd_num = 2; 260 int i, nic_changed; 261 int rc; 262 263 if ((rtsock_v4 = smbd_nicmon_setup_rtsock(AF_INET)) == -1) 264 return (NULL); 265 266 rc = smbd_nicmon_setup_eventpipe(&eventpipe_read, &eventpipe_write); 267 if (rc != 0) 268 return (NULL); 269 270 /* 271 * Listen for activity on any of the sockets. 272 * The delay before checking the rtsock will hopefully 273 * smooth things out when there is a lot of activity. 274 */ 275 for (;;) { 276 errno = 0; 277 nic_changed = 0; 278 pollfds[0].fd = rtsock_v4; 279 pollfds[0].events = POLLIN; 280 pollfds[1].fd = eventpipe_read; 281 pollfds[1].events = POLLIN; 282 283 if (poll(pollfds, pollfd_num, -1) < 0) { 284 if (errno == EINTR) 285 continue; 286 if ((throttle % SMBD_NICMON_THROTTLE) == 0) 287 smb_log(smbd.s_loghd, LOG_DEBUG, 288 "smbd_nicmon: poll failed: %d", errno); 289 ++throttle; 290 break; 291 } 292 293 for (i = 0; i < pollfd_num; i++) { 294 if ((pollfds[i].fd < 0) || 295 !(pollfds[i].revents & POLLIN)) 296 continue; 297 if (pollfds[i].fd == rtsock_v4) { 298 (void) sleep(SMBD_NICMON_DEBOUNCE); 299 nic_changed = smbd_nicmon_needscan(rtsock_v4); 300 } 301 if (pollfds[i].fd == eventpipe_read) 302 goto done; 303 } 304 305 /* 306 * If the monitor is enabled and something has changed, 307 * refresh the registered SMF service. 308 */ 309 if (smbd_nicmon_enabled && nic_changed && 310 smbd_nicmon_caller_fmri) { 311 if (smf_refresh_instance(smbd_nicmon_caller_fmri) != 0) 312 smb_log(smbd.s_loghd, LOG_ERR, 313 "smbd_nicmon: %s refresh failed", 314 smbd_nicmon_caller_fmri); 315 } 316 } 317 done: 318 (void) close(rtsock_v4); 319 (void) close(eventpipe_read); 320 (void) close(eventpipe_write); 321 eventpipe_write = -1; 322 return (NULL); 323 } 324