arping コマンドを実装した経験がなかったので、実装コードをカンニングしながらエッセンスを抽出して、動く簡易 arping コマンドを実装していきます。
手元環境の arp のパッケージを以下のようにして確認します。
$ rpm -qf /sbin/arping iputils-20121221-7.13.amzn1.x86_64
というわけで、iputils の実装を参考にします。
$ git clone git@github.com:iputils/iputils.git $ cd iputils $ vi ./arping.c
ARP は IPv4 アドレスと MAC アドレスの対応を確認するためのプロトコルで、例えば 192.168.2.128 にリクエストを送ろうとしたとき、それに対応するイーサネットフレームの宛先 MAC アドレスが知りたくなります。
初めて送る相手の場合 IPv4 アドレスに対応する MAC アドレスは当然わからないので ARP Request をブロードキャストします。そして対応する機器は MAC アドレスを添えて ARP Reply を返すという仕組みです。
一度 ARP Reply が帰ってきたらある程度の期間はそれを記憶して使いまわします。 arp -a
コマンドにより、自ホストが記憶している MAC アドレスと IPv4 アドレスの一覧である ARP テーブルを出力できますので、やってみます。
$ arp -a wrc-1750gs (192.168.2.1) at bc:5c:4c:c5:7e:7f on en0 ifscope [ethernet]
現状 192.168.2.1 のマシンしか ARP テーブルに存在しません。ここで、まだ MAC アドレスを知らない 192.168.2.102 に ping を打ってみます。その裏で ARP のパケットをキャプチャしてみます。
$ ping 192.168.2.111 PING 192.168.2.111 (192.168.2.111): 56 data bytes 64 bytes from 192.168.2.111: icmp_seq=0 ttl=64 time=96.430 ms 64 bytes from 192.168.2.111: icmp_seq=1 ttl=64 time=15.179 ms 64 bytes from 192.168.2.111: icmp_seq=2 ttl=64 time=36.652 ms 64 bytes from 192.168.2.111: icmp_seq=3 ttl=64 time=59.144 ms 64 bytes from 192.168.2.111: icmp_seq=4 ttl=64 time=40.198 ms ^C --- 192.168.2.111 ping statistics --- 5 packets transmitted, 5 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 15.179/49.521/96.430/27.295 ms Workspace $ ping 192.168.2.111 PING 192.168.2.111 (192.168.2.111): 56 data bytes 64 bytes from 192.168.2.111: icmp_seq=0 ttl=64 time=70.699 ms 64 bytes from 192.168.2.111: icmp_seq=1 ttl=64 time=62.401 ms ^C --- 192.168.2.111 ping statistics --- 2 packets transmitted, 2 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 62.401/66.550/70.699/4.149 ms #=> すると arp のパケットをキャプチャできる $ sudo tcpdump arp -x tcpdump: data link type PKTAP tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on pktap, link-type PKTAP (Apple DLT_PKTAP), capture size 262144 bytes 02:06:43.408795 ARP, Request who-has mbp tell 88e9fe4e377f, length 28 0x0000: ffff ffff ffff 88e9 fe4e 377f 0806 0001 0x0010: 0800 0604 0001 88e9 fe4e 377f c0a8 026b 0x0020: 0000 0000 0000 c0a8 026f 02:06:43.503762 ARP, Reply mbp is-at 8c:85:90:b5:57:9a (oui Unknown), length 28 0x0000: 88e9 fe4e 377f 8c85 90b5 579a 0806 0001 0x0010: 0800 0604 0002 8c85 90b5 579a c0a8 026f 0x0020: 88e9 fe4e 377f c0a8 026b #=> ARP テーブルに情報がキャッシュされた $ arp -a wrc-1750gs (192.168.2.1) at bc:5c:4c:c5:7e:7f on en0 ifscope [ethernet] mbp (192.168.2.111) at 8c:85:90:b5:57:9a on en0 ifscope [ethernet] #=> もう一度 ping を打っても ARP テーブルに情報があるので ARP パケットは流れない $ ping 192.168.2.111 PING 192.168.2.111 (192.168.2.111): 56 data bytes 64 bytes from 192.168.2.111: icmp_seq=0 ttl=64 time=70.699 ms 64 bytes from 192.168.2.111: icmp_seq=1 ttl=64 time=62.401 ms ^C --- 192.168.2.111 ping statistics --- 2 packets transmitted, 2 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 62.401/66.550/70.699/4.149 ms
以上のように試してみると ARP の基本的なふるまいはわりと単純なものであることがわかります。
RFC 826 - An Ethernet Address Resolution Protocol: Or Converting Network Protocol Addresses to 48.bit Ethernet Address for Transmission on Ethernet Hardware より ARP のフォーマットを確認します。
Definitions: ------------ Define the following for referring to the values put in the TYPE field of the Ethernet packet header: ether_type$XEROX_PUP, ether_type$DOD_INTERNET, ether_type$CHAOS, and a new one: ether_type$ADDRESS_RESOLUTION. Also define the following values (to be discussed later): ares_op$REQUEST (= 1, high byte transmitted first) and ares_op$REPLY (= 2), and ares_hrd$Ethernet (= 1). Packet format: -------------- To communicate mappings from <protocol, address> pairs to 48.bit Ethernet addresses, a packet format that embodies the Address Resolution protocol is needed. The format of the packet follows. Ethernet transmission layer (not necessarily accessible to the user): 48.bit: Ethernet address of destination 48.bit: Ethernet address of sender 16.bit: Protocol type = ether_type$ADDRESS_RESOLUTION Ethernet packet data: 16.bit: (ar$hrd) Hardware address space (e.g., Ethernet, Packet Radio Net.) 16.bit: (ar$pro) Protocol address space. For Ethernet hardware, this is from the set of type fields ether_typ$<protocol>. 8.bit: (ar$hln) byte length of each hardware address 8.bit: (ar$pln) byte length of each protocol address 16.bit: (ar$op) opcode (ares_op$REQUEST | ares_op$REPLY) nbytes: (ar$sha) Hardware address of sender of this packet, n from the ar$hln field. mbytes: (ar$spa) Protocol address of sender of this packet, m from the ar$pln field. nbytes: (ar$tha) Hardware address of target of this packet (if known). mbytes: (ar$tpa) Protocol address of target.
上記フォーマットと先ほどキャプチャした ARP Request/Reply を見比べてみましょう。Wireshark が食った結果が以下のようなものです。
0000 ff ff ff ff ff ff 88 e9 fe 4e 37 7f 08 06 00 01 .........N7..... 0010 08 00 06 04 00 01 88 e9 fe 4e 37 7f c0 a8 02 6b .........N7....k 0020 00 00 00 00 00 00 c0 a8 02 6f .........o
イーサネットヘッダが 14 オクテットあります。宛先 MAC アドレスが ff:ff:ff:ff:ff:ff
となっておりブロードキャストさせていることがわかります。
00 01
からが ARP Request メッセージです。それぞれ次のようになっています。
ちなみに送信元 IPv4 アドレスは 192.168.2.107 = 0xC0A8026B、送信先 IPv4 アドレスは 192.168.2.111 = 0xC0A8026F です。
続いて Reply をみてみます。
0000 88 e9 fe 4e 37 7f 8c 85 90 b5 57 9a 08 06 00 01 ...N7.....W..... 0010 08 00 06 04 00 02 8c 85 90 b5 57 9a c0 a8 02 6f ..........W....o 0020 88 e9 fe 4e 37 7f c0 a8 02 6b ...N7....k
宛先 MAC アドレスは 88:e9:fe:4e:37:7f となっており、戻りのパケットはブロードキャストではないことがわかります。00 01
から始まる ARP Reply メッセージの中身はそれぞれ以下のようになっています。
ウェブ界隈でエンジニアとして労働活動に励んでいる @gomi_ningen 個人のブログです