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
貼り付け方がいい加減なので、まともに使えるかどうか分かりませんが、参考になれば・・・