#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/errno.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/sockio.h>
#include <sys/filio.h>
#include <ctype.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <netinet/in.h>
#include <netinet/udp.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/bootp.h>
#include <arpa/inet.h>
#include <net/if_types.h>
#include <net/if_dl.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCPrivate.h>
#include "DHCPDUID.h"
#include "DHCPv6.h"
#include "DHCPv6Server.h"
#include "DHCPv6Options.h"
#include "util.h"
#include <syslog.h>
#include <sys/uio.h>
#include <dispatch/dispatch.h>
#include "IPv6Socket.h"
#include "symbol_scope.h"
#include "IPv6Sock_Compat.h"
#include "cfutil.h"
#include "interfaces.h"
#define my_log SC_log
#define DHCP6D_DUID_FILE \
"/var/db/com.apple.dhcp6d.plist"
STATIC const char * kDHCPv6ServerDUIDFile = DHCP6D_DUID_FILE;
STATIC const CFStringRef kDHCPv6ServerDUID = CFSTR("DUID");
#define DHCP6D_CONFIG_FILE \
"/Library/Preferences/SystemConfiguration/com.apple.dhcp6d.plist"
STATIC const char * kDHCPv6ServerConfigurationFile = DHCP6D_CONFIG_FILE;
STATIC const CFStringRef kDHCPv6ServerEnabledInterfaces = CFSTR("enabled_interfaces");
STATIC const CFStringRef kDHCPv6ServerOptions = CFSTR("options");
STATIC const CFStringRef kDHCPv6ServerVerbose = CFSTR("verbose");
STATIC bool S_verbose;
STATIC uint16_t S_client_port = DHCPV6_CLIENT_PORT;
STATIC uint16_t S_server_port = DHCPV6_SERVER_PORT;
STATIC const struct sockaddr_in6 dhcpv6_all_servers_and_relay_agents = {
sizeof(dhcpv6_all_servers_and_relay_agents),
AF_INET6, 0, 0,
All_DHCP_Relay_Agents_and_Servers_INIT, 0
};
STATIC const struct sockaddr_in6 blank_sin6 = {
sizeof(blank_sin6),
AF_INET6, 0, 0,
IN6ADDR_ANY_INIT, 0
};
typedef unsigned int IFIndex;
struct DHCPv6Server {
int sock_fd;
dispatch_source_t sock_source;
CFDataRef duid;
char * config_file;
CFDictionaryRef options;
CFArrayRef enabled_interfaces;
SCDynamicStoreRef store;
IFIndex * if_indices;
char * * if_names;
int if_count;
};
STATIC void
DHCPv6ServerSetEnabledInterfaces(DHCPv6ServerRef server,
CFArrayRef enabled_interfaces);
STATIC int
DHCPv6ServerTransmit(DHCPv6ServerRef server,
IFIndex if_index,
const struct in6_addr * dst_p,
DHCPv6PacketRef pkt, int pkt_len);
STATIC int
set_multicast_for_interface(int sock_fd, int sopt, IFIndex if_index)
{
struct group_req mcr;
int ret;
switch (sopt) {
case MCAST_JOIN_GROUP:
case MCAST_LEAVE_GROUP:
break;
default:
return (-1);
}
mcr.gr_interface = if_index;
memcpy(&mcr.gr_group, &dhcpv6_all_servers_and_relay_agents,
sizeof(dhcpv6_all_servers_and_relay_agents));
ret = setsockopt(sock_fd, IPPROTO_IPV6, sopt, &mcr, sizeof(mcr));
if (ret != 0) {
my_log(LOG_ERR, "setsockopt(%s) if_index %d failed, %s",
(sopt == MCAST_JOIN_GROUP) ? "MCAST_JOIN_GROUP"
: "MCAST_LEAVE_GROUP",
if_index,
strerror(errno));
}
return (ret);
}
STATIC int
open_dhcpv6_socket(uint16_t port)
{
struct sockaddr_in6 me;
int opt = 1;
int sock_fd;
sock_fd = socket(AF_INET6, SOCK_DGRAM, 0);
if (sock_fd < 0) {
my_log(LOG_ERR, "socket failed, %s", strerror(errno));
return (sock_fd);
}
bzero(&me, sizeof(me));
me.sin6_family = AF_INET6;
me.sin6_port = htons(port);
if (bind(sock_fd, (struct sockaddr *)&me, sizeof(me)) != 0) {
my_log(LOG_ERR, "bind failed, %s",
strerror(errno));
goto failed;
}
if (ioctl(sock_fd, FIONBIO, &opt) < 0) {
my_log(LOG_ERR, "ioctl FIONBIO failed, %s",
strerror(errno));
goto failed;
}
if (setsockopt(sock_fd, IPPROTO_IPV6,
IPCONFIG_SOCKOPT_PKTINFO, &opt, sizeof(opt)) < 0) {
my_log(LOG_ERR, "setsockopt(IPV6_PKTINFO) failed, %s",
strerror(errno));
goto failed;
}
#if defined(SO_RECV_ANYIF)
if (setsockopt(sock_fd, SOL_SOCKET, SO_RECV_ANYIF, (caddr_t)&opt,
sizeof(opt)) < 0) {
my_log(LOG_ERR, "setsockopt(SO_RECV_ANYIF) failed, %s",
strerror(errno));
}
#endif
#if defined(SO_TRAFFIC_CLASS)
opt = SO_TC_CTL;
if (setsockopt(sock_fd, SOL_SOCKET, SO_TRAFFIC_CLASS, &opt,
sizeof(opt)) < 0) {
my_log(LOG_ERR, "setsockopt(SO_TRAFFIC_CLASS) failed, %s",
strerror(errno));
}
#endif
return (sock_fd);
failed:
close(sock_fd);
return (-1);
}
#define kIFIndexListItemNotFound (-1)
STATIC int
IFIndexListGetItemIndex(const IFIndex * list, int list_count, CFIndex if_index)
{
for (int i = 0; i < list_count; i++) {
if (list[i] == if_index) {
return (i);
}
}
return (kIFIndexListItemNotFound);
}
STATIC Boolean
IFIndexListContainsItem(const IFIndex * list, int list_count, CFIndex if_index)
{
return (IFIndexListGetItemIndex(list, list_count, if_index)
!= kIFIndexListItemNotFound);
}
STATIC Boolean
DHCPv6ServerInterfaceIsEnabled(DHCPv6ServerRef server, IFIndex if_index)
{
return (IFIndexListContainsItem(server->if_indices,
server->if_count, if_index));
}
STATIC const char *
DHCPv6ServerGetEnabledInterfaceName(DHCPv6ServerRef server, IFIndex if_index)
{
int which;
which = IFIndexListGetItemIndex(server->if_indices, server->if_count,
if_index);
if (which == kIFIndexListItemNotFound) {
return (NULL);
}
return (server->if_names[which]);
}
STATIC void
DHCPv6ServerProcessRequest(DHCPv6ServerRef server,
const struct sockaddr_in6 * from_p,
DHCPv6PacketRef pkt, int pkt_len,
IFIndex if_index)
{
char buf[1500];
DHCPDUIDRef client_id;
int error;
const char * if_name;
DHCPv6OptionErrorString err;
char ntopbuf[INET6_ADDRSTRLEN];
DHCPv6OptionListRef options;
int option_len;
DHCPv6OptionArea oa;
DHCPv6PacketRef reply_pkt;
const void * requested_options;
DHCPDUIDRef server_id;
if_name = DHCPv6ServerGetEnabledInterfaceName(server, if_index);
if (if_name == NULL) {
my_log(LOG_DEBUG, "Interface %d not enabled, ignoring (%d bytes)",
if_index, pkt_len);
return;
}
if (pkt->msg_type != kDHCPv6MessageINFORMATION_REQUEST) {
my_log(LOG_DEBUG, "Ignoring %s (%d) packet on interface %d (%d bytes)",
DHCPv6MessageName(pkt->msg_type), pkt->msg_type,
if_index, pkt_len);
return;
}
options = DHCPv6OptionListCreateWithPacket(pkt, pkt_len, &err);
if (S_verbose) {
CFMutableStringRef str;
str = CFStringCreateMutable(NULL, 0);
DHCPv6PacketPrintToString(str, pkt, pkt_len);
if (options != NULL) {
DHCPv6OptionListPrintToString(str, options);
}
my_log(~LOG_NOTICE, "[%s] Receive from %s %@",
if_name,
inet_ntop(AF_INET6,
&from_p->sin6_addr, ntopbuf, sizeof(ntopbuf)),
str);
CFRelease(str);
}
else {
my_log(LOG_NOTICE, "[%s] Receive %s (%d) [%d bytes] from %s",
if_name,
DHCPv6MessageName(pkt->msg_type), pkt->msg_type, pkt_len,
inet_ntop(AF_INET6,
&from_p->sin6_addr, ntopbuf, sizeof(ntopbuf)));
}
if (options == NULL) {
my_log(LOG_NOTICE, "DHCPv6 options parse failed, %s",
err.str);
return;
}
server_id = (DHCPDUIDRef)
DHCPv6OptionListGetOptionDataAndLength(options,
kDHCPv6OPTION_SERVERID,
&option_len, NULL);
if (server_id != NULL) {
if (!DHCPDUIDIsValid(server_id, option_len)) {
my_log(LOG_NOTICE, "Request contains invalid SERVERID");
goto done;
}
if (CFDataGetLength(server->duid) != option_len
|| bcmp(server_id,
CFDataGetBytePtr(server->duid), option_len) != 0) {
my_log(LOG_NOTICE, "Request SERVERID doesn't match");
goto done;
}
}
reply_pkt = (DHCPv6PacketRef)buf;
DHCPv6PacketSetMessageType(reply_pkt, kDHCPv6MessageREPLY);
memcpy(reply_pkt->transaction_id, pkt->transaction_id,
sizeof(reply_pkt->transaction_id));
DHCPv6OptionAreaInit(&oa, reply_pkt->options,
sizeof(buf) - DHCPV6_PACKET_HEADER_LENGTH);
client_id = (DHCPDUIDRef)
DHCPv6OptionListGetOptionDataAndLength(options,
kDHCPv6OPTION_CLIENTID,
&option_len, NULL);
if (client_id != NULL) {
if (!DHCPDUIDIsValid(client_id, option_len)) {
my_log(LOG_NOTICE, "Request contains invalid CLIENTID");
goto done;
}
if (!DHCPv6OptionAreaAddOption(&oa, kDHCPv6OPTION_CLIENTID,
option_len, client_id, &err)) {
my_log(LOG_NOTICE, " failed to add CLIENTID, %s",
err.str);
goto done;
}
}
if (!DHCPv6OptionAreaAddOption(&oa, kDHCPv6OPTION_SERVERID,
CFDataGetLength(server->duid),
CFDataGetBytePtr(server->duid),
&err)) {
my_log(LOG_NOTICE, "failed to add SERVERID, %s",
err.str);
goto done;
}
requested_options =
DHCPv6OptionListGetOptionDataAndLength(options,
kDHCPv6OPTION_ORO,
&option_len, NULL);
if (requested_options != NULL && server->options != NULL) {
const void * scan;
scan = requested_options;
for (int i = 0; i < (option_len / sizeof(DHCPv6OptionLength));
i++, scan += sizeof(DHCPv6OptionLength)) {
CFDataRef data;
DHCPv6OptionCode code;
code = net_uint16_get(scan);
data = DHCPv6OptionsDictionaryGetOption(server->options, code);
if (data != NULL) {
if (!DHCPv6OptionAreaAddOption(&oa, code,
CFDataGetLength(data),
CFDataGetBytePtr(data),
&err)) {
my_log(LOG_NOTICE, "failed to add %s, %s",
DHCPv6OptionCodeGetName(code),
err.str);
goto done;
}
}
}
}
error = DHCPv6ServerTransmit(server, if_index, &from_p->sin6_addr,
reply_pkt,
DHCPV6_PACKET_HEADER_LENGTH
+ DHCPv6OptionAreaGetUsedLength(&oa));
switch (error) {
case 0:
case ENXIO:
case ENETDOWN:
break;
default:
my_log(LOG_NOTICE, "%s transmit failed, %s", if_name, strerror(error));
break;
}
done:
DHCPv6OptionListRelease(&options);
return;
}
STATIC void
DHCPv6ServerReceive(DHCPv6ServerRef server)
{
struct cmsghdr * cm;
char cmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
struct sockaddr_in6 from;
struct iovec iov;
struct msghdr mhdr;
ssize_t n;
struct in6_pktinfo *pktinfo = NULL;
char receive_buf[1500];
iov.iov_base = (caddr_t)receive_buf;
iov.iov_len = sizeof(receive_buf);
mhdr.msg_name = (caddr_t)&from;
mhdr.msg_namelen = sizeof(from);
mhdr.msg_iov = &iov;
mhdr.msg_iovlen = 1;
mhdr.msg_control = (caddr_t)cmsgbuf;
mhdr.msg_controllen = sizeof(cmsgbuf);
n = recvmsg(server->sock_fd, &mhdr, 0);
if (n < 0) {
if (errno != EAGAIN) {
my_log(LOG_ERR, "DHCPv6SocketRead: recvfrom failed %s (%d)",
strerror(errno), errno);
}
return;
}
if (n < DHCPV6_PACKET_HEADER_LENGTH) {
my_log(LOG_NOTICE,
"DHCPv6SocketRead: packet too short %ld < %d",
n, DHCPV6_PACKET_HEADER_LENGTH);
return;
}
pktinfo = NULL;
for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&mhdr);
cm != NULL;
cm = (struct cmsghdr *)CMSG_NXTHDR(&mhdr, cm)) {
if (cm->cmsg_level != IPPROTO_IPV6) {
continue;
}
switch (cm->cmsg_type) {
case IPV6_PKTINFO:
if (cm->cmsg_len < CMSG_LEN(sizeof(struct in6_pktinfo))) {
continue;
}
pktinfo = (struct in6_pktinfo *)(void *)(CMSG_DATA(cm));
break;
default:
my_log(LOG_NOTICE, "Why did we get control message type %d?",
cm->cmsg_type);
break;
}
}
if (pktinfo == NULL) {
my_log(LOG_NOTICE,
"DHCPv6SocketRead: missing IPV6_PKTINFO");
return;
}
DHCPv6ServerProcessRequest(server, &from,
(DHCPv6PacketRef)receive_buf, (int)n,
pktinfo->ipi6_ifindex);
return;
}
STATIC void
WriteServerDUID(CFDataRef duid)
{
CFDictionaryRef dict;
dict = CFDictionaryCreate(NULL,
(const void * *)&kDHCPv6ServerDUID,
(const void * *)&duid,
1,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (my_CFPropertyListWriteFile(dict, kDHCPv6ServerDUIDFile, 0644) < 0) {
my_log(LOG_DEBUG, "Failed to write DUID to %s", kDHCPv6ServerDUIDFile);
}
my_CFRelease(&dict);
return;
}
STATIC CFDataRef
CreateServerDUID(void)
{
CFDataRef duid = NULL;
interface_t * if_p;
interface_list_t * interfaces;
interfaces = ifl_init();
if (interfaces == NULL) {
my_log(LOG_NOTICE, "can't retrieve interface list");
goto done;
}
if_p = ifl_find_stable_interface(interfaces);
if (if_p == NULL) {
my_log(LOG_NOTICE, "can't find suitable interface for DUID");
goto done;
}
duid = DHCPDUID_LLTDataCreate(if_link_address(if_p),
if_link_length(if_p),
if_link_arptype(if_p));
if (duid == NULL) {
my_log(LOG_NOTICE, "failed to establish DUID");
goto done;
}
WriteServerDUID(duid);
my_log(LOG_NOTICE, "Derived DUID from %s", if_name(if_p));
done:
ifl_free(&interfaces);
return (duid);
}
STATIC CFDataRef
CopyServerDUID(void)
{
CFDictionaryRef dict;
CFDataRef duid = NULL;
dict = my_CFPropertyListCreateFromFile(kDHCPv6ServerDUIDFile);
if (isA_CFDictionary(dict) != NULL) {
duid = CFDictionaryGetValue(dict, kDHCPv6ServerDUID);
duid = isA_CFData(duid);
}
if (duid != NULL) {
CFRetain(duid);
}
else {
duid = CreateServerDUID();
}
if (duid != NULL) {
CFMutableStringRef str;
str = CFStringCreateMutable(NULL, 0);
DHCPDUIDPrintToString(str, (const DHCPDUIDRef)
CFDataGetBytePtr(duid),
(int)CFDataGetLength(duid));
my_log(LOG_NOTICE, "%@", str);
CFRelease(str);
}
my_CFRelease(&dict);
return (duid);
}
STATIC void
close_socket(void * ctx)
{
int sock_fd = (int)(uintptr_t)ctx;
close(sock_fd);
return;
}
STATIC void
DHCPv6ServerHandleNotification(SCDynamicStoreRef session,
CFArrayRef changes,
void * info)
{
DHCPv6ServerRef server = (DHCPv6ServerRef)info;
DHCPv6ServerSetEnabledInterfaces(server,
server->enabled_interfaces);
return;
}
STATIC void
DHCPv6ServerEnableNotifications(DHCPv6ServerRef server)
{
SCDynamicStoreContext context;
CFStringRef key;
CFArrayRef patterns;
SCDynamicStoreRef store;
bzero(&context, sizeof(context));
context.info = server;
store = SCDynamicStoreCreate(NULL, CFSTR("DHCPv6Server"),
DHCPv6ServerHandleNotification, &context);
key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
kSCDynamicStoreDomainState,
kSCCompAnyRegex,
kSCEntNetIPv6);
patterns = CFArrayCreate(NULL, (const void **)&key, 1,
&kCFTypeArrayCallBacks);
CFRelease(key);
SCDynamicStoreSetNotificationKeys(store, NULL, patterns);
CFRelease(patterns);
server->store = store;
SCDynamicStoreSetDispatchQueue(store, dispatch_get_main_queue());
return;
}
PRIVATE_EXTERN DHCPv6ServerRef
DHCPv6ServerCreate(const char * config_file)
{
CFDataRef duid = NULL;
DHCPv6ServerRef server;
int sock_fd = -1;
sock_fd = open_dhcpv6_socket(S_server_port);
if (sock_fd < 0) {
my_log(LOG_NOTICE, "socket() failed, %s", strerror(errno));
goto failed;
}
duid = CopyServerDUID();
if (duid == NULL) {
my_log(LOG_NOTICE, "Can't load DUID");
goto failed;
}
server = malloc(sizeof(*server));
bzero(server, sizeof(*server));
if (config_file != NULL) {
server->config_file = strdup(config_file);
}
else {
server->config_file = strdup(kDHCPv6ServerConfigurationFile);
}
server->duid = duid;
server->sock_fd = sock_fd;
server->sock_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
sock_fd,
0UL,
dispatch_get_main_queue());
dispatch_set_context(server->sock_source, (void *)(uintptr_t)sock_fd);
dispatch_set_finalizer_f(server->sock_source, close_socket);
dispatch_source_set_event_handler(server->sock_source,
^{ DHCPv6ServerReceive(server); });
DHCPv6ServerUpdateConfiguration(server);
DHCPv6ServerEnableNotifications(server);
dispatch_resume(server->sock_source);
return (server);
failed:
if (sock_fd >= 0) {
close(sock_fd);
}
my_CFRelease(&duid);
return (NULL);
}
STATIC int
my_if_nametoindex(struct if_nameindex * name_index, const char * if_name)
{
if (name_index == NULL) {
return (0);
}
for (struct if_nameindex * scan = name_index;
scan->if_name != NULL;
scan++) {
if (strcmp(scan->if_name, if_name) == 0) {
return (scan->if_index);
}
}
return (0);
}
STATIC IFIndex *
create_if_indices(char * * if_names, int if_count)
{
IFIndex * if_indices;
struct if_nameindex * name_index = NULL;
name_index = if_nameindex();
if_indices = malloc(sizeof(*if_indices) * if_count);
for (int i = 0; i < if_count; i++) {
if_indices[i] = my_if_nametoindex(name_index, if_names[i]);
}
if (name_index != NULL) {
if_freenameindex(name_index);
}
return (if_indices);
}
STATIC void
DHCPv6ServerEnableMulticastForAddedInterfaces(DHCPv6ServerRef server,
IFIndex * if_indices,
char * * if_names,
int if_count)
{
for (int i = 0; i < if_count; i++) {
int if_index = if_indices[i];
char * if_name = if_names[i];
if (if_index != 0
&& !DHCPv6ServerInterfaceIsEnabled(server, if_index)) {
if (set_multicast_for_interface(server->sock_fd, MCAST_JOIN_GROUP,
if_index) == 0) {
my_log(LOG_NOTICE,
"Added DHCPv6 multicast for interface %s", if_name);
}
}
}
return;
}
STATIC void
DHCPv6ServerDisableMulticastForRemovedInterfaces(DHCPv6ServerRef server,
IFIndex * if_indices,
int if_count)
{
for (int i = 0; i < server->if_count; i++) {
int if_index = server->if_indices[i];
const char * if_name = server->if_names[i];
if (if_index == 0) {
continue;
}
if (if_indices == NULL
|| !IFIndexListContainsItem(if_indices, if_count, if_index)) {
if (set_multicast_for_interface(server->sock_fd, MCAST_LEAVE_GROUP,
if_index) == 0) {
my_log(LOG_NOTICE,
"Removed DHCPv6 multicast for interface %s", if_name);
}
}
}
return;
}
STATIC void
DHCPv6ServerSetEnabledInterfaces(DHCPv6ServerRef server,
CFArrayRef enabled_interfaces)
{
int if_count = 0;
IFIndex * if_indices = NULL;
char * * if_names = NULL;
CFArrayRef old_enabled_interfaces;
old_enabled_interfaces = server->enabled_interfaces;
if (S_verbose
&& !my_CFEqual(old_enabled_interfaces, enabled_interfaces)) {
if (enabled_interfaces != NULL) {
my_log(LOG_NOTICE, "Enabled Interfaces: %@", enabled_interfaces);
}
else {
my_log(LOG_NOTICE, "Enabled Interfaces: none");
}
}
if (enabled_interfaces != NULL) {
if_names = my_CStringArrayCreate(enabled_interfaces, &if_count);
if (if_names != NULL) {
if_indices = create_if_indices(if_names, if_count);
if (if_indices != NULL) {
DHCPv6ServerEnableMulticastForAddedInterfaces(server,
if_indices,
if_names,
if_count);
}
}
}
DHCPv6ServerDisableMulticastForRemovedInterfaces(server, if_indices,
if_count);
if (server->if_indices != NULL) {
free(server->if_indices);
}
if (server->if_names != NULL) {
free(server->if_names);
}
server->if_count = if_count;
server->if_indices = if_indices;
server->if_names = if_names;
if (enabled_interfaces != NULL) {
CFRetain(enabled_interfaces);
}
server->enabled_interfaces = enabled_interfaces;
my_CFRelease(&old_enabled_interfaces);
return;
}
STATIC void
DHCPv6ServerSetGlobalOptions(DHCPv6ServerRef server, CFDictionaryRef options)
{
CFDictionaryRef new_options = NULL;
if (options != NULL) {
new_options = DHCPv6OptionsDictionaryCreate(options);
if (new_options == NULL) {
my_log(LOG_NOTICE, "Failed to create DHCPv6OptionsDictionary");
}
}
my_CFRelease(&server->options);
server->options = new_options;
}
STATIC void
DHCPv6ServerSetConfiguration(DHCPv6ServerRef server, CFDictionaryRef plist)
{
CFArrayRef enabled_interfaces = NULL;
CFDictionaryRef options = NULL;
if (plist != NULL) {
CFBooleanRef verbose;
enabled_interfaces
= CFDictionaryGetValue(plist, kDHCPv6ServerEnabledInterfaces);
enabled_interfaces = isA_CFArray(enabled_interfaces);
options = CFDictionaryGetValue(plist, kDHCPv6ServerOptions);
options = isA_CFDictionary(options);
verbose = CFDictionaryGetValue(plist, kDHCPv6ServerVerbose);
if (isA_CFBoolean(verbose) != NULL) {
Boolean new_verbose;
new_verbose = CFBooleanGetValue(verbose);
if (new_verbose != S_verbose) {
S_verbose = new_verbose;
my_log(LOG_NOTICE, "Verbose mode %s",
S_verbose ? "enabled" : "disabled");
}
}
}
DHCPv6ServerSetEnabledInterfaces(server, enabled_interfaces);
DHCPv6ServerSetGlobalOptions(server, options);
return;
}
PRIVATE_EXTERN void
DHCPv6ServerUpdateConfiguration(DHCPv6ServerRef server)
{
CFDictionaryRef dict;
dict = my_CFPropertyListCreateFromFile(server->config_file);
if (isA_CFDictionary(dict) == NULL) {
my_log(LOG_NOTICE, "Failed to load '%s'",
server->config_file);
}
DHCPv6ServerSetConfiguration(server, isA_CFDictionary(dict));
my_CFRelease(&dict);
return;
}
PRIVATE_EXTERN void
DHCPv6ServerRelease(DHCPv6ServerRef * server_p)
{
DHCPv6ServerRef server = *server_p;
if (server == NULL) {
return;
}
if (server->sock_source != NULL) {
dispatch_source_cancel(server->sock_source);
dispatch_release(server->sock_source);
server->sock_source = NULL;
}
if (server->config_file != NULL) {
free(server->config_file);
server->config_file = NULL;
}
my_CFRelease(&server->options);
my_CFRelease(&server->enabled_interfaces);
if (server->if_indices != NULL) {
free(server->if_indices);
server->if_indices = NULL;
}
if (server->if_names != NULL) {
free(server->if_names);
server->if_names = NULL;
}
my_CFRelease(&server->duid);
if (server->store != NULL) {
SCDynamicStoreSetDispatchQueue(server->store, NULL);
my_CFRelease(&server->store);
}
*server_p = NULL;
free(server);
return;
}
STATIC int
S_send_packet(int sock_fd, IFIndex if_index, const struct in6_addr * dst_p,
DHCPv6PacketRef pkt, int pkt_size)
{
struct sockaddr_in6 dst;
dst = blank_sin6;
dst.sin6_addr = *dst_p;
dst.sin6_port = htons(S_client_port);
return (IPv6SocketSend(sock_fd, if_index, &dst, pkt, pkt_size, -1));
}
STATIC int
DHCPv6ServerTransmit(DHCPv6ServerRef server,
IFIndex if_index,
const struct in6_addr * dst_p,
DHCPv6PacketRef pkt, int pkt_len)
{
DHCPv6OptionErrorString err;
char if_name[IFNAMSIZ];
char ntopbuf[INET6_ADDRSTRLEN];
int ret;
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(LOG_NOTICE, "parse options failed, %s", err.str);
}
else {
DHCPv6OptionListPrintToString(str, options);
DHCPv6OptionListRelease(&options);
}
my_log(~LOG_NOTICE, "[%s] Transmit [%d bytes] to %s %@",
if_indextoname(if_index, if_name),
pkt_len,
inet_ntop(AF_INET6, dst_p, ntopbuf, sizeof(ntopbuf)),
str);
CFRelease(str);
}
else {
my_log(LOG_NOTICE, "[%s] Transmit %s (%d) [%d bytes] to %s",
if_indextoname(if_index, if_name),
DHCPv6MessageName(pkt->msg_type),
pkt->msg_type,
pkt_len,
inet_ntop(AF_INET6, dst_p, ntopbuf, sizeof(ntopbuf)));
}
ret = S_send_packet(server->sock_fd, if_index, dst_p, pkt, pkt_len);
return (ret);
}