1/* 2 * Copyright (c) 2013-2014 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* 25 * IPMonitorControl.c 26 * - IPC channel to IPMonitor 27 * - used to create interface rank assertions 28 */ 29 30/* 31 * Modification History 32 * 33 * December 16, 2013 Dieter Siegmund (dieter@apple.com) 34 * - initial revision 35 */ 36 37#include "IPMonitorControl.h" 38#include "IPMonitorControlPrivate.h" 39#include "symbol_scope.h" 40#include <CoreFoundation/CFRuntime.h> 41#include <net/if.h> 42#include <xpc/xpc.h> 43#include <xpc/private.h> 44#include <SystemConfiguration/SCPrivate.h> 45 46#ifdef TEST_IPMONITOR_CONTROL 47#define my_log(__level, fmt, ...) SCPrint(TRUE, stdout, CFSTR(fmt "\n"), ## __VA_ARGS__) 48 49#else /* TEST_IPMONITOR_CONTROL */ 50 51#define my_log(__level, fmt, ...) SCLog(TRUE, __level, CFSTR(fmt), ## __VA_ARGS__) 52#endif /* TEST_IPMONITOR_CONTROL */ 53 54/** 55 ** IPMonitorControl CF object glue 56 **/ 57 58struct IPMonitorControl { 59 CFRuntimeBase cf_base; 60 61 dispatch_queue_t queue; 62 xpc_connection_t connection; 63 CFMutableDictionaryRef assertions; /* ifname<string> = rank<number> */ 64}; 65 66STATIC CFStringRef __IPMonitorControlCopyDebugDesc(CFTypeRef cf); 67STATIC void __IPMonitorControlDeallocate(CFTypeRef cf); 68 69STATIC CFTypeID __kIPMonitorControlTypeID = _kCFRuntimeNotATypeID; 70 71STATIC const CFRuntimeClass __IPMonitorControlClass = { 72 0, /* version */ 73 "IPMonitorControl", /* className */ 74 NULL, /* init */ 75 NULL, /* copy */ 76 __IPMonitorControlDeallocate, /* deallocate */ 77 NULL, /* equal */ 78 NULL, /* hash */ 79 NULL, /* copyFormattingDesc */ 80 __IPMonitorControlCopyDebugDesc /* copyDebugDesc */ 81}; 82 83STATIC CFStringRef 84__IPMonitorControlCopyDebugDesc(CFTypeRef cf) 85{ 86 CFAllocatorRef allocator = CFGetAllocator(cf); 87 IPMonitorControlRef control = (IPMonitorControlRef)cf; 88 89 return (CFStringCreateWithFormat(allocator, NULL, 90 CFSTR("<IPMonitorControl %p>"), 91 control)); 92} 93 94STATIC void 95__IPMonitorControlDeallocate(CFTypeRef cf) 96{ 97 IPMonitorControlRef control = (IPMonitorControlRef)cf; 98 99 if (control->connection != NULL) { 100 xpc_release(control->connection); 101 } 102 if (control->queue != NULL) { 103 xpc_release(control->queue); 104 } 105 return; 106} 107 108/** 109 ** IPMonitorControl support functions 110 **/ 111STATIC void 112__IPMonitorControlRegisterClass(void) 113{ 114 STATIC dispatch_once_t once; 115 116 dispatch_once(&once, ^{ 117 __kIPMonitorControlTypeID 118 = _CFRuntimeRegisterClass(&__IPMonitorControlClass); 119 }); 120 return; 121} 122 123STATIC IPMonitorControlRef 124__IPMonitorControlAllocate(CFAllocatorRef allocator) 125{ 126 IPMonitorControlRef control; 127 int size; 128 129 __IPMonitorControlRegisterClass(); 130 size = sizeof(*control) - sizeof(CFRuntimeBase); 131 control = (IPMonitorControlRef) 132 _CFRuntimeCreateInstance(allocator, 133 __kIPMonitorControlTypeID, size, NULL); 134 bzero(((void *)control) + sizeof(CFRuntimeBase), size); 135 return (control); 136} 137 138STATIC Boolean 139IPMonitorControlHandleResponse(xpc_object_t event, Boolean async, 140 Boolean * retry_p) 141{ 142 Boolean retry = FALSE; 143 Boolean success = FALSE; 144 xpc_type_t type; 145 146 type = xpc_get_type(event); 147 if (type == XPC_TYPE_DICTIONARY) { 148 if (async) { 149 /* we don't expect async responses messages */ 150 my_log(LOG_NOTICE, "IPMonitorControl: unexpected message"); 151 } 152 else { 153 int64_t error; 154 155 error = xpc_dictionary_get_int64(event, 156 kIPMonitorControlResponseKeyError); 157 if (error != 0) { 158 success = FALSE; 159#ifdef TEST_IPMONITOR_CONTROL 160 my_log(LOG_NOTICE, 161 "IPMonitorControl: failure code %lld", error); 162#endif /* TEST_IPMONITOR_CONTROL */ 163 } 164 else { 165 success = TRUE; 166 } 167 } 168 } 169 else if (type == XPC_TYPE_ERROR) { 170 if (event == XPC_ERROR_CONNECTION_INTERRUPTED) { 171#ifdef TEST_IPMONITOR_CONTROL 172 my_log(LOG_NOTICE, "IPMonitorControl: can retry"); 173#endif /* TEST_IPMONITOR_CONTROL */ 174 retry = TRUE; 175 } 176 else { 177 const char * desc; 178 179 desc = xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION); 180 my_log(LOG_NOTICE, "IPMonitorControl: %s", desc); 181 } 182 } 183 else { 184 my_log(LOG_NOTICE, "IPMonitorControl: unknown event type : %p", type); 185 } 186 if (retry_p != NULL) { 187 *retry_p = retry; 188 } 189 return (success); 190} 191 192 193STATIC void 194IPMonitorControlSetInterfaceRank(IPMonitorControlRef control, 195 CFStringRef ifname_cf, 196 SCNetworkServicePrimaryRank rank) 197{ 198 if (control->assertions == NULL) { 199 if (rank == kSCNetworkServicePrimaryRankDefault) { 200 /* no assertions, no need to store rank */ 201 return; 202 } 203 control->assertions 204 = CFDictionaryCreateMutable(NULL, 0, 205 &kCFTypeDictionaryKeyCallBacks, 206 &kCFTypeDictionaryValueCallBacks); 207 } 208 if (rank == kSCNetworkServicePrimaryRankDefault) { 209 CFDictionaryRemoveValue(control->assertions, ifname_cf); 210 if (CFDictionaryGetCount(control->assertions) == 0) { 211 CFRelease(control->assertions); 212 control->assertions = NULL; 213 } 214 } 215 else { 216 CFNumberRef rank_cf; 217 218 rank_cf = CFNumberCreate(NULL, kCFNumberSInt32Type, &rank); 219 CFDictionarySetValue(control->assertions, ifname_cf, rank_cf); 220 CFRelease(rank_cf); 221 } 222 return; 223} 224 225STATIC void 226ApplyInterfaceRank(const void * key, const void * value, void * context) 227{ 228 xpc_connection_t connection = (xpc_connection_t)context; 229 char ifname[IF_NAMESIZE]; 230 SCNetworkServicePrimaryRank rank; 231 xpc_object_t request; 232 233 if (CFStringGetCString(key, ifname, sizeof(ifname), 234 kCFStringEncodingUTF8) == FALSE) { 235 return; 236 } 237 if (CFNumberGetValue(value, kCFNumberSInt32Type, &rank) == FALSE) { 238 return; 239 } 240 request = xpc_dictionary_create(NULL, NULL, 0); 241 xpc_dictionary_set_uint64(request, 242 kIPMonitorControlRequestKeyType, 243 kIPMonitorControlRequestTypeSetInterfaceRank); 244 xpc_dictionary_set_string(request, 245 kIPMonitorControlRequestKeyInterfaceName, 246 ifname); 247 xpc_dictionary_set_uint64(request, 248 kIPMonitorControlRequestKeyPrimaryRank, 249 rank); 250 xpc_connection_send_message(connection, request); 251 xpc_release(request); 252 return; 253} 254 255 256/** 257 ** IPMonitorControl SPI 258 **/ 259PRIVATE_EXTERN IPMonitorControlRef 260IPMonitorControlCreate(void) 261{ 262 xpc_connection_t connection; 263 IPMonitorControlRef control; 264 uint64_t flags = XPC_CONNECTION_MACH_SERVICE_PRIVILEGED; 265 xpc_handler_t handler; 266 dispatch_queue_t queue; 267 268 control = __IPMonitorControlAllocate(NULL); 269 queue = dispatch_queue_create("IPMonitorControl", NULL); 270 connection 271 = xpc_connection_create_mach_service(kIPMonitorControlServerName, 272 queue, flags); 273 handler = ^(xpc_object_t event) { 274 Boolean retry; 275 276 (void)IPMonitorControlHandleResponse(event, TRUE, &retry); 277 if (retry && control->assertions != NULL) { 278 CFDictionaryApplyFunction(control->assertions, 279 ApplyInterfaceRank, 280 control->connection); 281 } 282 }; 283 xpc_connection_set_event_handler(connection, handler); 284 control->connection = connection; 285 control->queue = queue; 286 xpc_connection_resume(connection); 287 return (control); 288} 289 290PRIVATE_EXTERN Boolean 291IPMonitorControlSetInterfacePrimaryRank(IPMonitorControlRef control, 292 CFStringRef ifname_cf, 293 SCNetworkServicePrimaryRank rank) 294{ 295 char ifname[IF_NAMESIZE]; 296 xpc_object_t request; 297 Boolean success = FALSE; 298 299 if (CFStringGetCString(ifname_cf, ifname, sizeof(ifname), 300 kCFStringEncodingUTF8) == FALSE) { 301 return (FALSE); 302 } 303 request = xpc_dictionary_create(NULL, NULL, 0); 304 xpc_dictionary_set_uint64(request, 305 kIPMonitorControlRequestKeyType, 306 kIPMonitorControlRequestTypeSetInterfaceRank); 307 xpc_dictionary_set_string(request, 308 kIPMonitorControlRequestKeyInterfaceName, 309 ifname); 310 xpc_dictionary_set_uint64(request, 311 kIPMonitorControlRequestKeyPrimaryRank, 312 rank); 313 while (TRUE) { 314 xpc_object_t reply; 315 Boolean retry_on_error = FALSE; 316 317 reply = xpc_connection_send_message_with_reply_sync(control->connection, 318 request); 319 if (reply == NULL) { 320 my_log(LOG_NOTICE, "IPMonitorControl: failed to send message"); 321 break; 322 } 323 success = IPMonitorControlHandleResponse(reply, FALSE, 324 &retry_on_error); 325 xpc_release(reply); 326 if (success) { 327 break; 328 } 329 if (retry_on_error) { 330 continue; 331 } 332 my_log(LOG_NOTICE, "IPMonitorControl: fatal error"); 333 break; 334 } 335 xpc_release(request); 336 if (success) { 337 /* sync our state */ 338 CFRetain(ifname_cf); 339 CFRetain(control); 340 dispatch_async(control->queue, 341 ^{ 342 IPMonitorControlSetInterfaceRank(control, 343 ifname_cf, 344 rank); 345 CFRelease(ifname_cf); 346 CFRelease(control); 347 }); 348 } 349 return (success); 350} 351 352SCNetworkServicePrimaryRank 353IPMonitorControlGetInterfacePrimaryRank(IPMonitorControlRef control, 354 CFStringRef ifname_cf) 355{ 356 char ifname[IF_NAMESIZE]; 357 SCNetworkServicePrimaryRank rank; 358 xpc_object_t request; 359 360 rank = kSCNetworkServicePrimaryRankDefault; 361 if (CFStringGetCString(ifname_cf, ifname, sizeof(ifname), 362 kCFStringEncodingUTF8) == FALSE) { 363 goto done; 364 } 365 request = xpc_dictionary_create(NULL, NULL, 0); 366 xpc_dictionary_set_uint64(request, 367 kIPMonitorControlRequestKeyType, 368 kIPMonitorControlRequestTypeGetInterfaceRank); 369 xpc_dictionary_set_string(request, 370 kIPMonitorControlRequestKeyInterfaceName, 371 ifname); 372 while (TRUE) { 373 xpc_object_t reply; 374 Boolean retry_on_error = FALSE; 375 Boolean success; 376 377 reply = xpc_connection_send_message_with_reply_sync(control->connection, 378 request); 379 if (reply == NULL) { 380 my_log(LOG_NOTICE, "IPMonitorControl: failed to send message"); 381 break; 382 } 383 success = IPMonitorControlHandleResponse(reply, FALSE, &retry_on_error); 384 if (success) { 385 rank = (SCNetworkServicePrimaryRank) 386 xpc_dictionary_get_uint64(reply, 387 kIPMonitorControlResponseKeyPrimaryRank); 388 } 389 xpc_release(reply); 390 if (success) { 391 break; 392 } 393 if (retry_on_error) { 394 continue; 395 } 396 break; 397 } 398 xpc_release(request); 399 400 done: 401 return (rank); 402} 403 404