Linux ioctlによるネットワークインタフェース情報の取得手法

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_flagsIFF_*)はインタフェースの現在状態をビットマスクで表現します。主な定義は以下の通りです。

  • IFF_UP: 論理的に有効化されているか
  • IFF_RUNNING: 物理リンクが確立されているか(RFC2863 OPER_UP)
  • IFF_LOOPBACK: ループバックデバイスであるか
  • IFF_BROADCAST: ブロードキャストアドレスが有効か
  • IFF_MULTICAST: マルチキャスト対応か
  • IFF_PROMISC: プロミスキャスモードか

ioctlリクエストコード

sockios.h では SIOC プレフィックス付きのコマンド定数が提供されます。ネットワーク操作において頻出する代表的なコードは以下の通りです。

リクエストコード動作内容データ型
SIOCGIFCONF全インタフェース構成の取得struct ifconf
SIOCGIFADDRIPv4アドレスの取得struct ifreq
SIOCSIFADDRIPv4アドレスの設定struct ifreq
SIOCGIFFLAGSインタフェースフラグの取得struct ifreq
SIOCSIFFLAGSインタフェースフラグの設定struct ifreq
SIOCGIFHWADDRMACアドレスの取得struct ifreq
SIOCGIFMTUMTU値の取得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;
}

タグ: linux C Network-Programming ioctl syscalls

6月27日 17:29 投稿