プログラミングでメシが食えるか!?

Linuxのifconfigコマンドとipコマンド

»

このところ忙しくて、まじめな話しは全く書いていなかった気もしますので、たまには。。

LinuxでネットワークインターフェースにIPアドレスを設定したり、状態を確認したりするときには、古くからのUNIX系OSに慣れた人はifconfigコマンドを使います。私もそうでした。が、最近はipコマンドを使えということになり、そのうちifconfigコマンドはサポートされなくなってしまうかも知れません。まあ、そうは言っても、ifconfigコマンドも使えるからいいじゃない、と思っていたのですが、ifconfigコマンドでは困る場面に出くわしてしまいました。

1つのネットワークインターフェースに複数のIPアドレスを指定したい場合、ifconfigコマンドでは、

# ifconfig eth5 192.168.33.228/24
# ifconfig eth5:1 192.168.34.228/24

という感じにすると、eth0というネットワークインターフェースに2つのIPアドレスを持たせることができ、ifconfigコマンドを実行すると、

eth5      Link encap:Ethernet  HWaddr 00:0C:29:F2:38:CD
          inet addr:192.168.33.228  Bcast:192.168.33.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fef2:38cd/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:293578 errors:0 dropped:0 overruns:0 frame:0
          TX packets:21 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:18606362 (17.7 MiB)  TX bytes:1510 (1.4 KiB)
          Base address:0x20c0 Memory:d10a0000-d10c0000

eth5:1    Link encap:Ethernet  HWaddr 00:0C:29:F2:38:CD
          inet addr:192.168.34.228  Bcast:192.168.34.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          Base address:0x20c0 Memory:d10a0000-d10c0000

と確認することができます。

同じようなことを、ipコマンドでは以下のようにできます。

# ip addr add 192.168.33.228/24 dev eth5
# ip addr add 192.168.34.228/24 dev eth5

この状態でifconfigコマンドで確認してみると・・・

eth5      Link encap:Ethernet  HWaddr 00:0C:29:F2:38:CD
          inet addr:192.168.33.228  Bcast:0.0.0.0  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fef2:38cd/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:293581 errors:0 dropped:0 overruns:0 frame:0
          TX packets:22 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:18606542 (17.7 MiB)  TX bytes:1588 (1.5 KiB)
          Base address:0x20c0 Memory:d10a0000-d10c0000

1つしか出てこないのです。しかし、ipコマンドで確認すると、

7: eth5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether 00:0c:29:f2:38:cd brd ff:ff:ff:ff:ff:ff
    inet 192.168.33.228/24 scope global eth5
    inet 192.168.34.228/24 scope global eth5
    inet6 fe80::20c:29ff:fef2:38cd/64 scope link
       valid_lft forever preferred_lft forever

eth5に2つのIPアドレスが表示されます!

実はifconfigと同じようにするためには、2つ目を、

# ip addr add 192.168.34.228/24 dev eth5 label eth5:1

と追加すれば、ifconfigコマンドで、

eth5      Link encap:Ethernet  HWaddr 00:0C:29:F2:38:CD
          inet addr:192.168.33.228  Bcast:0.0.0.0  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fef2:38cd/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:293983 errors:0 dropped:0 overruns:0 frame:0
          TX packets:26 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:18630854 (17.7 MiB)  TX bytes:1888 (1.8 KiB)
          Base address:0x20c0 Memory:d10a0000-d10c0000

eth5:1    Link encap:Ethernet  HWaddr 00:0C:29:F2:38:CD
          inet addr:192.168.34.228  Bcast:0.0.0.0  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          Base address:0x20c0 Memory:d10a0000-d10c0000

ちゃんと出てくるのです。この状態をipコマンドで確認すると、

7: eth5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether 00:0c:29:f2:38:cd brd ff:ff:ff:ff:ff:ff
    inet 192.168.33.228/24 scope global eth5
    inet 192.168.34.228/24 scope global eth5:1
    inet6 fe80::20c:29ff:fef2:38cd/64 scope link
       valid_lft forever preferred_lft forever

こんな感じに、eth5の中なのですが、eth5:1とラベルが付いている感じに表示されます。

これはかなり分かりにくいですね。ifconfigコマンドになれているとip addr addコマンドでラベル指定しないで追加したものに気がつきません。

実はプログラマーとしてはもっと困ることもあります。

プログラム内でネットワークインターフェースに設定されているIPアドレスを調べる際に、ioctl()を使い、SIOCGIFADDRで得ることができるのですが、それを使うと2つ目以降のIPアドレスが得られません。ラベルが付いていれば、ifr_nameにラベル名を指定すれば得られますが、ラベルをつけていないと得られないのです。

追記:なお、SIOCGIFCONFでまとめて得ると、2つ目以降のIPアドレスの情報も得られました。ifr_nameに名前指定でSIOCGIFADDRすると最初のIPアドレスしか得られません。SCOCGIFCONFではネットマスクなどは得られませんので、IPアドレス(ユニキャストアドレス)以外も調べたい場合にはioctl()では上手く行きませんね。

ラベルなしでも得るためには、netlinkという、わりと理解しにくいカーネルとの通信を使う必要があります。意外と調べても分かりやすいソースコードが見つかりませんでしたので、いろいろなところのソースを参考に自分が使いやすそうな感じに作ってみましたので、張り付けておきます。自ホストのネットワークインターフェースを全部得て、それぞれのIPアドレス・IPv6アドレスを得るような作りにしました。大ざっぱにip addrでの結果と似た感じに表示されるようになっています。

#include	<arpa/inet.h>
#include	<inttypes.h>
#include	<linux/netlink.h>
#include	<linux/rtnetlink.h>
#include	<linux/if_arp.h>
#include	<stdio.h>
#include	<stdlib.h>
#include	<sys/socket.h>
#include	<sys/types.h>
#include	<unistd.h>

int	Pagesize;

struct {
	struct nlmsghdr		nlmsg_info;
	struct ifaddrmsg	ifaddrmsg_info;
}netlink_getaddr_req;

struct {
	struct nlmsghdr		nlmsg_info;
	struct ifinfomsg	ifinfomsg_info;
}netlink_getlink_req;

static int SendGetaddr(int soc,int family)
{
	bzero(&netlink_getaddr_req, sizeof(netlink_getaddr_req));

	netlink_getaddr_req.nlmsg_info.nlmsg_len=NLMSG_LENGTH(sizeof(struct ifaddrmsg));
	netlink_getaddr_req.nlmsg_info.nlmsg_flags=(NLM_F_REQUEST|NLM_F_DUMP);
	netlink_getaddr_req.nlmsg_info.nlmsg_type=RTM_GETADDR;
	netlink_getaddr_req.nlmsg_info.nlmsg_pid=getpid();

	netlink_getaddr_req.ifaddrmsg_info.ifa_family=family;

	if(send(soc,&netlink_getaddr_req,netlink_getaddr_req.nlmsg_info.nlmsg_len,0)<0){
		perror("send(): ");
		return(0);
	}
	else{
		return(1);
	}
}

static int ReadGetaddrOne(struct nlmsghdr *nlmsg_ptr, int nlmsg_len,int index)
{
	for( ;NLMSG_OK(nlmsg_ptr, nlmsg_len);nlmsg_ptr=NLMSG_NEXT(nlmsg_ptr,nlmsg_len)){
		struct ifaddrmsg	*ifaddrmsg_ptr;
		struct rtattr	*rtattr_ptr;
		int	ifaddrmsg_len;

		char	anycast_str[INET6_ADDRSTRLEN];
		char	ipaddr_str[INET6_ADDRSTRLEN];
		char	localaddr_str[INET6_ADDRSTRLEN];
		char	name_str[IFNAMSIZ];
		char	bcastaddr_str[INET6_ADDRSTRLEN];
		char	cacheinfo_str[128];
		char	multicast_str[INET6_ADDRSTRLEN];
		char	scope_str[16];

		ifaddrmsg_ptr=(struct ifaddrmsg *)NLMSG_DATA(nlmsg_ptr);

		anycast_str[0]=0;
		ipaddr_str[0]=0;
		localaddr_str[0]=0;
		name_str[0]=0;
		bcastaddr_str[0]=0;
		cacheinfo_str[0]=0;
		multicast_str[0]=0;
		scope_str[0]=0;

		rtattr_ptr=(struct rtattr *)IFA_RTA(ifaddrmsg_ptr);
		ifaddrmsg_len=IFA_PAYLOAD(nlmsg_ptr);

		if(ifaddrmsg_ptr->ifa_index!=index){
			continue;
		}

		if(ifaddrmsg_ptr->ifa_scope==RT_SCOPE_UNIVERSE){
			strcpy(scope_str,"global");
		}
		else if(ifaddrmsg_ptr->ifa_scope==RT_SCOPE_SITE){
			strcpy(scope_str,"site");
		}
		else if(ifaddrmsg_ptr->ifa_scope==RT_SCOPE_LINK){
			strcpy(scope_str,"link");
		}
		else if(ifaddrmsg_ptr->ifa_scope==RT_SCOPE_HOST){
			strcpy(scope_str,"host");
		}
		else if(ifaddrmsg_ptr->ifa_scope==RT_SCOPE_NOWHERE){
			strcpy(scope_str,"nowhere");
		}
		else{
			snprintf(scope_str,sizeof(scope_str),"%d",ifaddrmsg_ptr->ifa_scope);
		}

		for( ;RTA_OK(rtattr_ptr,ifaddrmsg_len);rtattr_ptr=RTA_NEXT(rtattr_ptr,ifaddrmsg_len)){
			switch(rtattr_ptr->rta_type){
				case IFA_ADDRESS:
					inet_ntop(ifaddrmsg_ptr->ifa_family,RTA_DATA(rtattr_ptr),ipaddr_str,sizeof(ipaddr_str));
					break;
				case IFA_LOCAL:
					inet_ntop(ifaddrmsg_ptr->ifa_family,RTA_DATA(rtattr_ptr),localaddr_str,sizeof(localaddr_str));
					break;
				case IFA_BROADCAST:
					inet_ntop(ifaddrmsg_ptr->ifa_family,RTA_DATA(rtattr_ptr),bcastaddr_str,sizeof(bcastaddr_str));
					break;
				case IFA_ANYCAST:
					inet_ntop(ifaddrmsg_ptr->ifa_family,RTA_DATA(rtattr_ptr),anycast_str,sizeof(anycast_str));
					break;
				case IFA_MULTICAST:
					inet_ntop(ifaddrmsg_ptr->ifa_family,RTA_DATA(rtattr_ptr),multicast_str,sizeof(multicast_str));
					break;
				case IFA_LABEL:
					snprintf(name_str,sizeof(name_str),"%s",(char *)RTA_DATA(rtattr_ptr));
					break;
				case IFA_CACHEINFO:
					{
					struct ifa_cacheinfo	*ci=(struct ifa_cacheinfo *)RTA_DATA(rtattr_ptr);
					char	prefered[32];
					char	valid[32];

					if(ci->ifa_valid==0xFFFFFFFFUL){
						strcpy(valid,"valid_lft forever");
					}
					else{
						snprintf(valid,sizeof(valid),"valid %"PRIu32"sec",ci->ifa_valid);
					}

					if(ci->ifa_prefered==0xFFFFFFFFUL){
						strcpy(prefered,"prefered_lft forever");
					}
					else{
						snprintf(prefered,sizeof(prefered),"prefered %"PRIu32"sec",ci->ifa_prefered);
					}

					snprintf(cacheinfo_str,sizeof(cacheinfo_str),"%s %s",valid, prefered);
					}
					break;
				default:
					//printf("unknown rta_type: %d\n",(int)rtattr_ptr->rta_type);
					break;
			}
		}

		if(ifaddrmsg_ptr->ifa_family==AF_INET){
			printf("    inet ");
		}
		else{
			printf("    inet6 ");
		}

		if(strlen(ipaddr_str)!=0){
			printf("%s/%d",ipaddr_str,ifaddrmsg_ptr->ifa_prefixlen);
		}

		printf(" scope %s",scope_str);
		if(strlen(cacheinfo_str)!=0){
			printf(" %s",cacheinfo_str);
		}

		if(strlen(name_str)!=0) {
			printf(" %s",name_str);
		}

		printf("\n");
	}

	return(1);
}

static int ReadGetaddr(int soc,int index)
{
char	read_buffer[Pagesize];
struct nlmsghdr	*nlmsg_ptr;
int	nlmsg_len;

	while(1){
		int	rtn;

		bzero(read_buffer,Pagesize);
		if((rtn=recv(soc,read_buffer,Pagesize,0))<0){
			perror("recv(): ");
			return(0);
		}

		nlmsg_ptr=(struct nlmsghdr *)read_buffer;
		nlmsg_len=rtn;

		if(nlmsg_len<sizeof(struct nlmsghdr)){
			fprintf(stderr,"received an uncomplete netlink packet:%d\n",nlmsg_len);
			return(0);
		}

		if(nlmsg_ptr->nlmsg_type==NLMSG_DONE){
			break;
		}

		ReadGetaddrOne(nlmsg_ptr,nlmsg_len,index);
	}

	return(1);
}

int GetAddr(int index)
{
int	soc;

	soc=socket(AF_NETLINK,SOCK_RAW,NETLINK_ROUTE);
	if(soc<0){
		perror("socket(): ");
		return(0);
	}

	SendGetaddr(soc,0/*AF_INET*/);
	ReadGetaddr(soc,index);

	close(soc);

	return(1);
}

char *my_ether_ntoa_r(const unsigned char *hwaddr,char *buf)
{
        sprintf(buf,"%02x:%02x:%02x:%02x:%02x:%02x",
                hwaddr[0],hwaddr[1],hwaddr[2],hwaddr[3],hwaddr[4],hwaddr[5]);

        return(buf);
}

char *FlagStr(int flag,char *buf)
{
	strcpy(buf,"");

	if(flag&IFF_BROADCAST){
		strcat(buf,"BROADCAST,");
	}
	if(flag&IFF_LOOPBACK){
		strcat(buf,"LOOPBACK,");
	}
	if(flag&IFF_NOARP){
		strcat(buf,"NOARP,");
	}
	if(flag&IFF_PROMISC){
		strcat(buf,"PROMISC,");
	}
	if(flag&IFF_ALLMULTI){
		strcat(buf,"ALLMULTI,");
	}
	if(flag&IFF_MULTICAST){
		strcat(buf,"MULTICAST,");
	}
	if(flag&IFF_UP){
		strcat(buf,"UP,");
	}
	if(flag&IFF_LOWER_UP){
		strcat(buf,"LOWER_UP,");
	}

	if(strlen(buf)>0){
		buf[strlen(buf)-1]='\0';
	}

	return(buf);
}

static int ReadGetlinkOne(struct nlmsghdr *nlmsg_ptr, int nlmsg_len)
{
char	buf0[512],buf1[80],buf2[80];

	for( ;NLMSG_OK(nlmsg_ptr, nlmsg_len);nlmsg_ptr=NLMSG_NEXT(nlmsg_ptr,nlmsg_len)){
		struct ifinfomsg	*ifinfomsg_ptr;
		struct rtattr	*rtattr_ptr;
		int	ifinfomsg_len;

		char	*typestr=NULL;
		char	*ifname=NULL;
		int	*mtu=NULL;
		unsigned char	*address=NULL;
		unsigned char	*broadcast=NULL;

		ifinfomsg_ptr=(struct ifinfomsg *)NLMSG_DATA(nlmsg_ptr);

		rtattr_ptr=(struct rtattr *)IFLA_RTA(ifinfomsg_ptr);
		ifinfomsg_len=IFLA_PAYLOAD(nlmsg_ptr);

		for( ;RTA_OK(rtattr_ptr,ifinfomsg_len);rtattr_ptr=RTA_NEXT(rtattr_ptr,ifinfomsg_len)){
			switch(rtattr_ptr->rta_type){
				case IFLA_IFNAME:
					ifname=(char *)RTA_DATA(rtattr_ptr);
					break;
				case IFLA_MTU:
					mtu=(int *)RTA_DATA(rtattr_ptr);
					break;
				case IFLA_ADDRESS:
					address=RTA_DATA(rtattr_ptr);
				case IFLA_BROADCAST:
					broadcast=RTA_DATA(rtattr_ptr);
					break;
				default:
					//printf("unknown rta_type: %d\n",(int)rtattr_ptr->rta_type);
					break;
			}
		}

		if(ifinfomsg_ptr->ifi_type==ARPHRD_ETHER){
			typestr="ether";
		}
		else if(ifinfomsg_ptr->ifi_type==ARPHRD_LOOPBACK){
			typestr="loopback";
		}
		else{
			typestr="unknown";
		}

		printf("%d: %s: <%s> mtu %d change %d\n",
			ifinfomsg_ptr->ifi_index,ifname,FlagStr(ifinfomsg_ptr->ifi_flags,buf0),*mtu,ifinfomsg_ptr->ifi_change);
		printf("    link/%s %s brd %s\n",
			typestr,my_ether_ntoa_r(address,buf1),my_ether_ntoa_r(broadcast,buf2));
		GetAddr(ifinfomsg_ptr->ifi_index);
	}

	return(1);
}

static int SendGetlink(int soc)
{
	bzero(&netlink_getlink_req, sizeof(netlink_getlink_req));

	netlink_getlink_req.nlmsg_info.nlmsg_len=NLMSG_LENGTH(sizeof(struct ifinfomsg));
	netlink_getlink_req.nlmsg_info.nlmsg_flags=(NLM_F_REQUEST|NLM_F_DUMP);
	netlink_getlink_req.nlmsg_info.nlmsg_type=RTM_GETLINK;
	netlink_getlink_req.nlmsg_info.nlmsg_pid=getpid();

	netlink_getlink_req.ifinfomsg_info.ifi_family=ARPHRD_ETHER;

	if(send(soc,&netlink_getlink_req,netlink_getlink_req.nlmsg_info.nlmsg_len,0)<0){
		perror("send(): ");
		return(0);
	}
	else{
		return(1);
	}
}

static int ReadGetlink(int soc)
{
char	read_buffer[Pagesize];
struct nlmsghdr	*nlmsg_ptr;
int	nlmsg_len;

	while(1){
		int	rtn;

		bzero(read_buffer,Pagesize);
		if((rtn=recv(soc,read_buffer,Pagesize,0))<0){
			perror("recv(): ");
			return(0);
		}

		nlmsg_ptr=(struct nlmsghdr *)read_buffer;
		nlmsg_len=rtn;

		if(nlmsg_len<sizeof(struct nlmsghdr)){
			fprintf(stderr,"received an uncomplete netlink packet:%d\n",nlmsg_len);
			return(0);
		}

		if(nlmsg_ptr->nlmsg_type==NLMSG_DONE){
			break;
		}

		ReadGetlinkOne(nlmsg_ptr,nlmsg_len);
	}

	return(1);
}

int GetLink()
{
int	soc;

	soc=socket(AF_NETLINK,SOCK_RAW,NETLINK_ROUTE);
	if(soc<0){
		perror("socket(): ");
		return(0);
	}

	SendGetlink(soc);
	ReadGetlink(soc);

	close(soc);

	return(1);
}

int main(int argc, char *argv[])
{
	Pagesize=sysconf(_SC_PAGESIZE);

	if(!Pagesize){
		Pagesize=4096;
	}

	GetLink(7);

	exit(0);
}

実行すると以下の感じになります。

7: eth5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 change 0
    link/ether 00:0c:29:f2:38:cd brd ff:ff:ff:ff:ff:ff
    inet 192.168.33.228/24 scope global eth5
    inet 192.168.34.228/24 scope global eth5:1
    inet6 fe80::20c:29ff:fef2:38cd/64 scope link valid_lft forever prefered_lft forever

貼り付け方がいい加減なので、まともに使えるかどうか分かりませんが、参考になれば・・・

Comment(0)

コメント

コメントを投稿する