STATIC boolean_t nd_flags_set_with_socket(int s, const char * if_name, uint32_t set_flags, uint32_t clear_flags) { uint32_t new_flags; struct in6_ndireq nd; bzero(&nd, sizeof(nd)); strncpy(nd.ifname, if_name, sizeof(nd.ifname)); if (ioctl(s, SIOCGIFINFO_IN6, &nd)) { my_log_fl(LOG_ERR, "SIOCGIFINFO_IN6(%s) failed, %s", if_name, strerror(errno)); return (FALSE); } new_flags = nd.ndi.flags; if (set_flags) { new_flags |= set_flags; } if (clear_flags) { new_flags &= ~clear_flags; } if (new_flags != nd.ndi.flags) { nd.ndi.flags = new_flags; if (ioctl(s, SIOCSIFINFO_FLAGS, (caddr_t)&nd)) { my_log_fl(LOG_ERR, "SIOCSIFINFO_FLAGS(%s) failed, %s", if_name, strerror(errno)); return (FALSE); } } return (TRUE); }
STATIC int open_rtadv_socket(void) { struct icmp6_filter filt; int sockfd; /* open socket */ sockfd = ICMPv6SocketOpen(TRUE); if (sockfd < 0) { my_log_fl(LOG_ERR, "error opening socket: %s", strerror(errno)); goto failed; } /* accept only router advertisement messages */ ICMP6_FILTER_SETBLOCKALL(&filt); ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt); if (setsockopt(sockfd, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, sizeof(filt)) == -1) { my_log_fl(LOG_ERR, "setsockopt(ICMP6_FILTER): %s", strerror(errno)); goto failed; } return (sockfd); failed: if (sockfd >= 0) { close(sockfd); } return (-1); }
PRIVATE_EXTERN void CGAPrepareSetForInterface(const char * ifname, struct in6_cga_prepare * cga_prep) { CFDictionaryRef dict; CFStringRef ifname_cf; CFDataRef modifier = NULL; uint8_t security_level; if (S_LinkLocalModifiers == NULL) { my_log_fl(LOG_NOTICE, "S_LinkLocalModifiers is NULL"); return; } ifname_cf = CFStringCreateWithCString(NULL, ifname, kCFStringEncodingASCII); dict = CFDictionaryGetValue(S_LinkLocalModifiers, ifname_cf); if (isA_CFDictionary(dict) != NULL) { modifier = CGAModifierDictGetModifier(dict, &security_level); } if (modifier == NULL) { security_level = kCGASecurityLevelZero; modifier = my_CFDataCreateWithRandomBytes(IN6_CGA_MODIFIER_LENGTH); dict = CGAModifierDictCreate(modifier, security_level); CFDictionarySetValue(S_LinkLocalModifiers, ifname_cf, dict); CFRelease(modifier); CGAWrite(HostUUIDGet(), S_GlobalModifier, S_LinkLocalModifiers); } CFRelease(ifname_cf); cga_prepare_set(cga_prep, modifier, security_level); return; }
PRIVATE_EXTERN boolean_t inet6_set_perform_nud(const char * if_name, boolean_t perform_nud) { boolean_t need_set = FALSE; struct in6_ndireq nd; int s; boolean_t success = FALSE; s = inet6_dgram_socket(); if (s < 0) { my_log_fl(LOG_ERR, "socket failed, %s", strerror(errno)); goto done; } bzero(&nd, sizeof(nd)); strncpy(nd.ifname, if_name, sizeof(nd.ifname)); if (ioctl(s, SIOCGIFINFO_IN6, &nd)) { my_log_fl(LOG_ERR, "SIOCGIFINFO_IN6(%s) failed, %s", if_name, strerror(errno)); goto done; } if (perform_nud) { if ((nd.ndi.flags & ND6_IFF_PERFORMNUD) == 0) { nd.ndi.flags |= ND6_IFF_PERFORMNUD; need_set = TRUE; } } else if ((nd.ndi.flags & ND6_IFF_PERFORMNUD) != 0) { nd.ndi.flags &= ~ND6_IFF_PERFORMNUD; need_set = TRUE; } if (need_set) { if (ioctl(s, SIOCSIFINFO_FLAGS, (caddr_t)&nd)) { my_log_fl(LOG_ERR, "SIOCSIFINFO_FLAGS(%s) failed, %s", if_name, strerror(errno)); goto done; } } success = TRUE; done: if (s >= 0) { close(s); } return (success); }
PRIVATE_EXTERN void RTADVSocketEnableReceive(RTADVSocketRef sock, RTADVSocketReceiveFuncPtr func, void * arg1, void * arg2) { sock->receive_func = func; sock->receive_arg1 = arg1; sock->receive_arg2 = arg2; if (RTADVSocketOpenSocket(sock) == FALSE) { my_log_fl(LOG_NOTICE, "%s: failed", if_name(sock->if_p)); } return; }
STATIC bool cga_is_enabled(void) { int enabled; size_t enabled_size = sizeof(enabled); if (sysctlbyname(knet_inet6_send_opmode, &enabled, &enabled_size, NULL, 0) != 0) { my_log_fl(LOG_NOTICE, "sysctl(%s) failed, %s", knet_inet6_send_opmode, strerror(errno)); enabled = 0; } return (enabled != 0); }
STATIC boolean_t nd_flags_set(const char * if_name, uint32_t set_flags, uint32_t clear_flags) { int s; boolean_t success = FALSE; s = inet6_dgram_socket(); if (s < 0) { my_log_fl(LOG_ERR, "socket failed, %s", strerror(errno)); } else { success = nd_flags_set_with_socket(s, if_name, set_flags, clear_flags); close(s); } return (success); }
PRIVATE_EXTERN void CGAInit(void) { CFDataRef host_uuid; if (G_is_netboot || cga_is_enabled() == FALSE) { return; } host_uuid = HostUUIDGet(); if (host_uuid == NULL) { my_log_fl(LOG_NOTICE, "Failed to get HostUUID"); return; } if (CGAParametersLoad(host_uuid) == FALSE) { return; } return; }
int DHCPv6SocketTransmit(DHCPv6SocketRef sock, DHCPv6PacketRef pkt, int pkt_len) { DHCPv6OptionErrorString err; int ret; boolean_t needs_close = FALSE; if (sock->fd_open == FALSE) { /* open the DHCPv6 socket in case it's needed */ if (DHCPv6SocketOpenSocket(sock) == FALSE) { my_log(LOG_ERR, "DHCPv6Socket: failed to open socket"); return (FALSE); } needs_close = TRUE; } if (S_verbose) { DHCPv6OptionListRef options; CFMutableStringRef str; str = CFStringCreateMutable(NULL, 0); DHCPv6PacketPrintToString(str, pkt, pkt_len); options = DHCPv6OptionListCreateWithPacket(pkt, pkt_len, &err); if (options == NULL) { my_log_fl(LOG_DEBUG, "parse options failed, %s", err.str); } else { DHCPv6OptionListPrintToString(str, options); DHCPv6OptionListRelease(&options); } my_log(-LOG_DEBUG,"[%s] Transmit %@", if_name(sock->if_p), str); CFRelease(str); } ret = S_send_packet(FDCalloutGetFD(S_globals->read_fd), if_link_index(sock->if_p), pkt, pkt_len); if (needs_close) { DHCPv6SocketCloseSocket(sock); } return (ret); }
STATIC bool cga_parameters_set(CFDataRef priv, CFDataRef pub, CFDataRef modifier, uint8_t security_level) { UInt8 buf[CGA_PARAMETERS_MAX_SIZE]; uint16_t key_len; UInt8 * offset; offset = buf; /* cga_prepare */ cga_prepare_set((struct in6_cga_prepare *)offset, modifier, security_level); offset += sizeof(struct in6_cga_prepare); /* private key length (uint16_t) */ key_len = CFDataGetLength(priv); bcopy(&key_len, offset, sizeof(key_len)); offset += sizeof(key_len); /* private key */ CFDataGetBytes(priv, CFRangeMake(0, key_len), offset); offset += key_len; /* public key length (uint16_t) */ key_len = CFDataGetLength(pub); bcopy(&key_len, offset, sizeof(key_len)); offset += sizeof(key_len); /* public key */ CFDataGetBytes(pub, CFRangeMake(0, key_len), offset); offset += key_len; if (sysctlbyname(knet_inet6_send_cga_parameters, NULL, NULL, buf, (offset - buf)) != 0) { my_log_fl(LOG_NOTICE, "sysctl(%s) failed, %s", knet_inet6_send_cga_parameters, strerror(errno)); return (FALSE); } return (TRUE); }
STATIC bool CGAParametersLoad(CFDataRef host_uuid) { CFDictionaryRef keys_info = NULL; CFDictionaryRef global_modifier = NULL; CFDictionaryRef linklocal_modifiers = NULL; CFDictionaryRef parameters = NULL; CFDataRef modifier = NULL; CFDataRef plist_host_uuid; CFDataRef priv; CFDataRef pub; uint8_t security_level; /* load CGA persistent information */ parameters = my_CFPropertyListCreateFromFile(CGA_FILE); if (isA_CFDictionary(parameters) == NULL) { if (parameters != NULL) { my_log_fl(LOG_NOTICE, "%s is not a dictionary", CGA_FILE); } goto done; } /* make sure that HostUUID matches */ plist_host_uuid = CFDictionaryGetValue(parameters, kHostUUID); if (isA_CFData(plist_host_uuid) == NULL || !CFEqual(plist_host_uuid, host_uuid)) { my_log_fl(LOG_NOTICE, "%@ missing/invalid", kHostUUID); goto done; } /* global modifier */ global_modifier = CFDictionaryGetValue(parameters, kGlobalModifier); if (global_modifier != NULL) { modifier = CGAModifierDictGetModifier(global_modifier, &security_level); } if (modifier == NULL) { global_modifier = NULL; my_log_fl(LOG_NOTICE, "%@ missing/invalid", kGlobalModifier); goto done; } /* load CGA keys */ keys_info = my_CFPropertyListCreateFromFile(CGA_KEYS_FILE); if (isA_CFDictionary(keys_info) == NULL) { if (keys_info != NULL) { my_log_fl(LOG_NOTICE, "%s is not a dictionary", CGA_KEYS_FILE); } goto done; } /* private key */ priv = CFDictionaryGetValue(keys_info, kPrivateKey); if (isA_CFData(priv) == NULL) { my_log_fl(LOG_NOTICE, "%@ missing/invalid", kPrivateKey); goto done; } /* public key */ pub = CFDictionaryGetValue(keys_info, kPublicKey); if (isA_CFData(pub) == NULL) { my_log_fl(LOG_NOTICE, "%@ missing/invalid", kPublicKey); goto done; } /* set CGA parameters in the kernel */ if (cga_parameters_set(priv, pub, modifier, security_level) == FALSE) { my_log_fl(LOG_NOTICE, "cga_parameters_set failed"); goto failed; } /* linklocal modifiers */ linklocal_modifiers = CFDictionaryGetValue(parameters, kLinkLocalModifiers); if (linklocal_modifiers != NULL && isA_CFDictionary(linklocal_modifiers) == NULL) { my_log_fl(LOG_NOTICE, "%s is not a dictionary", kLinkLocalModifiers); linklocal_modifiers = NULL; } done: if (global_modifier != NULL) { S_GlobalModifier = CFRetain(global_modifier); } else { global_modifier = CGAParametersCreate(host_uuid); if (global_modifier == NULL) { goto failed; } S_GlobalModifier = global_modifier; } if (linklocal_modifiers != NULL) { /* make a copy of the existing one */ S_LinkLocalModifiers = CFDictionaryCreateMutableCopy(NULL, 0, linklocal_modifiers); remove_old_linklocal_modifiers(S_LinkLocalModifiers); } else { /* create an empty modifiers dictionary */ S_LinkLocalModifiers = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); } failed: my_CFRelease(&keys_info); my_CFRelease(¶meters); return (S_GlobalModifier != NULL); }