普通にifconfigで調べられますが、
ソケットプログラミングの勉強として、
指定したインターフェースの
MACアドレスを取得してみました。
インタフェースを操作するには、
基本的にはifreq構造体と
ioctlシステムコールの2つを使います。
なおioctlシステムコールを使うためには、
ソケットを用意する必要があります。
コードはこちらです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#include<stdio.h> #include<string.h> #include<unistd.h> #include<sys/ioctl.h> #include<sys/socket.h> #include<net/if.h> #include<net/ethernet.h> #include<netpacket/packet.h> #include<arpa/inet.h> int main(void) { struct ifreq ifreq; int s=socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if(s<0){ perror("socket"); } memset(&ifreq, 0, sizeof(struct ifreq)); strncpy(ifreq.ifr_name, "enp0s3", sizeof(ifreq.ifr_name)-1); if(ioctl(s, SIOCGIFHWADDR, &ifreq)!=0) perror("ioctl"); printf("%02x:%02x:%02x:%02x:%02x:%02x \n",(unsigned char)ifreq.ifr_hwaddr.sa_data[0], (unsigned char)ifreq.ifr_hwaddr.sa_data[1], (unsigned char)ifreq.ifr_hwaddr.sa_data[2], (unsigned char)ifreq.ifr_hwaddr.sa_data[3], (unsigned char)ifreq.ifr_hwaddr.sa_data[4], (unsigned char)ifreq.ifr_hwaddr.sa_data[5]); close(s); return 0; } |
まずはifreq構造体に操作対象とする
インターフェース名を入れます。
ifreq構造体はインタフェース情報を格納
しておくためのプログラム上の格納場所です。
存在しないインターフェースを指定すると、
エラーになりますので注意です。
今回私は"enp0s3"を指定しましたが、
指定するときはインタフェース名を
ifconfigコマンドで確認しましょう。
eth0やeth1が多いかと思います。
インターフェース名を入れたら、
次はioctlでMACアドレスを取得します。
ioctlはネットワークインタフェースを操作
するためのシステムコールです。
ioctlを呼ぶときに様々なフラグを
引数に指定することが可能です。
今回はその中でも"SIOCGIFHWADDR"
というフラグを使って、インタフェースの
MACアドレスを取得します。
SIOCGIFHWADDRを指定したioctlは、
ifreq構造体のifr_hwaddrというメンバに
アドレスを格納してれくれます。
なおioctlを使うためには、
事前にリンクレイヤを扱うrawソケットを
生成しておかなければいけません。
参考までにifreq構造体は/usr/include/linux/if.hで定義されています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
struct ifreq { #define IFHWADDRLEN 6 union { char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */ } ifr_ifrn; union { struct sockaddr ifru_addr; struct sockaddr ifru_dstaddr; struct sockaddr ifru_broadaddr; struct sockaddr ifru_netmask; struct sockaddr ifru_hwaddr; short ifru_flags; int ifru_ivalue; int ifru_mtu; struct ifmap ifru_map; char ifru_slave[IFNAMSIZ]; /* Just fits the size */ char ifru_newname[IFNAMSIZ]; void * ifru_data; struct if_settings ifru_settings; } ifr_ifru; }; #define ifr_name ifr_ifrn.ifrn_name /* interface name */ #define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */ #define ifr_addr ifr_ifru.ifru_addr /* address */ #define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-p lnk */ #define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */ #define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */ #define ifr_flags ifr_ifru.ifru_flags /* flags */ #define ifr_metric ifr_ifru.ifru_ivalue /* metric */ #define ifr_mtu ifr_ifru.ifru_mtu /* mtu */ #define ifr_map ifr_ifru.ifru_map /* device map */ #define ifr_slave ifr_ifru.ifru_slave /* slave device */ #define ifr_data ifr_ifru.ifru_data /* for use by interface */ #define ifr_ifindex ifr_ifru.ifru_ivalue /* interface index */ #define ifr_bandwidth ifr_ifru.ifru_ivalue /* link bandwidth */ #define ifr_qlen ifr_ifru.ifru_ivalue /* Queue length */ #define ifr_newname ifr_ifru.ifru_newname /* New name */ #define ifr_settings ifr_ifru.ifru_settings /* Device/proto settings*/ |
ifreq構造体のメンバを見ていくと、
ifr_hwaddrがあります。
厳密にはifru_hwaddrとして宣言されていますが、
下の方にある#defineのところで
「ifr_ifru.ifru_hwaddrをifr_hwaddrとする」と
定義されています。
なので実際は、ifreq構造体の
ifr_hwaddrとしてメンバに
アクセスできるようになります。
しかし少し複雑なことに、
このMACアドレスを格納するifr_hwaddrは
sockaddr構造体として宣言されています。
つまり構造体の中に、メンバとして
さらに構造体が宣言されています。
初見だと分かりにくいですね。
なのでMACアドレスを操る際は、
さらにsockaddr構造体のメンバに
アクセスしなければいけません。
sockaddr構造体は、
/usr/include/bits/socket.hに定義されています。
1 2 3 4 5 6 |
/* Structure describing a generic socket address. */ struct sockaddr { __SOCKADDR_COMMON (sa_); /* Common data: address family and length. */ char sa_data[14]; /* Address data. */ }; |
sockaddr構造体は、
アドレス情報をひとまとめに格納した構造体です。
char型の配列として、
sa_data[14]と宣言されています。
これの[0]から[5]のところに
MACアドレスが入っています。
printfで表示してみると分かりますが、
[6]~[13]は0が入っています。
MACアドレスはifr_hwaddrのsa_dataに格納
されていることが分かったので、
最後にそれを出力するだけです。
実行結果とifconfigで表示された
MACアドレスと同じかどうか
確認してみてください。
一致していることが確認できるかと思います!
最後まで読んでいただきありがとうございました。
MACアドレスを表示出来れば、
書き換えることも可能です。
https://wireless-network.net/macaddr-change/