#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/wait.h>
#include <sys/errno.h>
#include <sys/socket.h>
#define KERNEL_PRIVATE
#include <sys/ioctl.h>
#undef KERNEL_PRIVATE
#include <ctype.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <net/firewire.h>
#include <netinet/in.h>
#include <netinet/udp.h>
#include <netinet/ip.h>
#include <netinet/bootp.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <paths.h>
#include <syslog.h>
#include <net/if_types.h>
#include <mach/boolean.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCPrivate.h>
#include <SystemConfiguration/SCValidation.h>
#include <SystemConfiguration/SCDPlugin.h>
#include <IOKit/IOMessage.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/pwr_mgt/IOPMLib.h>
#include <IOKit/IOHibernatePrivate.h>
#include <TargetConditionals.h>
#include <Availability.h>
#if ! TARGET_OS_EMBEDDED
#include <CoreFoundation/CFUserNotification.h>
#include <CoreFoundation/CFUserNotificationPriv.h>
#include <IOKit/pwr_mgt/IOPMLibPrivate.h>
#endif
#include "rfc_options.h"
#include "dhcp_options.h"
#include "interfaces.h"
#include "util.h"
#include "dhcplib.h"
#include "ioregpath.h"
#include "ipconfig_types.h"
#include "ipconfigd.h"
#include "server.h"
#include "timer.h"
#include "ipconfigd_globals.h"
#include "ipconfigd_threads.h"
#include "FDSet.h"
#include "symbol_scope.h"
#include "dprintf.h"
#include "cfutil.h"
#include "sysconfig.h"
#include "ifutil.h"
#include "rtutil.h"
#include "DHCPv6Client.h"
#include "DHCPv6Socket.h"
#include "IPConfigurationServiceInternal.h"
#define RIFLAGS_IADDR_VALID ((uint32_t)0x1)
#define RIFLAGS_HWADDR_VALID ((uint32_t)0x2)
#define RIFLAGS_ARP_VERIFIED ((uint32_t)0x4)
#define RIFLAGS_ALL_VALID (RIFLAGS_IADDR_VALID | RIFLAGS_HWADDR_VALID | RIFLAGS_ARP_VERIFIED)
#define kARPResolvedIPAddress CFSTR("ARPResolvedIPAddress")
#define kARPResolvedHardwareAddress CFSTR("ARPResolvedHardwareAddress")
typedef struct {
uint32_t flags;
struct in_addr iaddr;
uint8_t hwaddr[MAX_LINK_ADDR_LEN];
} router_info_t;
typedef struct IFState * IFStateRef;
typedef struct {
struct {
struct in_addr addr;
struct in_addr mask;
} requested_ip;
inet_addrinfo_t info;
router_info_t router;
} ServiceIPv4, * ServiceIPv4Ref;
typedef struct {
struct in6_addr addr;
int prefix_length;
} inet6_addr_prefix_t;
typedef struct {
inet6_addr_prefix_t requested_ip;
} ServiceIPv6, * ServiceIPv6Ref;
struct ServiceInfo {
CFStringRef serviceID;
IFStateRef ifstate;
ipconfig_method_t method;
ipconfig_status_t status;
boolean_t is_dynamic;
boolean_t no_publish;
boolean_t ready;
CFStringRef parent_serviceID;
CFStringRef child_serviceID;
dispatch_source_t pid_source;
void * private;
#if ! TARGET_OS_EMBEDDED
CFUserNotificationRef user_notification;
CFRunLoopSourceRef user_rls;
#endif
union {
ServiceIPv4 v4;
ServiceIPv6 v6;
} u;
};
struct IFState {
interface_t * if_p;
CFStringRef ifname;
dynarray_t services;
dynarray_t services_v6;
ServiceRef linklocal_service_p;
boolean_t startup_ready;
boolean_t netboot;
CFStringRef ssid;
struct ether_addr bssid;
timer_callout_t * timer;
};
typedef dynarray_t IFStateList_t;
#define IS_IPV4 TRUE
#define IS_IPV6 FALSE
#define NETWORK_CHANGED_NONE ((uint32_t)0x0)
#define NETWORK_CHANGED_SSID ((uint32_t)0x1)
#define NETWORK_CHANGED_BSSID ((uint32_t)0x2)
#ifndef kSCEntNetRefreshConfiguration
#define kSCEntNetRefreshConfiguration CFSTR("RefreshConfiguration")
#endif
#ifndef kSCEntNetIPv4ARPCollision
#define kSCEntNetIPv4ARPCollision CFSTR("IPv4ARPCollision")
#endif
#ifndef kSCEntNetDHCPv6
#define kSCEntNetDHCPv6 CFSTR("DHCPv6")
#endif
#ifndef kSCValNetIPv4ConfigMethodFailover
static const CFStringRef kIPConfigurationConfigMethodFailover = CFSTR("Failover");
#define kSCValNetIPv4ConfigMethodFailover kIPConfigurationConfigMethodFailover
#endif
#ifndef kSCValNetIPv6ConfigMethodLinkLocal
static const CFStringRef kIPConfigurationIPv6ConfigMethodLinkLocal = CFSTR("LinkLocal");
#define kSCValNetIPv6ConfigMethodLinkLocal kIPConfigurationIPv6ConfigMethodLinkLocal
#endif
#ifndef kSCPropNetIPv4FailoverAddressTimeout
static const CFStringRef kIPConfigurationFailoverAddressTimeout = CFSTR("FailoverAddressTimeout");
#define kSCPropNetIPv4FailoverAddressTimeout kIPConfigurationFailoverAddressTimeout
#endif
#ifndef kSCPropNetIgnoreLinkStatus
static const CFStringRef kIPConfigurationIgnoreLinkStatus = CFSTR("IgnoreLinkStatus");
#define kSCPropNetIgnoreLinkStatus kIPConfigurationIgnoreLinkStatus
#endif
#ifndef kSCPropNetIPv66to4Relay
static const CFStringRef kSCPropNetIPv66to4Relay = CFSTR("6to4Relay");
#endif
#define kDHCPClientPreferencesID CFSTR("DHCPClient.plist")
#define kDHCPClientApplicationPref CFSTR("Application")
#define kDHCPRequestedParameterList CFSTR("DHCPRequestedParameterList")
#define kDHCPv6RequestedOptions CFSTR("DHCPv6RequestedOptions")
#define MAX_RETRIES 9
#define INITIAL_WAIT_SECS 1
#define MAX_WAIT_SECS 8
#define GATHER_SECS 1
#define LINK_INACTIVE_WAIT_SECS 1
#define DHCP_INIT_REBOOT_RETRY_COUNT 2
#define DHCP_SELECT_RETRY_COUNT 3
#define DHCP_ALLOCATE_LINKLOCAL_AT_RETRY_COUNT 4
#define DHCP_ROUTER_ARP_AT_RETRY_COUNT 7
#define DHCP_FAILURE_CONFIGURES_LINKLOCAL TRUE
#define DHCP_SUCCESS_DECONFIGURES_LINKLOCAL TRUE
#define DHCP_LOCAL_HOSTNAME_LENGTH_MAX 15
#define DISCOVER_ROUTER_MAC_ADDRESS_SECS 60
#define DHCP_DEFEND_IP_ADDRESS_INTERVAL_SECS 10
#define DHCP_DEFEND_IP_ADDRESS_COUNT 3
#define DHCP_LEASE_WRITE_T1_THRESHOLD_SECS 3600
#define MANUAL_CONFLICT_RETRY_INTERVAL_SECS 300
#define USER_ERROR 1
#define UNEXPECTED_ERROR 2
#define TIMEOUT_ERROR 3
uint16_t G_client_port = IPPORT_BOOTPC;
boolean_t G_dhcp_accepts_bootp = FALSE;
boolean_t G_dhcp_failure_configures_linklocal
= DHCP_FAILURE_CONFIGURES_LINKLOCAL;
boolean_t G_dhcp_success_deconfigures_linklocal
= DHCP_SUCCESS_DECONFIGURES_LINKLOCAL;
int G_dhcp_allocate_linklocal_at_retry_count
= DHCP_ALLOCATE_LINKLOCAL_AT_RETRY_COUNT;
int G_dhcp_router_arp_at_retry_count
= DHCP_ROUTER_ARP_AT_RETRY_COUNT;
int G_dhcp_init_reboot_retry_count
= DHCP_INIT_REBOOT_RETRY_COUNT;
int G_dhcp_select_retry_count
= DHCP_SELECT_RETRY_COUNT;
int G_dhcp_defend_ip_address_interval_secs
= DHCP_DEFEND_IP_ADDRESS_INTERVAL_SECS;
int G_dhcp_defend_ip_address_count
= DHCP_DEFEND_IP_ADDRESS_COUNT;
int G_dhcp_lease_write_t1_threshold_secs
= DHCP_LEASE_WRITE_T1_THRESHOLD_SECS;
uint16_t G_server_port = IPPORT_BOOTPS;
int G_manual_conflict_retry_interval_secs
= MANUAL_CONFLICT_RETRY_INTERVAL_SECS;
boolean_t G_is_netboot;
static int S_link_inactive_secs = LINK_INACTIVE_WAIT_SECS;
int G_gather_secs = GATHER_SECS;
int G_initial_wait_secs = INITIAL_WAIT_SECS;
int G_max_retries = MAX_RETRIES;
int G_max_wait_secs = MAX_WAIT_SECS;
boolean_t G_must_broadcast = FALSE;
int G_IPConfiguration_verbose = FALSE;
int G_debug = FALSE;
bootp_session_t * G_bootp_session = NULL;
arp_session_t * G_arp_session = NULL;
boolean_t G_router_arp = TRUE;
boolean_t G_discover_and_publish_router_mac_address = TRUE;
#define DHCPv6_ENABLED TRUE
#define DHCPv6_STATEFUL_ENABLED TRUE
boolean_t G_dhcpv6_enabled = DHCPv6_ENABLED;
boolean_t G_dhcpv6_stateful_enabled = DHCPv6_STATEFUL_ENABLED;
int G_dhcp_duid_type = kDHCPDUIDTypeLLT;
const unsigned char G_rfc_magic[4] = RFC_OPTIONS_MAGIC;
const struct in_addr G_ip_broadcast = { INADDR_BROADCAST };
const struct in_addr G_ip_zeroes = { 0 };
static interface_list_t * S_interfaces = NULL;
static CFBundleRef S_bundle = NULL;
static CFRunLoopObserverRef S_observer = NULL;
static boolean_t S_linklocal_needs_attention = FALSE;
static IFStateList_t S_ifstate_list;
static io_connect_t S_power_connection;
static SCDynamicStoreRef S_scd_session = NULL;
static CFStringRef S_setup_service_prefix = NULL;
static CFStringRef S_state_interface_prefix = NULL;
static char * S_computer_name = NULL;
static CFStringRef S_computer_name_key = NULL;
static CFStringRef S_hostnames_key = NULL;
static int S_arp_probe_count = ARP_PROBE_COUNT;
static int S_arp_gratuitous_count = ARP_GRATUITOUS_COUNT;
static struct timeval S_arp_retry = {
ARP_RETRY_SECS,
ARP_RETRY_USECS
};
static int S_arp_detect_count = ARP_DETECT_COUNT;
static struct timeval S_arp_detect_retry = {
ARP_DETECT_RETRY_SECS,
ARP_DETECT_RETRY_USECS
};
static struct timeval S_arp_resolve_retry = {
ARP_RESOLVE_RETRY_SECS,
ARP_RESOLVE_RETRY_USECS
};
static int S_discover_router_mac_address_secs
= DISCOVER_ROUTER_MAC_ADDRESS_SECS;
static int S_arp_conflict_retry = ARP_CONFLICT_RETRY_COUNT;
static struct timeval S_arp_conflict_delay = {
ARP_CONFLICT_RETRY_DELAY_SECS,
ARP_CONFLICT_RETRY_DELAY_USECS
};
static int S_dhcp_local_hostname_length_max = DHCP_LOCAL_HOSTNAME_LENGTH_MAX;
static CFArrayRef S_router_arp_excluded_ssids;
static CFRange S_router_arp_excluded_ssids_range;
#define IPCONFIGURATION_BOOTP_LOG "/var/log/com.apple.IPConfiguration.bootp"
static FILE * S_dhcp_packet_log;
#define IPCONFIGURATION_DHCPV6_LOG "/var/log/com.apple.IPConfiguration.DHCPv6"
static FILE * S_dhcpv6_packet_log;
static struct in_addr S_netboot_ip;
static struct in_addr S_netboot_server_ip;
static char S_netboot_ifname[IFNAMSIZ + 1];
#if ! TARGET_OS_EMBEDDED
static boolean_t S_use_maintenance_wake = TRUE;
static boolean_t S_awake = TRUE;
#endif
#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
#if __IPHONE_OS_VERSION_MIN_REQUIRED < 40000
#define IPV6_MAP_AUTOMATIC_TO_LINKLOCAL TRUE
#endif
#endif
#ifndef IPV6_MAP_AUTOMATIC_TO_LINKLOCAL
#define IPV6_MAP_AUTOMATIC_TO_LINKLOCAL FALSE
#endif
static boolean_t S_ipv6_map_automatic_to_linklocal = IPV6_MAP_AUTOMATIC_TO_LINKLOCAL;
static boolean_t S_configure_ipv6 = TRUE;
#define PROP_SERVICEID CFSTR("ServiceID")
static void
S_add_dhcp_parameters(SCPreferencesRef prefs);
static void
configuration_changed(SCDynamicStoreRef session);
static ipconfig_status_t
config_method_event(ServiceRef service_p, IFEventID_t event, void * data);
static ipconfig_status_t
config_method_start(ServiceRef service_p, ipconfig_method_t method,
ipconfig_method_data_t * method_data);
static ipconfig_status_t
config_method_change(ServiceRef service_p, ipconfig_method_t method,
ipconfig_method_data_t * method_data,
boolean_t * needs_stop);
static ipconfig_status_t
config_method_stop(ServiceRef service_p);
static ipconfig_status_t
config_method_media(ServiceRef service_p, void * network_changed);
static ipconfig_status_t
config_method_bssid_changed(ServiceRef service_p);
static ipconfig_status_t
config_method_renew(ServiceRef service_p);
static void
service_publish_clear(ServiceRef service_p);
static boolean_t
all_services_ready();
static void
S_linklocal_elect(CFArrayRef service_order);
static CFArrayRef
S_get_service_order(SCDynamicStoreRef session);
static __inline__ IFStateRef
service_ifstate(ServiceRef service_p)
{
return (service_p->ifstate);
}
static boolean_t
S_get_plist_boolean_quiet(CFDictionaryRef plist, CFStringRef key,
boolean_t def);
static int
S_get_plist_int_quiet(CFDictionaryRef plist, CFStringRef key,
int def);
static unsigned int
S_get_service_rank(CFArrayRef arr, ServiceRef service_p);
static IFStateRef
IFStateList_ifstate_with_name(IFStateList_t * list, const char * ifname,
int * where);
static void
IFStateFreeService(IFStateRef ifstate, ServiceRef service_p);
static ServiceRef
IFState_service_with_ip(IFStateRef ifstate, struct in_addr iaddr);
static uint8_t
IFState_set_ssid_bssid(IFStateRef ifstate, CFStringRef ssid,
const struct ether_addr * bssid);
static void
S_linklocal_start(ServiceRef parent_service_p, boolean_t allocate);
static CFStringRef
S_copy_ssid_bssid(CFStringRef ifname, struct ether_addr * ap_mac);
static int
S_remove_ip_address(const char * ifname, struct in_addr this_ip);
STATIC ipconfig_status_t
S_remove_service_with_id_str(CFStringRef serviceID);
static ipconfig_status_t
method_info_from_dict(CFDictionaryRef dict,
ipconfig_method_t * ret_method,
ipconfig_method_data_t * * ret_method_data);
static ipconfig_status_t
method_info_from_ipv6_dict(CFDictionaryRef dict,
ipconfig_method_t * ret_method,
ipconfig_method_data_t * * ret_method_data);
STATIC CFDictionaryRef
ServiceIPv4CopyMergedDNS(ServiceRef service_p, dhcp_info_t * info_p);
STATIC CFDictionaryRef
ServiceIPv6CopyMergedDNS(ServiceRef service_p, dhcpv6_info_t * info_v6_p);
static boolean_t
S_is_our_hardware_address(interface_t * ignored,
int hwtype, void * hwaddr, int hwlen)
{
int i;
for (i = 0; i < ifl_count(S_interfaces); i++) {
interface_t * if_p = ifl_at_index(S_interfaces, i);
int link_length = if_link_length(if_p);
if (hwlen != link_length) {
continue;
}
if (hwtype != if_link_arptype(if_p)) {
continue;
}
if (bcmp(hwaddr, if_link_address(if_p), link_length) == 0) {
return (TRUE);
}
}
return (FALSE);
}
char *
computer_name()
{
return (S_computer_name);
}
static void
computer_name_update(SCDynamicStoreRef session)
{
char buf[256];
CFStringEncoding encoding;
CFStringRef name;
if (session == NULL)
return;
if (S_computer_name) {
free(S_computer_name);
S_computer_name = NULL;
}
name = SCDynamicStoreCopyComputerName(session, &encoding);
if (name == NULL) {
goto done;
}
if (_SC_CFStringIsValidDNSName(name) == FALSE) {
my_CFRelease(&name);
name = SCDynamicStoreCopyLocalHostName(session);
if (name == NULL) {
goto done;
}
if (_SC_CFStringIsValidDNSName(name) == FALSE) {
goto done;
}
if (CFStringGetLength(name) > S_dhcp_local_hostname_length_max) {
goto done;
}
}
if (CFStringGetCString(name, buf, sizeof(buf),
kCFStringEncodingASCII) == FALSE) {
goto done;
}
S_computer_name = strdup(buf);
done:
my_CFRelease(&name);
return;
}
static void
service_resolve_router_complete(void * arg1, void * arg2,
const arp_result_t * result)
{
service_resolve_router_callback_t * callback_func;
interface_t * if_p;
ServiceRef service_p;
router_arp_status_t status;
service_p = (ServiceRef)arg1;
callback_func = (service_resolve_router_callback_t *)arg2;
if_p = service_interface(service_p);
if (result->error) {
my_log(LOG_ERR, "service_resolve_router_complete %s: ARP failed, %s",
if_name(if_p),
arp_client_errmsg(result->client));
status = router_arp_status_failed_e;
}
else if (result->in_use) {
bcopy(result->addr.target_hardware, service_p->u.v4.router.hwaddr,
if_link_length(if_p));
service_router_set_all_valid(service_p);
my_log(LOG_DEBUG, "service_resolve_router_complete %s: ARP "
IP_FORMAT ": response received", if_name(if_p),
IP_LIST(&service_p->u.v4.router.iaddr));
status = router_arp_status_success_e;
}
else {
status = router_arp_status_no_response_e;
my_log(LOG_DEBUG, "service_resolve_router_complete %s: ARP router "
IP_FORMAT ": no response", if_name(if_p),
IP_LIST(&service_p->u.v4.router.iaddr));
}
(*callback_func)(service_p, status);
return;
}
boolean_t
service_resolve_router(ServiceRef service_p, arp_client_t * arp,
service_resolve_router_callback_t * callback_func,
struct in_addr our_ip)
{
interface_t * if_p = service_interface(service_p);
struct in_addr router_ip;
if (G_discover_and_publish_router_mac_address == FALSE) {
return (FALSE);
}
service_router_clear_arp_verified(service_p);
if (service_router_is_iaddr_valid(service_p) == 0) {
my_log(LOG_NOTICE,
"service_resolve_router %s: IP address missing", if_name(if_p));
return (FALSE);
}
router_ip = service_router_iaddr(service_p);
my_log(LOG_DEBUG, "service_resolve_router %s: sender " IP_FORMAT
" target " IP_FORMAT " started",
if_name(if_p), IP_LIST(&our_ip), IP_LIST(&router_ip));
arp_client_resolve(arp, service_resolve_router_complete,
service_p, callback_func, our_ip, router_ip,
S_discover_router_mac_address_secs);
return (TRUE);
}
boolean_t
service_populate_router_arpinfo(ServiceRef service_p,
arp_address_info_t * info_p)
{
interface_t * if_p = service_interface(service_p);
struct in_addr router_ip;
if (G_discover_and_publish_router_mac_address == FALSE) {
return (FALSE);
}
service_router_clear_arp_verified(service_p);
if (service_router_is_iaddr_valid(service_p) == 0) {
my_log(LOG_DEBUG,
"service_populate_router_arpinfo %s: "
"Router IP address missing",
if_name(if_p));
return (FALSE);
}
router_ip = service_router_iaddr(service_p);
my_log(LOG_DEBUG, "service_populate_router_arpinfo %s: "
"found gateway " IP_FORMAT,
if_name(if_p), IP_LIST(&router_ip));
info_p->target_ip = router_ip;
bcopy(service_router_hwaddr(service_p), info_p->target_hardware,
service_router_hwaddr_size(service_p));
return (TRUE);
}
boolean_t
service_update_router_address(ServiceRef service_p,
dhcpol_t * options, struct in_addr our_ip)
{
struct in_addr * router_p;
router_p = dhcp_get_router_from_options(options, our_ip);
if (router_p == NULL) {
goto failed;
}
if (service_router_all_valid(service_p)
&& router_p->s_addr == service_router_iaddr(service_p).s_addr) {
return (FALSE);
}
service_router_clear(service_p);
service_router_set_iaddr(service_p, *router_p);
service_router_set_iaddr_valid(service_p);
return (TRUE);
failed:
service_router_clear(service_p);
return (FALSE);
}
#define STARTUP_KEY CFSTR("Plugin:IPConfiguration")
static __inline__ void
unblock_startup(SCDynamicStoreRef session)
{
(void)SCDynamicStoreSetValue(session, STARTUP_KEY, STARTUP_KEY);
}
static void
ServiceFree(void * arg)
{
IFStateRef ifstate;
ServiceRef service_p = (ServiceRef)arg;
if (G_IPConfiguration_verbose) {
SCLog(TRUE, LOG_NOTICE, CFSTR("ServiceFree(%@) %s"),
service_p->serviceID, ipconfig_method_string(service_p->method));
}
ifstate = service_ifstate(service_p);
if (ifstate != NULL && ifstate->linklocal_service_p == service_p) {
ifstate->linklocal_service_p = NULL;
}
config_method_stop(service_p);
service_publish_clear(service_p);
#if ! TARGET_OS_EMBEDDED
ServiceRemoveAddressConflict(service_p);
#endif
my_CFRelease(&service_p->serviceID);
my_CFRelease(&service_p->parent_serviceID);
my_CFRelease(&service_p->child_serviceID);
if (service_p->pid_source != NULL) {
CFStringRef serviceID;
serviceID = (CFStringRef)dispatch_get_context(service_p->pid_source);
CFRelease(serviceID);
dispatch_source_cancel(service_p->pid_source);
dispatch_release(service_p->pid_source);
service_p->pid_source = NULL;
}
free(service_p);
return;
}
static ServiceRef
ServiceCreate(IFStateRef ifstate, CFStringRef serviceID,
ipconfig_method_t method,
ipconfig_method_data_t * method_data,
ServiceRef parent_service_p, ipconfig_status_t * status_p)
{
ServiceRef service_p;
ipconfig_status_t status = ipconfig_status_success_e;
if (method == ipconfig_method_linklocal_e
&& ifstate->linklocal_service_p != NULL) {
IFStateFreeService(ifstate,
ifstate->linklocal_service_p);
}
service_p = (ServiceRef)malloc(sizeof(*service_p));
if (service_p == NULL) {
status = ipconfig_status_allocation_failed_e;
goto failed;
}
bzero(service_p, sizeof(*service_p));
service_p->method = method;
service_p->ifstate = ifstate;
if (serviceID != NULL) {
service_p->serviceID = (void *)CFRetain(serviceID);
}
else {
service_p->serviceID = (void *)
CFStringCreateWithFormat(NULL, NULL,
CFSTR("%s-%s"),
ipconfig_method_string(method),
if_name(ifstate->if_p));
}
if (parent_service_p != NULL) {
service_p->parent_serviceID
= (void *)CFRetain(parent_service_p->serviceID);
}
status = config_method_start(service_p, method, method_data);
if (status != ipconfig_status_success_e) {
goto failed;
}
if (parent_service_p != NULL) {
my_CFRelease(&parent_service_p->child_serviceID);
parent_service_p->child_serviceID
= (void *)CFRetain(service_p->serviceID);
}
if (service_p->method == ipconfig_method_linklocal_e) {
ifstate->linklocal_service_p = service_p;
}
*status_p = status;
return (service_p);
failed:
if (service_p != NULL) {
my_CFRelease(&service_p->serviceID);
my_CFRelease(&service_p->parent_serviceID);
free(service_p);
}
*status_p = status;
return (NULL);
}
static void
ServiceHandleProcessExit(dispatch_source_t source)
{
pid_t pid;
CFStringRef serviceID;
ipconfig_status_t status;
pid = (pid_t)dispatch_source_get_handle(source);
my_log(LOG_DEBUG, "IPConfiguration: pid %d exited", pid);
serviceID = (CFStringRef)dispatch_get_context(source);
status = S_remove_service_with_id_str(serviceID);
if (status != ipconfig_status_success_e) {
SCLog(TRUE, LOG_ERR,
CFSTR("IPConfiguration: failed to stop service %@, %s"),
serviceID, ipconfig_status_string(status));
}
return;
}
static CFRunLoopRef S_plugin_runloop;
static void
ServiceProcessExited(dispatch_source_t source)
{
CFRunLoopPerformBlock(S_plugin_runloop,
kCFRunLoopDefaultMode,
^{ ServiceHandleProcessExit(source); });
CFRunLoopWakeUp(S_plugin_runloop);
return;
}
static void
ServiceMonitorPID(ServiceRef service_p, pid_t pid)
{
dispatch_source_t source;
if (S_plugin_runloop == NULL) {
S_plugin_runloop = CFRunLoopGetCurrent();
}
source = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid,
DISPATCH_PROC_EXIT,
dispatch_get_main_queue());
if (source == NULL) {
my_log(LOG_ERR, "IPConfiguration: dispatch_source_create failed");
return;
}
CFRetain(service_p->serviceID);
dispatch_set_context(source, (void *)service_p->serviceID);
dispatch_source_set_event_handler(source,
^{ ServiceProcessExited(source); });
dispatch_resume(source);
service_p->pid_source = source;
return;
}
static ServiceRef
IFStateGetServiceWithID(IFStateRef ifstate, CFStringRef serviceID,
boolean_t is_ipv4)
{
int i;
dynarray_t * list;
if (is_ipv4) {
list = &ifstate->services;
}
else {
list = &ifstate->services_v6;
}
for (i = 0; i < dynarray_count(list); i++) {
ServiceRef service_p = dynarray_element(list, i);
if (CFEqual(serviceID, service_p->serviceID)) {
return (service_p);
}
}
return (NULL);
}
static ServiceRef
IFState_service_with_ip(IFStateRef ifstate, struct in_addr iaddr)
{
int j;
for (j = 0; j < dynarray_count(&ifstate->services); j++) {
ServiceRef service_p = dynarray_element(&ifstate->services, j);
if (service_p->u.v4.info.addr.s_addr == iaddr.s_addr) {
return (service_p);
}
}
return (NULL);
}
static ServiceRef
IFStateGetServiceMatchingIPv4Method(IFStateRef ifstate,
ipconfig_method_t method,
ipconfig_method_data_t * method_data,
boolean_t just_dynamic)
{
int i;
boolean_t is_dhcp_or_bootp = FALSE;
boolean_t is_manual;
is_manual = ipconfig_method_is_manual(method);
if (is_manual == FALSE) {
is_dhcp_or_bootp = ipconfig_method_is_dhcp_or_bootp(method);
}
for (i = 0; i < dynarray_count(&ifstate->services); i++) {
ServiceRef service_p = dynarray_element(&ifstate->services, i);
if (just_dynamic && service_p->is_dynamic == FALSE) {
continue;
}
if (is_manual) {
if (ipconfig_method_is_manual(service_p->method)
&& (method_data->manual.addr.s_addr
== service_requested_ip_addr(service_p).s_addr)) {
return (service_p);
}
}
else if (is_dhcp_or_bootp
&& ipconfig_method_is_dhcp_or_bootp(service_p->method)) {
return (service_p);
}
else if (service_p->method == method) {
return (service_p);
}
}
return (NULL);
}
static ServiceRef
IFStateGetServiceMatchingIPv6Method(IFStateRef ifstate,
ipconfig_method_t method,
ipconfig_method_data_t * method_data,
boolean_t just_dynamic)
{
int i;
for (i = 0; i < dynarray_count(&ifstate->services_v6); i++) {
ServiceRef service_p = dynarray_element(&ifstate->services_v6, i);
ServiceIPv6Ref v6_p;
if (just_dynamic && service_p->is_dynamic == FALSE) {
continue;
}
if (service_p->method != method) {
continue;
}
if (method != ipconfig_method_manual_v6_e) {
return (service_p);
}
v6_p = (ServiceIPv6Ref)service_p;
if (IN6_ARE_ADDR_EQUAL(&method_data->manual_v6.addr,
&v6_p->requested_ip.addr)) {
return (service_p);
}
}
return (NULL);
}
static ServiceRef
IFStateGetServiceMatchingMethod(IFStateRef ifstate,
ipconfig_method_t method,
ipconfig_method_data_t * method_data,
boolean_t just_dynamic)
{
if (ipconfig_method_is_v4(method)) {
return (IFStateGetServiceMatchingIPv4Method(ifstate, method,
method_data,
just_dynamic));
}
return (IFStateGetServiceMatchingIPv6Method(ifstate, method,
method_data,
just_dynamic));
}
static ServiceRef
IFStateGetServiceWithIPv4Method(IFStateRef ifstate,
ipconfig_method_t method,
ipconfig_method_data_t * method_data,
boolean_t just_dynamic)
{
int j;
boolean_t is_manual;
is_manual = ipconfig_method_is_manual(method);
for (j = 0; j < dynarray_count(&ifstate->services); j++) {
ServiceRef service_p = dynarray_element(&ifstate->services, j);
if (just_dynamic && service_p->is_dynamic == FALSE) {
continue;
}
if (method == service_p->method) {
if (is_manual == FALSE
|| (method_data->manual.addr.s_addr
== service_requested_ip_addr(service_p).s_addr)) {
return (service_p);
}
}
}
return (NULL);
}
static __inline__ ServiceRef
IFStateGetServiceWithIPv6Method(IFStateRef ifstate,
ipconfig_method_t method,
ipconfig_method_data_t * method_data,
boolean_t just_dynamic)
{
return (IFStateGetServiceMatchingIPv6Method(ifstate, method,
method_data,
just_dynamic));
}
static ServiceRef
IFStateGetServiceWithMethod(IFStateRef ifstate,
ipconfig_method_t method,
ipconfig_method_data_t * method_data,
boolean_t just_dynamic)
{
if (ipconfig_method_is_v4(method)) {
return (IFStateGetServiceWithIPv4Method(ifstate, method,
method_data,
just_dynamic));
}
return (IFStateGetServiceWithIPv6Method(ifstate, method,
method_data,
just_dynamic));
}
static void
S_FreeNonDynamicServices(dynarray_t * services_p)
{
int count;
int i;
count = dynarray_count(services_p);
for (i = 0; i < count; ) {
ServiceRef service_p = dynarray_element(services_p, i);
if (service_p->is_dynamic) {
i++;
continue;
}
dynarray_free_element(services_p, i);
count--;
}
return;
}
static void
IFStateFreeIPv4Services(IFStateRef ifstate, boolean_t all)
{
if (all) {
dynarray_free(&ifstate->services);
}
else {
S_FreeNonDynamicServices(&ifstate->services);
}
ifstate->startup_ready = TRUE;
if (dynarray_count(&ifstate->services) == 0
&& if_ift_type(ifstate->if_p) != IFT_STF) {
inet_detach_interface(if_name(ifstate->if_p));
}
return;
}
static void
IFStateFreeIPv6Services(IFStateRef ifstate, boolean_t all)
{
if (all) {
dynarray_free(&ifstate->services_v6);
}
else {
S_FreeNonDynamicServices(&ifstate->services_v6);
}
if (dynarray_count(&ifstate->services_v6) == 0) {
(void)inet6_linklocal_stop(if_name(ifstate->if_p));
inet6_detach_interface(if_name(ifstate->if_p));
}
return;
}
static void
IFStateFreeServiceWithID(IFStateRef ifstate, CFStringRef serviceID,
boolean_t is_ipv4)
{
int i;
dynarray_t * list;
if (is_ipv4) {
list = &ifstate->services;
}
else {
list = &ifstate->services_v6;
}
for (i = 0; i < dynarray_count(list); i++) {
ServiceRef service_p = dynarray_element(list, i);
if (CFEqual(serviceID, service_p->serviceID)) {
dynarray_free_element(list, i);
return;
}
}
return;
}
static void
IFStateFreeService(IFStateRef ifstate, ServiceRef service_p)
{
IFStateFreeServiceWithID(ifstate, service_p->serviceID,
ipconfig_method_is_v4(service_p->method));
return;
}
static ipconfig_status_t
IFState_service_add(IFStateRef ifstate, CFStringRef serviceID,
ipconfig_method_t method, void * method_data,
ServiceRef parent_service_p, ServiceRef * ret_service_p)
{
interface_t * if_p = ifstate->if_p;
ServiceRef service_p = NULL;
ipconfig_status_t status = ipconfig_status_success_e;
boolean_t started_v6_linklocal = FALSE;
if (ipconfig_method_is_v4(method)) {
inet_attach_interface(if_name(if_p));
}
else {
int ift_type = if_ift_type(if_p);
inet6_attach_interface(if_name(if_p));
if (ift_type != IFT_LOOP && ift_type != IFT_STF) {
link_status_t link = if_get_link_status(if_p);
if (link.valid == FALSE || link.active) {
(void)inet6_linklocal_start(if_name(if_p));
started_v6_linklocal = TRUE;
}
}
}
service_p = ServiceCreate(ifstate, serviceID, method,
method_data,
parent_service_p, &status);
if (service_p == NULL) {
my_log(LOG_DEBUG, "status from %s was %s",
ipconfig_method_string(method),
ipconfig_status_string(status));
if (ipconfig_method_is_v4(method)) {
if (dynarray_count(&ifstate->services) == 0) {
ifstate->startup_ready = TRUE;
inet_detach_interface(if_name(if_p));
}
}
else {
if (dynarray_count(&ifstate->services_v6) == 0) {
if (started_v6_linklocal) {
(void)inet6_linklocal_stop(if_name(if_p));
}
inet6_detach_interface(if_name(if_p));
}
}
all_services_ready();
}
else if (ipconfig_method_is_v4(method)) {
dynarray_add(&ifstate->services, service_p);
}
else {
dynarray_add(&ifstate->services_v6, service_p);
}
if (ret_service_p) {
*ret_service_p = service_p;
}
return (status);
}
static void
IFState_update_media_status(IFStateRef ifstate)
{
const char * ifname = if_name(ifstate->if_p);
link_status_t link;
link = if_link_status_update(ifstate->if_p);
if (link.valid == FALSE) {
my_log(LOG_DEBUG, "%s link is unknown", ifname);
}
else {
my_log(LOG_DEBUG, "%s link is %s", ifname, link.active ? "up" : "down");
}
if (if_is_wireless(ifstate->if_p)) {
struct ether_addr bssid;
CFStringRef ssid;
ssid = S_copy_ssid_bssid(ifstate->ifname, &bssid);
if (G_IPConfiguration_verbose) {
if (ssid != NULL) {
SCLog(TRUE, LOG_NOTICE, CFSTR("%s: SSID %@ BSSID %s"),
if_name(ifstate->if_p), ssid, ether_ntoa(&bssid));
}
else {
my_log(LOG_NOTICE, "%s: no SSID",
if_name(ifstate->if_p));
}
}
IFState_set_ssid_bssid(ifstate, ssid, &bssid);
my_CFRelease(&ssid);
}
return;
}
static IFStateRef
IFState_init(interface_t * if_p)
{
IFStateRef ifstate;
ifstate = malloc(sizeof(*ifstate));
if (ifstate == NULL) {
my_log(LOG_ERR, "IFState_init: malloc ifstate failed");
return (NULL);
}
bzero(ifstate, sizeof(*ifstate));
ifstate->if_p = if_dup(if_p);
ifstate->ifname = CFStringCreateWithCString(NULL, if_name(if_p),
kCFStringEncodingASCII);
IFState_update_media_status(ifstate);
ifstate->timer = timer_callout_init();
dynarray_init(&ifstate->services, ServiceFree, NULL);
dynarray_init(&ifstate->services_v6, ServiceFree, NULL);
return (ifstate);
}
static uint8_t
IFState_set_ssid_bssid(IFStateRef ifstate, CFStringRef ssid,
const struct ether_addr * bssid)
{
uint8_t network_changed = NETWORK_CHANGED_NONE;
if (ssid != NULL) {
CFRetain(ssid);
}
if (my_CFEqual(ssid, ifstate->ssid) == FALSE) {
network_changed |= NETWORK_CHANGED_SSID;
}
if (ifstate->ssid != NULL) {
CFRelease(ifstate->ssid);
}
ifstate->ssid = ssid;
if (bssid != NULL
&& bcmp(bssid, &ifstate->bssid, sizeof(bssid)) != 0) {
ifstate->bssid = *bssid;
network_changed |= NETWORK_CHANGED_BSSID;
}
return (network_changed);
}
static void
IFState_free(void * arg)
{
IFStateRef ifstate = (IFStateRef)arg;
if (G_IPConfiguration_verbose) {
my_log(LOG_NOTICE, "IFState_free(%s)", if_name(ifstate->if_p));
}
IFStateFreeIPv4Services(ifstate, TRUE);
IFStateFreeIPv6Services(ifstate, TRUE);
my_CFRelease(&ifstate->ifname);
IFState_set_ssid_bssid(ifstate, NULL, NULL);
if_free(&ifstate->if_p);
timer_callout_free(&ifstate->timer);
free(ifstate);
return;
}
static IFStateRef
IFStateList_ifstate_with_name(IFStateList_t * list, const char * ifname,
int * where)
{
int i;
for (i = 0; i < dynarray_count(list); i++) {
IFStateRef element = dynarray_element(list, i);
if (strcmp(if_name(element->if_p), ifname) == 0) {
if (where != NULL) {
*where = i;
}
return (element);
}
}
return (NULL);
}
static IFStateRef
IFStateList_ifstate_create(IFStateList_t * list, interface_t * if_p)
{
IFStateRef ifstate;
ifstate = IFStateList_ifstate_with_name(list, if_name(if_p), NULL);
if (ifstate == NULL) {
ifstate = IFState_init(if_p);
if (ifstate) {
dynarray_add(list, ifstate);
}
}
return (ifstate);
}
static void
IFStateList_ifstate_free(IFStateList_t * list, const char * ifname)
{
IFStateRef ifstate;
int where = -1;
ifstate = IFStateList_ifstate_with_name(list, ifname, &where);
if (ifstate == NULL) {
return;
}
dynarray_free_element(list, where);
return;
}
#ifdef DEBUG
static void
IFStateList_print(IFStateList_t * list)
{
int i;
printf("-------start--------\n");
for (i = 0; i < dynarray_count(list); i++) {
IFStateRef ifstate = dynarray_element(list, i);
int j;
printf("%s:", if_name(ifstate->if_p));
for (j = 0; j < dynarray_count(&ifstate->services); j++) {
ServiceRef service_p = dynarray_element(&ifstate->services, j);
printf("%s%s", (j == 0) ? "" : ", ",
ipconfig_method_string(service_p->method));
}
printf("\n");
}
printf("-------end--------\n");
return;
}
#endif
static IFStateRef
IFStateListGetServiceWithID(IFStateList_t * list, CFStringRef serviceID,
ServiceRef * ret_service, boolean_t is_ipv4)
{
int i;
for (i = 0; i < dynarray_count(list); i++) {
IFStateRef ifstate = dynarray_element(list, i);
ServiceRef service_p;
service_p = IFStateGetServiceWithID(ifstate, serviceID, is_ipv4);
if (service_p) {
if (ret_service) {
*ret_service = service_p;
}
return (ifstate);
}
}
if (ret_service) {
*ret_service = NULL;
}
return (NULL);
}
static IFStateRef
IFStateList_service_with_ip(IFStateList_t * list, struct in_addr iaddr,
ServiceRef * ret_service)
{
int i;
for (i = 0; i < dynarray_count(list); i++) {
IFStateRef ifstate = dynarray_element(list, i);
ServiceRef service_p;
service_p = IFState_service_with_ip(ifstate, iaddr);
if (service_p) {
if (ret_service) {
*ret_service = service_p;
}
return (ifstate);
}
}
if (ret_service) {
*ret_service = NULL;
}
return (NULL);
}
boolean_t
service_is_using_ip(ServiceRef exclude_service_p, struct in_addr iaddr)
{
int i;
for (i = 0; i < dynarray_count(&S_ifstate_list); i++) {
IFStateRef ifstate = dynarray_element(&S_ifstate_list, i);
int j;
for (j = 0; j < dynarray_count(&ifstate->services); j++) {
ServiceRef service_p = dynarray_element(&ifstate->services, j);
if (service_p == exclude_service_p) {
continue;
}
if (service_p->u.v4.info.addr.s_addr == iaddr.s_addr) {
return (FALSE);
}
}
}
return (TRUE);
}
void
netboot_addresses(struct in_addr * ip, struct in_addr * server_ip)
{
if (ip)
*ip = S_netboot_ip;
if (server_ip)
*server_ip = S_netboot_server_ip;
}
#ifndef KERN_NETBOOT
#define KERN_NETBOOT 40
#endif
static boolean_t
S_netboot_root()
{
int mib[2];
size_t len;
int netboot = 0;
mib[0] = CTL_KERN;
mib[1] = KERN_NETBOOT;
len = sizeof(netboot);
sysctl(mib, 2, &netboot, &len, NULL, 0);
return (netboot);
}
static boolean_t
S_netboot_init()
{
CFDictionaryRef chosen = NULL;
struct dhcp * dhcp;
struct in_addr * iaddr_p;
interface_t * if_p;
IFStateRef ifstate;
boolean_t is_dhcp = TRUE;
int length;
CFDataRef response = NULL;
if (S_netboot_root() == FALSE) {
goto done;
}
chosen = myIORegistryEntryCopyValue("IODeviceTree:/chosen");
if (chosen == NULL) {
goto done;
}
response = CFDictionaryGetValue(chosen, CFSTR("dhcp-response"));
if (isA_CFData(response) == NULL) {
response = CFDictionaryGetValue(chosen, CFSTR("bootp-response"));
if (isA_CFData(response) == NULL) {
goto done;
}
is_dhcp = FALSE;
}
dhcp = (struct dhcp *)(void *)CFDataGetBytePtr(response);
length = CFDataGetLength(response);
if (dhcp->dp_yiaddr.s_addr != 0) {
S_netboot_ip = dhcp->dp_yiaddr;
}
else if (dhcp->dp_ciaddr.s_addr != 0) {
S_netboot_ip = dhcp->dp_ciaddr;
}
else {
goto done;
}
S_netboot_server_ip = dhcp->dp_siaddr;
if_p = ifl_find_ip(S_interfaces, S_netboot_ip);
if (if_p == NULL) {
goto done;
}
ifstate = IFStateList_ifstate_create(&S_ifstate_list, if_p);
ifstate->netboot = TRUE;
if (is_dhcp == TRUE) {
dhcpol_t options;
(void)dhcpol_parse_packet(&options, dhcp, length, NULL);
iaddr_p = (struct in_addr *)
dhcpol_find_with_length(&options,
dhcptag_server_identifier_e,
sizeof(*iaddr_p));
if (iaddr_p != NULL) {
S_netboot_server_ip = *iaddr_p;
}
dhcpol_free(&options);
}
strlcpy(S_netboot_ifname, if_name(if_p), sizeof(S_netboot_ifname));
G_is_netboot = TRUE;
done:
my_CFRelease(&chosen);
return (G_is_netboot);
}
static void
set_entity_value(CFStringRef * entities,
CFDictionaryRef * values, int size,
CFStringRef entity, CFDictionaryRef value,
int * count_p)
{
int i;
i = *count_p;
if (i >= size) {
my_log(LOG_NOTICE, "IPConfiguration: set_entity_value %d >= %d",
i, size);
return;
}
entities[i] = entity;
values[i] = value;
(*count_p)++;
return;
}
const char *
ServiceGetMethodString(ServiceRef service_p)
{
return (ipconfig_method_string(service_p->method));
}
static void
service_clear(ServiceRef service_p)
{
service_p->ready = FALSE;
service_p->status = ipconfig_status_success_e;
return;
}
#define N_PUBLISH_ENTITIES 5
static void
service_publish_clear(ServiceRef service_p)
{
CFDictionaryRef dns_dict = NULL;
CFStringRef entities[N_PUBLISH_ENTITIES];
int entity_count;
CFDictionaryRef values[N_PUBLISH_ENTITIES];
service_clear(service_p);
if (S_scd_session == NULL) {
return;
}
if (ServiceIsIPv4(service_p)) {
entity_count = 0;
set_entity_value(entities, values, N_PUBLISH_ENTITIES,
kSCEntNetIPv4, NULL, &entity_count);
dns_dict = ServiceIPv4CopyMergedDNS(service_p, NULL);
set_entity_value(entities, values, N_PUBLISH_ENTITIES,
kSCEntNetDNS, dns_dict, &entity_count);
set_entity_value(entities, values, N_PUBLISH_ENTITIES,
kSCEntNetDHCP, NULL, &entity_count);
#if ! TARGET_OS_EMBEDDED
set_entity_value(entities, values, N_PUBLISH_ENTITIES,
kSCEntNetSMB, NULL, &entity_count);
#endif
}
else {
entity_count = 0;
set_entity_value(entities, values, N_PUBLISH_ENTITIES,
kSCEntNetIPv6, NULL, &entity_count);
set_entity_value(entities, values, N_PUBLISH_ENTITIES,
kSCEntNetDHCPv6, NULL, &entity_count);
dns_dict = ServiceIPv6CopyMergedDNS(service_p, NULL);
set_entity_value(entities, values, N_PUBLISH_ENTITIES,
kSCEntNetDNS, dns_dict, &entity_count);
}
my_SCDynamicStoreSetService(S_scd_session,
service_p->serviceID,
entities, values, entity_count,
service_p->no_publish);
my_CFRelease(&dns_dict);
return;
}
static boolean_t
all_services_ready()
{
int i;
for (i = 0; i < dynarray_count(&S_ifstate_list); i++) {
int j;
IFStateRef ifstate = dynarray_element(&S_ifstate_list, i);
if (dynarray_count(&ifstate->services) == 0
&& ifstate->startup_ready == FALSE) {
return (FALSE);
}
for (j = 0; j < dynarray_count(&ifstate->services); j++) {
ServiceRef service_p = dynarray_element(&ifstate->services, j);
if (service_p->ready == FALSE) {
return (FALSE);
}
}
}
unblock_startup(S_scd_session);
return (TRUE);
}
static void
dict_insert_router_info(ServiceRef service_p, CFMutableDictionaryRef dict)
{
interface_t * if_p = service_interface(service_p);
char link_addr[MAX_LINK_ADDR_LEN * 3];
CFStringRef link_addr_cf;
CFStringRef router_ip;
CFStringRef sig_str;
if (service_router_all_valid(service_p) == FALSE) {
return;
}
router_ip
= CFStringCreateWithFormat(NULL, NULL,
CFSTR(IP_FORMAT),
IP_LIST(&service_p->u.v4.router.iaddr));
link_addr_to_string(link_addr, sizeof(link_addr),
service_p->u.v4.router.hwaddr,
if_link_length(if_p));
link_addr_cf = CFStringCreateWithCString(NULL,
link_addr,
kCFStringEncodingASCII);
sig_str
= CFStringCreateWithFormat(NULL, NULL,
CFSTR("IPv4.Router=%@;IPv4.RouterHardwareAddress=%s"),
router_ip,
link_addr);
CFDictionarySetValue(dict, kNetworkSignature, sig_str);
CFDictionarySetValue(dict, kARPResolvedIPAddress, router_ip);
CFDictionarySetValue(dict, kARPResolvedHardwareAddress, link_addr_cf);
CFRelease(sig_str);
CFRelease(router_ip);
CFRelease(link_addr_cf);
return;
}
STATIC CFDictionaryRef
ServiceIPv4CopyMergedDNS(ServiceRef service_p, dhcp_info_t * info_p)
{
dhcpv6_info_t info_v6;
ServiceRef ipv6_service_p;
ipv6_service_p = IFStateGetServiceWithID(service_p->ifstate,
service_p->serviceID,
IS_IPV6);
bzero(&info_v6, sizeof(info_v6));
if (ipv6_service_p != NULL) {
(void)config_method_event(ipv6_service_p, IFEventID_get_dhcpv6_info_e,
&info_v6);
}
return (DNSEntityCreateWithDHCPv4AndDHCPv6Info(info_p, &info_v6));
}
void
ServicePublishSuccessIPv4(ServiceRef service_p, dhcp_info_t * dhcp_info_p)
{
CFDictionaryRef dhcp_dict = NULL;
CFDictionaryRef dns_dict = NULL;
CFStringRef entities[N_PUBLISH_ENTITIES];
int entity_count;
inet_addrinfo_t * info_p;
CFMutableDictionaryRef ipv4_dict = NULL;
dhcpol_t * options = NULL;
ServiceRef parent_service_p = NULL;
#if ! TARGET_OS_EMBEDDED
CFMutableDictionaryRef smb_dict = NULL;
const uint8_t * smb_nodetype = NULL;
int smb_nodetype_len = 0;
struct in_addr * smb_server = NULL;
int smb_server_len = 0;
#endif
CFDictionaryRef values[N_PUBLISH_ENTITIES];
if (service_p->serviceID == NULL) {
return;
}
info_p = &service_p->u.v4.info;
service_p->ready = TRUE;
service_p->status = ipconfig_status_success_e;
if (S_scd_session == NULL) {
return;
}
if (dhcp_info_p != NULL) {
options = dhcp_info_p->options;
}
if (service_p->parent_serviceID != NULL) {
parent_service_p
= IFStateGetServiceWithID(service_ifstate(service_p),
service_p->parent_serviceID,
IS_IPV4);
if (parent_service_p == NULL
|| parent_service_p->u.v4.info.addr.s_addr != 0) {
return;
}
}
ipv4_dict = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
my_CFDictionarySetIPAddressAsArrayValue(ipv4_dict,
kSCPropNetIPv4Addresses,
info_p->addr);
my_CFDictionarySetIPAddressAsArrayValue(ipv4_dict,
kSCPropNetIPv4SubnetMasks,
info_p->mask);
CFDictionarySetValue(ipv4_dict, CFSTR("InterfaceName"),
service_ifstate(service_p)->ifname);
if (service_ifstate(service_p)->netboot
&& service_p->parent_serviceID == NULL) {
CFNumberRef primary;
int enabled = 1;
primary = CFNumberCreate(NULL, kCFNumberIntType, &enabled);
CFDictionarySetValue(ipv4_dict, kSCPropNetOverridePrimary,
primary);
CFRelease(primary);
}
if (options != NULL) {
char * host_name = NULL;
int host_name_len = 0;
if (service_p->method == ipconfig_method_bootp_e
|| dhcp_parameter_is_ok(dhcptag_host_name_e)) {
host_name = (char *)
dhcpol_find(options,
dhcptag_host_name_e,
&host_name_len, NULL);
if (host_name && host_name_len > 0) {
CFStringRef str;
str = CFStringCreateWithBytes(NULL, (UInt8 *)host_name,
host_name_len,
kCFStringEncodingUTF8,
FALSE);
if (str != NULL) {
CFDictionarySetValue(ipv4_dict, CFSTR("Hostname"), str);
CFRelease(str);
}
}
}
if (dhcp_parameter_is_ok(dhcptag_router_e)) {
struct in_addr * router = NULL;
router = (struct in_addr *)
dhcpol_find_with_length(options,
dhcptag_router_e,
sizeof(*router));
if (router != NULL) {
CFStringRef str;
str = my_CFStringCreateWithIPAddress(*router);
CFDictionarySetValue(ipv4_dict, kSCPropNetIPv4Router, str);
CFRelease(str);
}
}
}
dict_insert_router_info(service_p, ipv4_dict);
entity_count = 0;
set_entity_value(entities, values, N_PUBLISH_ENTITIES,
kSCEntNetIPv4, ipv4_dict, &entity_count);
if (parent_service_p != NULL) {
dns_dict = ServiceIPv4CopyMergedDNS(parent_service_p, dhcp_info_p);
}
else {
dns_dict = ServiceIPv4CopyMergedDNS(service_p, dhcp_info_p);
}
set_entity_value(entities, values, N_PUBLISH_ENTITIES,
kSCEntNetDNS, dns_dict, &entity_count);
#if ! TARGET_OS_EMBEDDED
if (options != NULL) {
if (dhcp_parameter_is_ok(dhcptag_nb_over_tcpip_name_server_e)) {
smb_server = (struct in_addr *)
dhcpol_find(options,
dhcptag_nb_over_tcpip_name_server_e,
&smb_server_len, NULL);
}
if (dhcp_parameter_is_ok(dhcptag_nb_over_tcpip_node_type_e)) {
smb_nodetype = (uint8_t *)
dhcpol_find(options,
dhcptag_nb_over_tcpip_node_type_e,
&smb_nodetype_len, NULL);
}
}
if ((smb_server && smb_server_len >= sizeof(struct in_addr))
|| (smb_nodetype && smb_nodetype_len == sizeof(uint8_t))) {
smb_dict
= CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (smb_server && smb_server_len >= sizeof(struct in_addr)) {
CFMutableArrayRef array = NULL;
int i;
array = CFArrayCreateMutable(NULL,
smb_server_len / sizeof(struct in_addr),
&kCFTypeArrayCallBacks);
for (i = 0; i < (smb_server_len / sizeof(struct in_addr)); i++) {
CFStringRef str;
str = my_CFStringCreateWithIPAddress(smb_server[i]);
CFArrayAppendValue(array, str);
CFRelease(str);
}
CFDictionarySetValue(smb_dict, kSCPropNetSMBWINSAddresses, array);
CFRelease(array);
}
if (smb_nodetype && smb_nodetype_len == sizeof(uint8_t)) {
switch (smb_nodetype[0]) {
case 1 :
CFDictionarySetValue(smb_dict, kSCPropNetSMBNetBIOSNodeType,
kSCValNetSMBNetBIOSNodeTypeBroadcast);
break;
case 2 :
CFDictionarySetValue(smb_dict, kSCPropNetSMBNetBIOSNodeType,
kSCValNetSMBNetBIOSNodeTypePeer);
break;
case 4:
CFDictionarySetValue(smb_dict, kSCPropNetSMBNetBIOSNodeType,
kSCValNetSMBNetBIOSNodeTypeMixed);
break;
case 8 :
CFDictionarySetValue(smb_dict, kSCPropNetSMBNetBIOSNodeType,
kSCValNetSMBNetBIOSNodeTypeHybrid);
break;
}
}
}
set_entity_value(entities, values, N_PUBLISH_ENTITIES,
kSCEntNetSMB, smb_dict, &entity_count);
#endif
if (dhcp_info_p != NULL && dhcp_info_p->pkt_size != 0) {
dhcp_dict = DHCPInfoDictionaryCreate(service_p->method,
dhcp_info_p->options,
dhcp_info_p->lease_start,
dhcp_info_p->lease_expiration);
}
set_entity_value(entities, values, N_PUBLISH_ENTITIES,
kSCEntNetDHCP, dhcp_dict, &entity_count);
if (parent_service_p != NULL) {
my_SCDynamicStoreSetService(S_scd_session,
service_p->parent_serviceID,
entities, values, entity_count,
service_p->no_publish);
}
else {
my_SCDynamicStoreSetService(S_scd_session,
service_p->serviceID,
entities, values, entity_count,
service_p->no_publish);
}
my_CFRelease(&ipv4_dict);
my_CFRelease(&dns_dict);
my_CFRelease(&dhcp_dict);
#if ! TARGET_OS_EMBEDDED
my_CFRelease(&smb_dict);
#endif
all_services_ready();
return;
}
static void
my_CFDictionarySetIPv6AddressAsString(CFMutableDictionaryRef dict,
CFStringRef prop,
struct in6_addr * ip6_addr)
{
CFStringRef str;
str = my_CFStringCreateWithIPv6Address(ip6_addr);
CFDictionarySetValue(dict, prop, str);
CFRelease(str);
return;
}
static void
dict_set_inet6_info(CFMutableDictionaryRef dict,
inet6_addrinfo_t * addr, int addr_count)
{
CFMutableArrayRef address_list;
int i;
CFMutableArrayRef prefix_list;
address_list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
prefix_list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
for (i = 0; i < addr_count; i++) {
CFStringRef str;
CFNumberRef num;
int val;
str = my_CFStringCreateWithIPv6Address(&(addr[i].addr));
CFArrayAppendValue(address_list, str);
CFRelease(str);
val = addr[i].prefix_length;
num = CFNumberCreate(NULL, kCFNumberIntType, &val);
CFArrayAppendValue(prefix_list, num);
CFRelease(num);
}
CFDictionarySetValue(dict, kSCPropNetIPv6Addresses, address_list);
CFRelease(address_list);
CFDictionarySetValue(dict, kSCPropNetIPv6PrefixLength, prefix_list);
CFRelease(prefix_list);
return;
}
STATIC CFDictionaryRef
ServiceIPv6CopyMergedDNS(ServiceRef service_p, dhcpv6_info_t * info_v6_p)
{
dhcp_info_t info;
ServiceRef ipv4_service_p;
ipv4_service_p = IFStateGetServiceWithID(service_p->ifstate,
service_p->serviceID,
IS_IPV4);
bzero(&info, sizeof(info));
if (ipv4_service_p != NULL) {
(void)config_method_event(ipv4_service_p, IFEventID_get_dhcp_info_e,
&info);
}
return (DNSEntityCreateWithDHCPv4AndDHCPv6Info(&info, info_v6_p));
}
void
ServicePublishSuccessIPv6(ServiceRef service_p,
inet6_addrinfo_t * addresses, int addresses_count,
struct in6_addr * router, int router_count,
dhcpv6_info_t * dhcp_info_p,
CFStringRef signature)
{
CFStringRef entities[N_PUBLISH_ENTITIES];
int entity_count;
CFDictionaryRef dhcp_dict = NULL;
CFDictionaryRef dns_dict = NULL;
CFMutableDictionaryRef ipv6_dict = NULL;
DHCPv6OptionListRef options = NULL;
CFDictionaryRef values[N_PUBLISH_ENTITIES];
if (service_p->serviceID == NULL) {
return;
}
if (addresses == NULL || addresses_count == 0) {
return;
}
service_p->ready = TRUE;
service_p->status = ipconfig_status_success_e;
if (S_scd_session == NULL) {
return;
}
if (dhcp_info_p != NULL) {
options = dhcp_info_p->options;
}
ipv6_dict = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
dict_set_inet6_info(ipv6_dict, addresses, addresses_count);
if (router != NULL) {
my_CFDictionarySetIPv6AddressAsString(ipv6_dict,
kSCPropNetIPv6Router,
router);
}
CFDictionarySetValue(ipv6_dict, CFSTR("InterfaceName"),
service_ifstate(service_p)->ifname);
if (signature != NULL) {
CFDictionarySetValue(ipv6_dict, kNetworkSignature,
signature);
}
dns_dict = ServiceIPv6CopyMergedDNS(service_p, dhcp_info_p);
if (options != NULL) {
dhcp_dict = DHCPv6InfoDictionaryCreate(options);
}
entity_count = 0;
set_entity_value(entities, values, N_PUBLISH_ENTITIES,
kSCEntNetIPv6, ipv6_dict, &entity_count);
set_entity_value(entities, values, N_PUBLISH_ENTITIES,
kSCEntNetDNS, dns_dict, &entity_count);
set_entity_value(entities, values, N_PUBLISH_ENTITIES,
kSCEntNetDHCPv6, dhcp_dict, &entity_count);
my_SCDynamicStoreSetService(S_scd_session,
service_p->serviceID,
entities, values, entity_count,
service_p->no_publish);
my_CFRelease(&ipv6_dict);
my_CFRelease(&dns_dict);
my_CFRelease(&dhcp_dict);
return;
}
void
service_publish_failure_sync(ServiceRef service_p, ipconfig_status_t status,
boolean_t sync)
{
if (ipconfig_method_is_v4(service_p->method)) {
ServiceRef child_service_p = NULL;
ServiceRef parent_service_p = NULL;
if (service_p->child_serviceID != NULL) {
child_service_p
= IFStateGetServiceWithID(service_ifstate(service_p),
service_p->child_serviceID,
IS_IPV4);
}
if (service_p->parent_serviceID != NULL) {
parent_service_p
= IFStateGetServiceWithID(service_ifstate(service_p),
service_p->parent_serviceID,
IS_IPV4);
}
if (child_service_p != NULL
&& child_service_p->u.v4.info.addr.s_addr != 0) {
ServicePublishSuccessIPv4(child_service_p, NULL);
service_clear(service_p);
}
else if (parent_service_p != NULL
&& parent_service_p->u.v4.info.addr.s_addr == 0) {
ipconfig_status_t status;
status = parent_service_p->status;
service_publish_clear(parent_service_p);
parent_service_p->status = status;
}
else {
service_publish_clear(service_p);
}
}
else {
service_publish_clear(service_p);
}
service_p->ready = TRUE;
service_p->status = status;
my_log(LOG_DEBUG, "%s %s: status = '%s'",
ServiceGetMethodString(service_p),
if_name(service_interface(service_p)),
ipconfig_status_string(status));
if (sync == TRUE) {
all_services_ready();
}
return;
}
void
service_publish_failure(ServiceRef service_p, ipconfig_status_t status)
{
service_publish_failure_sync(service_p, status, TRUE);
return;
}
int
service_enable_autoaddr(ServiceRef service_p)
{
return (inet_set_autoaddr(if_name(service_interface(service_p)), 1));
}
int
service_disable_autoaddr(ServiceRef service_p)
{
flush_routes(if_link_index(service_interface(service_p)),
G_ip_zeroes, G_ip_zeroes);
return (inet_set_autoaddr(if_name(service_interface(service_p)), 0));
}
#define RANK_LOWEST (1024 * 1024)
#define RANK_NONE (RANK_LOWEST + 1)
static unsigned int
S_get_service_rank(CFArrayRef arr, ServiceRef service_p)
{
int i;
CFStringRef serviceID = service_p->serviceID;
if (service_ifstate(service_p)->netboot
&& service_p->method == ipconfig_method_dhcp_e) {
return (0);
}
if (serviceID != NULL && arr != NULL) {
int count = CFArrayGetCount(arr);
for (i = 0; i < count; i++) {
CFStringRef s = isA_CFString(CFArrayGetValueAtIndex(arr, i));
if (s == NULL) {
continue;
}
if (CFEqual(serviceID, s)) {
return (i);
}
}
}
return (RANK_LOWEST);
}
static CFArrayRef
S_get_service_order(SCDynamicStoreRef session)
{
CFArrayRef order = NULL;
CFStringRef ipv4_key = NULL;
CFDictionaryRef ipv4_dict = NULL;
if (session == NULL)
goto done;
ipv4_key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCEntNetIPv4);
if (ipv4_key == NULL) {
goto done;
}
ipv4_dict = my_SCDynamicStoreCopyDictionary(session, ipv4_key);
if (ipv4_dict != NULL) {
order = CFDictionaryGetValue(ipv4_dict, kSCPropNetServiceOrder);
order = isA_CFArray(order);
if (order) {
CFRetain(order);
}
}
done:
my_CFRelease(&ipv4_key);
my_CFRelease(&ipv4_dict);
return (order);
}
ServiceRef
service_parent_service(ServiceRef service_p)
{
ipconfig_method_t method;
if (service_p == NULL || service_p->parent_serviceID == NULL) {
return (NULL);
}
method = service_p->method;
return (IFStateGetServiceWithID(service_ifstate(service_p),
service_p->parent_serviceID,
ipconfig_method_is_v4(method)));
}
boolean_t
service_should_do_router_arp(ServiceRef service_p)
{
IFStateRef ifstate = service_ifstate(service_p);
CFStringRef ssid = ifstate->ssid;
if (if_is_wireless(ifstate->if_p) == FALSE) {
return (TRUE);
}
if (S_router_arp_excluded_ssids == NULL
|| S_router_arp_excluded_ssids_range.length == 0) {
return (TRUE);
}
if (ssid == NULL) {
return (FALSE);
}
if (CFArrayContainsValue(S_router_arp_excluded_ssids,
S_router_arp_excluded_ssids_range, ssid)) {
if (G_IPConfiguration_verbose) {
SCLog(TRUE, LOG_NOTICE,
CFSTR("SSID '%@' is in the Router ARP exclude list"),
ssid);
}
return (FALSE);
}
return (TRUE);
}
void
linklocal_service_change(ServiceRef parent_service_p, boolean_t allocate)
{
ipconfig_method_data_t method_data;
IFStateRef ifstate = service_ifstate(parent_service_p);
ServiceRef ll_service_p;
ServiceRef ll_parent_p = NULL;
boolean_t needs_stop;
ll_service_p = ifstate->linklocal_service_p;
if (ll_service_p != NULL) {
if (ll_service_p->parent_serviceID == NULL) {
return;
}
ll_parent_p = IFStateGetServiceWithID(ifstate,
ll_service_p->parent_serviceID,
IS_IPV4);
}
if (ll_parent_p == NULL) {
linklocal_set_needs_attention();
return;
}
if (parent_service_p != ll_parent_p) {
linklocal_set_needs_attention();
return;
}
bzero(&method_data, sizeof(method_data));
method_data.linklocal.allocate = allocate;
(void)config_method_change(ll_service_p,
ipconfig_method_linklocal_e,
&method_data, &needs_stop);
return;
}
void
linklocal_set_needs_attention()
{
S_linklocal_needs_attention = TRUE;
return;
}
static void
S_linklocal_start(ServiceRef parent_service_p, boolean_t allocate)
{
ipconfig_method_data_t method_data;
IFStateRef ifstate = service_ifstate(parent_service_p);
ServiceRef service_p;
ipconfig_status_t status;
bzero(&method_data, sizeof(method_data));
method_data.linklocal.allocate = allocate;
status = IFState_service_add(ifstate, NULL, ipconfig_method_linklocal_e,
&method_data, parent_service_p,
&service_p);
if (status != ipconfig_status_success_e) {
my_log(LOG_NOTICE,
"IPConfiguration: failed to start link-local service on %s, %s",
if_name(ifstate->if_p),
ipconfig_status_string(status));
}
return;
}
static void
S_linklocal_elect(CFArrayRef service_order)
{
unsigned int all_best_rank = RANK_NONE;
ServiceRef all_best_service_p = NULL;
int i;
struct in_addr mask;
struct in_addr netaddr;
for (i = 0; i < dynarray_count(&S_ifstate_list); i++) {
unsigned int best_rank = RANK_NONE;
ServiceRef best_service_p = NULL;
boolean_t election_required = TRUE;
IFStateRef ifstate = dynarray_element(&S_ifstate_list, i);
int j;
ServiceRef ll_parent_p = NULL;
ServiceRef ll_service_p;
unsigned int rank;
if (if_ift_type(ifstate->if_p) == IFT_LOOP) {
continue;
}
ll_service_p = ifstate->linklocal_service_p;
if (ll_service_p != NULL) {
if (ll_service_p->parent_serviceID == NULL) {
election_required = FALSE;
if (ll_service_p->u.v4.info.addr.s_addr != 0) {
best_service_p = ll_service_p;
best_rank = S_get_service_rank(service_order, ll_service_p);
}
}
else {
ll_parent_p
= IFStateGetServiceWithID(ifstate,
ll_service_p->parent_serviceID,
IS_IPV4);
if (ll_parent_p == NULL) {
IFStateFreeService(ifstate, ll_service_p);
ll_service_p = NULL;
}
}
}
if (election_required) {
for (j = 0; j < dynarray_count(&ifstate->services); j++) {
ServiceRef service_p;
inet_addrinfo_t * info_p;
service_p = dynarray_element(&ifstate->services, j);
if (service_p->method == ipconfig_method_linklocal_e) {
continue;
}
info_p = &service_p->u.v4.info;
if (info_p->addr.s_addr == 0) {
if (service_p->method != ipconfig_method_dhcp_e
|| G_dhcp_failure_configures_linklocal == FALSE
|| (service_p->status != ipconfig_status_no_server_e)) {
continue;
}
}
rank = S_get_service_rank(service_order, service_p);
if (best_service_p == NULL
|| rank < best_rank
|| (best_service_p->u.v4.info.addr.s_addr == 0
&& info_p->addr.s_addr != 0)) {
best_service_p = service_p;
best_rank = rank;
}
}
if (ll_parent_p != best_service_p) {
if (ll_parent_p != NULL) {
my_CFRelease(&ll_parent_p->child_serviceID);
IFStateFreeService(ifstate, ll_service_p);
}
if (best_service_p != NULL) {
boolean_t allocate = LINKLOCAL_NO_ALLOCATE;
if (best_service_p->u.v4.info.addr.s_addr == 0) {
allocate = LINKLOCAL_ALLOCATE;
}
S_linklocal_start(best_service_p, allocate);
}
}
}
if (best_service_p != NULL) {
if (all_best_service_p == NULL || best_rank < all_best_rank) {
all_best_service_p = best_service_p;
all_best_rank = best_rank;
}
}
}
mask.s_addr = htonl(IN_CLASSB_NET);
netaddr.s_addr = htonl(IN_LINKLOCALNETNUM);
if (all_best_service_p == NULL) {
my_log(LOG_DEBUG, "removing 169.254/16 subnet");
subnet_route_delete(G_ip_zeroes, netaddr, mask);
}
else {
int if_index;
interface_t * route_if_p = NULL;
if_index = subnet_route_if_index(netaddr, mask);
if (if_index != 0) {
route_if_p = ifl_find_link(S_interfaces, if_index);
}
if (route_if_p == NULL
|| strcmp(if_name(route_if_p),
if_name(service_interface(all_best_service_p))) != 0) {
const char * ifn;
ifn = if_name(service_interface(all_best_service_p));
subnet_route_delete(G_ip_zeroes, netaddr, mask);
subnet_route_add(all_best_service_p->u.v4.info.addr,
netaddr, mask, ifn);
my_log(LOG_DEBUG,
"setting 169.254/16 subnet on %s", ifn);
}
else {
my_log(LOG_DEBUG,
"leaving 169.254/16 subnet on %s",
if_name(route_if_p));
}
}
return;
}
int
service_set_address(ServiceRef service_p,
struct in_addr addr,
struct in_addr mask,
struct in_addr broadcast)
{
interface_t * if_p = service_interface(service_p);
int ret = 0;
struct in_addr netaddr = { 0 };
int s = inet_dgram_socket();
if (mask.s_addr == 0) {
u_int32_t ipval = ntohl(addr.s_addr);
if (IN_CLASSA(ipval)) {
mask.s_addr = htonl(IN_CLASSA_NET);
}
else if (IN_CLASSB(ipval)) {
mask.s_addr = htonl(IN_CLASSB_NET);
}
else {
mask.s_addr = htonl(IN_CLASSC_NET);
}
}
if (broadcast.s_addr == 0) {
broadcast = hltoip(iptohl(addr) | ~iptohl(mask));
}
netaddr = hltoip(iptohl(addr) & iptohl(mask));
if (G_IPConfiguration_verbose) {
my_log(LOG_NOTICE,
"%s %s: setting " IP_FORMAT " netmask " IP_FORMAT
" broadcast " IP_FORMAT,
ServiceGetMethodString(service_p),
if_name(if_p),
IP_LIST(&addr), IP_LIST(&mask), IP_LIST(&broadcast));
}
if (s < 0) {
ret = errno;
my_log(LOG_ERR,
"service_set_address(%s): socket() failed, %s (%d)",
if_name(if_p), strerror(errno), errno);
}
else {
inet_addrinfo_t * info_p = &service_p->u.v4.info;
if (inet_aifaddr(s, if_name(if_p), addr, &mask, &broadcast) < 0) {
ret = errno;
my_log(LOG_DEBUG, "service_set_address(%s) "
IP_FORMAT " inet_aifaddr() failed, %s (%d)", if_name(if_p),
IP_LIST(&addr), strerror(errno), errno);
}
bzero(info_p, sizeof(*info_p));
info_p->addr = addr;
info_p->mask = mask;
info_p->netaddr = netaddr;
info_p->broadcast = broadcast;
close(s);
(void)host_route(RTM_DELETE, addr);
(void)host_route(RTM_ADD, addr);
}
flush_routes(if_link_index(if_p), G_ip_zeroes, broadcast);
linklocal_set_needs_attention();
return (ret);
}
static int
S_remove_ip_address(const char * ifname, struct in_addr this_ip)
{
int ret = 0;
int s;
s = inet_dgram_socket();
if (s < 0) {
ret = errno;
my_log(LOG_DEBUG,
"S_remove_ip_address(%s) socket() failed, %s (%d)",
ifname, strerror(errno), errno);
}
else {
if (inet_difaddr(s, ifname, this_ip) < 0) {
ret = errno;
my_log(LOG_DEBUG, "S_remove_ip_address(%s) "
IP_FORMAT " failed, %s (%d)", ifname,
IP_LIST(&this_ip), strerror(errno), errno);
}
close(s);
}
return (ret);
}
int
service_remove_address(ServiceRef service_p)
{
interface_t * if_p = service_interface(service_p);
inet_addrinfo_t * info_p = &service_p->u.v4.info;
int ret = 0;
if (info_p->addr.s_addr != 0) {
inet_addrinfo_t saved_info;
saved_info = service_p->u.v4.info;
bzero(info_p, sizeof(*info_p));
if (IFState_service_with_ip(service_ifstate(service_p),
saved_info.addr) == NULL) {
if (G_IPConfiguration_verbose) {
my_log(LOG_NOTICE, "%s %s: removing " IP_FORMAT,
ServiceGetMethodString(service_p),
if_name(if_p), IP_LIST(&saved_info.addr));
}
ret = S_remove_ip_address(if_name(if_p), saved_info.addr);
}
if (IFStateList_service_with_ip(&S_ifstate_list,
saved_info.addr, NULL) == NULL) {
(void)host_route(RTM_DELETE, saved_info.addr);
}
flush_routes(if_link_index(if_p),
saved_info.addr, saved_info.broadcast);
}
linklocal_set_needs_attention();
return (ret);
}
PRIVATE_EXTERN interface_t *
service_interface(ServiceRef service_p)
{
return (service_p->ifstate->if_p);
}
PRIVATE_EXTERN link_status_t
service_link_status(ServiceRef service_p)
{
return (if_get_link_status(service_interface(service_p)));
}
PRIVATE_EXTERN bool
service_is_address_set(ServiceRef service_p)
{
if (ServiceIsIPv4(service_p)) {
ServiceIPv4Ref v4_p = &service_p->u.v4;
return (v4_p->info.addr.s_addr == v4_p->requested_ip.addr.s_addr);
}
return (FALSE);
}
PRIVATE_EXTERN void
service_set_requested_ip_addr(ServiceRef service_p, struct in_addr ip)
{
if (ServiceIsIPv4(service_p)) {
ServiceIPv4Ref v4_p = &service_p->u.v4;
v4_p->requested_ip.addr = ip;
}
return;
}
PRIVATE_EXTERN struct in_addr
service_requested_ip_addr(ServiceRef service_p)
{
if (ServiceIsIPv4(service_p)) {
ServiceIPv4Ref v4_p = &service_p->u.v4;
return (v4_p->requested_ip.addr);
}
return (G_ip_zeroes);
}
PRIVATE_EXTERN void
service_set_requested_ip_mask(ServiceRef service_p, struct in_addr mask)
{
if (ServiceIsIPv4(service_p)) {
ServiceIPv4Ref v4_p = &service_p->u.v4;
v4_p->requested_ip.mask = mask;
}
return;
}
PRIVATE_EXTERN struct in_addr
service_requested_ip_mask(ServiceRef service_p)
{
if (ServiceIsIPv4(service_p)) {
ServiceIPv4Ref v4_p = &service_p->u.v4;
return (v4_p->requested_ip.mask);
}
return (G_ip_zeroes);
}
PRIVATE_EXTERN boolean_t
service_router_is_hwaddr_valid(ServiceRef service_p)
{
if (ServiceIsIPv4(service_p)) {
ServiceIPv4Ref v4_p = &service_p->u.v4;
return ((v4_p->router.flags & RIFLAGS_HWADDR_VALID) != 0);
}
return (FALSE);
}
PRIVATE_EXTERN void
service_router_set_hwaddr_valid(ServiceRef service_p)
{
if (ServiceIsIPv4(service_p)) {
ServiceIPv4Ref v4_p = &service_p->u.v4;
v4_p->router.flags |= RIFLAGS_HWADDR_VALID;
}
return;
}
PRIVATE_EXTERN void
service_router_clear_hwaddr_valid(ServiceRef service_p)
{
if (ServiceIsIPv4(service_p)) {
ServiceIPv4Ref v4_p = &service_p->u.v4;
v4_p->router.flags &= ~RIFLAGS_HWADDR_VALID;
}
return;
}
PRIVATE_EXTERN boolean_t
service_router_is_iaddr_valid(ServiceRef service_p)
{
if (ServiceIsIPv4(service_p)) {
ServiceIPv4Ref v4_p = &service_p->u.v4;
return ((v4_p->router.flags & RIFLAGS_IADDR_VALID) != 0);
}
return (FALSE);
}
PRIVATE_EXTERN void
service_router_set_iaddr_valid(ServiceRef service_p)
{
if (ServiceIsIPv4(service_p)) {
ServiceIPv4Ref v4_p = &service_p->u.v4;
v4_p->router.flags |= RIFLAGS_IADDR_VALID;
}
return;
}
PRIVATE_EXTERN void
service_router_clear_iaddr_valid(ServiceRef service_p)
{
if (ServiceIsIPv4(service_p)) {
ServiceIPv4Ref v4_p = &service_p->u.v4;
v4_p->router.flags &= ~RIFLAGS_IADDR_VALID;
}
return;
}
PRIVATE_EXTERN boolean_t
service_router_is_arp_verified(ServiceRef service_p)
{
if (ServiceIsIPv4(service_p)) {
ServiceIPv4Ref v4_p = &service_p->u.v4;
return ((v4_p->router.flags & RIFLAGS_ARP_VERIFIED) != 0);
}
return (FALSE);
}
PRIVATE_EXTERN void
service_router_set_arp_verified(ServiceRef service_p)
{
if (ServiceIsIPv4(service_p)) {
ServiceIPv4Ref v4_p = &service_p->u.v4;
v4_p->router.flags |= RIFLAGS_ARP_VERIFIED;
}
return;
}
PRIVATE_EXTERN void
service_router_clear_arp_verified(ServiceRef service_p)
{
if (ServiceIsIPv4(service_p)) {
ServiceIPv4Ref v4_p = &service_p->u.v4;
v4_p->router.flags &= ~RIFLAGS_ARP_VERIFIED;
}
return;
}
PRIVATE_EXTERN void
service_router_clear(ServiceRef service_p)
{
if (ServiceIsIPv4(service_p)) {
ServiceIPv4Ref v4_p = &service_p->u.v4;
v4_p->router.flags = 0;
}
return;
}
PRIVATE_EXTERN uint8_t *
service_router_hwaddr(ServiceRef service_p)
{
if (ServiceIsIPv4(service_p)) {
ServiceIPv4Ref v4_p = &service_p->u.v4;
return (v4_p->router.hwaddr);
}
return (NULL);
}
PRIVATE_EXTERN int
service_router_hwaddr_size(ServiceRef service_p)
{
if (ServiceIsIPv4(service_p)) {
ServiceIPv4Ref v4_p = &service_p->u.v4;
return (sizeof(v4_p->router.hwaddr));
}
return (0);
}
PRIVATE_EXTERN struct in_addr
service_router_iaddr(ServiceRef service_p)
{
if (ServiceIsIPv4(service_p)) {
ServiceIPv4Ref v4_p = &service_p->u.v4;
return (v4_p->router.iaddr);
}
return (G_ip_zeroes);
}
PRIVATE_EXTERN void
service_router_set_iaddr(ServiceRef service_p, struct in_addr iaddr)
{
if (ServiceIsIPv4(service_p)) {
ServiceIPv4Ref v4_p = &service_p->u.v4;
v4_p->router.iaddr = iaddr;
}
return;
}
PRIVATE_EXTERN boolean_t
service_router_all_valid(ServiceRef service_p)
{
if (ServiceIsIPv4(service_p)) {
ServiceIPv4Ref v4_p = &service_p->u.v4;
return ((v4_p->router.flags & RIFLAGS_ALL_VALID) == RIFLAGS_ALL_VALID);
}
return (FALSE);
}
PRIVATE_EXTERN void
service_router_set_all_valid(ServiceRef service_p)
{
if (ServiceIsIPv4(service_p)) {
ServiceIPv4Ref v4_p = &service_p->u.v4;
v4_p->router.flags = RIFLAGS_ALL_VALID;
}
return;
}
PRIVATE_EXTERN boolean_t
ServiceIsIPv4(ServiceRef service_p)
{
return (ipconfig_method_is_v4(service_p->method));
}
PRIVATE_EXTERN boolean_t
ServiceIsIPv6(ServiceRef service_p)
{
return (ipconfig_method_is_v6(service_p->method));
}
PRIVATE_EXTERN boolean_t
ServiceIsNetBoot(ServiceRef service_p)
{
return (service_p->ifstate->netboot);
}
PRIVATE_EXTERN void *
ServiceGetPrivate(ServiceRef service_p)
{
return (service_p->private);
}
PRIVATE_EXTERN void
ServiceSetPrivate(ServiceRef service_p, void * private)
{
service_p->private = private;
return;
}
PRIVATE_EXTERN struct in_addr
ServiceGetActiveIPAddress(ServiceRef service_p)
{
if (ServiceIsIPv4(service_p)) {
return (service_p->u.v4.info.addr);
}
return (G_ip_zeroes);
}
PRIVATE_EXTERN struct in_addr
ServiceGetActiveSubnetMask(ServiceRef service_p)
{
if (ServiceIsIPv4(service_p)) {
return (service_p->u.v4.info.mask);
}
return (G_ip_zeroes);
}
PRIVATE_EXTERN void
ServiceSetStatus(ServiceRef service_p, ipconfig_status_t status)
{
service_p->status = status;
return;
}
PRIVATE_EXTERN void
ServiceSetRequestedIPv6Address(ServiceRef service_p,
const struct in6_addr * addr_p,
int prefix_length)
{
if (ServiceIsIPv6(service_p) == FALSE) {
return;
}
service_p->u.v6.requested_ip.addr = *addr_p;
service_p->u.v6.requested_ip.prefix_length = prefix_length;
return;
}
PRIVATE_EXTERN void
ServiceGetRequestedIPv6Address(ServiceRef service_p,
struct in6_addr * addr_p,
int * prefix_length)
{
if (ServiceIsIPv6(service_p) == FALSE) {
return;
}
*addr_p = service_p->u.v6.requested_ip.addr;
*prefix_length = service_p->u.v6.requested_ip.prefix_length;
return;
}
PRIVATE_EXTERN int
ServiceSetIPv6Address(ServiceRef service_p, const struct in6_addr * addr_p,
int prefix_length, u_int32_t valid_lifetime,
u_int32_t preferred_lifetime)
{
interface_t * if_p = service_interface(service_p);
int ret = 0;
int s;
if (ServiceIsIPv6(service_p) == FALSE) {
return (EINVAL);
}
if (G_IPConfiguration_verbose) {
char ntopbuf[INET6_ADDRSTRLEN];
my_log(LOG_NOTICE, "%s %s: setting %s/%d",
ServiceGetMethodString(service_p),
if_name(if_p),
inet_ntop(AF_INET6, addr_p, ntopbuf, sizeof(ntopbuf)),
prefix_length);
}
s = inet6_dgram_socket();
if (s < 0) {
ret = errno;
my_log(LOG_ERR,
"ServiceSetIPv6Address(%s): socket() failed, %s (%d)",
if_name(if_p), strerror(errno), errno);
}
else {
if (inet6_aifaddr(s, if_name(if_p), addr_p, NULL, prefix_length,
valid_lifetime, preferred_lifetime) < 0) {
ret = errno;
my_log(LOG_DEBUG,
"ServiceSetIPv6Address(%s): socket() failed, %s (%d)",
if_name(if_p), strerror(errno), errno);
}
close(s);
}
return (ret);
}
PRIVATE_EXTERN void
ServiceRemoveIPv6Address(ServiceRef service_p,
const struct in6_addr * addr_p, int prefix_length)
{
interface_t * if_p = service_interface(service_p);
int s;
if (ServiceIsIPv6(service_p) == FALSE) {
return;
}
if (IN6_IS_ADDR_UNSPECIFIED(addr_p)) {
return;
}
if (G_IPConfiguration_verbose) {
char ntopbuf[INET6_ADDRSTRLEN];
my_log(LOG_NOTICE,
"%s %s: removing %s/%d",
ServiceGetMethodString(service_p),
if_name(if_p),
inet_ntop(AF_INET6, addr_p, ntopbuf, sizeof(ntopbuf)),
prefix_length);
}
s = inet6_dgram_socket();
if (s < 0) {
my_log(LOG_ERR,
"ServiceRemoveIPv6Address(%s): socket() failed, %s (%d)",
if_name(if_p), strerror(errno), errno);
}
else {
inet6_difaddr(s, if_name(if_p), addr_p);
close(s);
}
return;
}
static void
set_loopback()
{
struct in_addr loopback;
struct in_addr loopback_net;
struct in_addr loopback_mask;
int s = inet_dgram_socket();
#ifndef INADDR_LOOPBACK_NET
#define INADDR_LOOPBACK_NET (u_int32_t)0x7f000000
#endif
loopback.s_addr = htonl(INADDR_LOOPBACK);
loopback_mask.s_addr = htonl(IN_CLASSA_NET);
loopback_net.s_addr = htonl(INADDR_LOOPBACK_NET);
if (s < 0) {
my_log(LOG_ERR,
"set_loopback(): socket() failed, %s (%d)",
strerror(errno), errno);
return;
}
if (inet_aifaddr(s, "lo0", loopback, &loopback_mask, NULL) < 0) {
my_log(LOG_DEBUG, "set_loopback: inet_aifaddr() failed, %s (%d)",
strerror(errno), errno);
}
close(s);
if (subnet_route_add(loopback, loopback_net, loopback_mask, "lo0")
== FALSE) {
my_log(LOG_DEBUG, "set_loopback: subnet_route_add() failed, %s (%d)",
strerror(errno), errno);
}
return;
}
void
remove_unused_ip(const char * ifname, struct in_addr ip)
{
IFStateRef ifstate;
ifstate = IFStateList_ifstate_with_name(&S_ifstate_list, ifname, NULL);
if (ifstate != NULL
&& IFState_service_with_ip(ifstate, ip) == NULL) {
if (G_IPConfiguration_verbose) {
my_log(LOG_NOTICE, "IPConfiguration %s: removing " IP_FORMAT,
ifname, IP_LIST(&ip));
}
S_remove_ip_address(if_name(ifstate->if_p), ip);
}
return;
}
extern ipconfig_status_t
ipconfig_method_info_from_plist(CFPropertyListRef plist,
ipconfig_method_t * method_p,
ipconfig_method_data_t * * method_data_p)
{
CFDictionaryRef dict;
boolean_t is_ipv4;
ipconfig_status_t status = ipconfig_status_invalid_parameter_e;
*method_p = ipconfig_method_none_e;
*method_data_p = NULL;
if (plist == NULL) {
status = ipconfig_status_success_e;
goto done;
}
if (isA_CFDictionary(plist) == NULL) {
goto done;
}
dict = CFDictionaryGetValue((CFDictionaryRef)plist, kSCEntNetIPv4);
if (dict != NULL) {
is_ipv4 = TRUE;
}
else {
dict = CFDictionaryGetValue((CFDictionaryRef)plist, kSCEntNetIPv6);
if (dict != NULL) {
is_ipv4 = FALSE;
}
else {
dict = (CFDictionaryRef)plist;
is_ipv4 = TRUE;
}
}
if (isA_CFDictionary(dict) == NULL) {
my_log(LOG_NOTICE, "IPConfiguration: invalid IPv%c entity",
is_ipv4 ? '4' : '6');
goto done;
}
if (CFDictionaryGetCount(dict) == 0) {
*method_p = (is_ipv4)
? ipconfig_method_none_v4_e
: ipconfig_method_none_v6_e;
status = ipconfig_status_success_e;
goto done;
}
if (is_ipv4) {
status = method_info_from_dict(dict, method_p, method_data_p);
}
else {
status = method_info_from_ipv6_dict(dict, method_p, method_data_p);
}
done:
return (status);
}
static FILE *
logfile_fopen(const char * path)
{
FILE * f;
f = fopen(path, "a");
if (f == NULL) {
my_log(LOG_DEBUG, "logfile_fopen fopen '%s' failed, %s",
path, strerror(errno));
return (NULL);
}
my_log(LOG_DEBUG, "IPConfiguration logfile '%s' opened", path);
return (f);
}
static boolean_t
service_get_option(ServiceRef service_p, int option_code, void * option_data,
unsigned int * option_dataCnt)
{
boolean_t ret = FALSE;
switch (service_p->method) {
case ipconfig_method_inform_e:
case ipconfig_method_dhcp_e:
case ipconfig_method_bootp_e: {
void * data;
dhcp_info_t dhcp_info;
int len;
if (service_p->ready == FALSE) {
break;
}
bzero(&dhcp_info, sizeof(dhcp_info));
(void)config_method_event(service_p, IFEventID_get_dhcp_info_e,
&dhcp_info);
if (dhcp_info.pkt_size == 0) {
break;
}
data = dhcpol_find(dhcp_info.options, option_code,
&len, NULL);
if (data) {
if (len > *option_dataCnt) {
break;
}
*option_dataCnt = len;
bcopy(data, option_data, *option_dataCnt);
ret = TRUE;
}
break;
}
default:
break;
}
return (ret);
}
ipconfig_status_t
set_verbose(int verbose)
{
my_log(LOG_NOTICE, "IPConfiguration: verbose mode %s",
verbose ? "enabled" : "disabled");
G_IPConfiguration_verbose = verbose;
if (verbose == 0) {
if (S_dhcp_packet_log != NULL) {
(void)fclose(S_dhcp_packet_log);
S_dhcp_packet_log = NULL;
}
if (S_dhcpv6_packet_log != NULL) {
(void)fclose(S_dhcpv6_packet_log);
S_dhcpv6_packet_log = NULL;
}
}
else {
if (S_dhcp_packet_log == NULL) {
S_dhcp_packet_log
= logfile_fopen(IPCONFIGURATION_BOOTP_LOG);
}
if (G_dhcpv6_enabled && S_dhcpv6_packet_log == NULL) {
S_dhcpv6_packet_log
= logfile_fopen(IPCONFIGURATION_DHCPV6_LOG);
}
}
bootp_session_set_debug(G_bootp_session, S_dhcp_packet_log);
DHCPv6SocketSetLogFile(S_dhcpv6_packet_log);
return (ipconfig_status_success_e);
}
int
get_if_count()
{
return (dynarray_count(&S_ifstate_list));
}
ipconfig_status_t
get_if_addr(const char * name, u_int32_t * addr)
{
IFStateRef ifstate;
int j;
ifstate = IFStateList_ifstate_with_name(&S_ifstate_list, name, NULL);
if (ifstate == NULL) {
return (ipconfig_status_interface_does_not_exist_e);
}
for (j = 0; j < dynarray_count(&ifstate->services); j++) {
ServiceRef service_p = dynarray_element(&ifstate->services, j);
if (service_p->u.v4.info.addr.s_addr != 0) {
*addr = service_p->u.v4.info.addr.s_addr;
return (ipconfig_status_success_e);
}
}
return (ipconfig_status_not_found_e);
}
ipconfig_status_t
get_if_option(const char * name, int option_code, void * option_data,
unsigned int * option_dataCnt)
{
int i;
boolean_t name_match;
for (i = 0, name_match = FALSE;
i < dynarray_count(&S_ifstate_list) && name_match == FALSE;
i++) {
IFStateRef ifstate = dynarray_element(&S_ifstate_list, i);
int j;
if (name[0] != '\0') {
if (strcmp(if_name(ifstate->if_p), name) != 0) {
continue;
}
name_match = TRUE;
}
for (j = 0; j < dynarray_count(&ifstate->services); j++) {
ServiceRef service_p = dynarray_element(&ifstate->services, j);
if (service_get_option(service_p, option_code, option_data,
option_dataCnt)) {
return (ipconfig_status_success_e);
}
}
}
if (name_match == FALSE) {
return (ipconfig_status_interface_does_not_exist_e);
}
return (ipconfig_status_not_found_e);
}
ipconfig_status_t
get_if_packet(const char * name, void * packet_data,
unsigned int * packet_dataCnt)
{
dhcp_info_t dhcp_info;
IFStateRef ifstate;
int j;
ifstate = IFStateList_ifstate_with_name(&S_ifstate_list, name, NULL);
if (ifstate == NULL) {
return (ipconfig_status_interface_does_not_exist_e);
}
for (j = 0; j < dynarray_count(&ifstate->services); j++) {
ServiceRef service_p = dynarray_element(&ifstate->services, j);
switch (service_p->method) {
case ipconfig_method_inform_e:
case ipconfig_method_dhcp_e:
case ipconfig_method_bootp_e:
if (service_p->ready == FALSE) {
break;
}
bzero(&dhcp_info, sizeof(dhcp_info));
(void)config_method_event(service_p, IFEventID_get_dhcp_info_e,
&dhcp_info);
if (dhcp_info.pkt_size == 0
|| dhcp_info.pkt_size > *packet_dataCnt) {
break;
}
*packet_dataCnt = dhcp_info.pkt_size;
bcopy(dhcp_info.pkt, packet_data, *packet_dataCnt);
return (ipconfig_status_success_e);
default:
break;
}
}
return (ipconfig_status_not_found_e);
}
ipconfig_status_t
get_if_v6_packet(const char * name, void * packet_data,
unsigned int * packet_dataCnt)
{
dhcpv6_info_t dhcp_info;
IFStateRef ifstate;
int j;
ifstate = IFStateList_ifstate_with_name(&S_ifstate_list, name, NULL);
if (ifstate == NULL) {
return (ipconfig_status_interface_does_not_exist_e);
}
for (j = 0; j < dynarray_count(&ifstate->services_v6); j++) {
ServiceRef service_p = dynarray_element(&ifstate->services_v6, j);
switch (service_p->method) {
case ipconfig_method_automatic_v6_e:
case ipconfig_method_rtadv_e:
if (service_p->ready == FALSE) {
break;
}
bzero(&dhcp_info, sizeof(dhcp_info));
(void)config_method_event(service_p, IFEventID_get_dhcpv6_info_e,
&dhcp_info);
if (dhcp_info.pkt_len == 0
|| dhcp_info.pkt_len > *packet_dataCnt) {
break;
}
*packet_dataCnt = dhcp_info.pkt_len;
bcopy(dhcp_info.pkt, packet_data, *packet_dataCnt);
return (ipconfig_status_success_e);
default:
break;
}
}
return (ipconfig_status_not_found_e);
}
static IPConfigFuncRef
lookup_func(ipconfig_method_t method)
{
IPConfigFuncRef func = NULL;
switch (method) {
case ipconfig_method_linklocal_e:
func = linklocal_thread;
break;
case ipconfig_method_inform_e:
func = inform_thread;
break;
case ipconfig_method_manual_e:
func = manual_thread;
break;
case ipconfig_method_dhcp_e:
func = dhcp_thread;
break;
case ipconfig_method_bootp_e:
func = bootp_thread;
break;
case ipconfig_method_failover_e:
func = failover_thread;
break;
case ipconfig_method_rtadv_e:
case ipconfig_method_automatic_v6_e:
if (S_configure_ipv6) {
if (S_ipv6_map_automatic_to_linklocal
&& method == ipconfig_method_automatic_v6_e) {
func = linklocal_v6_thread;
}
else {
func = rtadv_thread;
}
}
break;
case ipconfig_method_stf_e:
if (S_configure_ipv6) {
func = stf_thread;
}
break;
case ipconfig_method_manual_v6_e:
if (S_configure_ipv6) {
func = manual_v6_thread;
}
break;
case ipconfig_method_linklocal_v6_e:
if (S_configure_ipv6) {
func = linklocal_v6_thread;
}
break;
default:
break;
}
return (func);
}
static ipconfig_status_t
config_method_start(ServiceRef service_p, ipconfig_method_t method,
ipconfig_method_data_t * method_data)
{
IPConfigFuncRef func;
interface_t * if_p = service_interface(service_p);
int type = if_link_type(if_p);
if (method == ipconfig_method_stf_e && type != IFT_STF) {
return (ipconfig_status_invalid_operation_e);
}
switch (type) {
case IFT_STF:
if (method != ipconfig_method_stf_e) {
return (ipconfig_status_invalid_operation_e);
}
break;
case IFT_IEEE1394:
if (method == ipconfig_method_bootp_e) {
return (ipconfig_status_invalid_operation_e);
}
break;
case IFT_ETHER:
break;
case IFT_LOOP:
if (method != ipconfig_method_manual_e
&& method != ipconfig_method_manual_v6_e) {
return (ipconfig_status_invalid_operation_e);
}
break;
default:
switch (method) {
case ipconfig_method_linklocal_e:
case ipconfig_method_inform_e:
case ipconfig_method_dhcp_e:
case ipconfig_method_bootp_e:
return (ipconfig_status_invalid_operation_e);
default:
break;
}
}
func = lookup_func(method);
if (func == NULL) {
return (ipconfig_status_operation_not_supported_e);
}
return (*func)(service_p, IFEventID_start_e, method_data);
}
static ipconfig_status_t
config_method_change(ServiceRef service_p,
ipconfig_method_t method,
ipconfig_method_data_t * method_data,
boolean_t * needs_stop)
{
change_event_data_t change_event;
IPConfigFuncRef func;
ipconfig_status_t status;
*needs_stop = FALSE;
func = lookup_func(method);
if (func == NULL) {
return (ipconfig_status_operation_not_supported_e);
}
change_event.method_data = method_data;
change_event.needs_stop = FALSE;
status = (*func)(service_p, IFEventID_change_e, &change_event);
*needs_stop = change_event.needs_stop;
return (status);
}
static ipconfig_status_t
config_method_event(ServiceRef service_p, IFEventID_t event, void * data)
{
ipconfig_status_t status = ipconfig_status_success_e;
IPConfigFuncRef func;
ipconfig_method_t method = service_p->method;
func = lookup_func(method);
if (func == NULL) {
my_log(LOG_NOTICE,
"config_method_event(%s): lookup_func(%d) failed",
IFEventID_names(event), method);
status = ipconfig_status_internal_error_e;
goto done;
}
(*func)(service_p, event, data);
done:
return (status);
}
static ipconfig_status_t
config_method_stop(ServiceRef service_p)
{
return (config_method_event(service_p, IFEventID_stop_e, NULL));
}
static ipconfig_status_t
config_method_media(ServiceRef service_p, void * network_changed)
{
service_router_clear_arp_verified(service_p);
return (config_method_event(service_p, IFEventID_link_status_changed_e,
network_changed));
}
static ipconfig_status_t
config_method_bssid_changed(ServiceRef service_p)
{
service_router_clear_arp_verified(service_p);
return (config_method_event(service_p, IFEventID_bssid_changed_e,
NULL));
}
static ipconfig_status_t
config_method_renew(ServiceRef service_p)
{
service_router_clear_arp_verified(service_p);
return (config_method_event(service_p, IFEventID_renew_e, NULL));
}
static void
service_list_event(dynarray_t * services_p, IFEventID_t event, void * data)
{
int i;
for (i = 0; i < dynarray_count(services_p); i++) {
ServiceRef service_p = dynarray_element(services_p, i);
config_method_event(service_p, event, data);
}
}
static void
IFStateList_all_services_event(IFStateList_t * list,
IFEventID_t event, void * evdata)
{
int i;
int if_count = dynarray_count(list);
for (i = 0; i < if_count; i++) {
IFStateRef ifstate = dynarray_element(list, i);
service_list_event(&ifstate->services, event, evdata);
service_list_event(&ifstate->services_v6, event, evdata);
}
return;
}
ipconfig_status_t
set_if(const char * name, ipconfig_method_t method,
ipconfig_method_data_t * method_data)
{
interface_t * if_p = ifl_find_name(S_interfaces, name);
IFStateRef ifstate;
if (G_IPConfiguration_verbose) {
my_log(LOG_NOTICE, "set %s %s", name, ipconfig_method_string(method));
}
if (if_p == NULL) {
return (ipconfig_status_interface_does_not_exist_e);
}
ifstate = IFStateList_ifstate_create(&S_ifstate_list, if_p);
if (ifstate == NULL) {
return (ipconfig_status_allocation_failed_e);
}
if (method == ipconfig_method_none_e
|| method == ipconfig_method_none_v4_e
|| ipconfig_method_is_v4(method)) {
IFStateFreeIPv4Services(ifstate, TRUE);
}
else {
IFStateFreeIPv6Services(ifstate, TRUE);
}
switch (method) {
case ipconfig_method_none_e:
case ipconfig_method_none_v4_e:
case ipconfig_method_none_v6_e:
return (ipconfig_status_success_e);
default:
break;
}
return (IFState_service_add(ifstate, NULL, method, method_data,
NULL, NULL));
}
static CFStringRef
myCFUUIDStringCreate(CFAllocatorRef alloc)
{
CFUUIDRef uuid;
CFStringRef uuid_str;
uuid = CFUUIDCreate(alloc);
uuid_str = CFUUIDCreateString(alloc, uuid);
CFRelease(uuid);
return (uuid_str);
}
static ipconfig_status_t
add_or_set_service(const char * name, ipconfig_method_t method,
bool add_only,
void * method_data,
void * service_id, unsigned int * service_id_len,
CFDictionaryRef plist, pid_t pid)
{
interface_t * if_p = ifl_find_name(S_interfaces, name);
IFStateRef ifstate;
unsigned int in_length;
ServiceRef service_p;
CFStringRef serviceID;
ipconfig_status_t status;
in_length = *service_id_len;
*service_id_len = 0;
switch (method) {
case ipconfig_method_none_e:
case ipconfig_method_none_v4_e:
case ipconfig_method_none_v6_e:
return (ipconfig_status_invalid_parameter_e);
default:
break;
}
if (if_p == NULL) {
return (ipconfig_status_interface_does_not_exist_e);
}
ifstate = IFStateList_ifstate_create(&S_ifstate_list, if_p);
if (ifstate == NULL) {
return (ipconfig_status_allocation_failed_e);
}
service_p = IFStateGetServiceMatchingMethod(ifstate, method,
method_data,
FALSE);
if (service_p != NULL) {
boolean_t needs_stop = FALSE;
if (add_only) {
return (ipconfig_status_duplicate_service_e);
}
status = config_method_change(service_p, method,
method_data,
&needs_stop);
if (status == ipconfig_status_success_e
&& needs_stop == FALSE) {
return (ipconfig_status_success_e);
}
IFStateFreeService(ifstate, service_p);
}
serviceID = myCFUUIDStringCreate(NULL);
if (serviceID == NULL) {
return (ipconfig_status_allocation_failed_e);
}
if (G_IPConfiguration_verbose) {
my_log(LOG_NOTICE, "%s %s %s", add_only ? "add_service" : "set_service",
name, ipconfig_method_string(method));
}
status = IFState_service_add(ifstate, serviceID, method, method_data,
NULL, &service_p);
if (status == ipconfig_status_success_e) {
boolean_t no_publish = FALSE;
CFDictionaryRef options_dict = NULL;
pid_t monitor_pid = -1;
CFIndex len = 0;
if (plist != NULL) {
options_dict = CFDictionaryGetValue(plist,
kIPConfigurationServiceOptions);
if (isA_CFDictionary(options_dict) != NULL) {
if (S_get_plist_boolean_quiet(options_dict,
kIPConfigurationServiceOptionMonitorPID,
FALSE)) {
monitor_pid = pid;
}
no_publish
= S_get_plist_boolean_quiet(options_dict,
kIPConfigurationServiceOptionNoPublish,
FALSE);
}
}
service_p->is_dynamic = TRUE;
service_p->no_publish = no_publish;
if (monitor_pid != -1) {
ServiceMonitorPID(service_p, monitor_pid);
}
(void)CFStringGetBytes(serviceID,
CFRangeMake(0, CFStringGetLength(serviceID)),
kCFStringEncodingASCII,
0, FALSE, service_id, in_length,
&len);
*service_id_len = len;
}
CFRelease(serviceID);
return (status);
}
PRIVATE_EXTERN ipconfig_status_t
add_service(const char * name, ipconfig_method_t method,
ipconfig_method_data_t * method_data,
void * service_id, unsigned int * service_id_len,
CFDictionaryRef plist, pid_t pid)
{
return (add_or_set_service(name, method, TRUE, method_data,
service_id, service_id_len, plist, pid));
}
PRIVATE_EXTERN ipconfig_status_t
set_service(const char * name, ipconfig_method_t method,
ipconfig_method_data_t * method_data,
void * service_id, unsigned int * service_id_len)
{
return (add_or_set_service(name, method, FALSE, method_data,
service_id, service_id_len, NULL, -1));
}
STATIC ipconfig_status_t
S_remove_service(IFStateRef ifstate, ServiceRef service_p)
{
boolean_t is_ipv4;
is_ipv4 = ipconfig_method_is_v4(service_p->method);
if (service_p->is_dynamic == FALSE) {
return (ipconfig_status_invalid_operation_e);
}
if (G_IPConfiguration_verbose) {
my_log(LOG_NOTICE, "remove_service %s %s", if_name(ifstate->if_p),
ServiceGetMethodString(service_p));
}
IFStateFreeService(ifstate, service_p);
if (is_ipv4 == FALSE
&& dynarray_count(&ifstate->services_v6) == 0) {
(void)inet6_linklocal_stop(if_name(ifstate->if_p));
inet6_detach_interface(if_name(ifstate->if_p));
}
return (ipconfig_status_success_e);
}
STATIC ipconfig_status_t
S_remove_service_with_id_str(CFStringRef serviceID)
{
IFStateRef ifstate;
ServiceRef service_p;
ipconfig_status_t status;
ifstate = IFStateListGetServiceWithID(&S_ifstate_list,
serviceID, &service_p,
IS_IPV6);
if (ifstate == NULL) {
ifstate = IFStateListGetServiceWithID(&S_ifstate_list,
serviceID, &service_p,
IS_IPV4);
}
if (ifstate == NULL) {
status = ipconfig_status_no_such_service_e;
}
else {
status = S_remove_service(ifstate, service_p);
}
return (status);
}
PRIVATE_EXTERN ipconfig_status_t
remove_service_with_id(void * service_id, unsigned int service_id_len)
{
CFStringRef serviceID;
ipconfig_status_t status;
serviceID = CFStringCreateWithBytes(NULL, service_id, service_id_len,
kCFStringEncodingASCII, FALSE);
if (serviceID == NULL) {
return (ipconfig_status_allocation_failed_e);
}
status = S_remove_service_with_id_str(serviceID);
CFRelease(serviceID);
return (status);
}
PRIVATE_EXTERN ipconfig_status_t
find_service(const char * name, boolean_t exact,
ipconfig_method_t method,
ipconfig_method_data_t * method_data,
void * service_id, unsigned int * service_id_len)
{
IFStateRef ifstate;
unsigned int in_length;
CFIndex len = 0;
ServiceRef service_p;
in_length = *service_id_len;
*service_id_len = 0;
switch (method) {
case ipconfig_method_none_e:
case ipconfig_method_none_v4_e:
case ipconfig_method_none_v6_e:
return (ipconfig_status_invalid_parameter_e);
default:
break;
}
ifstate = IFStateList_ifstate_with_name(&S_ifstate_list, name, NULL);
if (ifstate == NULL) {
return (ipconfig_status_interface_does_not_exist_e);
}
if (exact) {
service_p
= IFStateGetServiceWithMethod(ifstate, method,
method_data,
FALSE);
}
else {
service_p
= IFStateGetServiceMatchingMethod(ifstate, method,
method_data,
FALSE);
}
if (service_p == NULL) {
return (ipconfig_status_no_such_service_e);
}
(void)CFStringGetBytes(service_p->serviceID,
CFRangeMake(0,
CFStringGetLength(service_p->serviceID)),
kCFStringEncodingASCII,
0, FALSE, service_id, in_length,
&len);
*service_id_len = len;
return (ipconfig_status_success_e);
}
PRIVATE_EXTERN ipconfig_status_t
remove_service(const char * name, ipconfig_method_t method,
ipconfig_method_data_t * method_data)
{
IFStateRef ifstate;
ServiceRef service_p;
switch (method) {
case ipconfig_method_none_e:
case ipconfig_method_none_v4_e:
case ipconfig_method_none_v6_e:
return (ipconfig_status_invalid_parameter_e);
default:
break;
}
ifstate = IFStateList_ifstate_with_name(&S_ifstate_list, name, NULL);
if (ifstate == NULL) {
return (ipconfig_status_interface_does_not_exist_e);
}
service_p = IFStateGetServiceWithMethod(ifstate, method, method_data,
FALSE);
if (service_p == NULL) {
return (ipconfig_status_no_such_service_e);
}
return (S_remove_service(ifstate, service_p));
}
static boolean_t
ipconfig_method_from_cfstring(CFStringRef m, ipconfig_method_t * method)
{
if (isA_CFString(m) == NULL) {
return (FALSE);
}
if (CFEqual(m, kSCValNetIPv4ConfigMethodBOOTP)) {
*method = ipconfig_method_bootp_e;
}
else if (CFEqual(m, kSCValNetIPv4ConfigMethodDHCP)) {
*method = ipconfig_method_dhcp_e;
}
else if (CFEqual(m, kSCValNetIPv4ConfigMethodManual)) {
*method = ipconfig_method_manual_e;
}
else if (CFEqual(m, kSCValNetIPv4ConfigMethodINFORM)) {
*method = ipconfig_method_inform_e;
}
else if (CFEqual(m, kSCValNetIPv4ConfigMethodLinkLocal)) {
*method = ipconfig_method_linklocal_e;
}
else if (CFEqual(m, kSCValNetIPv4ConfigMethodFailover)) {
*method = ipconfig_method_failover_e;
}
else {
return (FALSE);
}
return (TRUE);
}
static ipconfig_status_t
method_info_from_dict(CFDictionaryRef dict,
ipconfig_method_t * ret_method,
ipconfig_method_data_t * * ret_method_data)
{
ipconfig_method_t method = ipconfig_method_none_e;
CFStringRef method_cf;
ipconfig_method_data_t * method_data = NULL;
int method_data_len = 0;
boolean_t status = ipconfig_status_invalid_parameter_e;
method_cf = CFDictionaryGetValue(dict,
kSCPropNetIPv4ConfigMethod);
if (ipconfig_method_from_cfstring(method_cf, &method) == FALSE) {
my_log(LOG_ERR,
"IPConfiguration: IPv4 ConfigMethod is missing/invalid");
goto done;
}
if (ipconfig_method_is_manual(method)) {
struct in_addr address;
CFArrayRef addresses;
CFStringRef address_cf;
int count = 0;
CFArrayRef masks;
CFStringRef mask_cf = NULL;
struct in_addr mask = { 0 };
addresses = isA_CFArray(CFDictionaryGetValue(dict,
kSCPropNetIPv4Addresses));
masks = isA_CFArray(CFDictionaryGetValue(dict,
kSCPropNetIPv4SubnetMasks));
if (addresses != NULL) {
count = CFArrayGetCount(addresses);
}
if (count == 0) {
my_log(LOG_ERR,
"IPConfiguration: %s Addresses missing/invalid\n",
ipconfig_method_string(method));
goto done;
}
address_cf = CFArrayGetValueAtIndex(addresses, 0);
if (my_CFStringToIPAddress(address_cf, &address) == FALSE) {
my_log(LOG_ERR,
"IPConfiguration: %s Addresses invalid",
ipconfig_method_string(method));
goto done;
}
if (masks != NULL) {
if (count != CFArrayGetCount(masks)) {
my_log(LOG_ERR,
"IPConfiguration: "
"%s Addresses/SubnetMasks are different sizes",
ipconfig_method_string(method));
goto done;
}
mask_cf = CFArrayGetValueAtIndex(masks, 0);
if (my_CFStringToIPAddress(mask_cf, &mask) == FALSE) {
my_log(LOG_ERR,
"IPConfiguration: %s SubnetMask invalid",
ipconfig_method_string(method));
goto done;
}
}
if (count > 1) {
my_log(LOG_NOTICE,
"IPConfiguration: %s "
"multiple addresses specified - ignoring all but first",
ipconfig_method_string(method));
}
method_data_len = sizeof(ipconfig_method_data_manual_t);
method_data = (ipconfig_method_data_t *)malloc(method_data_len);
if (method_data == NULL) {
my_log(LOG_ERR, "IPConfiguration: malloc method_data failed");
status = ipconfig_status_allocation_failed_e;
goto done;
}
bzero(method_data, method_data_len);
method_data->manual.addr = address;
method_data->manual.mask = mask;
if (method == ipconfig_method_manual_e) {
CFBooleanRef b;
CFStringRef router = NULL;
b = isA_CFBoolean(CFDictionaryGetValue(dict,
kSCPropNetIgnoreLinkStatus));
method_data->manual.ignore_link_status
= (b != NULL) ? CFBooleanGetValue(b) : FALSE;
router = CFDictionaryGetValue(dict, kSCPropNetIPv4Router);
if (router != NULL
&& my_CFStringToIPAddress(router, &method_data->manual.router)
== FALSE) {
my_log(LOG_NOTICE,
"IPConfiguration: %s Router invalid",
ipconfig_method_string(method));
}
}
else if (method == ipconfig_method_failover_e) {
CFNumberRef num;
num = CFDictionaryGetValue(dict,
kSCPropNetIPv4FailoverAddressTimeout);
if (num != NULL
&& (isA_CFNumber(num) == NULL
|| (CFNumberGetValue(num, kCFNumberSInt32Type,
&method_data->manual.failover_timeout)
== FALSE))) {
my_log(LOG_NOTICE,
"IPConfiguration: FailoverAddressTimeout invalid");
}
}
}
else if (method == ipconfig_method_dhcp_e) {
char cid[255];
int cid_len = 0;
CFStringRef client_id = NULL;
client_id = CFDictionaryGetValue(dict, kSCPropNetIPv4DHCPClientID);
if (isA_CFString(client_id) != NULL) {
cid_len = my_CFStringToCStringAndLength(client_id, cid, sizeof(cid));
}
if (cid_len != 0) {
cid_len--;
method_data_len = offsetof(ipconfig_method_data_dhcp_t, client_id)
+ cid_len;
}
if (method_data_len > 0) {
method_data = (ipconfig_method_data_t *)malloc(method_data_len);
if (method_data == NULL) {
my_log(LOG_ERR, "IPConfiguration: malloc DHCPClientID failed");
status = ipconfig_status_allocation_failed_e;
goto done;
}
method_data->dhcp.client_id_len = cid_len;
bcopy(cid, method_data->dhcp.client_id, cid_len);
}
}
status = ipconfig_status_success_e;
done:
*ret_method_data = method_data;
*ret_method = method;
return (status);
}
static boolean_t
ipconfig_method_from_cfstring_ipv6(CFStringRef m, ipconfig_method_t * method)
{
if (isA_CFString(m) == NULL) {
return (FALSE);
}
if (CFEqual(m, kSCValNetIPv6ConfigMethodManual)) {
*method = ipconfig_method_manual_v6_e;
}
else if (CFEqual(m, kSCValNetIPv6ConfigMethodAutomatic)) {
*method = ipconfig_method_automatic_v6_e;
}
else if (CFEqual(m, kSCValNetIPv6ConfigMethodRouterAdvertisement)) {
*method = ipconfig_method_rtadv_e;
}
else if (CFEqual(m, kSCValNetIPv6ConfigMethod6to4)) {
*method = ipconfig_method_stf_e;
}
else if (CFEqual(m, kSCValNetIPv6ConfigMethodLinkLocal)) {
*method = ipconfig_method_linklocal_v6_e;
}
else {
return (FALSE);
}
return (TRUE);
}
static bool
my_CFStringToIPv6Address(CFStringRef str, struct in6_addr * ret_ip)
{
char buf[64];
if (isA_CFString(str) == NULL) {
return (FALSE);
}
if (CFStringGetCString(str, buf, sizeof(buf), kCFStringEncodingASCII)
== FALSE) {
return (FALSE);
}
if (inet_pton(AF_INET6, buf, ret_ip) == 1) {
return (TRUE);
}
return (FALSE);
}
static ipconfig_status_t
method_info_from_ipv6_dict(CFDictionaryRef dict,
ipconfig_method_t * ret_method,
ipconfig_method_data_t * * ret_method_data)
{
ipconfig_method_t method = ipconfig_method_none_v6_e;
CFStringRef method_cf;
ipconfig_method_data_t * method_data = NULL;
int method_data_len = 0;
boolean_t status = ipconfig_status_invalid_parameter_e;
method_cf = CFDictionaryGetValue(dict,
kSCPropNetIPv6ConfigMethod);
if (ipconfig_method_from_cfstring_ipv6(method_cf, &method) == FALSE) {
my_log(LOG_ERR,
"IPConfiguration: IPv6 ConfigMethod is missing/invalid");
goto done;
}
if (method == ipconfig_method_manual_v6_e) {
struct in6_addr address;
CFArrayRef addresses;
CFStringRef address_cf;
int count = 0;
CFArrayRef prefixes;
CFNumberRef prefix_cf = NULL;
int prefix = 0;
addresses
= isA_CFArray(CFDictionaryGetValue(dict, kSCPropNetIPv6Addresses));
prefixes
= isA_CFArray(CFDictionaryGetValue(dict,
kSCPropNetIPv6PrefixLength));
if (addresses != NULL) {
count = CFArrayGetCount(addresses);
}
if (count == 0) {
my_log(LOG_ERR,
"IPConfiguration: %s Addresses missing/invalid\n",
ipconfig_method_string(method));
goto done;
}
address_cf = CFArrayGetValueAtIndex(addresses, 0);
if (my_CFStringToIPv6Address(address_cf, &address) == FALSE) {
my_log(LOG_ERR,
"IPConfiguration: %s Addresses invalid",
ipconfig_method_string(method));
goto done;
}
if (IN6_IS_ADDR_LINKLOCAL(&address)) {
my_log(LOG_ERR,
"IPConfiguration: %s cannot configure IPv6 Link Local address",
ipconfig_method_string(method));
goto done;
}
if (prefixes != NULL) {
if (count != CFArrayGetCount(prefixes)) {
my_log(LOG_ERR,
"IPConfiguration: "
"%s Addresses/PrefixLength are different sizes",
ipconfig_method_string(method));
goto done;
}
prefix_cf = CFArrayGetValueAtIndex(prefixes, 0);
if (isA_CFNumber(prefix_cf) == NULL
|| (CFNumberGetValue(prefix_cf, kCFNumberIntType, &prefix)
== FALSE)) {
my_log(LOG_ERR, "IPConfiguration: %s PrefixLength invalid",
ipconfig_method_string(method));
goto done;
}
}
if (count > 1) {
my_log(LOG_NOTICE,
"IPConfiguration: %s "
"multiple addresses specified - ignoring all but first",
ipconfig_method_string(method));
}
method_data_len = sizeof(ipconfig_method_data_manual_v6_t);
method_data = (ipconfig_method_data_t *)malloc(method_data_len);
if (method_data == NULL) {
my_log(LOG_ERR, "IPConfiguration: malloc method_data failed");
status = ipconfig_status_allocation_failed_e;
goto done;
}
bzero(method_data, method_data_len);
method_data->manual_v6.addr = address;
method_data->manual_v6.prefix_length = prefix;
}
else if (method == ipconfig_method_stf_e) {
CFStringRef relay_cf;
relay_cf = CFDictionaryGetValue(dict, kSCPropNetIPv66to4Relay);
if (relay_cf != NULL) {
char buf[256];
int len;
address_type_t relay_addr_type;
struct in_addr relay_ip;
struct in6_addr relay_ipv6;
if (isA_CFString(relay_cf) == NULL) {
my_log(LOG_ERR, "IPConfiguration: %s 6to4 Relay invalid",
ipconfig_method_string(method));
goto done;
}
len = my_CFStringToCStringAndLength(relay_cf, buf, sizeof(buf));
if (len == 0) {
my_log(LOG_ERR, "IPConfiguration: %s 6to4 Relay empty",
ipconfig_method_string(method));
goto done;
}
if (inet_aton(buf, &relay_ip) == 1) {
relay_addr_type = address_type_ipv4_e;
method_data_len = sizeof(ipconfig_method_data_stf_t);
}
else if (inet_pton(AF_INET6, buf, &relay_ipv6) == 1) {
relay_addr_type = address_type_ipv6_e;
method_data_len = sizeof(ipconfig_method_data_stf_t);
}
else {
relay_addr_type = address_type_dns_e;
method_data_len = offsetof(ipconfig_method_data_stf_t,
relay_addr.dns) + len + 1;
}
method_data = (ipconfig_method_data_t *)malloc(method_data_len);
if (method_data == NULL) {
my_log(LOG_ERR, "IPConfiguration: malloc method_data failed");
status = ipconfig_status_allocation_failed_e;
goto done;
}
method_data->stf.relay_addr_type = relay_addr_type;
switch (relay_addr_type) {
case address_type_ipv4_e:
method_data->stf.relay_addr.v4 = relay_ip;
break;
case address_type_ipv6_e:
method_data->stf.relay_addr.v6 = relay_ipv6;
break;
case address_type_dns_e:
default:
bcopy(buf, method_data->stf.relay_addr.dns, len);
method_data->stf.relay_addr.dns[len] = '\0';
break;
}
}
}
status = ipconfig_status_success_e;
done:
*ret_method_data = method_data;
*ret_method = method;
return (status);
}
static CFArrayRef
get_order_array_from_values(CFDictionaryRef values, CFStringRef order_key)
{
CFDictionaryRef dict;
CFArrayRef order_array = NULL;
dict = isA_CFDictionary(CFDictionaryGetValue(values, order_key));
if (dict) {
order_array = CFDictionaryGetValue(dict,
kSCPropNetServiceOrder);
order_array = isA_CFArray(order_array);
if (order_array && CFArrayGetCount(order_array) == 0) {
order_array = NULL;
}
}
return (order_array);
}
#define ARBITRARILY_LARGE_NUMBER (1000 * 1000)
static int
lookup_order(CFArrayRef order, CFStringRef serviceID)
{
int count;
int i;
if (order == NULL)
goto done;
count = CFArrayGetCount(order);
for (i = 0; i < count; i++) {
CFStringRef sid = CFArrayGetValueAtIndex(order, i);
if (CFEqual(sid, serviceID))
return (i);
}
done:
return (ARBITRARILY_LARGE_NUMBER);
}
static CFComparisonResult
compare_serviceIDs(const void *val1, const void *val2, void *context)
{
CFArrayRef order_array = (CFArrayRef)context;
int rank1;
int rank2;
rank1 = lookup_order(order_array, (CFStringRef)val1);
rank2 = lookup_order(order_array, (CFStringRef)val2);
if (rank1 == rank2)
return (kCFCompareEqualTo);
if (rank1 < rank2)
return (kCFCompareLessThan);
return (kCFCompareGreaterThan);
}
static CFDictionaryRef
copy_ipv4_service_dict(CFDictionaryRef values, CFStringRef serviceID,
CFStringRef type, CFStringRef ifn_cf)
{
CFDictionaryRef dict;
CFStringRef key;
CFMutableDictionaryRef service_dict;
if (CFEqual(type, kSCValNetInterfaceTypeEthernet) == FALSE
&& CFEqual(type, kSCValNetInterfaceTypeFireWire) == FALSE
&& CFEqual(type, kSCValNetInterfaceTypeLoopback) == FALSE) {
return (NULL);
}
key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
serviceID,
kSCEntNetIPv4);
dict = CFDictionaryGetValue(values, key);
CFRelease(key);
if (isA_CFDictionary(dict) == NULL) {
return (NULL);
}
service_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
CFDictionarySetValue(service_dict, kSCPropNetInterfaceDeviceName,
ifn_cf);
CFDictionarySetValue(service_dict, PROP_SERVICEID, serviceID);
return (service_dict);
}
static CFDictionaryRef
copy_ipv6_service_dict(CFDictionaryRef values, CFStringRef serviceID,
CFStringRef type, CFStringRef ifn_cf)
{
CFDictionaryRef dict;
CFStringRef key;
CFMutableDictionaryRef service_dict;
if (CFEqual(type, kSCValNetInterfaceTypeEthernet) == FALSE
&& CFEqual(type, kSCValNetInterfaceTypeFireWire) == FALSE
&& CFEqual(type, kSCValNetInterfaceType6to4) == FALSE
&& CFEqual(type, kSCValNetInterfaceTypeLoopback) == FALSE) {
return (NULL);
}
key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
serviceID,
kSCEntNetIPv6);
dict = CFDictionaryGetValue(values, key);
CFRelease(key);
if (isA_CFDictionary(dict) == NULL) {
return (NULL);
}
service_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
CFDictionarySetValue(service_dict, kSCPropNetInterfaceDeviceName,
ifn_cf);
CFDictionarySetValue(service_dict, PROP_SERVICEID, serviceID);
key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
serviceID,
kSCEntNet6to4);
dict = CFDictionaryGetValue(values, key);
CFRelease(key);
if (isA_CFDictionary(dict) != NULL) {
CFStringRef stf_relay;
stf_relay = CFDictionaryGetValue(dict, kSCPropNet6to4Relay);
if (stf_relay != NULL) {
CFDictionarySetValue(service_dict,
kSCPropNetIPv66to4Relay,
stf_relay);
}
}
return (service_dict);
}
static CFArrayRef
copy_serviceIDs_from_values(CFDictionaryRef values, CFArrayRef order_array)
{
int count;
int i;
const void * * keys;
CFMutableArrayRef list = NULL;
int list_count;
count = CFDictionaryGetCount(values);
if (count == 0) {
return (NULL);
}
list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
keys = (const void * *)malloc(sizeof(*keys) * count);
CFDictionaryGetKeysAndValues(values, (const void * *)keys, NULL);
for (i = 0; i < count; i++) {
CFStringRef serviceID;
if (CFStringHasPrefix(keys[i], S_setup_service_prefix) == FALSE) {
continue;
}
serviceID = my_CFStringCopyComponent(keys[i], CFSTR("/"), 3);
if (serviceID == NULL) {
continue;
}
my_CFArrayAppendUniqueValue(list, serviceID);
CFRelease(serviceID);
}
free(keys);
list_count = CFArrayGetCount(list);
if (list_count == 0) {
my_CFRelease(&list);
}
else if (order_array != NULL) {
CFArraySortValues(list, CFRangeMake(0, list_count),
compare_serviceIDs, (void *)order_array);
}
return (list);
}
static CFArrayRef
entity_all(SCDynamicStoreRef session, CFArrayRef * ret_ipv6_services)
{
CFMutableArrayRef all_services = NULL;
CFMutableArrayRef all_v6_services = NULL;
CFMutableArrayRef get_keys = NULL;
CFMutableArrayRef get_patterns = NULL;
int i;
CFStringRef key = NULL;
CFArrayRef service_IDs = NULL;
int service_IDs_count;
CFStringRef order_key = NULL;
CFArrayRef order_array = NULL;
CFDictionaryRef values = NULL;
get_keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
get_patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
all_services = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
all_v6_services = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCCompAnyRegex,
kSCEntNetIPv4);
CFArrayAppendValue(get_patterns, key);
CFRelease(key);
key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCCompAnyRegex,
kSCEntNetIPv6);
CFArrayAppendValue(get_patterns, key);
CFRelease(key);
key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCCompAnyRegex,
kSCEntNet6to4);
CFArrayAppendValue(get_patterns, key);
CFRelease(key);
key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCCompAnyRegex,
kSCEntNetInterface);
CFArrayAppendValue(get_patterns, key);
CFRelease(key);
order_key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCEntNetIPv4);
CFArrayAppendValue(get_keys, order_key);
values = SCDynamicStoreCopyMultiple(session, get_keys, get_patterns);
if (values == NULL) {
goto done;
}
order_array = get_order_array_from_values(values, order_key);
service_IDs = copy_serviceIDs_from_values(values, order_array);
if (service_IDs == NULL) {
goto done;
}
service_IDs_count = CFArrayGetCount(service_IDs);
for (i = 0; i < service_IDs_count; i++) {
CFStringRef key;
CFDictionaryRef if_dict;
CFStringRef ifname;
CFDictionaryRef service_dict = NULL;
CFStringRef serviceID;
CFStringRef type;
serviceID = CFArrayGetValueAtIndex(service_IDs, i);
key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
serviceID,
kSCEntNetInterface);
if_dict = CFDictionaryGetValue(values, key);
CFRelease(key);
if_dict = isA_CFDictionary(if_dict);
if (if_dict == NULL) {
continue;
}
type = CFDictionaryGetValue(if_dict, kSCPropNetInterfaceType);
if (isA_CFString(type) == NULL) {
SCLog(TRUE, LOG_NOTICE,
CFSTR("IPConfiguration: Interface Type missing/invalid"
"\nInterface = %@"), if_dict);
continue;
}
ifname = CFDictionaryGetValue(if_dict, kSCPropNetInterfaceDeviceName);
if (isA_CFString(ifname) == NULL) {
continue;
}
service_dict = copy_ipv4_service_dict(values, serviceID,
type, ifname);
if (service_dict != NULL) {
CFArrayAppendValue(all_services, service_dict);
CFRelease(service_dict);
}
service_dict = copy_ipv6_service_dict(values, serviceID,
type, ifname);
if (service_dict != NULL) {
CFArrayAppendValue(all_v6_services, service_dict);
CFRelease(service_dict);
}
}
done:
my_CFRelease(&values);
my_CFRelease(&order_key);
my_CFRelease(&get_keys);
my_CFRelease(&get_patterns);
my_CFRelease(&service_IDs);
if (all_services != NULL && CFArrayGetCount(all_services) == 0) {
my_CFRelease(&all_services);
}
if (all_v6_services != NULL && CFArrayGetCount(all_v6_services) == 0) {
my_CFRelease(&all_v6_services);
}
*ret_ipv6_services = all_v6_services;
return (all_services);
}
static CFDictionaryRef
lookup_entity(CFArrayRef all, CFStringRef ifn_cf)
{
int count;
int i;
if (all == NULL)
return (NULL);
count = CFArrayGetCount(all);
for (i = 0; i < count; i++) {
CFDictionaryRef item = CFArrayGetValueAtIndex(all, i);
CFStringRef name;
name = CFDictionaryGetValue(item, kSCPropNetInterfaceDeviceName);
if (CFEqual(name, ifn_cf)) {
return (item);
}
}
return (NULL);
}
static CFArrayRef
interface_services_copy(CFArrayRef all, CFStringRef ifn_cf)
{
int count;
int i;
CFMutableArrayRef list = NULL;
if (all == NULL) {
return (NULL);
}
list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (list == NULL) {
return (NULL);
}
count = CFArrayGetCount(all);
for (i = 0; i < count; i++) {
CFDictionaryRef item = CFArrayGetValueAtIndex(all, i);
CFStringRef name;
name = CFDictionaryGetValue(item, kSCPropNetInterfaceDeviceName);
if (CFEqual(name, ifn_cf)) {
CFArrayAppendValue(list, item);
}
}
if (CFArrayGetCount(list) == 0) {
my_CFRelease(&list);
}
return (list);
}
typedef struct {
CFStringRef serviceID;
ipconfig_method_t method;
ipconfig_method_data_t * method_data;
} ServiceConfig, * ServiceConfigRef;
typedef struct {
ServiceConfigRef list;
int count;
boolean_t is_ipv4;
} ServiceConfigList, * ServiceConfigListRef;
static void
ServiceConfigListFree(ServiceConfigListRef scl)
{
int i;
ServiceConfigRef scan;
if (scl->list == NULL) {
return;
}
for (i = 0, scan = scl->list; i < scl->count; i++, scan++) {
my_CFRelease(&scan->serviceID);
if (scan->method_data != NULL) {
free(scan->method_data);
}
}
free(scl->list);
scl->list = NULL;
return;
}
#ifdef DEBUG
void
ServiceConfigListPrint(ServiceConfigListRef scl)
{
int i;
ServiceConfigRef scan;
my_log(LOG_NOTICE,
"%d %s configs\n", scl->count, scl->is_ipv4 ? "IPv4" : "IPv6");
for (i = 0, scan = scl->list; i < scl->count; i++, scan++) {
SCLog(TRUE, LOG_NOTICE, CFSTR("serviceID %@ %s Data %p"),
scan->serviceID,
ipconfig_method_string(scan->method),
scan->method_data);
if (scan->method_data != NULL
&& scan->method == ipconfig_method_stf_e) {
char ntopbuf[INET6_ADDRSTRLEN];
ipconfig_method_data_stf_t * stf;
stf = &scan->method_data->stf;
switch (stf->relay_addr_type) {
case address_type_ipv4_e:
my_log(LOG_NOTICE, "IPv4 relay " IP_FORMAT,
IP_LIST(&stf->relay_addr.v4));
break;
case address_type_ipv6_e:
my_log(LOG_NOTICE, "IPv6 relay %s",
inet_ntop(AF_INET6, &stf->relay_addr.v6,
ntopbuf, sizeof(ntopbuf)));
break;
case address_type_dns_e:
my_log(LOG_NOTICE, "DNS relay %s", stf->relay_addr.dns);
break;
default:
my_log(LOG_NOTICE, "Bogus relay type %d", stf->relay_addr_type);
break;
}
}
}
}
#endif
static ServiceConfigRef
ServiceConfigListLookupMethod(ServiceConfigListRef scl,
ipconfig_method_t method,
ipconfig_method_data_t * method_data)
{
int i;
ServiceConfigRef scan;
switch (method) {
case ipconfig_method_stf_e:
case ipconfig_method_linklocal_e:
for (i = 0, scan = scl->list; i < scl->count; i++, scan++) {
if (method == scan->method) {
return (scan);
}
}
break;
case ipconfig_method_rtadv_e:
case ipconfig_method_automatic_v6_e:
for (i = 0, scan = scl->list; i < scl->count; i++, scan++) {
switch (scan->method) {
case ipconfig_method_rtadv_e:
case ipconfig_method_automatic_v6_e:
return (scan);
default:
break;
}
}
break;
case ipconfig_method_dhcp_e:
case ipconfig_method_bootp_e:
for (i = 0, scan = scl->list; i < scl->count; i++, scan++) {
if (ipconfig_method_is_dhcp_or_bootp(scan->method))
return (scan);
}
break;
case ipconfig_method_failover_e:
case ipconfig_method_manual_e:
case ipconfig_method_inform_e:
for (i = 0, scan = scl->list; i < scl->count; i++, scan++) {
if (ipconfig_method_is_manual(scan->method)
&& (method_data->manual.addr.s_addr
== scan->method_data->manual.addr.s_addr)) {
return (scan);
}
}
break;
case ipconfig_method_manual_v6_e:
for (i = 0, scan = scl->list; i < scl->count; i++, scan++) {
if (scan->method != ipconfig_method_manual_v6_e) {
continue;
}
if (IN6_ARE_ADDR_EQUAL(&method_data->manual_v6.addr,
&scan->method_data->manual_v6.addr)) {
return (scan);
}
}
break;
default:
break;
}
return (NULL);
}
static ServiceConfigRef
ServiceConfigListLookupService(ServiceConfigListRef scl, CFStringRef serviceID)
{
int i;
ServiceConfigRef scan;
for (i = 0, scan = scl->list; i < scl->count; i++, scan++) {
if (CFEqual(serviceID, scan->serviceID)) {
return (scan);
}
}
return (NULL);
}
static ServiceRef
find_dynamic_service(const char * ifname, ipconfig_method_t method,
ipconfig_method_data_t * method_data)
{
interface_t * if_p = ifl_find_name(S_interfaces, ifname);
IFStateRef ifstate = NULL;
if (if_p == NULL) {
return (NULL);
}
ifstate = IFStateList_ifstate_with_name(&S_ifstate_list, ifname, NULL);
if (ifstate == NULL) {
return (NULL);
}
return (IFStateGetServiceMatchingMethod(ifstate, method,
method_data, TRUE));
}
static boolean_t
ServiceConfigListInit(ServiceConfigListRef scl, boolean_t is_ipv4,
CFArrayRef all_services, const char * ifname)
{
int i;
CFArrayRef if_service_list;
int if_service_count;
CFStringRef ifn_cf = NULL;
boolean_t ret;
bzero(scl, sizeof(*scl));
scl->is_ipv4 = is_ipv4;
ifn_cf = CFStringCreateWithCString(NULL, ifname,
kCFStringEncodingASCII);
if_service_list = interface_services_copy(all_services, ifn_cf);
if (if_service_list == NULL) {
goto done;
}
if_service_count = CFArrayGetCount(if_service_list);
scl->list = (ServiceConfigRef)malloc(if_service_count * sizeof(*scl->list));
if (scl->list == NULL) {
goto done;
}
for (i = 0; i < if_service_count; i++) {
boolean_t duplicate_config = FALSE;
boolean_t duplicate_dynamic = FALSE;
ipconfig_method_t method;
ipconfig_method_data_t *method_data = NULL;
CFDictionaryRef service_dict;
CFStringRef serviceID;
service_dict = CFArrayGetValueAtIndex(if_service_list, i);
if (is_ipv4) {
if (method_info_from_dict(service_dict, &method, &method_data)
!= ipconfig_status_success_e) {
continue;
}
}
else {
if (method_info_from_ipv6_dict(service_dict, &method, &method_data)
!= ipconfig_status_success_e) {
continue;
}
}
duplicate_config
= (ServiceConfigListLookupMethod(scl, method, method_data) != NULL);
if (duplicate_config == FALSE) {
duplicate_dynamic = (find_dynamic_service(ifname, method,
method_data) != NULL);
}
if (duplicate_config || duplicate_dynamic) {
my_log(LOG_NOTICE, "%s: %s %s",
ifname, ipconfig_method_string(method),
duplicate_config
? "duplicate configured service"
: "configured service conflicts with dynamic service");
free(method_data);
continue;
}
serviceID = CFDictionaryGetValue(service_dict, PROP_SERVICEID);
scl->list[scl->count].serviceID = CFRetain(serviceID);
scl->list[scl->count].method = method;
scl->list[scl->count].method_data = method_data;
scl->count++;
}
done:
if (scl->count == 0) {
ServiceConfigListFree(scl);
ret = FALSE;
}
else {
ret = TRUE;
}
my_CFRelease(&ifn_cf);
my_CFRelease(&if_service_list);
return (ret);
}
static void
ServiceConfigListFreeInactiveServices(ServiceConfigListRef scl,
const char * ifname)
{
CFMutableArrayRef inactive_list = NULL;
int inactive_list_count;
int i;
IFStateRef ifstate;
dynarray_t * list;
ifstate = IFStateList_ifstate_with_name(&S_ifstate_list, ifname, NULL);
if (ifstate == NULL) {
return;
}
if (scl->is_ipv4) {
list = &ifstate->services;
}
else {
list = &ifstate->services_v6;
}
inactive_list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (inactive_list == NULL) {
return;
}
for (i = 0; i < dynarray_count(list); i++) {
ServiceRef service_p = dynarray_element(list, i);
CFStringRef serviceID = service_p->serviceID;
if (service_p->is_dynamic) {
continue;
}
if (service_p->parent_serviceID != NULL) {
continue;
}
if (ServiceConfigListLookupService(scl, serviceID) == NULL) {
CFArrayAppendValue(inactive_list, serviceID);
}
}
inactive_list_count = CFArrayGetCount(inactive_list);
for (i = 0; i < inactive_list_count; i++) {
CFStringRef serviceID = CFArrayGetValueAtIndex(inactive_list, i);
IFStateFreeServiceWithID(ifstate, serviceID, scl->is_ipv4);
}
my_CFRelease(&inactive_list);
return;
}
static ipconfig_status_t
S_set_service(IFStateRef ifstate, ServiceConfigRef config, boolean_t is_ipv4)
{
CFStringRef serviceID = config->serviceID;
ServiceRef service_p;
IFStateRef this_ifstate = NULL;
service_p = IFStateGetServiceWithID(ifstate, serviceID, is_ipv4);
if (service_p != NULL) {
boolean_t needs_stop = FALSE;
ipconfig_status_t status;
if (service_p->method == config->method) {
status = config_method_change(service_p, config->method,
config->method_data,
&needs_stop);
if (status == ipconfig_status_success_e
&& needs_stop == FALSE) {
return (ipconfig_status_success_e);
}
}
IFStateFreeService(ifstate, service_p);
}
else {
this_ifstate = IFStateListGetServiceWithID(&S_ifstate_list,
serviceID,
&service_p,
is_ipv4);
if (this_ifstate) {
IFStateFreeService(this_ifstate, service_p);
}
}
return (IFState_service_add(ifstate, serviceID, config->method,
config->method_data,
NULL, NULL));
}
static void
interface_configuration_changed(interface_t * if_p, CFArrayRef all,
boolean_t is_ipv4)
{
IFStateRef ifstate;
ServiceConfigList scl;
if (ServiceConfigListInit(&scl, is_ipv4, all, if_name(if_p)) == FALSE) {
ifstate = IFStateList_ifstate_with_name(&S_ifstate_list,
if_name(if_p), NULL);
if (ifstate != NULL) {
if (is_ipv4) {
IFStateFreeIPv4Services(ifstate, FALSE);
}
else {
IFStateFreeIPv6Services(ifstate, FALSE);
}
}
return;
}
ServiceConfigListFreeInactiveServices(&scl, if_name(if_p));
ifstate = IFStateList_ifstate_create(&S_ifstate_list, if_p);
if (ifstate != NULL) {
int i;
ServiceConfigRef config;
for (i = 0, config = scl.list; i < scl.count; i++, config++) {
(void)S_set_service(ifstate, config, scl.is_ipv4);
}
}
ServiceConfigListFree(&scl);
return;
}
static void
handle_configuration_changed(SCDynamicStoreRef session,
CFArrayRef all_ipv4, CFArrayRef all_ipv6)
{
int i;
for (i = 0; i < ifl_count(S_interfaces); i++) {
interface_t * if_p = ifl_at_index(S_interfaces, i);
interface_configuration_changed(if_p, all_ipv4, IS_IPV4);
interface_configuration_changed(if_p, all_ipv6, IS_IPV6);
}
return;
}
static void
configuration_changed(SCDynamicStoreRef session)
{
CFArrayRef all_ipv4 = NULL;
CFArrayRef all_ipv6 = NULL;
all_ipv4 = entity_all(session, &all_ipv6);
handle_configuration_changed(session, all_ipv4, all_ipv6);
my_CFRelease(&all_ipv4);
my_CFRelease(&all_ipv6);
return;
}
static void
configure_from_cache(SCDynamicStoreRef session)
{
CFArrayRef all_ipv4 = NULL;
CFArrayRef all_ipv6 = NULL;
int count = 0;
int i;
all_ipv4 = entity_all(session, &all_ipv6);
if (all_ipv4 == NULL) {
goto done;
}
for (i = 0; i < ifl_count(S_interfaces); i++) {
CFDictionaryRef dict;
interface_t * if_p = ifl_at_index(S_interfaces, i);
CFStringRef ifn_cf = NULL;
ifn_cf = CFStringCreateWithCString(NULL,
if_name(if_p),
kCFStringEncodingASCII);
if (ifn_cf == NULL) {
continue;
}
dict = lookup_entity(all_ipv4, ifn_cf);
if (dict != NULL) {
(void)IFStateList_ifstate_create(&S_ifstate_list, if_p);
count++;
}
CFRelease(ifn_cf);
}
done:
if (count == 0) {
unblock_startup(session);
}
else {
handle_configuration_changed(session, all_ipv4, all_ipv6);
}
my_CFRelease(&all_ipv4);
my_CFRelease(&all_ipv6);
return;
}
static void
notifier_init(SCDynamicStoreRef session)
{
CFMutableArrayRef keys = NULL;
CFStringRef key;
CFMutableStringRef pattern;
CFMutableArrayRef patterns = NULL;
CFRunLoopSourceRef rls;
if (session == NULL) {
return;
}
keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCCompAnyRegex,
kSCEntNetIPv4);
CFArrayAppendValue(patterns, key);
CFRelease(key);
key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCCompAnyRegex,
kSCEntNetIPv6);
CFArrayAppendValue(patterns, key);
CFRelease(key);
key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCCompAnyRegex,
kSCEntNet6to4);
CFArrayAppendValue(patterns, key);
CFRelease(key);
key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
kSCDynamicStoreDomainState,
kSCCompAnyRegex,
kSCEntNetIPv6);
CFArrayAppendValue(patterns, key);
CFRelease(key);
key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCCompAnyRegex,
kSCEntNetInterface);
CFArrayAppendValue(patterns, key);
CFRelease(key);
key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
kSCDynamicStoreDomainState,
kSCCompAnyRegex,
kSCEntNetLink);
CFArrayAppendValue(patterns, key);
CFRelease(key);
key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
kSCDynamicStoreDomainState,
kSCCompAnyRegex,
kSCEntNetAirPort);
CFArrayAppendValue(patterns, key);
CFRelease(key);
key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
kSCDynamicStoreDomainState,
kSCCompAnyRegex,
kSCEntNetRefreshConfiguration);
CFArrayAppendValue(patterns, key);
CFRelease(key);
key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
kSCDynamicStoreDomainState,
kSCCompAnyRegex,
kSCEntNetIPv4ARPCollision);
pattern = CFStringCreateMutableCopy(NULL, 0, key);
CFStringAppend(pattern, CFSTR(".*"));
CFRelease(key);
CFArrayAppendValue(patterns, pattern);
CFRelease(pattern);
key = SCDynamicStoreKeyCreateNetworkInterface(NULL,
kSCDynamicStoreDomainState);
CFArrayAppendValue(keys, key);
CFRelease(key);
key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCEntNetIPv4);
CFArrayAppendValue(keys, key);
CFRelease(key);
S_computer_name_key = SCDynamicStoreKeyCreateComputerName(NULL);
CFArrayAppendValue(keys, S_computer_name_key);
S_hostnames_key = SCDynamicStoreKeyCreateHostNames(NULL);
CFArrayAppendValue(keys, S_hostnames_key);
SCDynamicStoreSetNotificationKeys(session, keys, patterns);
CFRelease(keys);
CFRelease(patterns);
rls = SCDynamicStoreCreateRunLoopSource(NULL, session, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
CFRelease(rls);
computer_name_update(session);
return;
}
static boolean_t
update_interface_list()
{
interface_list_t * new_interfaces = NULL;
new_interfaces = ifl_init();
if (new_interfaces == NULL) {
my_log(LOG_ERR, "IPConfiguration: ifl_init failed");
return (FALSE);
}
if (S_interfaces) {
ifl_free(&S_interfaces);
}
S_interfaces = new_interfaces;
return (TRUE);
}
interface_list_t *
get_interface_list(void)
{
if (S_interfaces == NULL) {
S_interfaces = ifl_init();
}
return (S_interfaces);
}
static void
check_for_detached_interfaces()
{
int count = dynarray_count(&S_ifstate_list);
const char * * names = NULL;
int names_count = 0;
int i;
if (count == 0) {
return;
}
names = (const char * *)malloc(sizeof(char *) * count);
if (names == NULL) {
return;
}
for (i = 0; i < count; i++) {
IFStateRef ifstate = dynarray_element(&S_ifstate_list, i);
if (ifl_find_name(S_interfaces, if_name(ifstate->if_p)) == NULL) {
names[names_count++] = if_name(ifstate->if_p);
}
}
for (i = 0; i < names_count; i++) {
IFStateList_ifstate_free(&S_ifstate_list, names[i]);
}
free(names);
return;
}
static void
runloop_observer(CFRunLoopObserverRef observer,
CFRunLoopActivity activity, void *info)
{
if (S_scd_session == NULL) {
return;
}
if (S_linklocal_needs_attention) {
CFArrayRef service_order = NULL;
S_linklocal_needs_attention = FALSE;
service_order = S_get_service_order(S_scd_session);
if (G_IPConfiguration_verbose) {
my_log(LOG_NOTICE, "runloop_observer: calling S_linklocal_elect");
}
S_linklocal_elect(service_order);
my_CFRelease(&service_order);
}
my_SCDynamicStorePublish(S_scd_session);
return;
}
#define IO_PATH_PM_ROOT_DOMAIN kIOPowerPlane ":/IOPowerConnection/IOPMrootDomain"
STATIC boolean_t
woke_from_hibernation(void)
{
CFDataRef hib_prop;
uint32_t hibernate_state;
hibernate_state = kIOHibernateStateInactive;
hib_prop = myIORegistryEntryCopyProperty(IO_PATH_PM_ROOT_DOMAIN,
CFSTR(kIOHibernateStateKey));
if (isA_CFData(hib_prop) != NULL
&& CFDataGetLength(hib_prop) == sizeof(hibernate_state)) {
hibernate_state = *((uint32_t *)(void *)CFDataGetBytePtr(hib_prop));
}
my_CFRelease(&hib_prop);
return (hibernate_state == kIOHibernateStateWakingFromHibernate);
}
STATIC void
S_deliver_wake_event(void)
{
WakeFlags flags = 0;
wake_data_t event_data;
int i;
int if_count = dynarray_count(&S_ifstate_list);
if (woke_from_hibernation()) {
flags |= kWakeFlagsFromHibernation;
}
for (i = 0; i < if_count; i++) {
IFStateRef ifstate = dynarray_element(&S_ifstate_list, i);
event_data.flags = flags;
if (if_is_wireless(ifstate->if_p)) {
CFStringRef ssid;
struct ether_addr bssid;
ssid = S_copy_ssid_bssid(ifstate->ifname, &bssid);
if (ssid != NULL
&& ifstate->ssid != NULL) {
if (!CFEqual(ssid, ifstate->ssid)) {
event_data.flags |= kWakeFlagsSSIDChanged;
}
else if (bcmp(&bssid, &ifstate->bssid, sizeof(bssid))) {
event_data.flags |= kWakeFlagsBSSIDChanged;
}
IFState_set_ssid_bssid(ifstate, ssid, &bssid);
}
my_CFRelease(&ssid);
}
service_list_event(&ifstate->services, IFEventID_wake_e,
(void *)&event_data);
service_list_event(&ifstate->services_v6, IFEventID_wake_e,
(void *)&event_data);
}
return;
}
static void
power_changed(void * refcon, io_service_t service, natural_t msg_type,
void * msg)
{
boolean_t ack_msg = TRUE;
switch (msg_type) {
case kIOMessageSystemWillPowerOff:
case kIOMessageSystemWillRestart:
break;
case kIOMessageSystemWillNotSleep:
case kIOMessageSystemWillNotPowerOff:
ack_msg = FALSE;
break;
case kIOMessageSystemWillSleep:
my_log(LOG_DEBUG, "IPConfiguration: Sleep");
IFStateList_all_services_event(&S_ifstate_list,
IFEventID_sleep_e, NULL);
break;
case kIOMessageSystemWillPowerOn:
my_log(LOG_DEBUG, "IPConfiguration: Wake");
S_deliver_wake_event();
break;
case kIOMessageSystemHasPoweredOn:
ack_msg = FALSE;
break;
default:
break;
}
if (ack_msg) {
IOAllowPowerChange(S_power_connection, (long)msg);
}
return;
}
static io_connect_t
power_notification_init()
{
io_object_t obj;
CFRunLoopSourceRef rls;
IONotificationPortRef port;
io_connect_t power_connection;
power_connection = IORegisterForSystemPower(NULL, &port,
power_changed, &obj);
if (power_connection != 0) {
rls = IONotificationPortGetRunLoopSource(port);
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
}
return (power_connection);
}
#if ! TARGET_OS_EMBEDDED
#define DISK_AND_NET (kIOPMSystemPowerStateCapabilityDisk \
| kIOPMSystemPowerStateCapabilityNetwork)
void
new_power_changed(void * param,
IOPMConnection connection,
IOPMConnectionMessageToken token,
IOPMSystemPowerStateCapabilities capabilities)
{
IOReturn ret;
if ((capabilities & kIOPMSystemPowerStateCapabilityCPU) != 0) {
if ((capabilities & DISK_AND_NET) != DISK_AND_NET) {
}
else if (S_awake == FALSE) {
S_awake = TRUE;
my_log(LOG_DEBUG, "IPConfiguration: Wake");
S_deliver_wake_event();
}
}
else {
S_awake = FALSE;
my_log(LOG_DEBUG, "IPConfiguration: Sleep");
IFStateList_all_services_event(&S_ifstate_list,
IFEventID_sleep_e, NULL);
}
ret = IOPMConnectionAcknowledgeEvent(connection, token);
if (ret != kIOReturnSuccess) {
my_log(LOG_NOTICE, "IPConfiguration: "
"IOPMConnectionAcknowledgeEvent failed, 0x%08x", ret);
}
return;
}
static void
new_power_notification_init(void)
{
IOPMConnection connection = NULL;
IOReturn ret;
ret = IOPMConnectionCreate(CFSTR("IPConfiguration"),
DISK_AND_NET,
&connection);
if (ret != kIOReturnSuccess) {
my_log(LOG_ERR,
"IPConfiguration: IOPMConnectionCreate failed, 0x%08x", ret);
goto failed;
}
ret = IOPMConnectionSetNotification(connection, NULL,
new_power_changed);
if (ret != kIOReturnSuccess) {
my_log(LOG_ERR, "IPConfiguration:"
"IOPMConnectionSetNotification failed, 0x%08x", ret);
goto failed;
}
ret = IOPMConnectionScheduleWithRunLoop(connection,
CFRunLoopGetCurrent(),
kCFRunLoopDefaultMode);
if (ret != kIOReturnSuccess) {
my_log(LOG_ERR, "IPConfiguration:"
"IOPMConnectionScheduleWithRunloop failed, 0x%08x", ret);
goto failed;
}
return;
failed:
if (connection != NULL) {
IOPMConnectionRelease(connection);
}
return;
}
#endif
static boolean_t
start_initialization(SCDynamicStoreRef session)
{
CFPropertyListRef value = NULL;
S_observer = CFRunLoopObserverCreate(NULL,
kCFRunLoopAllActivities,
TRUE, 0, runloop_observer, NULL);
if (S_observer != NULL) {
CFRunLoopAddObserver(CFRunLoopGetCurrent(), S_observer,
kCFRunLoopDefaultMode);
}
else {
my_log(LOG_NOTICE,
"start_initialization: CFRunLoopObserverCreate failed!");
}
S_setup_service_prefix = SCDynamicStoreKeyCreate(NULL,
CFSTR("%@/%@/%@/"),
kSCDynamicStoreDomainSetup,
kSCCompNetwork,
kSCCompService);
S_state_interface_prefix = SCDynamicStoreKeyCreate(NULL,
CFSTR("%@/%@/%@/"),
kSCDynamicStoreDomainState,
kSCCompNetwork,
kSCCompInterface);
value = SCDynamicStoreCopyValue(session, kSCDynamicStoreDomainSetup);
if (value == NULL) {
my_log(LOG_NOTICE,
"IPConfiguration needs PreferencesMonitor to run first");
}
my_CFRelease(&value);
notifier_init(session);
(void)update_interface_list();
(void)S_netboot_init();
configure_from_cache(session);
#if ! TARGET_OS_EMBEDDED
if (S_use_maintenance_wake) {
new_power_notification_init();
}
else {
S_power_connection = power_notification_init();
}
#else
S_power_connection = power_notification_init();
#endif
return (TRUE);
}
static void
link_refresh(SCDynamicStoreRef session, CFStringRef cache_key)
{
CFStringRef ifn_cf = NULL;
char ifn[IFNAMSIZ + 1];
IFStateRef ifstate;
int j;
if (CFStringHasPrefix(cache_key, S_state_interface_prefix) == FALSE) {
return;
}
ifn_cf = my_CFStringCopyComponent(cache_key, CFSTR("/"), 3);
if (ifn_cf == NULL) {
return;
}
my_CFStringToCStringAndLength(ifn_cf, ifn, sizeof(ifn));
ifstate = IFStateList_ifstate_with_name(&S_ifstate_list, ifn, NULL);
if (ifstate == NULL || ifstate->netboot) {
goto done;
}
for (j = 0; j < dynarray_count(&ifstate->services); j++) {
ServiceRef service_p = dynarray_element(&ifstate->services, j);
config_method_renew(service_p);
}
service_list_event(&ifstate->services_v6, IFEventID_renew_e, NULL);
done:
my_CFRelease(&ifn_cf);
return;
}
static void
ipv6_interface_address_changed(SCDynamicStoreRef session, CFStringRef cache_key)
{
inet6_addrlist_t addr_list;
CFStringRef ifn_cf;
char ifn[IFNAMSIZ + 1];
IFStateRef ifstate;
if (CFStringHasPrefix(cache_key, S_state_interface_prefix) == FALSE) {
return;
}
ifn_cf = my_CFStringCopyComponent(cache_key, CFSTR("/"), 3);
if (ifn_cf == NULL) {
return;
}
my_CFStringToCStringAndLength(ifn_cf, ifn, sizeof(ifn));
ifstate = IFStateList_ifstate_with_name(&S_ifstate_list, ifn, NULL);
if (ifstate == NULL) {
goto done;
}
inet6_addrlist_copy(&addr_list, if_link_index(ifstate->if_p));
service_list_event(&ifstate->services_v6,
IFEventID_ipv6_address_changed_e, &addr_list);
inet6_addrlist_free(&addr_list);
done:
my_CFRelease(&ifn_cf);
return;
}
#include "my_darwin.h"
#ifndef NO_WIRELESS
#include <Apple80211/Apple80211API.h>
#include <Kernel/IOKit/apple80211/apple80211_ioctl.h>
static CFStringRef
S_copy_ssid_bssid(CFStringRef ifname, struct ether_addr * ap_mac)
{
Apple80211Err error;
CFMutableDataRef ssid;
CFStringRef ssid_str = NULL;
Apple80211Ref wref;
error = Apple80211Open(&wref);
if (error != kA11NoErr) {
my_log(LOG_DEBUG, "Apple80211Open failed, 0x%x");
return (NULL);
}
error = Apple80211BindToInterface(wref, ifname);
if (error != kA11NoErr) {
goto done;
}
ssid = CFDataCreateMutable(kCFAllocatorDefault, 0);
if (Apple80211Get((Apple80211Ref)wref, APPLE80211_IOC_SSID, 0,
ssid, 0) == kA11NoErr) {
ssid_str = CFStringCreateWithBytes(NULL,
CFDataGetBytePtr(ssid),
CFDataGetLength(ssid),
kCFStringEncodingUTF8,
FALSE);
if (ssid_str == NULL) {
ssid_str = CFStringCreateWithBytes(NULL,
CFDataGetBytePtr(ssid),
CFDataGetLength(ssid),
kCFStringEncodingMacRoman,
FALSE);
}
}
CFRelease(ssid);
if (ap_mac != NULL) {
(void)Apple80211Get((Apple80211Ref)wref, APPLE80211_IOC_BSSID, 0,
ap_mac, sizeof(*ap_mac));
}
done:
Apple80211Close(wref);
return (ssid_str);
}
#else
static CFStringRef
S_copy_ssid_bssid(CFStringRef ifname, struct ether_addr * ap_mac)
{
return (NULL);
}
#endif
static void
link_timer_expired(void * arg0, void * arg1, void * arg2)
{
IFStateRef ifstate = (IFStateRef)arg0;
if (G_IPConfiguration_verbose) {
my_log(LOG_NOTICE, "%s: link inactive timer fired",
if_name(ifstate->if_p));
}
service_list_event(&ifstate->services,
IFEventID_link_timer_expired_e, NULL);
service_list_event(&ifstate->services_v6,
IFEventID_link_timer_expired_e, NULL);
if (dynarray_count(&ifstate->services_v6) != 0) {
(void)inet6_linklocal_stop(if_name(ifstate->if_p));
}
return;
}
static void
ap_key_changed(SCDynamicStoreRef session, CFStringRef cache_key)
{
interface_t * if_p = NULL;
CFStringRef ifn_cf = NULL;
char ifn[IFNAMSIZ + 1];
IFStateRef ifstate;
link_status_t link;
uint8_t network_changed = NETWORK_CHANGED_NONE;
uint32_t j;
struct ether_addr bssid;
CFStringRef ssid;
if (CFStringHasPrefix(cache_key, S_state_interface_prefix) == FALSE) {
return;
}
ifn_cf = my_CFStringCopyComponent(cache_key, CFSTR("/"), 3);
if (ifn_cf == NULL) {
return;
}
my_CFStringToCStringAndLength(ifn_cf, ifn, sizeof(ifn));
ifstate = IFStateList_ifstate_with_name(&S_ifstate_list, ifn, NULL);
if (ifstate == NULL || ifstate->netboot) {
goto done;
}
if_p = ifl_find_name(S_interfaces, ifn);
if (if_p == NULL) {
goto done;
}
link = if_link_status_update(if_p);
if ((link.valid && link.active == FALSE)
|| (ifstate->if_p->link_status.valid
&& (ifstate->if_p->link_status.active == FALSE))) {
goto done;
}
if (if_is_wireless(ifstate->if_p) == FALSE) {
goto done;
}
ssid = S_copy_ssid_bssid(ifstate->ifname, &bssid);
if (G_IPConfiguration_verbose) {
if (ssid != NULL) {
SCLog(TRUE, LOG_NOTICE, CFSTR("%s: SSID %@ BSSID %s"),
ifn, ssid, ether_ntoa(&bssid));
}
else {
my_log(LOG_NOTICE, "%s: no SSID", ifn);
}
}
if (ssid != NULL) {
network_changed = IFState_set_ssid_bssid(ifstate, ssid, &bssid);
if (G_IPConfiguration_verbose) {
my_log(LOG_NOTICE, "%s network changed flag 0x%x.",
ifn, network_changed);
}
CFRelease(ssid);
}
if (network_changed == NETWORK_CHANGED_NONE
|| network_changed & NETWORK_CHANGED_SSID) {
goto done;
}
for (j = 0; j < dynarray_count(&ifstate->services); j++) {
ServiceRef service_p = dynarray_element(&ifstate->services, j);
config_method_bssid_changed(service_p);
}
service_list_event(&ifstate->services_v6, IFEventID_bssid_changed_e,
NULL);
done:
my_CFRelease(&ifn_cf);
return;
}
static void
link_key_changed(SCDynamicStoreRef session, CFStringRef cache_key)
{
CFDictionaryRef dict = NULL;
interface_t * if_p = NULL;
CFStringRef ifn_cf = NULL;
char ifn[IFNAMSIZ + 1];
IFStateRef ifstate;
int j;
link_status_t link;
void * network_changed = NULL;
if (CFStringHasPrefix(cache_key, S_state_interface_prefix) == FALSE) {
return;
}
ifn_cf = my_CFStringCopyComponent(cache_key, CFSTR("/"), 3);
if (ifn_cf == NULL) {
return;
}
my_CFStringToCStringAndLength(ifn_cf, ifn, sizeof(ifn));
ifstate = IFStateList_ifstate_with_name(&S_ifstate_list, ifn, NULL);
dict = my_SCDynamicStoreCopyDictionary(session, cache_key);
if (dict != NULL) {
if (CFDictionaryContainsKey(dict, kSCPropNetLinkDetaching)) {
if (ifstate != NULL) {
IFStateFreeIPv4Services(ifstate, TRUE);
IFStateFreeIPv6Services(ifstate, TRUE);
}
goto done;
}
}
if_p = ifl_find_name(S_interfaces, ifn);
if (if_p == NULL) {
goto done;
}
link = if_link_status_update(if_p);
if (link.valid) {
if_link_update(if_p);
}
if (ifstate == NULL || ifstate->netboot) {
goto done;
}
if (if_p != NULL) {
if_link_copy(ifstate->if_p, if_p);
}
if (G_IPConfiguration_verbose) {
if (link.valid == FALSE) {
my_log(LOG_NOTICE, "%s link is unknown", ifn);
}
else {
my_log(LOG_NOTICE, "%s link %s",
ifn, link.active ? "ACTIVE" : "INACTIVE");
}
}
if (if_is_wireless(ifstate->if_p)) {
struct ether_addr bssid;
CFStringRef ssid;
ssid = S_copy_ssid_bssid(ifstate->ifname, &bssid);
if (G_IPConfiguration_verbose) {
if (ssid != NULL) {
SCLog(TRUE, LOG_NOTICE, CFSTR("%s: SSID %@ BSSID %s"),
ifn, ssid, ether_ntoa(&bssid));
}
else {
my_log(LOG_NOTICE, "%s: no SSID", ifn);
}
}
if (ssid != NULL) {
if (ifstate->ssid != NULL
&& !CFEqual(ssid, ifstate->ssid)) {
network_changed = (void *)1;
}
IFState_set_ssid_bssid(ifstate, ssid, &bssid);
CFRelease(ssid);
}
}
if (link.valid && link.active == FALSE) {
struct timeval tv;
if (G_IPConfiguration_verbose) {
my_log(LOG_NOTICE,
"%s: scheduling link inactive timer for %d second(s)",
ifn, S_link_inactive_secs);
}
tv.tv_sec = S_link_inactive_secs;
tv.tv_usec = 0;
timer_set_relative(ifstate->timer, tv, link_timer_expired,
ifstate, NULL, NULL);
}
else {
timer_cancel(ifstate->timer);
}
for (j = 0; j < dynarray_count(&ifstate->services); j++) {
ServiceRef service_p = dynarray_element(&ifstate->services, j);
config_method_media(service_p, network_changed);
}
if ((link.valid == FALSE || link.active)
&& dynarray_count(&ifstate->services_v6) != 0
&& if_ift_type(ifstate->if_p) != IFT_STF) {
(void)inet6_linklocal_start(if_name(if_p));
}
service_list_event(&ifstate->services_v6, IFEventID_link_status_changed_e,
(void *)network_changed);
done:
my_CFRelease(&dict);
my_CFRelease(&ifn_cf);
return;
}
static void
arp_collision(SCDynamicStoreRef session, CFStringRef cache_key)
{
arp_collision_data_t evdata;
void * hwaddr = NULL;
int hwlen;
CFStringRef ifn_cf = NULL;
char ifn[IFNAMSIZ + 1];
struct in_addr ip_addr;
IFStateRef ifstate;
ifn_cf = IPv4ARPCollisionKeyParse(cache_key, &ip_addr, &hwaddr, &hwlen);
if (ifn_cf == NULL || hwaddr == NULL) {
goto done;
}
my_CFStringToCStringAndLength(ifn_cf, ifn, sizeof(ifn));
ifstate = IFStateList_ifstate_with_name(&S_ifstate_list, ifn, NULL);
if (ifstate == NULL || ifstate->netboot) {
goto done;
}
if (S_is_our_hardware_address(NULL, if_link_arptype(ifstate->if_p),
hwaddr, hwlen)) {
goto done;
}
evdata.ip_addr = ip_addr;
evdata.hwaddr = hwaddr;
evdata.hwlen = hwlen;
service_list_event(&ifstate->services, IFEventID_arp_collision_e, &evdata);
done:
if (hwaddr != NULL) {
free(hwaddr);
}
my_CFRelease(&ifn_cf);
return;
}
static void
dhcp_preferences_changed(SCPreferencesRef prefs,
SCPreferencesNotification type,
void * info)
{
int i;
if ((type & kSCPreferencesNotificationApply) == 0) {
return;
}
S_add_dhcp_parameters(prefs);
SCPreferencesSynchronize(prefs);
for (i = 0; i < dynarray_count(&S_ifstate_list); i++) {
IFStateRef ifstate = dynarray_element(&S_ifstate_list, i);
int j;
for (j = 0; j < dynarray_count(&ifstate->services); j++) {
ServiceRef service_p = dynarray_element(&ifstate->services, j);
config_method_renew(service_p);
}
}
return;
}
static void
handle_change(SCDynamicStoreRef session, CFArrayRef changes, void * arg)
{
boolean_t config_changed = FALSE;
CFIndex count;
CFIndex i;
boolean_t iflist_changed = FALSE;
boolean_t name_changed = FALSE;
boolean_t order_changed = FALSE;
CFStringRef setup_global_ipv4_key = NULL;
CFMutableArrayRef state_changes = NULL;
count = CFArrayGetCount(changes);
if (count == 0) {
goto done;
}
if (G_IPConfiguration_verbose) {
SCLog(TRUE, LOG_NOTICE,
CFSTR("Changes: %@ (%d)"), changes, count);
}
for (i = 0; i < count; i++) {
CFStringRef cache_key = CFArrayGetValueAtIndex(changes, i);
if (CFEqual(cache_key, S_computer_name_key)
|| CFEqual(cache_key, S_hostnames_key)) {
name_changed = TRUE;
}
else if (CFStringHasPrefix(cache_key, kSCDynamicStoreDomainSetup)) {
if (setup_global_ipv4_key == NULL) {
setup_global_ipv4_key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCEntNetIPv4);
}
if (CFEqual(cache_key, setup_global_ipv4_key)) {
order_changed = TRUE;
}
config_changed = TRUE;
}
else if (CFStringHasSuffix(cache_key, kSCCompInterface)) {
iflist_changed = TRUE;
}
else {
if (state_changes == NULL) {
state_changes
= CFArrayCreateMutable(NULL, count, &kCFTypeArrayCallBacks);
}
CFArrayAppendValue(state_changes, cache_key);
}
}
if (name_changed) {
computer_name_update(session);
}
if (iflist_changed) {
if (update_interface_list()) {
config_changed = TRUE;
check_for_detached_interfaces();
}
}
if (config_changed) {
configuration_changed(session);
}
if (state_changes != NULL) {
count = CFArrayGetCount(state_changes);
}
else {
count = 0;
}
for (i = 0; i < count; i++) {
CFStringRef cache_key = CFArrayGetValueAtIndex(state_changes, i);
if (CFStringHasSuffix(cache_key, kSCEntNetLink)) {
link_key_changed(session, cache_key);
}
else if (CFStringHasSuffix(cache_key, kSCEntNetAirPort)) {
ap_key_changed(session, cache_key);
}
else if (CFStringHasSuffix(cache_key, kSCEntNetRefreshConfiguration)) {
link_refresh(session, cache_key);
}
else if (CFStringHasSuffix(cache_key, kSCEntNetIPv6)) {
ipv6_interface_address_changed(session, cache_key);
}
else {
CFRange range = CFRangeMake(0, CFStringGetLength(cache_key));
if (CFStringFindWithOptions(cache_key, kSCEntNetIPv4ARPCollision,
range, 0, NULL)) {
arp_collision(session, cache_key);
}
}
}
if (order_changed) {
linklocal_set_needs_attention();
}
done:
my_CFRelease(&setup_global_ipv4_key);
my_CFRelease(&state_changes);
return;
}
#if ! TARGET_OS_EMBEDDED
static void
user_confirm(CFUserNotificationRef userNotification,
CFOptionFlags response_flags)
{
int i;
for (i = 0; i < dynarray_count(&S_ifstate_list); i++) {
IFStateRef ifstate = dynarray_element(&S_ifstate_list, i);
int j;
for (j = 0; j < dynarray_count(&ifstate->services); j++) {
ServiceRef service_p = dynarray_element(&ifstate->services, j);
if (service_p->user_notification == userNotification) {
CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
service_p->user_rls,
kCFRunLoopDefaultMode);
my_CFRelease(&service_p->user_rls);
my_CFRelease(&service_p->user_notification);
return;
}
}
}
return;
}
static CFURLRef
copy_icon_url(CFStringRef icon)
{
CFBundleRef np_bundle;
CFURLRef np_url;
CFURLRef url = NULL;
#define kNetworkPrefPanePath "/System/Library/PreferencePanes/Network.prefPane"
np_url = CFURLCreateWithFileSystemPath(NULL,
CFSTR(kNetworkPrefPanePath),
kCFURLPOSIXPathStyle, FALSE);
if (np_url != NULL) {
np_bundle = CFBundleCreate(NULL, np_url);
if (np_bundle != NULL) {
url = CFBundleCopyResourceURL(np_bundle, icon,
CFSTR("icns"), NULL);
CFRelease(np_bundle);
}
CFRelease(np_url);
}
return (url);
}
void
ServiceRemoveAddressConflict(ServiceRef service_p)
{
if (service_p->user_rls != NULL) {
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), service_p->user_rls,
kCFRunLoopDefaultMode);
my_CFRelease(&service_p->user_rls);
}
if (service_p->user_notification != NULL) {
CFUserNotificationCancel(service_p->user_notification);
my_CFRelease(&service_p->user_notification);
}
return;
}
static void
service_notify_user(ServiceRef service_p, CFArrayRef header,
CFStringRef message)
{
CFMutableDictionaryRef dict;
SInt32 error;
CFURLRef icon_url;
CFUserNotificationRef notify;
CFRunLoopSourceRef rls;
CFURLRef url;
dict = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(dict, kCFUserNotificationAlertHeaderKey,
header);
CFDictionarySetValue(dict, kCFUserNotificationAlertMessageKey,
message);
url = CFBundleCopyBundleURL(S_bundle);
CFDictionarySetValue(dict, kCFUserNotificationLocalizationURLKey,
url);
CFRelease(url);
icon_url = copy_icon_url(CFSTR("Network"));
if (icon_url != NULL) {
CFDictionarySetValue(dict, kCFUserNotificationIconURLKey,
icon_url);
CFRelease(icon_url);
}
ServiceRemoveAddressConflict(service_p);
CFDictionaryAddValue(dict,
kCFUserNotificationHelpAnchorKey,
CFSTR("mh27606"));
CFDictionaryAddValue(dict,
kCFUserNotificationHelpBookKey,
CFSTR("com.apple.machelp"));
CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey,
CFSTR("OK"));
notify = CFUserNotificationCreate(NULL, 0, 0, &error, dict);
CFRelease(dict);
if (notify == NULL) {
my_log(LOG_ERR, "CFUserNotificationCreate() failed, %d",
error);
return;
}
rls = CFUserNotificationCreateRunLoopSource(NULL, notify,
user_confirm, 0);
if (rls == NULL) {
my_log(LOG_ERR, "CFUserNotificationCreateRunLoopSource() failed");
my_CFRelease(¬ify);
}
else {
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls,
kCFRunLoopDefaultMode);
service_p->user_rls = rls;
service_p->user_notification = notify;
}
return;
}
static void
service_report_conflict(ServiceRef service_p, CFStringRef ip_str)
{
CFArrayRef array = NULL;
const void * values[3];
values[0] = CFSTR("CONFLICT_HEADER_BEFORE_IP");
values[1] = ip_str;
values[2] = CFSTR("CONFLICT_HEADER_AFTER_IP");
array = CFArrayCreate(NULL, (const void **)values, 3,
&kCFTypeArrayCallBacks);
service_notify_user(service_p, array, CFSTR("CONFLICT_MESSAGE"));
CFRelease(array);
return;
}
void
ServiceReportIPv4AddressConflict(ServiceRef service_p, struct in_addr addr)
{
CFStringRef ip_str = NULL;
ip_str = my_CFStringCreateWithIPAddress(addr);
service_report_conflict(service_p, ip_str);
CFRelease(ip_str);
return;
}
void
ServiceReportIPv6AddressConflict(ServiceRef service_p,
const struct in6_addr * addr_p)
{
CFStringRef ip_str = NULL;
ip_str = my_CFStringCreateWithIPv6Address(addr_p);
service_report_conflict(service_p, ip_str);
CFRelease(ip_str);
return;
}
void
ServiceRemoveIPv6AddressConflict(service_p)
{
}
#endif
static boolean_t
S_get_plist_boolean_quiet(CFDictionaryRef plist, CFStringRef key,
boolean_t def)
{
CFBooleanRef b;
boolean_t ret = def;
b = isA_CFBoolean(CFDictionaryGetValue(plist, key));
if (b) {
ret = CFBooleanGetValue(b);
}
return (ret);
}
static boolean_t
S_get_plist_boolean(CFDictionaryRef plist, CFStringRef key,
boolean_t def)
{
boolean_t ret;
ret = S_get_plist_boolean_quiet(plist, key, def);
if (G_IPConfiguration_verbose) {
SCLog(TRUE, LOG_NOTICE,
CFSTR("%@ = %s"), key, ret == TRUE ? "true" : "false");
}
return (ret);
}
static int
S_get_plist_int_quiet(CFDictionaryRef plist, CFStringRef key,
int def)
{
CFNumberRef n;
int ret = def;
n = isA_CFNumber(CFDictionaryGetValue(plist, key));
if (n) {
if (CFNumberGetValue(n, kCFNumberIntType, &ret) == FALSE) {
ret = def;
}
}
return (ret);
}
static int
S_get_plist_int(CFDictionaryRef plist, CFStringRef key,
int def)
{
int ret;
ret = S_get_plist_int_quiet(plist, key, def);
if (G_IPConfiguration_verbose) {
SCLog(TRUE, LOG_NOTICE, CFSTR("%@ = %d"), key, ret);
}
return (ret);
}
#include <math.h>
static struct timeval
S_get_plist_timeval(CFDictionaryRef plist, CFStringRef key,
struct timeval def)
{
CFNumberRef n;
struct timeval ret = def;
n = CFDictionaryGetValue(plist, key);
if (n) {
double f;
if (CFNumberGetValue(n, kCFNumberDoubleType, &f) == TRUE) {
ret.tv_sec = (int)floor(f);
ret.tv_usec = (int)((f - floor(f)) * 1000000.0);
}
}
if (G_IPConfiguration_verbose) {
SCLog(TRUE, LOG_NOTICE,
CFSTR("%@ = %d.%06d"), key, ret.tv_sec,
ret.tv_usec);
}
return (ret);
}
static void *
S_get_number_array(CFArrayRef arr, int num_size, int * ret_count)
{
void * buf = NULL;
int count;
int i;
int real_count = 0;
void * scan;
switch (num_size) {
case 1:
case 2:
case 4:
break;
default:
goto done;
}
count = CFArrayGetCount(arr);
if (count == 0) {
goto done;
}
buf = malloc(count * num_size);
if (buf == NULL) {
goto done;
}
for (i = 0, real_count = 0, scan = buf; i < count; i++) {
CFNumberRef n = isA_CFNumber(CFArrayGetValueAtIndex(arr, i));
int val;
if (n == NULL
|| CFNumberGetValue(n, kCFNumberIntType, &val) == FALSE) {
continue;
}
switch (num_size) {
case 1:
*((uint8_t *)scan) = val;
break;
case 2:
*((uint16_t *)scan) = val;
break;
case 4:
*((uint32_t *)scan) = val;
break;
default:
break;
}
real_count++;
scan += num_size;
}
done:
*ret_count = real_count;
if (real_count == 0 && buf != NULL) {
free(buf);
buf = NULL;
}
return (buf);
}
static void *
S_get_plist_number_array(CFDictionaryRef plist, CFStringRef key,
int num_size, int * ret_count)
{
CFArrayRef a;
a = isA_CFArray(CFDictionaryGetValue(plist, key));
if (a == NULL) {
return (NULL);
}
return (S_get_number_array(a, num_size, ret_count));
}
static uint8_t *
S_get_plist_uint8_array(CFDictionaryRef plist, CFStringRef key,
int * ret_count)
{
return (S_get_plist_number_array(plist, key, sizeof(uint8_t), ret_count));
}
static uint16_t *
S_get_plist_uint16_array(CFDictionaryRef plist, CFStringRef key,
int * ret_count)
{
return (S_get_plist_number_array(plist, key, sizeof(uint16_t), ret_count));
}
static void
my_CFNumberAddUniqueToArray(CFNumberRef number, CFMutableArrayRef merge)
{
number = isA_CFNumber(number);
if (number == NULL) {
return;
}
my_CFArrayAppendUniqueValue(merge, number);
}
static void
merge_arrays(const void *key, const void *value, void *context)
{
CFArrayRef arr;
CFDictionaryRef dict;
CFMutableArrayRef merge = (CFMutableArrayRef)context;
dict = isA_CFDictionary(value);
if (dict == NULL) {
return;
}
arr = CFDictionaryGetValue(dict,
kDHCPRequestedParameterList);
if (isA_CFArray(arr) == NULL) {
return;
}
CFArrayApplyFunction(arr, CFRangeMake(0, CFArrayGetCount(arr)),
(CFArrayApplierFunction)my_CFNumberAddUniqueToArray,
merge);
return;
}
static CFArrayRef
applicationRequestedParametersCopy(SCPreferencesRef prefs)
{
CFPropertyListRef data = NULL;
CFMutableArrayRef array = NULL;
data = SCPreferencesGetValue(prefs, kDHCPClientApplicationPref);
if (isA_CFDictionary(data) == NULL) {
goto done;
}
if (G_IPConfiguration_verbose) {
SCLog(TRUE, LOG_NOTICE,
CFSTR("dictionary is %@"), data);
}
array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (array == NULL) {
goto done;
}
CFDictionaryApplyFunction(data, merge_arrays, array);
if (CFArrayGetCount(array) == 0) {
CFRelease(array);
array = NULL;
}
done:
return (array);
}
static CFArrayRef
S_copy_plist_array_and_range(CFDictionaryRef plist, CFStringRef key,
CFRange * range_p)
{
CFArrayRef val;
val = isA_CFArray(CFDictionaryGetValue(plist, key));
if (val != NULL) {
CFRetain(val);
if (G_IPConfiguration_verbose) {
SCLog(TRUE, LOG_NOTICE, CFSTR("%@ = %@"), key, val);
}
range_p->location = 0;
range_p->length = CFArrayGetCount(val);
}
return (val);
}
static void
S_set_globals(void)
{
uint8_t * dhcp_params = NULL;
CFDictionaryRef info_dict;
int n_dhcp_params = 0;
CFDictionaryRef plist;
if (S_bundle == NULL) {
return;
}
info_dict = CFBundleGetInfoDictionary(S_bundle);
if (info_dict == NULL) {
return;
}
plist = CFDictionaryGetValue(info_dict, CFSTR("IPConfiguration"));
if (isA_CFDictionary(plist) == NULL) {
return;
}
G_IPConfiguration_verbose
= S_get_plist_boolean(plist, CFSTR("Verbose"), FALSE);
G_must_broadcast
= S_get_plist_boolean(plist, CFSTR("MustBroadcast"), FALSE);
G_max_retries = S_get_plist_int(plist, CFSTR("RetryCount"),
MAX_RETRIES);
G_gather_secs = S_get_plist_int(plist, CFSTR("GatherTimeSeconds"),
GATHER_SECS);
S_link_inactive_secs
= S_get_plist_int(plist, CFSTR("LinkInactiveWaitTimeSeconds"),
LINK_INACTIVE_WAIT_SECS);
G_initial_wait_secs
= S_get_plist_int(plist, CFSTR("InitialRetryTimeSeconds"),
INITIAL_WAIT_SECS);
G_max_wait_secs
= S_get_plist_int(plist, CFSTR("MaximumRetryTimeSeconds"),
MAX_WAIT_SECS);
S_arp_probe_count
= S_get_plist_int(plist, CFSTR("ARPProbeCount"),
ARP_PROBE_COUNT);
S_arp_gratuitous_count
= S_get_plist_int(plist, CFSTR("ARPGratuitousCount"),
ARP_GRATUITOUS_COUNT);
S_arp_retry
= S_get_plist_timeval(plist, CFSTR("ARPRetryTimeSeconds"),
S_arp_retry);
S_arp_detect_count
= S_get_plist_int(plist, CFSTR("ARPDetectCount"),
ARP_DETECT_COUNT);
S_arp_detect_retry
= S_get_plist_timeval(plist, CFSTR("ARPDetectRetryTimeSeconds"),
S_arp_detect_retry);
S_arp_resolve_retry
= S_get_plist_timeval(plist, CFSTR("ARPResolveRetryTimeSeconds"),
S_arp_resolve_retry);
G_dhcp_accepts_bootp
= S_get_plist_boolean(plist, CFSTR("DHCPAcceptsBOOTP"), FALSE);
G_dhcp_failure_configures_linklocal
= S_get_plist_boolean(plist,
CFSTR("DHCPFailureConfiguresLinkLocal"),
DHCP_FAILURE_CONFIGURES_LINKLOCAL);
G_dhcp_success_deconfigures_linklocal
= S_get_plist_boolean(plist,
CFSTR("DHCPSuccessDeconfiguresLinkLocal"),
DHCP_SUCCESS_DECONFIGURES_LINKLOCAL);
G_dhcp_init_reboot_retry_count
= S_get_plist_int(plist, CFSTR("DHCPInitRebootRetryCount"),
DHCP_INIT_REBOOT_RETRY_COUNT);
G_dhcp_select_retry_count
= S_get_plist_int(plist, CFSTR("DHCPSelectRetryCount"),
DHCP_SELECT_RETRY_COUNT);
G_dhcp_allocate_linklocal_at_retry_count
= S_get_plist_int(plist, CFSTR("DHCPAllocateLinkLocalAtRetryCount"),
DHCP_ALLOCATE_LINKLOCAL_AT_RETRY_COUNT);
G_dhcp_router_arp_at_retry_count
= S_get_plist_int(plist, CFSTR("DHCPRouterARPAtRetryCount"),
DHCP_ROUTER_ARP_AT_RETRY_COUNT);
dhcp_params
= S_get_plist_uint8_array(plist,
kDHCPRequestedParameterList,
&n_dhcp_params);
dhcp_set_default_parameters(dhcp_params, n_dhcp_params);
G_router_arp
= S_get_plist_boolean(plist, CFSTR("RouterARPEnabled"), TRUE);
S_dhcp_local_hostname_length_max
= S_get_plist_int(plist, CFSTR("DHCPLocalHostNameLengthMax"),
DHCP_LOCAL_HOSTNAME_LENGTH_MAX);
G_discover_and_publish_router_mac_address
= S_get_plist_boolean(plist,
CFSTR("DiscoverAndPublishRouterMACAddress"),
TRUE);
S_discover_router_mac_address_secs
= S_get_plist_int(plist,
CFSTR("DiscoverRouterMACAddressTimeSeconds"),
DISCOVER_ROUTER_MAC_ADDRESS_SECS);
G_dhcp_defend_ip_address_interval_secs
= S_get_plist_int(plist,
CFSTR("DHCPDefendIPAddressIntervalSeconds"),
DHCP_DEFEND_IP_ADDRESS_INTERVAL_SECS);
G_dhcp_defend_ip_address_count
= S_get_plist_int(plist,
CFSTR("DHCPDefendIPAddressCount"),
DHCP_DEFEND_IP_ADDRESS_COUNT);
G_dhcp_lease_write_t1_threshold_secs
= S_get_plist_int(plist,
CFSTR("DHCPLeaseWriteT1ThresholdSeconds"),
DHCP_LEASE_WRITE_T1_THRESHOLD_SECS);
S_router_arp_excluded_ssids
= S_copy_plist_array_and_range(plist,
CFSTR("RouterARPExcludedSSIDs"),
&S_router_arp_excluded_ssids_range);
S_arp_conflict_retry
= S_get_plist_int(plist,
CFSTR("ARPConflictRetryCount"),
ARP_CONFLICT_RETRY_COUNT);
S_arp_conflict_delay
= S_get_plist_timeval(plist, CFSTR("ARPConflictRetryDelaySeconds"),
S_arp_conflict_delay);
G_manual_conflict_retry_interval_secs
= S_get_plist_int(plist,
CFSTR("ManualConflictRetryIntervalSeconds"),
MANUAL_CONFLICT_RETRY_INTERVAL_SECS);
#if ! TARGET_OS_EMBEDDED
S_use_maintenance_wake
= S_get_plist_boolean(plist,
CFSTR("UseMaintenanceWake"),
TRUE);
#endif
S_configure_ipv6 = S_get_plist_boolean(plist,
CFSTR("ConfigureIPv6"),
TRUE);
if (S_configure_ipv6) {
uint16_t * dhcpv6_options;
int dhcpv6_options_count;
G_dhcpv6_enabled = S_get_plist_boolean(plist,
CFSTR("DHCPv6Enabled"),
DHCPv6_ENABLED);
dhcpv6_options = S_get_plist_uint16_array(plist,
kDHCPv6RequestedOptions,
&dhcpv6_options_count);
DHCPv6ClientSetRequestedOptions(dhcpv6_options,
dhcpv6_options_count);
G_dhcpv6_stateful_enabled = S_get_plist_boolean(plist,
CFSTR("DHCPv6StatefulEnabled"),
DHCPv6_STATEFUL_ENABLED);
S_ipv6_map_automatic_to_linklocal
= S_get_plist_boolean(plist,
CFSTR("IPv6MapAutomaticToLinkLocal"),
IPV6_MAP_AUTOMATIC_TO_LINKLOCAL);
G_dhcp_duid_type = S_get_plist_int(plist,
CFSTR("DHCPDUIDType"),
kDHCPDUIDTypeLLT);
switch (G_dhcp_duid_type) {
case kDHCPDUIDTypeLLT:
case kDHCPDUIDTypeLL:
break;
default:
G_dhcp_duid_type = kDHCPDUIDTypeLLT;
break;
}
}
return;
}
static void
S_add_dhcp_parameters(SCPreferencesRef prefs)
{
uint8_t * dhcp_params = NULL;
int n_dhcp_params = 0;
CFArrayRef rp = applicationRequestedParametersCopy(prefs);
if (rp != NULL) {
dhcp_params = S_get_number_array(rp, sizeof(*dhcp_params),
&n_dhcp_params);
my_CFRelease(&rp);
}
dhcp_set_additional_parameters(dhcp_params, n_dhcp_params);
return;
}
void
load(CFBundleRef bundle, Boolean bundleVerbose)
{
S_bundle = (CFBundleRef)CFRetain(bundle);
return;
}
void
start(const char *bundleName, const char *bundleDir)
{
arp_session_values_t arp_values;
SCPreferencesRef prefs = NULL;
if (server_active()) {
my_log(LOG_NOTICE, "ipconfig server already active");
fprintf(stderr, "ipconfig server already active\n");
return;
}
S_set_globals();
prefs = SCPreferencesCreate(NULL, CFSTR("IPConfiguration.DHCPClient"),
kDHCPClientPreferencesID);
if (prefs == NULL) {
my_log(LOG_ERR,
"IPConfiguration: SCPreferencesCreate failed: %s",
SCErrorString(SCError()));
return;
}
if (SCPreferencesSetCallback(prefs,
dhcp_preferences_changed,
NULL) == FALSE
|| SCPreferencesScheduleWithRunLoop(prefs,
CFRunLoopGetCurrent(),
kCFRunLoopDefaultMode) == FALSE) {
my_log(LOG_ERR, "IPConfigurationSCPreferencesSetCallback failed: %s",
SCErrorString(SCError()));
my_CFRelease(&prefs);
return;
}
S_add_dhcp_parameters(prefs);
SCPreferencesSynchronize(prefs);
S_scd_session = SCDynamicStoreCreate(NULL,
CFSTR("IPConfiguration"),
handle_change, NULL);
if (S_scd_session == NULL) {
S_scd_session = NULL;
my_log(LOG_ERR, "SCDynamicStoreCreate failed: %s",
SCErrorString(SCError()));
}
G_bootp_session = bootp_session_init(G_client_port);
if (G_bootp_session == NULL) {
my_log(LOG_DEBUG, "bootp_session_init() failed");
return;
}
bzero(&arp_values, sizeof(arp_values));
arp_values.probe_count = &S_arp_probe_count;
arp_values.probe_gratuitous_count = &S_arp_gratuitous_count;
arp_values.probe_interval = &S_arp_retry;
arp_values.detect_count = &S_arp_detect_count;
arp_values.detect_interval = &S_arp_detect_retry;
arp_values.conflict_retry_count = &S_arp_conflict_retry;
arp_values.conflict_delay_interval = &S_arp_conflict_delay;
arp_values.resolve_interval = &S_arp_resolve_retry;
G_arp_session = arp_session_init(S_is_our_hardware_address,
&arp_values);
if (G_arp_session == NULL) {
my_log(LOG_DEBUG, "arp_session_init() failed");
return;
}
if (G_IPConfiguration_verbose) {
S_dhcp_packet_log = logfile_fopen(IPCONFIGURATION_BOOTP_LOG);
bootp_session_set_debug(G_bootp_session, S_dhcp_packet_log);
if (G_dhcpv6_enabled) {
S_dhcpv6_packet_log
= logfile_fopen(IPCONFIGURATION_DHCPV6_LOG);
DHCPv6SocketSetLogFile(S_dhcpv6_packet_log);
}
}
dynarray_init(&S_ifstate_list, IFState_free, NULL);
set_loopback();
return;
}
void
prime()
{
if (G_bootp_session == NULL) {
return;
}
if (S_scd_session == NULL) {
update_interface_list();
}
else {
start_initialization(S_scd_session);
}
server_init();
}
void
stop(CFRunLoopSourceRef stopRls)
{
if (G_bootp_session != NULL) {
IFStateList_all_services_event(&S_ifstate_list,
IFEventID_power_off_e, NULL);
}
CFRunLoopSourceSignal(stopRls);
}