以下の内容はhttps://thinkami.hatenablog.com/entry/2025/09/15/212825より取得しました。


Rust + pnetクレートで、macのインタフェースの一覧を表示してみた

以前、ネットワークまわりをPythonで実装してみたことがありました。

 
Rustでもネットワークまわりを実装したくなり、以下の書籍を写経したりしました。
書籍「Rustで始めるネットワークプログラミング」を写経した - メモ的な思考的な

 
次は、Rustでもう少し低レイヤーあたりをさわってみようと考えて調べたところ、Rustと pnet クレートを使うことでデータリンク層の通信も実装できそうでした。 https://github.com/libpnet/libpnet

 
まず最初に、 pnet クレートのstruct NetworkInterface まわりをさわってみたことから、メモを残します。

 
目次

 

環境

  • mac
  • Rust 1.89.0
  • pnet 0.35.0
  • RustRover 2025.2.1

 

環境構築

手元のmacにはRust環境がなかったため、Rustの公式ドキュメントに従い環境構築をしました。
Install Rust - Rust Programming Language

 
curl でインストールします。

% curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

**info:** downloading installer

**Welcome to Rust!**

This will download and install the official compiler for the Rust programming language, and its package manager, Cargo.

Rustup metadata and toolchains will be installed into the Rustup home directory, located at:

  /Users/<UserName>/.rustup

This can be modified with the RUSTUP_HOME environment variable.

The Cargo home directory is located at:

  /Users/<UserName>/.cargo

This can be modified with the CARGO_HOME environment variable.

The **cargo**, **rustc**, **rustup** and other commands will be added to Cargo's bin directory, located at:

  /Users/<UserName>/.cargo/bin

This path will then be added to your **PATH** environment variable by modifying the profile files located at:

  /Users/<UserName>/.profile
  /Users/<UserName>/.zshenv

You can uninstall at any time with **rustup self uninstall** and these changes will be reverted.

Current installation options:

   default host triple: **aarch64-apple-darwin**
     default toolchain: **stable (default)**
               profile: **default**
  modify PATH variable: **yes**

1) Proceed with standard installation (default - just press enter)
2) Customize installation
3) Cancel installation

> (1を選択してEnter)

 
選択した後はインストールが続きます。

**info:** profile set to 'default'
**info:** default host triple is aarch64-apple-darwin
**info:** syncing channel updates for 'stable-aarch64-apple-darwin'
**info:** latest update on 2025-08-07, rust version 1.89.0 (29483883e 2025-08-04)
**info:** downloading component 'cargo'
**info:** downloading component 'clippy'
**info:** downloading component 'rust-docs'
**info:** downloading component 'rust-std'
**info:** downloading component 'rustc'
**info:** downloading component 'rustfmt'
**info:** installing component 'cargo'
**info:** installing component 'clippy'
**info:** installing component 'rust-docs'
**info:** installing component 'rust-std'
**info:** installing component 'rustc'
**info:** installing component 'rustfmt'
**info:** default toolchain set to 'stable-aarch64-apple-darwin'
  **stable-aarch64-apple-darwin installed** - rustc 1.89.0 (29483883e 2025-08-04)
**Rust is installed now. Great!**

To get started you may need to restart your current shell.

This would reload your **PATH** environment variable to include Cargo's bin directory ($HOME/.cargo/bin).

To configure your current shell, you need to source the corresponding **env** file under $HOME/.cargo.

This is usually done by running one of the following (note the leading DOT):

. "$HOME/.cargo/env"            # For sh/bash/zsh/ash/dash/pdksh

source "$HOME/.cargo/env.fish"  # For fish

source $"($nu.home-path)/.cargo/env.nu"  # For nushell

 
インストールはできたものの

This is usually done by running one of the following

と言われているため、以下を実行しました。

% . "$HOME/.cargo/env"

 
続いて、今回のプログラム用に cargo newします。

% cargo new list_network_interfaces

    **Creating** binary (application) `list_network_interfaces` package

**note****:** see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

 
最後に、 cargo.toml に対し pnet クレートへの依存を追加します。

[dependencies]  
pnet = "0.35.0"

 

pnetのinterfaces関数でネットワークインタフェースを列挙

pnetのGitHubリポジトリでは、 examplesディレクトリの中にネットワークインタフェースを列挙する例があります。
https://github.com/libpnet/libpnet/blob/v0.35.0/examples/list_interfaces.rs

例では {}を使っていますが、 interfaces() 関数の戻り値である NetworkInterfaceでは Debug traitも実装していました。

そこで、Debug trait用に {?:}も追加し、両者の違いを見てみることにしました。

use pnet::datalink;  
  
fn main() {  
    println!("Network Interfaces");  
    println!("================================>");  
    println!();  
  
    print_interfaces();  
  
    print_interface_fields();  
}  
  
fn print_interfaces() {  
    for interface in datalink::interfaces() {  
        println!("=========================>");  
        // Displayトレイトでの表示  
        println!("{}", interface);  
  
        println!("================");  
  
        // Debugトレイトでの表示  
        println!("{:?}", interface);  
        println!("<=========================");  
    }  
}

 
手元のmacで実行してみたところ、結果が表示されました。以下は抜粋 & マスク済です。

Display traitのほうが読みやすいですが、実装する上ではDebug traitのほうが分かりやすく感じました。

Network Interfaces
================================>
=========================>
lo0: flags=8049<UP,LOOPBACK,MULTICAST,RUNNING>
      index: 1
      ether: 00:00:00:00:00:00
       inet: 127.0.0.1/8
      inet6: ::1/128
      inet6: fe80::1/64
================
NetworkInterface { name: "lo0", description: "", index: 1, mac: Some(00:00:00:00:00:00), ips: [V4(Ipv4Network { addr: 127.0.0.1, prefix: 8 }), V6(Ipv6Network { addr: ::1, prefix: 128 }), V6(Ipv6Network { addr: fe80::1, prefix: 64 })], flags: 32841 }
<=========================
...
=========================>
en1: flags=8863<UP,BROADCAST,MULTICAST,RUNNING>
      index: 15
      ether: **:**:**:**:**:**
      inet6: ****::****:****:****:****/64
      inet6: ****::****:****:****:****/64
      inet6: ****::****:****:****:****/64
       inet: 192.168.***.***/24
================
NetworkInterface { name: "en1", description: "", index: 15, mac: Some(**:**:**:**:**:**), ips: [V6(Ipv6Network { addr: ****::****:****:****:****, prefix: 64 }), V6(Ipv6Network { addr: ****::****:****:****:****, prefix: 64 }), V6(Ipv6Network { addr: ****::****:****:****:****, prefix: 64 }), V4(Ipv4Network { addr: 192.168.***.***, prefix: 24 })], flags: 34915 }
<=========================

 
上記では省略しましたが、

  • gif0
  • stf0
  • anpi0
  • bridge0
  • utun1
  • ap1
  • awdl0
  • llw0

などのインタフェースが表示されました。

 

各インタフェースの詳細を確認する

NetworkInterfaceのドキュメントを読むと、

  • is_up()
  • is_loopback()

などのメソッドも用意されているようでした。
https://docs.rs/pnet/latest/pnet/datalink/struct.NetworkInterface.html

そこで、どんな値を取得できるか試してみます。

use pnet::datalink;  
  
fn main() {  
    println!("Network Interfaces");  
    println!("================================>");  
  
    print_interfaces();  
  
    // 追加
    print_interface_fields();  
}  
  
fn print_interface_fields() {  
    for interface in datalink::interfaces() {  
        println!("=========================>");  
  
        // インタフェース名  
        println!("Name: {:?}", interface.name);  
  
        // インターフェースの説明  
        println!("Description: {:?}", interface.description);  
  
        // MACアドレスがあれば、そのMACアドレスを表示  
        match interface.mac {  
            Some(mac) => println!("MAC: {:?}", mac),  
            None => println!("Macアドレスはありません"),  
        }  
  
        // IPアドレスがあれば、そのIPアドレスの一覧を表示  
        if interface.ips.is_empty() {  
            println!("IPアドレスがありません")  
        } else {  
            // 所有権の移動を防ぐため、interfaceに `&` を使って参照だけを使う  
            for ip in &interface.ips {  
                println!("IP: {:?}", ip);  
            }  
        }  
        // インタフェースの状態  
        println!("Status: {}", if interface.is_up() { "UP" } else { "DOWN" });  
  
        // ループバックインタフェースかどうかを確認  
        println!("Loopback: {}", if interface.is_loopback() { "YES" } else { "NO" });  
  
        // ブロードキャストに対応しているか  
        println!("Broadcast: {}", if interface.is_broadcast() { "YES" } else { "NO" });  
  
        // マルチキャストに対応しているか  
        println!("Multicast: {}", if interface.is_multicast() { "YES" } else { "NO" });  
  
        // P2Pに対応しているか  
        println!("P2P: {}", if interface.is_point_to_point() { "YES" } else { "NO" });  
  
        println!("<=========================");  
    }  
}

 
実行結果を抜粋します。

Name: "lo0"
Description: ""
MAC: 00:00:00:00:00:00
IP: V4(Ipv4Network { addr: 127.0.0.1, prefix: 8 })
IP: V6(Ipv6Network { addr: ::1, prefix: 128 })
IP: V6(Ipv6Network { addr: fe80::1, prefix: 64 })
Status: UP
Loopback: YES
Broadcast: NO
Multicast: YES
P2P: NO
<=========================
=========================>
Name: "en1"
Description: ""
MAC: **:**:**:**:**:**
IP: V6(Ipv6Network { addr: ****::****:****:****:****, prefix: 64 })
IP: V6(Ipv6Network { addr: ****::****:****:****:****, prefix: 64 })
IP: V6(Ipv6Network { addr: ****::****:****:****:****, prefix: 64 })
IP: V4(Ipv4Network { addr: 192.168.***.***, prefix: 24 })
Status: UP
Loopback: NO
Broadcast: YES
Multicast: YES
P2P: NO

 
他のインタフェースだと、P2PなどがYESとなっているものもありました。

Name: "gif0"
Description: ""
MAC: 00:00:00:00:00:00
IPアドレスがありません
Status: DOWN
Loopback: NO
Broadcast: NO
Multicast: YES
P2P: YES

 

ソースコード

GitHubに上げました。
https://github.com/thinkAmi-sandbox/rust_network_programming-practice

今回のプルリクはこちら。
https://github.com/thinkAmi-sandbox/rust_network_programming-practice/pull/1




以上の内容はhttps://thinkami.hatenablog.com/entry/2025/09/15/212825より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14