Qiitaから引っ越ししました。(せっかくコメントを残してくださったのに申し訳ない…)
リモート接続をするコンピュータ上でプログラムを動かすとき、何かしら固有のIDを自動的に振りたいときがあります。
それはPythonで使いたいときがあれば、C++で使いたいと思うときがあったり、Bashで使いたいと思うときもあったり…
しかし、自分でいちいち探してカスタマイズするのも面倒に感じ、ほとんど同じ挙動をするIPv4アドレス取得プログラムを作ろうと思いました。
この記事では、Raspberry Piでよく使われる言語であるC・C++・Python・Bash・Rustの5種類においてIPv4アドレスを取得する関数を作成しました。プログラム作成や移植に役立ててください。
動作環境
共通の挙動
プログラムは全て以下のリポジトリにUPしています。
https://github.com/Ar-Ray-code/ip4_detector
実際のGitHubのソースコードと異なって省略している部分があります。
(例:引数チェックの省略)
get_ip4_address(string)
- 引数:対象のネットワークアダプタ
eth0, docker0
- 返り値:文字列のIPv4アドレス
get_ip4_address_all()
lo
以外のIPv4アドレスの文字列が格納されたリストを作成します。ネットワークアダプタ名とのリンクは行っていません。(プログラムを改造したらできると思います。)
※C言語は実装していません。
C/C++
CとC++はIPv4アドレスを取得する部分については同じプログラムで動きます。
参考サイト
- https://www.geekpage.jp/programming/linux-network/get-ipaddr.php
- https://www.geekpage.jp/programming/linux-network/get-iflist.php
get_ip4_address(string)
// get_ip.h #ifndef _GET_IP_H_ #define _GET_IP_H_ #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <netinet/in.h> #include <net/if.h> #include <arpa/inet.h> #define MAX_IFR 10 //取得するネットワークアダプタの上限値 char* get_ip4_address(const char* ifname) { int fd = socket(AF_INET, SOCK_DGRAM, 0); struct ifreq ifr; ifr.ifr_addr.sa_family = AF_INET; strncpy(ifr.ifr_name, ifname, IFNAMSIZ-1); ioctl(fd, SIOCGIFADDR, &ifr); close(fd); return inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr); } #endif
// show_ip4.cpp show_ip4.c 共通 #include "get_ip.h" int main(int argc, char *argv[]) { printf("%s\n",get_ip4_address(argv[1])); }
get_ip4_address_all()
// show_ip4_all.cpp #include "get_ip.h" #include <iostream> #include <vector> #include <sstream> std::vector<std::string> get_ip4_address_all() { struct ifreq ifr[MAX_IFR]; struct ifconf ifc; int fd; int nifs, i; char* name[MAX_IFR] = {}; auto ip = new std::vector<std::string>(); //初期は空っぽ fd = socket(AF_INET, SOCK_DGRAM, 0); ifc.ifc_len = sizeof(ifr); ifc.ifc_ifcu.ifcu_buf = (caddr_t) ifr; ioctl(fd, SIOCGIFCONF, &ifc); nifs = ifc.ifc_len / sizeof(struct ifreq); int target_ips_count = 0; for (i=0; i<nifs; i++) { // if not "lo" if (strncmp(ifr[i].ifr_name, "lo", 2) != 0) { name[target_ips_count] = ifr[i].ifr_name; target_ips_count++; } } // 1度ネットワークアダプタを閉じる close(fd); // get_ip4_address()を実行して返り値を追加していく for (i=0; i<target_ips_count; i++) { ip->push_back(std::string(get_ip4_address(name[i]))); } return *ip; } int main(int argc, char *argv[]) { // IPアドレスのリストを取得 auto ip_all = get_ip4_address_all(); for (int i=0; i<ip_all.size(); i++) { std::cout << ip_all[i] << std::endl; } }
実行
cd c++_unix/ make ./show_ip4_all_exe
Python
Pythonは、psutilとnetifacesモジュールを使います。
get_ip4_address(string)
#!/bin/python3 import netifaces as ni import psutil import sys def ip_get(interface:str)->str: i = 0 result = [] address_list = psutil.net_if_addrs() for nic in address_list.keys(): if (interface in ni.interfaces()[i]): ip = ni.ifaddresses(ni.interfaces()[i])[ni.AF_INET][0]['addr'] return ip i += 1 return "0.0.0.0" if __name__=='__main__': argv = sys.argv[1] print(ip_get(argv))
get_ip4_address_all()
#!/bin/python3 import netifaces as ni import psutil def ip_get(): i = 0 result = [] address_list = psutil.net_if_addrs() for nic in address_list.keys(): if ("lo" not in ni.interfaces()[i]): ip = ni.ifaddresses(ni.interfaces()[i])[ni.AF_INET][0]['addr'] result.append(ip) i = i + 1 return result if __name__=='__main__': print(ip_get())
Rust
Rustはpnetというモジュールを使ってIPアドレスを取得できます。Cargo使ってください。
# Cargo.toml [package] name = "show_ip4" version = "0.1.0" edition = "2018" [dependencies] pnet = "0.28.0"
get_ip4_address(&str)
extern crate pnet; use std::env; use pnet::datalink; fn get_ip4_address(interface_name: &str) -> String{ for iface in datalink::interfaces() { let ips = iface.ips; let mut i = 0; if !(iface.name == "lo") { for ip in ips { if i == 0 { let addr = ip.to_string(); if iface.name == interface_name { let addr_fixed: Vec<&str> = addr.split("/").collect(); return addr_fixed[0].to_string(); } } i += 1; } } } return "".to_string(); } fn main() { let args: Vec<String> = env::args().collect(); println!("{}", get_ip4_address(&args[1])); }
get_ip4_address_all()
extern crate pnet; use pnet::datalink; fn get_ip4_address_all() -> Vec<String> { let mut vec_ip4 = Vec::new(); for iface in datalink::interfaces() { let ips = iface.ips; let mut i = 0; if !(iface.name == "lo") { for ip in ips { if i == 0 { let addr = ip.to_string(); let addr_fixed: Vec<&str> = addr.split("/").collect(); vec_ip4.push(addr_fixed[0].to_string()); } i += 1; } } } return vec_ip4; } fn main() { println!("{:?}", get_ip4_address_all()); }
Bash
Bashは自分自身でネットワーク接続確認とリセットが可能なので、BashでのIPアドレス取得を推奨します。コマンドを工夫することで1行で取得可能です。
また、ネットワークアダプタ名の一覧を取得することが容易なので、他のプログラムにおけるshow_ip4_address
の引数にしてもいいと思います。
変数に代入する場合は、Bashファイル上で次のように記載してください。
A=`ip -4 a | grep -oP '(?<=:\s).+(?=:)'` echo $A
show_interfaces
echo `ip -4 a | grep -oP '(?<=:\s).+(?=:)'` # 実行結果例:lo wlan0 docker0
show_ip4
egrep -v '^127'
は、lo
除外のために使用しています。$1
をwlan0
などにして実行してみてください。
echo `ip -4 a | grep $1 | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | egrep -v '^127'` # 実行結果例:172.17.0.1(引数$1がdocker0だった場合)
show_ip4_all
echo `ip -4 a | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -v 127` # 実行結果例:10.2.82.26 172.17.0.1
リンク
https://github.com/Ar-Ray-code/ip4_detector
https://www.geekpage.jp/programming/linux-network/get-ipaddr.php
https://www.geekpage.jp/programming/linux-network/get-iflist.php