1/* 2 * Copyright (c) 2003-2013 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 * rtadv.c 26 * - IPv6 Router Advertisement 27 * - sends router solicitation requests, and waits for responses 28 * - reads router advertisement messages, and from it, gleans the source 29 * IPv6 address to use as the Router 30 */ 31 32/* 33 * Modification History 34 * 35 * October 6, 2009 Dieter Siegmund (dieter@apple.com) 36 * - added support for DHCPv6 37 */ 38#include <stdlib.h> 39#include <unistd.h> 40#include <string.h> 41#include <sys/types.h> 42#include <sys/socket.h> 43#include <sys/ioctl.h> 44#include <sys/sockio.h> 45#include <sys/uio.h> 46#include <sys/time.h> 47#include <sys/param.h> 48#include <ctype.h> 49#include <net/if.h> 50#include <net/if_dl.h> 51#include <net/route.h> 52#include <netinet/in.h> 53#include <netinet/ip6.h> 54#include <netinet6/ip6_var.h> 55#define KERNEL_PRIVATE 56#include <netinet6/in6_var.h> 57#undef KERNEL_PRIVATE 58#include <netinet/icmp6.h> 59#include <netinet6/nd6.h> 60#include <arpa/inet.h> 61#include <syslog.h> 62#include <CoreFoundation/CFSocket.h> 63#include <SystemConfiguration/SystemConfiguration.h> 64 65#include "cfutil.h" 66#include "ipconfigd_threads.h" 67#include "FDSet.h" 68#include "globals.h" 69#include "timer.h" 70#include "ifutil.h" 71#include "util.h" 72#include "symbol_scope.h" 73#include "DHCPv6Client.h" 74#include "RTADVSocket.h" 75 76typedef struct { 77 timer_callout_t * timer; 78 int try; 79 Boolean data_received; 80 struct in6_addr our_router; 81 uint8_t our_router_hwaddr[MAX_LINK_ADDR_LEN]; 82 int our_router_hwaddr_len; 83 RTADVSocketRef sock; 84 boolean_t lladdr_ok; /* ok to send link-layer address */ 85 DHCPv6ClientRef dhcp_client; 86 struct in6_addr * dns_servers; 87 int dns_servers_count; 88} Service_rtadv_t; 89 90 91STATIC void 92rtadv_set_dns_servers(Service_rtadv_t * rtadv, 93 const struct in6_addr * dns_servers, 94 int dns_servers_count) 95{ 96 if (rtadv->dns_servers != NULL) { 97 free(rtadv->dns_servers); 98 rtadv->dns_servers = NULL; 99 } 100 if (dns_servers != NULL) { 101 rtadv->dns_servers = (struct in6_addr *) 102 malloc(sizeof(*dns_servers) * dns_servers_count); 103 bcopy(dns_servers, rtadv->dns_servers, 104 sizeof(*dns_servers) * dns_servers_count); 105 rtadv->dns_servers_count = dns_servers_count; 106 } 107 return; 108} 109 110STATIC void 111rtadv_cancel_pending_events(ServiceRef service_p) 112{ 113 Service_rtadv_t * rtadv = (Service_rtadv_t *)ServiceGetPrivate(service_p); 114 115 timer_cancel(rtadv->timer); 116 RTADVSocketDisableReceive(rtadv->sock); 117 return; 118} 119 120STATIC void 121rtadv_failed(ServiceRef service_p, ipconfig_status_t status) 122{ 123 Service_rtadv_t * rtadv = (Service_rtadv_t *)ServiceGetPrivate(service_p); 124 125 rtadv->try = 0; 126 rtadv_cancel_pending_events(service_p); 127 inet6_rtadv_disable(if_name(service_interface(service_p))); 128 rtadv_set_dns_servers(rtadv, NULL, 0); 129 service_publish_failure(service_p, status); 130 return; 131} 132 133STATIC void 134rtadv_inactive(ServiceRef service_p) 135{ 136 interface_t * if_p = service_interface(service_p); 137 138 inet6_flush_prefixes(if_name(if_p)); 139 inet6_flush_routes(if_name(if_p)); 140 rtadv_failed(service_p, ipconfig_status_media_inactive_e); 141 return; 142} 143 144STATIC void 145rtadv_start(ServiceRef service_p, IFEventID_t event_id, void * event_data) 146{ 147 RTADVSocketReceiveDataRef data; 148 int error; 149 interface_t * if_p = service_interface(service_p); 150 char ntopbuf[INET6_ADDRSTRLEN]; 151 Service_rtadv_t * rtadv; 152 struct timeval tv; 153 154 rtadv = (Service_rtadv_t *)ServiceGetPrivate(service_p); 155 switch (event_id) { 156 case IFEventID_start_e: 157 my_log(LOG_DEBUG, "RTADV: start %s", if_name(if_p)); 158 rtadv_cancel_pending_events(service_p); 159 RTADVSocketEnableReceive(rtadv->sock, 160 (RTADVSocketReceiveFuncPtr)rtadv_start, 161 service_p, (void *)IFEventID_data_e); 162 if (inet6_rtadv_enable(if_name(if_p)) != 0) { 163 rtadv_failed(service_p, ipconfig_status_internal_error_e); 164 return; 165 } 166 bzero(&rtadv->our_router, sizeof(rtadv->our_router)); 167 rtadv_set_dns_servers(rtadv, NULL, 0); 168 rtadv->our_router_hwaddr_len = 0; 169 rtadv->try = 0; 170 rtadv->data_received = FALSE; 171 172 /* FALL THROUGH */ 173 174 case IFEventID_timeout_e: 175 rtadv->try++; 176 if (rtadv->try > 1) { 177 link_status_t link_status = service_link_status(service_p); 178 179 if (link_status.valid == TRUE 180 && link_status.active == FALSE) { 181 rtadv_inactive(service_p); 182 return; 183 } 184 } 185 if (rtadv->try > MAX_RTR_SOLICITATIONS) { 186 /* now we just wait to see if something comes in */ 187 return; 188 } 189 my_log(LOG_DEBUG, 190 "RTADV %s: sending Router Solicitation (%d of %d)", 191 if_name(if_p), rtadv->try, MAX_RTR_SOLICITATIONS); 192 error = RTADVSocketSendSolicitation(rtadv->sock, 193 rtadv->lladdr_ok); 194 switch (error) { 195 case 0: 196 case ENXIO: 197 case ENETDOWN: 198 case EADDRNOTAVAIL: 199 break; 200 default: 201 my_log(LOG_ERR, "RTADV %s: send Router Solicitation: failed, %s", 202 if_name(if_p), strerror(error)); 203 break; 204 } 205 206 /* set timer values and wait for responses */ 207 tv.tv_sec = RTR_SOLICITATION_INTERVAL; 208 tv.tv_usec = random_range(0, USECS_PER_SEC - 1); 209 timer_set_relative(rtadv->timer, tv, 210 (timer_func_t *)rtadv_start, 211 service_p, (void *)IFEventID_timeout_e, NULL); 212 break; 213 214 case IFEventID_data_e: 215 data = (RTADVSocketReceiveDataRef)event_data; 216 /* save the router and flags, and start DHCPv6 if necessary */ 217 if (G_IPConfiguration_verbose) { 218 char link_addr_buf[MAX_LINK_ADDR_LEN * 3 + 1]; 219 220 link_addr_buf[0] = '\0'; 221 if (data->router_hwaddr != NULL) { 222 if (data->router_hwaddr_len == ETHER_ADDR_LEN) { 223 snprintf(link_addr_buf, sizeof(link_addr_buf), 224 " (" EA_FORMAT ")", 225 EA_LIST(data->router_hwaddr)); 226 } 227 else if (data->router_hwaddr_len == 8) { 228 snprintf(link_addr_buf, sizeof(link_addr_buf), 229 " (" FWA_FORMAT ")", 230 FWA_LIST(data->router_hwaddr)); 231 } 232 } 233 234 my_log(LOG_DEBUG, 235 "RTADV %s: Received RA from %s%s%s%s", 236 if_name(if_p), 237 inet_ntop(AF_INET6, &data->router, 238 ntopbuf, sizeof(ntopbuf)), 239 link_addr_buf, 240 data->managed_bit ? " [Managed]" : "", 241 data->other_bit ? " [OtherConfig]" : ""); 242 if (data->dns_servers != NULL) { 243 int i; 244 245 for (i = 0; i < data->dns_servers_count; i++) { 246 my_log(LOG_DEBUG, 247 "RTADV %s: DNS Server %s", 248 if_name(if_p), 249 inet_ntop(AF_INET6, data->dns_servers + i, 250 ntopbuf, sizeof(ntopbuf))); 251 } 252 } 253 } 254 rtadv->data_received = TRUE; 255 rtadv_cancel_pending_events(service_p); 256 rtadv->our_router = data->router; 257 if (data->router_hwaddr != NULL) { 258 int len; 259 260 len = data->router_hwaddr_len; 261 if (len > sizeof(rtadv->our_router_hwaddr)) { 262 len = sizeof(rtadv->our_router_hwaddr); 263 } 264 bcopy(data->router_hwaddr, rtadv->our_router_hwaddr, len); 265 rtadv->our_router_hwaddr_len = len; 266 } 267 rtadv_set_dns_servers(rtadv, data->dns_servers, 268 data->dns_servers_count); 269 if (rtadv->dhcp_client != NULL) { 270 if (data->managed_bit || data->other_bit) { 271 DHCPv6ClientStart(rtadv->dhcp_client, 272 (G_dhcpv6_stateful_enabled 273 && data->managed_bit)); 274 } 275 else { 276 DHCPv6ClientStop(rtadv->dhcp_client, FALSE); 277 } 278 } 279 break; 280 default: 281 break; 282 } 283 return; 284} 285 286STATIC CFStringRef 287rtadv_create_signature(ServiceRef service_p, 288 inet6_addrinfo_t * list_p, int list_count) 289{ 290 struct in6_addr netaddr; 291 char ntopbuf[INET6_ADDRSTRLEN]; 292 Service_rtadv_t * rtadv = (Service_rtadv_t *)ServiceGetPrivate(service_p); 293 CFMutableStringRef sig_str; 294 295 if (list_p == NULL || list_count == 0 296 || rtadv->our_router_hwaddr_len == 0) { 297 return (NULL); 298 } 299 netaddr = list_p[0].addr; 300 in6_netaddr(&netaddr, list_p[0].prefix_length); 301 sig_str = CFStringCreateMutable(NULL, 0); 302 CFStringAppendFormat(sig_str, NULL, 303 CFSTR("IPv6.Prefix=%s/%d;IPv6.RouterHardwareAddress="), 304 inet_ntop(AF_INET6, &netaddr, 305 ntopbuf, sizeof(ntopbuf)), 306 list_p[0].prefix_length); 307 my_CFStringAppendBytesAsHex(sig_str, rtadv->our_router_hwaddr, 308 rtadv->our_router_hwaddr_len, ':'); 309 return (sig_str); 310} 311 312STATIC void 313rtadv_address_changed(ServiceRef service_p, 314 inet6_addrlist_t * addr_list_p) 315{ 316 interface_t * if_p = service_interface(service_p); 317 inet6_addrinfo_t * linklocal; 318 Service_rtadv_t * rtadv = (Service_rtadv_t *)ServiceGetPrivate(service_p); 319 320 linklocal = inet6_addrlist_get_linklocal(addr_list_p); 321 if (linklocal == NULL) { 322 /* no linklocal address assigned, nothing to do */ 323 my_log(LOG_DEBUG, 324 "RTADV %s: link-local address not present", 325 if_name(if_p)); 326 return; 327 } 328 if ((linklocal->addr_flags & IN6_IFF_NOTREADY) != 0) { 329 /* linklocal address isn't ready */ 330 my_log(LOG_DEBUG, 331 "RTADV %s: link-local address is not ready", 332 if_name(if_p)); 333 return; 334 } 335#ifdef IN6_IFF_DADPROGRESS 336 rtadv->lladdr_ok = (linklocal->addr_flags & IN6_IFF_DADPROGRESS) == 0; 337 my_log(LOG_DEBUG, 338 "RTADV %s: link-layer option in Router Solicitation is %sOK", 339 if_name(if_p), rtadv->lladdr_ok ? "" : "not "); 340#else /* IN6_IFF_DADPROGRESS */ 341 rtadv->lladdr_ok = TRUE; 342#endif /* IN6_IFF_DADPROGRESS */ 343 344 if (rtadv->try == 0) { 345 link_status_t link_status = service_link_status(service_p); 346 347 if (link_status.valid == FALSE 348 || link_status.active == TRUE) { 349 my_log(LOG_DEBUG, 350 "RTADV %s: link-local address is ready, starting", 351 if_name(if_p)); 352 rtadv_start(service_p, IFEventID_start_e, NULL); 353 return; 354 } 355 } 356 else { 357 int count; 358 inet6_addrlist_t dhcp_addr_list; 359 int i; 360 dhcpv6_info_t info; 361 dhcpv6_info_t * info_p = NULL; 362 inet6_addrinfo_t * scan; 363 inet6_addrinfo_t list[addr_list_p->count]; 364 struct in6_addr * router = NULL; 365 int router_count = 0; 366 CFStringRef signature = NULL; 367 368 inet6_addrlist_init(&dhcp_addr_list); 369 if (rtadv->dhcp_client != NULL) { 370 DHCPv6ClientCopyAddresses(rtadv->dhcp_client, &dhcp_addr_list); 371 } 372 373 /* only copy autoconf and DHCP addresses */ 374 for (i = 0, count = 0, scan = addr_list_p->list; 375 i < addr_list_p->count; i++, scan++) { 376 if ((scan->addr_flags & IN6_IFF_NOTREADY) != 0) { 377 continue; 378 } 379 if ((scan->addr_flags & IN6_IFF_AUTOCONF) != 0 380 || inet6_addrlist_contains_address(&dhcp_addr_list, scan)) { 381 list[count++] = *scan; 382 } 383 } 384 inet6_addrlist_free(&dhcp_addr_list); 385 if (count == 0) { 386 return; 387 } 388 if (IN6_IS_ADDR_UNSPECIFIED(&rtadv->our_router) == FALSE) { 389 router = &rtadv->our_router; 390 router_count = 1; 391 } 392 if (rtadv->dhcp_client != NULL 393 && DHCPv6ClientGetInfo(rtadv->dhcp_client, &info)) { 394 info_p = &info; 395 } 396 if (rtadv->dns_servers != NULL) { 397 info.dns_servers = rtadv->dns_servers; 398 info.dns_servers_count = rtadv->dns_servers_count; 399 info_p = &info; 400 } 401 signature = rtadv_create_signature(service_p, list, count); 402 ServicePublishSuccessIPv6(service_p, list, count, router, router_count, 403 info_p, signature); 404 my_CFRelease(&signature); 405 } 406 return; 407} 408 409STATIC void 410rtadv_dhcp_callback(void * callback_arg, DHCPv6ClientRef client) 411{ 412 inet6_addrlist_t addrs; 413 ServiceRef service_p = (ServiceRef)callback_arg; 414 415 inet6_addrlist_copy(&addrs, if_link_index(service_interface(service_p))); 416 rtadv_address_changed(service_p, &addrs); 417 inet6_addrlist_free(&addrs); 418 return; 419} 420 421STATIC void 422rtadv_init(ServiceRef service_p) 423{ 424 inet6_addrlist_t addrs; 425 Service_rtadv_t * rtadv = (Service_rtadv_t *)ServiceGetPrivate(service_p); 426 427 rtadv->try = 0; 428 inet6_addrlist_copy(&addrs, 429 if_link_index(service_interface(service_p))); 430 rtadv_address_changed(service_p, &addrs); 431 inet6_addrlist_free(&addrs); 432 return; 433} 434 435PRIVATE_EXTERN ipconfig_status_t 436rtadv_thread(ServiceRef service_p, IFEventID_t evid, void * event_data) 437{ 438 interface_t * if_p = service_interface(service_p); 439 ipconfig_status_t status = ipconfig_status_success_e; 440 Service_rtadv_t * rtadv = (Service_rtadv_t *)ServiceGetPrivate(service_p); 441 442 switch (evid) { 443 case IFEventID_start_e: 444 if (if_flags(if_p) & IFF_LOOPBACK) { 445 status = ipconfig_status_invalid_operation_e; 446 break; 447 } 448 if (rtadv != NULL) { 449 my_log(LOG_DEBUG, "RTADV %s: re-entering start state", 450 if_name(if_p)); 451 status = ipconfig_status_internal_error_e; 452 break; 453 } 454 rtadv = malloc(sizeof(*rtadv)); 455 if (rtadv == NULL) { 456 my_log(LOG_ERR, "RTADV %s: malloc failed", if_name(if_p)); 457 status = ipconfig_status_allocation_failed_e; 458 break; 459 } 460 bzero(rtadv, sizeof(*rtadv)); 461 ServiceSetPrivate(service_p, rtadv); 462 rtadv->timer = timer_callout_init(); 463 if (rtadv->timer == NULL) { 464 my_log(LOG_ERR, "RTADV %s: timer_callout_init failed", 465 if_name(if_p)); 466 status = ipconfig_status_allocation_failed_e; 467 goto stop; 468 } 469 rtadv->sock = RTADVSocketCreate(if_p); 470 if (rtadv->sock == NULL) { 471 my_log(LOG_ERR, "RTADV %s: RTADVSocketCreate failed", 472 if_name(if_p)); 473 status = ipconfig_status_allocation_failed_e; 474 goto stop; 475 } 476 if (G_dhcpv6_enabled) { 477 rtadv->dhcp_client = DHCPv6ClientCreate(if_p); 478 if (rtadv->dhcp_client == NULL) { 479 my_log(LOG_ERR, "RTADV %s: DHCPv6ClientCreate failed", 480 if_name(if_p)); 481 status = ipconfig_status_allocation_failed_e; 482 goto stop; 483 } 484 DHCPv6ClientSetNotificationCallBack(rtadv->dhcp_client, 485 rtadv_dhcp_callback, 486 service_p); 487 } 488 rtadv_init(service_p); 489 break; 490 491 stop: 492 case IFEventID_stop_e: 493 if (rtadv == NULL) { 494 my_log(LOG_DEBUG, "RTADV %s: already stopped", 495 if_name(if_p)); 496 status = ipconfig_status_internal_error_e; 497 break; 498 } 499 my_log(LOG_DEBUG, "RTADV %s: stop", if_name(if_p)); 500 501 /* close/release the RTADV socket */ 502 RTADVSocketRelease(&rtadv->sock); 503 504 /* stop DHCPv6 client */ 505 DHCPv6ClientRelease(&rtadv->dhcp_client); 506 507 /* this flushes the addresses */ 508 (void)inet6_rtadv_disable(if_name(if_p)); 509 510 /* clean-up resources */ 511 if (rtadv->timer) { 512 timer_callout_free(&rtadv->timer); 513 } 514 rtadv_set_dns_servers(rtadv, NULL, 0); 515 inet6_flush_prefixes(if_name(if_p)); 516 inet6_flush_routes(if_name(if_p)); 517 ServiceSetPrivate(service_p, NULL); 518 free(rtadv); 519 break; 520 521 case IFEventID_ipv6_address_changed_e: 522 if (rtadv == NULL) { 523 my_log(LOG_DEBUG, "RTADV %s: private data is NULL", 524 if_name(if_p)); 525 status = ipconfig_status_internal_error_e; 526 break; 527 } 528 if (rtadv->dhcp_client != NULL) { 529 DHCPv6ClientAddressChanged(rtadv->dhcp_client, event_data); 530 } 531 rtadv_address_changed(service_p, event_data); 532 break; 533 case IFEventID_renew_e: 534 case IFEventID_link_status_changed_e: { 535 link_status_t link_status; 536 void * network_changed = event_data; 537 538 if (rtadv == NULL) { 539 return (ipconfig_status_internal_error_e); 540 } 541 link_status = service_link_status(service_p); 542 if (link_status.valid == FALSE 543 || link_status.active == TRUE) { 544 if (network_changed != NULL) { 545 inet6_flush_prefixes(if_name(if_p)); 546 inet6_flush_routes(if_name(if_p)); 547 inet6_rtadv_disable(if_name(if_p)); 548 if (rtadv->dhcp_client != NULL) { 549 DHCPv6ClientStop(rtadv->dhcp_client, TRUE); 550 } 551 service_publish_failure(service_p, 552 ipconfig_status_network_changed_e); 553 } 554 else if (evid == IFEventID_link_status_changed_e 555 && rtadv->try == 1 556 && rtadv->data_received == FALSE) { 557 /* we're already on it */ 558 break; 559 } 560 rtadv_init(service_p); 561 } 562 else { 563 rtadv->try = 0; 564 } 565 break; 566 } 567 case IFEventID_link_timer_expired_e: 568 rtadv_inactive(service_p); 569 if (rtadv->dhcp_client != NULL) { 570 DHCPv6ClientStop(rtadv->dhcp_client, FALSE); 571 } 572 break; 573 574 case IFEventID_wake_e: 575 break; 576 577 case IFEventID_get_dhcpv6_info_e: { 578 dhcpv6_info_t * info_p = (dhcpv6_info_t *)event_data; 579 580 if (rtadv->dhcp_client != NULL) { 581 (void)DHCPv6ClientGetInfo(rtadv->dhcp_client, info_p); 582 } 583 if (rtadv->dns_servers != NULL) { 584 info_p->dns_servers = rtadv->dns_servers; 585 info_p->dns_servers_count = rtadv->dns_servers_count; 586 } 587 break; 588 } 589 default: 590 break; 591 } /* switch */ 592 593 return (status); 594} 595