Linux ネットワーク プログラミング - struct ifreq および ioctl() システム コール

構造体 ifreq

struct ifreqさまざまなインターフェイス関連の入出力制御 (ioctl) 呼び出しに使用されるデータ構造です。その主な用途は、ネットワーク プログラミングでネットワーク インターフェイスのプロパティを取得および設定することです。この構造は<net/if.h>ヘッダー ファイルで定義されます。

struct ifreqの主なフィールドとその用途の一部を次に示します。

  1. ifr_name : 「eth0」、「wlan0」などのインターフェイスの名前を表す文字配列。

  2. ifr_addr :struct sockaddrインターフェイスのアドレスを表すタイプの構造体。

  3. ifr_netmask : これは、struct sockaddrインターフェイスのネットワーク マスクを表す型構造体でもあります。

  4. ifr_broadaddr : インターフェイスのブロードキャスト アドレスを表します。

  5. ifr_flags : (インターフェースIFF_UPが実行中)、(インターフェースIFF_BROADCASTがブロードキャストをサポートしている)、IFF_PROMISC(インターフェースが無差別モードである) など、インターフェースを表すフラグ。

  6. ifr_hwaddr : インターフェイスのハードウェア (通常は MAC) アドレスを表します。

  7. ifr_mtu : インターフェースの最大伝送単位 (MTU) を示します。

他にもいくつかのフィールドがありますが、上記のフィールドが最も一般的に使用されます。

インターフェイスの特定のプロパティをクエリまたは設定する場合は、struct ifreq適切なフィールドにデータが入力され、ioctl()システム コールが使用されます。たとえば、インターフェイスを無差別モードに設定するには、ifr_nameフィールドにデータを入力してIFF_PROMISCフラグを設定し、 を呼び出しますioctl()

例えば:

struct ifreq ifr;
int sockfd;

// 创建一个套接字
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
    
    
    perror("socket");
    exit(EXIT_FAILURE);
}

// 设置ifr_name为我们想查询或修改的接口名
strncpy(ifr.ifr_name, "eth0", IFNAMSIZ);

// 获取接口标志
if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
    
    
    perror("ioctl");
    close(sockfd);
    exit(EXIT_FAILURE);
}

// 打印是否处于混杂模式
if (ifr.ifr_flags & IFF_PROMISC) {
    
    
    printf("eth0 is in promiscuous mode\n");
} else {
    
    
    printf("eth0 is not in promiscuous mode\n");
}

close(sockfd);

以下は、ネットワーク インターフェイス (この場合は「eth0」) が無差別モードかどうかを確認する方法を示す簡単な例です。


ioctl() システムコール

ioctl()システム コールは、さまざまなデバイス固有の操作と制御に使用できる一般的なデバイス ドライバーまたはインターフェイス操作メカニズムを提供します。通常、通常のシステム コール ( read()、など)での表現に適していないデバイス固有の操作に使用されます。write()open()

関数プロトタイプ:

int ioctl(int fd, unsigned long request, ...);

パラメータ:

  1. fd : は、すでに開いているファイルまたはソケットのファイル記述子です。

  2. request : はデバイス固有のリクエスト コードです。このリクエスト コードは、実行する特定のアクションまたはコマンドを定義します。

  3. : これは、特定の に応じて変化するパラメータですrequestデータへのポインタである場合もあれば、直接値である場合もあります。

戻り値:

  • ioctl()戻り値は特定の操作によって異なります。通常、成功した場合は負でない値が返され、失敗した場合は -1 が返され、 set が返されますerrno

使用:

  1. ネットワーク プログラミング:ioctl()ネットワーク インターフェイスのパラメータのクエリと設定によく使用されます。たとえば、インターフェイスの IP アドレスの取得または設定、インターフェイスのフラグのクエリまたは設定などです。

  2. デバイス制御: さまざまなデバイス (端末、サウンド カード、グラフィックス カードなど) に対して、ioctl()特定の操作を実行したり、情報を照会したりするために使用できます。

  3. ファイルシステム: 場合によっては、ioctl()ファイルシステム操作にも使用できます。

いくつかの例:

  1. ネットワークインターフェースのフラグを取得します

    struct ifreq ifr;
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    strncpy(ifr.ifr_name, "eth0", IFNAMSIZ);
    ioctl(fd, SIOCGIFFLAGS, &ifr);
    
  2. ノンブロッキングソケットを設定します

    int flags = fcntl(fd, F_GETFL, 0);
    ioctl(fd, FIONBIO, &flags);
    
  3. オーディオデバイスのプロパティをクエリします

    int volume;
    ioctl(fd, SOUND_MIXER_READ_VOLUME, &volume);
    

予防:

  1. すべてのデバイスがすべての ioctl 操作をサポートしているわけではありませんデバイスがどのような操作をサポートしているかを知るには、特定のデバイスのドキュメントまたはヘッダー ファイルを参照する必要があります。

  2. エラー処理: 他のシステム コールと同様に、ioctl()を使用する場合は、適切なエラー処理を実装する必要があります。失敗して が設定される可能性があるerrnoため、戻り値をチェックし、それに応じてエラーを処理する必要があります。

  3. 移植性:ioctl()非常に一般的で強力な機能ですが、多くの場合、デバイスまたはプラットフォームに固有です。これは、異なるオペレーティング システムまたはハードウェア プラットフォームでは異なる ioctl リクエスト コードまたはパラメータが必要になる可能性があることを意味します。

要約すると、ioctl()デバイスと対話する方法が提供され、開発者がさまざまなデバイス固有の操作を実行できるようになります。ただし、これを正しく使用するには、特定のデバイスがどのように動作するかを深く理解する必要があります。


簡単な例を使用して、(たとえば) ネットワーク インターフェイスの IP アドレスioctl()を取得および設定する方法を示します。eth0

1. IPアドレスを取得する

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <unistd.h>

int main() {
    
    
    int sockfd;
    struct ifreq ifr;

    // 创建一个 socket
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
    
    
        perror("socket");
        exit(1);
    }

    // 指定要查询的接口名
    strncpy(ifr.ifr_name, "enp5s0", IFNAMSIZ);

    // 获取接口的 IP 地址
    if (ioctl(sockfd, SIOCGIFADDR, &ifr) < 0) {
    
    
        perror("ioctl - SIOCGIFADDR ");
        exit(1);
    }

    struct sockaddr_in *ipaddr = (struct sockaddr_in *)&ifr.ifr_addr;
    printf("IP address of enp5s0: %s\n", inet_ntoa(ipaddr->sin_addr));

    close(sockfd);
    return 0;
}

プログラムを実行した結果は次のようになります。

majn@tiger:~/C_Project/network_project$ ./ioctl_setip 
IP address of enp5s0: 192.168.31.6

2. IP アドレスを設定します (使用には注意してください!!!)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <unistd.h>

int main() {
    
    
    int sockfd;
    struct ifreq ifr;
    struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;

    // 创建一个 socket
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
    
    
        perror("socket");
        exit(1);
    }

    // 指定要设置的接口名
    strncpy(ifr.ifr_name, "eth0", IFNAMSIZ);

    // 设置新的 IP 地址
    sin->sin_family = AF_INET;
    inet_aton("192.168.1.100", &sin->sin_addr);

    if (ioctl(sockfd, SIOCSIFADDR, &ifr) < 0) {
    
    
        perror("ioctl - SIOCSIFADDR ");
        exit(1);
    }

    printf("IP address of eth0 set to: %s\n", inet_ntoa(sin->sin_addr));

    close(sockfd);
    return 0;
}

: IP アドレスの設定には通常 root 権限が必要なので、上記のコードを実行するときにこれを使用する必要がある場合がありますsudo

上記の例では、ioctl()ネットワーク インターフェイスの IP アドレスを照会または設定するために使用されます。SIOCGIFADDR具体的な操作は、 (アドレスの取得)とSIOCSIFADDR(アドレスの設定)で指定します。


inet_aton()

inet_aton()この関数は、ドット付き 10 進数形式の文字列 (「192.168.1.1」など) をネットワーク バイト オーダーの IPv4 アドレスに変換するために使用されます。<arpa/inet.h>ヘッダーファイルで定義されます。

関数プロトタイプ:

int inet_aton(const char *cp, struct in_addr *inp);

パラメータ:

  • cp : ドット付き 10 進数形式の IPv4 アドレス文字列へのポインタ。
  • inp :struct in_addr変換されたアドレスを格納するために使用される構造体へのポインタ。

戻り値:

  • 成功した場合は 1 を返し、失敗した場合は 0 を返します。

と比較するとinet_addr()inet_aton()関数はエラーを示すために特別なアドレス値を使用するのではなく、成功または失敗のステータスを明示的に返すため、より明確なエラー チェック メカニズムを提供します。

例:

#include <stdio.h>
#include <arpa/inet.h>

int main() {
    
    
    const char *ip_string = "192.168.1.1";
    struct in_addr ip_addr;

    if (inet_aton(ip_string, &ip_addr)) {
    
    
        printf("Conversion successful. Network byte order value: 0x%x\n", ip_addr.s_addr);
    } else {
    
    
        printf("Conversion failed.\n");
    }

    return 0;
}

上記のプログラムを実行した結果は次のようになります。

majn@tiger:~/C_Project/network_project$ ./inet_aton_demo 
Conversion successful. Network byte order value: 0x101a8c0

inet_aton()この例では、ドット付き 10 進数の IP アドレス文字列「192.168.1.1」を を使用した形式に変換しstruct in_addr、変換結果を出力します。

注:inet_aton()と はinet_ntoa()ペアであり、1 つは文字列をバイナリ形式に変換するために使用され、もう 1 つはその逆を行うために使用されます。スレッドセーフな方法を探している場合、または IPv6 アドレスを処理する必要がある場合は、代わりにinet_pton()および関数を使用することをお勧めします。inet_ntop()


inet_war()

inet_ntoa()ネットワーク バイト オーダーの IPv4 アドレスをドット付き 10 進文字列形式に変換するために使用されるネットワーク ユーティリティ関数です。<arpa/inet.h>ヘッダーファイルで定義されます。

関数プロトタイプ:

char *inet_ntoa(struct in_addr in);

パラメータ:

  • in : は、struct in_addrIPv4 アドレス (ネットワーク バイト オーダー) を含む構造体です。

戻り値:

  • IPv4 アドレスのドット付き 10 進数表現を表す、静的に割り当てられた文字列へのポインタを返します。静的な文字列が返されるため、後続のinet_ntoa()呼び出しによって以前の内容が上書きされる可能性があります。

予防:

  1. inet_ntoa()静的ストレージへのポインタを返します。これは、各呼び出しが前の結果を上書きすることを意味します。変換されたアドレスを保存する必要がある場合は、返された文字列を自分でコピーする必要があります。
  2. スレッドの安全性を考慮すると、inet_ntoa()マルチスレッド環境では問題が発生する可能性があります。これを回避するには、より最新のスレッドセーフな関数の使用を検討してくださいinet_ntop()

例:

#include <stdio.h>
#include <arpa/inet.h>

int main() {
    
    
    struct in_addr ip_addr;
    ip_addr.s_addr = htonl(0xC0A80101);  // 0xC0A80101 = 192.168.1.1 in hexadecimal

    char *ip_str = inet_ntoa(ip_addr);
    printf("IP address in dotted-decimal format: %s\n", ip_str);

    return 0;
}

この例では、struct in_addr構造体を作成し、(ネットワーク バイト オーダーで) IP アドレスを設定し、inet_ntoa()それを を使用して文字列に変換します。

上記のプログラムを実行した結果は次のようになります。

majn@tiger:~/C_Project/network_project$ ./inet_ntoa_demo 
IP address in dotted-decimal format: 192.168.1.1

おすすめ

転載: blog.csdn.net/weixin_43844521/article/details/133348367