リモート接続をするコンピュータ上でプログラムを動かすとき、何かしら固有の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
リンク
↧