LinkConfiguration.c [plain text]
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCPrivate.h> // for SCLog()
#include <SystemConfiguration/SCValidation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/network/IONetworkInterface.h>
#include <IOKit/network/IONetworkController.h>
#include "dy_framework.h"
static const struct ifmedia_description ifm_subtype_shared_descriptions[] =
IFM_SUBTYPE_SHARED_DESCRIPTIONS;
static const struct ifmedia_description ifm_subtype_ethernet_descriptions[] =
IFM_SUBTYPE_ETHERNET_DESCRIPTIONS;
static const struct ifmedia_description ifm_shared_option_descriptions[] =
IFM_SHARED_OPTION_DESCRIPTIONS;
static const struct ifmedia_description ifm_subtype_ethernet_option_descriptions[] =
IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS;
static CFDictionaryRef
__createMediaDictionary(int media_options, Boolean filter)
{
CFMutableDictionaryRef dict = NULL;
int i;
CFMutableArrayRef options = NULL;
CFStringRef val;
if (IFM_TYPE(media_options) != IFM_ETHER) {
return NULL;
}
if (filter && (IFM_SUBTYPE(media_options) == IFM_NONE)) {
return NULL;
}
dict = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
val = NULL;
for (i = 0; !val && ifm_subtype_shared_descriptions[i].ifmt_string; i++) {
if (IFM_SUBTYPE(media_options) == ifm_subtype_shared_descriptions[i].ifmt_word) {
val = CFStringCreateWithCString(NULL,
ifm_subtype_shared_descriptions[i].ifmt_string,
kCFStringEncodingASCII);
break;
}
}
for (i = 0; !val && ifm_subtype_ethernet_descriptions[i].ifmt_string; i++) {
if (IFM_SUBTYPE(media_options) == ifm_subtype_ethernet_descriptions[i].ifmt_word) {
val = CFStringCreateWithCString(NULL,
ifm_subtype_ethernet_descriptions[i].ifmt_string,
kCFStringEncodingASCII);
break;
}
}
if (val) {
CFDictionaryAddValue(dict, kSCPropNetEthernetMediaSubType, val);
CFRelease(val);
}
options = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
while (IFM_OPTIONS(media_options) != 0) {
if (filter && (IFM_OPTIONS(media_options) & IFM_LOOP)) {
media_options &= ~IFM_LOOP;
continue;
}
val = NULL;
for (i = 0; !val && ifm_shared_option_descriptions[i].ifmt_string; i++) {
if (IFM_OPTIONS(media_options) & ifm_shared_option_descriptions[i].ifmt_word) {
val = CFStringCreateWithCString(NULL,
ifm_shared_option_descriptions[i].ifmt_string,
kCFStringEncodingASCII);
media_options &= ~ifm_shared_option_descriptions[i].ifmt_word;
break;
}
}
for (i = 0; !val && ifm_subtype_ethernet_option_descriptions[i].ifmt_string; i++) {
if (IFM_OPTIONS(media_options) & ifm_subtype_ethernet_option_descriptions[i].ifmt_word) {
val = CFStringCreateWithCString(NULL,
ifm_subtype_ethernet_option_descriptions[i].ifmt_string,
kCFStringEncodingASCII);
media_options &= ~ifm_shared_option_descriptions[i].ifmt_word;
break;
}
}
if (val) {
CFArrayAppendValue(options, val);
CFRelease(val);
}
}
CFDictionaryAddValue(dict, kSCPropNetEthernetMediaOptions, options);
CFRelease(options);
return dict;
}
int
__createMediaOptions(CFDictionaryRef media_options)
{
CFIndex i;
Boolean match;
int ifm_new = IFM_ETHER;
CFIndex n;
CFArrayRef options;
char *str;
CFStringRef val;
val = CFDictionaryGetValue(media_options, kSCPropNetEthernetMediaSubType);
if (!isA_CFString(val)) {
return -1;
}
str = _SC_cfstring_to_cstring(val, NULL, 0, kCFStringEncodingASCII);
if (str == NULL) {
return -1;
}
match = FALSE;
for (i = 0; !match && ifm_subtype_shared_descriptions[i].ifmt_string; i++) {
if (strcasecmp(str, ifm_subtype_shared_descriptions[i].ifmt_string) == 0) {
ifm_new |= ifm_subtype_shared_descriptions[i].ifmt_word;
match = TRUE;
break;
}
}
for (i = 0; !match && ifm_subtype_ethernet_descriptions[i].ifmt_string; i++) {
if (strcasecmp(str, ifm_subtype_ethernet_descriptions[i].ifmt_string) == 0) {
ifm_new |= ifm_subtype_ethernet_descriptions[i].ifmt_word;
match = TRUE;
break;
}
}
CFAllocatorDeallocate(NULL, str);
if (!match) {
return -1;
}
options = CFDictionaryGetValue(media_options, kSCPropNetEthernetMediaOptions);
if (!isA_CFArray(options)) {
return -1;
}
n = CFArrayGetCount(options);
for (i = 0; i < n; i++) {
CFIndex j;
val = CFArrayGetValueAtIndex(options, i);
if (!isA_CFString(val)) {
return -1;
}
str = _SC_cfstring_to_cstring(val, NULL, 0, kCFStringEncodingASCII);
if (str == NULL) {
return -1;
}
match = FALSE;
for (j = 0; !match && ifm_shared_option_descriptions[j].ifmt_string; j++) {
if (strcasecmp(str, ifm_shared_option_descriptions[j].ifmt_string) == 0) {
ifm_new |= ifm_shared_option_descriptions[j].ifmt_word;
match = TRUE;
break;
}
}
for (j = 0; !match && ifm_subtype_ethernet_option_descriptions[j].ifmt_string; j++) {
if (strcasecmp(str, ifm_subtype_ethernet_option_descriptions[j].ifmt_string) == 0) {
ifm_new |= ifm_subtype_ethernet_option_descriptions[j].ifmt_word;
match = TRUE;
break;
}
}
CFAllocatorDeallocate(NULL, str);
if (!match) {
return -1;
}
}
return ifm_new;
}
Boolean
NetworkInterfaceCopyMediaOptions(CFStringRef interface,
CFDictionaryRef *current,
CFDictionaryRef *active,
CFArrayRef *available,
Boolean filter)
{
int i;
struct ifmediareq ifm;
int *media_list = NULL;
Boolean ok = FALSE;
int sock = -1;
bzero((void *)&ifm, sizeof(ifm));
if (_SC_cfstring_to_cstring(interface, ifm.ifm_name, sizeof(ifm.ifm_name), kCFStringEncodingASCII) == NULL) {
SCLog(TRUE, LOG_ERR, CFSTR("could not convert inteface name"));
goto done;
}
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
SCLog(TRUE, LOG_ERR, CFSTR("socket() failed: %s"), strerror(errno));
goto done;
}
if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifm) < 0) {
goto done;
}
if (ifm.ifm_count > 0) {
media_list = (int *)CFAllocatorAllocate(NULL, ifm.ifm_count * sizeof(int), 0);
ifm.ifm_ulist = media_list;
if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifm) < 0) {
SCLog(TRUE, LOG_DEBUG, CFSTR("ioctl(SIOCGIFMEDIA) failed: %s"), strerror(errno));
goto done;
}
}
if (active) *active = NULL;
if (current) *current = NULL;
if (available) {
CFMutableArrayRef media_options;
media_options = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
for (i = 0; i < ifm.ifm_count; i++) {
CFDictionaryRef options;
options = __createMediaDictionary(media_list[i], filter);
if (!options) {
continue;
}
if (active && (*active == NULL) && (ifm.ifm_active == media_list[i])) {
*active = CFRetain(options);
}
if (current && (*current == NULL) && (ifm.ifm_current == media_list[i])) {
*current = CFRetain(options);
}
if (!CFArrayContainsValue(media_options, CFRangeMake(0, CFArrayGetCount(media_options)), options)) {
CFArrayAppendValue(media_options, options);
}
CFRelease(options);
}
*available = (CFArrayRef)media_options;
}
if (active && (*active == NULL)) {
*active = __createMediaDictionary(ifm.ifm_active, FALSE);
}
if (current && (*current == NULL)) {
if (active && (ifm.ifm_active == ifm.ifm_current)) {
if (*active) *current = CFRetain(active);
} else {
*current = __createMediaDictionary(ifm.ifm_current, FALSE);
}
}
ok = TRUE;
done :
if (sock >= 0) (void)close(sock);
if (media_list) CFAllocatorDeallocate(NULL, media_list);
return ok;
}
CFArrayRef
NetworkInterfaceCopyMediaSubTypes(CFArrayRef available)
{
CFIndex i;
CFIndex n;
CFMutableArrayRef subTypes;
if (!isA_CFArray(available)) {
return NULL;
}
subTypes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
n = CFArrayGetCount(available);
for (i = 0; i < n; i++) {
CFDictionaryRef options;
CFStringRef subType;
options = CFArrayGetValueAtIndex(available, i);
if (!isA_CFDictionary(options)) {
continue;
}
subType = CFDictionaryGetValue(options, kSCPropNetEthernetMediaSubType);
if (!isA_CFString(subType)) {
continue;
}
if (!CFArrayContainsValue(subTypes, CFRangeMake(0, CFArrayGetCount(subTypes)), subType)) {
CFArrayAppendValue(subTypes, subType);
}
}
if (CFArrayGetCount(subTypes) == 0) {
CFRelease(subTypes);
subTypes = NULL;
}
return subTypes;
}
CFArrayRef
NetworkInterfaceCopyMediaSubTypeOptions(CFArrayRef available,
CFStringRef subType)
{
CFIndex i;
CFIndex n;
CFMutableArrayRef subTypeOptions;
if (!isA_CFArray(available)) {
return NULL;
}
subTypeOptions = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
n = CFArrayGetCount(available);
for (i = 0; i < n; i++) {
CFDictionaryRef options;
CFArrayRef mediaOptions;
CFStringRef mediaSubType;
options = CFArrayGetValueAtIndex(available, i);
if (!isA_CFDictionary(options)) {
continue;
}
mediaSubType = CFDictionaryGetValue(options, kSCPropNetEthernetMediaSubType);
if (!isA_CFString(mediaSubType) || !CFEqual(subType, mediaSubType)) {
continue;
}
mediaOptions = CFDictionaryGetValue(options, kSCPropNetEthernetMediaOptions);
if (!isA_CFArray(mediaOptions)) {
continue;
}
if (!CFArrayContainsValue(subTypeOptions, CFRangeMake(0, CFArrayGetCount(subTypeOptions)), mediaOptions)) {
CFArrayAppendValue(subTypeOptions, mediaOptions);
}
}
if (CFArrayGetCount(subTypeOptions) == 0) {
CFRelease(subTypeOptions);
subTypeOptions = NULL;
}
return subTypeOptions;
}
Boolean
NetworkInterfaceCopyMTU(CFStringRef interface,
int *mtu_cur,
int *mtu_min,
int *mtu_max)
{
struct ifreq ifr;
Boolean ok = FALSE;
int sock = -1;
bzero((void *)&ifr, sizeof(ifr));
if (_SC_cfstring_to_cstring(interface, ifr.ifr_name, sizeof(ifr.ifr_name), kCFStringEncodingASCII) == NULL) {
SCLog(TRUE, LOG_ERR, CFSTR("could not convert inteface name"));
goto done;
}
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
SCLog(TRUE, LOG_ERR, CFSTR("socket() failed: %s"), strerror(errno));
goto done;
}
if (ioctl(sock, SIOCGIFMTU, (caddr_t)&ifr) < 0) {
goto done;
}
if (mtu_cur) *mtu_cur = ifr.ifr_mtu;
if (mtu_min || mtu_max) {
int ifType = 0;
io_iterator_t io_iter = 0;
io_registry_entry_t io_interface = 0;
io_registry_entry_t io_controller = 0;
kern_return_t kr;
mach_port_t masterPort = MACH_PORT_NULL;
CFMutableDictionaryRef matchingDict;
if (mtu_min) *mtu_min = -1;
if (mtu_max) *mtu_max = -1;
matchingDict = IOBSDNameMatching(masterPort, 0, ifr.ifr_name);
if (matchingDict) {
kr = IOServiceGetMatchingServices(masterPort, matchingDict, &io_iter);
if ((kr == KERN_SUCCESS) && io_iter) {
io_interface = IOIteratorNext(io_iter);
}
if (io_iter) IOObjectRelease(io_iter);
}
if (io_interface) {
CFNumberRef num;
num = IORegistryEntryCreateCFProperty(io_interface, CFSTR(kIOInterfaceType), NULL, kNilOptions);
if (num) {
if (isA_CFNumber(num)) {
CFNumberGetValue(num, kCFNumberIntType, &ifType);
}
CFRelease(num);
}
(void)IORegistryEntryGetParentEntry(io_interface, kIOServicePlane, &io_controller);
IOObjectRelease(io_interface);
}
if (io_controller) {
CFNumberRef num;
num = IORegistryEntryCreateCFProperty(io_controller, CFSTR(kIOMaxPacketSize), NULL, kNilOptions);
if (num) {
if (isA_CFNumber(num)) {
int value;
CFNumberGetValue(num, kCFNumberIntType, &value);
if (ifType == IFT_ETHER) {
value -= (ETHER_HDR_LEN + ETHER_CRC_LEN);
}
if (mtu_min) *mtu_min = IF_MINMTU;
if (mtu_max) *mtu_max = value;
}
CFRelease(num);
}
IOObjectRelease(io_controller);
}
}
ok = TRUE;
done :
if (sock >= 0) (void)close(sock);
return ok;
}