Quantcast
Channel: Bashタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 2861

IPv4アドレスの取得(5言語間の移植用)

$
0
0
リモート接続をするコンピュータ上でプログラムを動かすとき、何かしら固有のIDを自動的に振りたいときがあります。 それはPythonで使いたいときがあれば、C++で使いたいと思うときがあったり、Bashで使いたいと思うときもあったり… しかし、自分でいちいち探してカスタマイズするのも面倒に感じ、ほとんど同じ挙動をするIPv4アドレス取得プログラムを作ろうと思いました。 この記事では、Raspberry Piでよく使われる言語であるC・C++・Python・Bash・Rustの5種類においてIPv4アドレスを取得する関数を作成しました。プログラム作成や移植に役立ててください。 動作環境 OS : Ubuntu20 Python : Python3.8 C++/C : cmake version 3.16.3 Rust : cargo 1.54.0 共通の挙動 プログラムは全て以下のリポジトリにUPしています。 実際のGitHubのソースコードと異なって省略している部分があります。 (例:引数チェックの省略) get_ip4_address(string) 引数:対象のネットワークアダプタeth0, docker0 返り値:文字列のIPv4アドレス get_ip4_address_all() lo以外のIPv4アドレスの文字列が格納されたリストを作成します。ネットワークアダプタ名とのリンクは行っていません。(プログラムを改造したらできると思います。) ※C言語は実装していません。 引数:なし 返り値:lo以外のIPv4アドレスのリスト(C++はVector(std::string)型で返されます) 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() C++は可変のリストをVectorで代用します。 // 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 -v 127は、lo除外のために使用しています。$1をwlan0などにして実行してみてください。 echo `ip -4 a | grep $1 | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -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 リンク

Viewing all articles
Browse latest Browse all 2861

Trending Articles