以前ネットワークをキャプチャするコマンドラインツールを作成しましたが、ioctl を使ってネットワークインターフェイス名を取得していました。探索を I/O Kit の流儀でおこなえるようにします。その前にごくごく簡単に I/O Kit について。
I/O Kit は 以前の Mac OS 9 や流れをくむ FreeBSD のドライバモデルの機能が不十分なので、Mac OS X のために Apple が開発したものです。そして、
I/O Kit は Mac OS X でデバイスドライバを作成するためのシステムフレームワーク、ライブラリ、ツール、その他リソースの集まりです。マルチスレッドのカーネルでの使用に適さない機能を省略した、制限された形式の C++ で実装されているオブジェクト指向プログラミングモデルに基づいています。I/O Kit は Mac OS X システムに接続されているハードウェアをモデル化することによって、特定のカテゴリのなかでデバイスに共通の機能を抽象化し、デバイスドライバ開発過程を効率化します。
なるほど。さらに、
I/O Kit とは、カーネルに常駐していて、システムハードウェアのモデルを提供する Darwin 中にあるオブジェクト指向の環境です。サービスやデバイスの各タイプはファミリのひとつ以上の C++ のクラスで表現されています。それぞれ利用可能なサービスやデバイスはそのクラスのインスタンス(オブジェクト)によって表現されています。
つまり?
I/O Kit とは
- I/O Kit とはデバイスドライバを開発するためのフレームワークのこと。目的の機能を持つデバイスドライバがない場合、デバイスドライバを開発することになります。限定された C++ を使って目的の機能を実現するデバイスドライバを作成します。
- I/O Kit とはカーネルに常駐するシステムハードウェアモデルを提供する、オブジェクトのこと。Mac OS X に接続されているハードウェアのデバイスやドライバの情報を管理しています。デバイスやドライバを利用する場合は I/O Kit から目的のデバイスやドライバを絞り込み、I/O Kit が提供する目的のインターフェイスを使用します。
ioctl を使ってネットワークインターフェイス名を取得する
+ (NSArray *)interfaces
{
int socketFD,length, lastLength;
char *buffer;
struct ifconf ifc;
socketFD = socket(AF_INET, SOCK_DGRAM, 0);
lastLength = 0;
length = sizeof(struct ifreq) * 100; // 初期値
/*//////////////////////////////////////////////////////////////////////////
ioctl を使って ネットワークインターフェイスの情報を問い合わせる。
この方法は SIOCGIFCONF が適切な情報を全て必ず返しているかは
バッファに収まるかどうかによるので、
ifc構造体の長さが前回に取得した長さと一致した場合に
全ての情報を適切に取得できたと看做している。
/////////////////////////////////////////////////////////////////////////*/
for (; ;) {
buffer = (char *)calloc(length, sizeof(char));
ifc.ifc_len = length;
ifc.ifc_ifcu.ifcu_buf = buffer;
if ( ioctl(socketFD, SIOCGIFCONF, &ifc) < 0) {
free(buffer);
return nil;
}
else {
if (ifc.ifc_len == lastLength) {
break;
}
lastLength = ifc.ifc_len;
}
length += sizeof(struct ifreq) * 10;
free(buffer);
}
close(socketFD);
struct ifreq *ifr = (struct ifreq *)buffer;
int next = 0;
NSMutableArray *interfaces = [NSMutableArray array];
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
while (ifr < (struct ifreq *)(buffer + lastLength)) {
ifr = (struct ifreq *)(buffer + next);
NSString *name = [NSString stringWithCString:ifr->ifr_name
encoding:NSUTF8StringEncoding];
if ([name isEqualToString:@""] == NO) {
// 同じ名前のインターフェイスはいらない。
[interfaces removeObject:name];
[interfaces addObject:name];
}
next += IFNAMSIZ + ifr->ifr_ifru.ifru_addr.sa_len;
}
free(buffer);
[pool drain];
return (NSArray *)interfaces;
}
I/O Kit を使ってネットワークインターフェイス名を取得する
+ (NSArray *)interfaces
{
IOReturn ioReturnCode;
mach_port_t masterPort;
CFMutableDictionaryRef matchingDictionary;
io_iterator_t iterator;
io_object_t object;
NSMutableArray *interfaces = [NSMutableArray array];
// マスターポートの取得
ioReturnCode = IOMasterPort(MACH_PORT_NULL, &masterPort);
if (ioReturnCode != KERN_SUCCESS) {
NSLog(@"IOMasterPort did not return KERN_SUCCESS");
return nil;
}
// ディクショナリの作成
matchingDictionary = IOServiceMatching(kIOEthernetInterfaceClass);
if (matchingDictionary == NULL) {
NSLog(@"IOServiceMatching returned a NULL Dictionary.");
return nil;
}
// I/O Registry の中から辞書に一致するものを検索する
ioReturnCode = KERN_SUCCESS;
ioReturnCode = IOServiceGetMatchingServices(masterPort,
matchingDictionary,
&iterator);
if (ioReturnCode != KERN_SUCCESS) {
NSLog(@"IOServiceGetMatchingServicesdid did not return KERN_SUCCESS.");
return nil;
}
while (object = IOIteratorNext(iterator)) {
CFStringRef deviceName = NULL;
// 一致したオブジェクトから BSD 名を取得する。
deviceName = IORegistryEntryCreateCFProperty(object,
CFSTR(kIOBSDNameKey),
kCFAllocatorDefault,
0);
if (deviceName)
[interfaces addObject:(NSString *)deviceName];
CFRelease(deviceName);
}
IOObjectRelease(object);
}
return (NSArray *)interfaces;
}
ioctl のではバッファに収まったかどうかで取得できたと看做しているのが少し気持ち悪い感がありましたが、I/O Kit は的確に答えを出してくれています。BPF のフィルタマシンに渡すプログラムのコンパイルを libpcap に肩代わりさせていましたが、こうゆうことまで I/O Kit を通して出来るのでしょうか?
0 件のコメント:
コメントを投稿