ioctlシステムコールとネットワーク制御の概要
Linuxカーネルのデバイスドライバやネットワークスタックとユーザ空間プログラムが通信する際、ioctl() が標準的な制御インターフェースとして利用されます。ソケットやキャラクタデバイスなどのファイルディスクリプタに対して特定のリクエストコードを渡すことで、ネットワークインタフェースのIPv4/IPv6アドレス、MTU、リンク状態、MACアドレスの読み書きが可能になります。
コアデータ構造とヘッダー定義
ネットワーク設定の操作は主に <net/if.h> および <linux/sockios.h> で定義される構造体に依存しています。
ifreq:単一インタフェースのパラメータ保持
struct ifreq は1つのネットワークデバイスに関する設定や状態を格納します。インタフェース名(ifr_name)と共用体領域(ifr_ifru)で構成され、共用体部分は目的に応じてIPアドレス(ifr_addr)、ハードウェアアドレス(ifr_hwaddr)、フラグ値(ifr_flags)、MTU(ifr_mtu)などを指し示します。
ifconf:複数インタフェースリストの取得用
システムに存在するすべてのインタフェース情報を一括取得する際は struct ifconf を使用します。この構造体はバッファ長を示す ifc_len と、実際のデータ配列へのポインタ ifc_req を保持します。カーネル空間とユーザ空間間でサイズ調整が必要なため、通常は初期サイズで呼び出し、必要に応じてバッファを拡張する方式が採られます。
動作フラグの定義
enum net_device_flags(IFF_*)はインタフェースの現在状態をビットマスクで表現します。主な定義は以下の通りです。
IFF_UP: 論理的に有効化されているかIFF_RUNNING: 物理リンクが確立されているか(RFC2863 OPER_UP)IFF_LOOPBACK: ループバックデバイスであるかIFF_BROADCAST: ブロードキャストアドレスが有効かIFF_MULTICAST: マルチキャスト対応かIFF_PROMISC: プロミスキャスモードか
ioctlリクエストコード
sockios.h では SIOC プレフィックス付きのコマンド定数が提供されます。ネットワーク操作において頻出する代表的なコードは以下の通りです。
| リクエストコード | 動作内容 | データ型 |
|---|---|---|
| SIOCGIFCONF | 全インタフェース構成の取得 | struct ifconf |
| SIOCGIFADDR | IPv4アドレスの取得 | struct ifreq |
| SIOCSIFADDR | IPv4アドレスの設定 | struct ifreq |
| SIOCGIFFLAGS | インタフェースフラグの取得 | struct ifreq |
| SIOCSIFFLAGS | インタフェースフラグの設定 | struct ifreq |
| SIOCGIFHWADDR | MACアドレスの取得 | struct ifreq |
| SIOCGIFMTU | MTU値の取得 | struct ifreq |
実装パターン:特定インタフェースのIP取得
インタフェース名を指定し、そのデバイスに割り当てられたIPv4アドレスを抽出する処理です。UDPソケットを介してカーネルと通信します。
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <net/if.h>
#include <sys/ioctl.h>
int extract_iface_ipv4(const char *dev_name) {
int sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd < 0) {
perror("Failed to create socket");
return -1;
}
struct ifreq target_info;
memset(&target_info, 0, sizeof(target_info));
strncpy(target_info.ifr_name, dev_name, IFNAMSIZ - 1);
if (ioctl(sock_fd, SIOCGIFADDR, &target_info) == -1) {
perror("ioctl SIOCGIFADDR failed");
close(sock_fd);
return -1;
}
struct sockaddr_in *ipv4_ptr = (struct sockaddr_in *)&target_info.ifr_addr;
printf("Device: %s - Address: %s\n", dev_name, inet_ntoa(ipv4_ptr->sin_addr));
close(sock_fd);
return 0;
}
実装パターン:全インタフェースの詳細列挙
システム上のすべてのネットワークデバイスに対して、IPアドレス、リンク状態、およびハードウェアアドレスをまとめて出力します。ifconf の初期サイズを渡し、カーネルが返す実際のサイズを基に配列を走査します。共用体の仕様上、フラグやMACアドレスは別途 ioctl を呼び出して取得する必要があります。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <net/if.h>
#include <sys/ioctl.h>
#define BUF_INITIAL_SIZE (1024 * 4)
static void decode_link_state(short flags) {
printf("\tState: [");
if (flags & IFF_UP) printf(" UP ");
if (flags & IFF_RUNNING) printf(" RUNNING ");
if (flags & IFF_LOOPBACK) printf(" LOOPBACK ");
if (flags & IFF_BROADCAST)printf(" BROADCAST ");
if (flags & IFF_MULTICAST)printf(" MULTICAST ");
printf("]\n");
}
static void fetch_full_details(int sock_fd, struct ifreq *entry) {
printf("Interface: %-10s", entry->ifr_name);
// IP address extraction
struct sockaddr_in *addr = (struct sockaddr_in *)&entry->ifr_addr;
printf("IPv4: %-15s", inet_ntoa(addr->sin_addr));
// Flags retrieval
if (ioctl(sock_fd, SIOCGIFFLAGS, entry) == 0) {
decode_link_state(entry->ifr_flags);
} else {
printf("\t[Flags fetch failed]\n");
}
// MAC address retrieval
if (ioctl(sock_fd, SIOCGIFHWADDR, entry) == 0) {
unsigned char *mac = (unsigned char *)entry->ifr_hwaddr.sa_data;
printf("\tMAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
printf("\n");
}
int scan_all_network_devices(void) {
int ctrl_sock = socket(AF_INET, SOCK_DGRAM, 0);
if (ctrl_sock < 0) {
perror("Socket creation failed");
return -1;
}
char *buffer = malloc(BUF_INITIAL_SIZE);
if (!buffer) {
perror("Memory allocation failed");
close(ctrl_sock);
return -1;
}
struct ifconf config_block;
config_block.ifc_len = BUF_INITIAL_SIZE;
config_block.ifc_buf = buffer;
if (ioctl(ctrl_sock, SIOCGIFCONF, &config_block) < 0) {
perror("ioctl SIOCGIFCONF failed");
free(buffer);
close(ctrl_sock);
return -1;
}
int total_entries = config_block.ifc_len / sizeof(struct ifreq);
struct ifreq *current = (struct ifreq *)config_block.ifc_buf;
printf("--- Scanned %d interfaces ---\n", total_entries);
for (int idx = 0; idx < total_entries; idx++) {
fetch_full_details(ctrl_sock, current + idx);
}
free(buffer);
close(ctrl_sock);
return 0;
}