Declaration
#define ICMP_ECHO_REQUEST 8 #define ICMP_ECHO_REPLY 0 namespace icmp { template <int Family> class basic_socket : public basic_rawsocket<SOCK_RAW, Family, IPPROTO_ICMP> typedef basic_rawsocket<SOCK_RAW, AF_INET, IPPROTO_ICMP> rawsocket; typedef basic_socket<AF_INET> socket; typedef basic_sockaddress<AF_INET, IPPROTO_ICMP> sockaddress; typedef basic_sockmanager<SOCK_RAW, AF_INET, IPPROTO_ICMP> sockmanager; };
Overview
icmp::socket は,ICMP 用ソケットのラッパクラスです.現在は,ICMP ECHO REQUEST の送信,および ICMP ECHO REPLY の受信しか想定していません(値を define していない). IPヘッダおよびICMPヘッダは,それぞれ ip.h,icmp.h のグローバル名前空間において, 独自に定義しています.
struct iphdr { u_int8_t ihl:4; u_int8_t version:4; u_int8_t tos; u_int16_t tot_len; u_int16_t id; u_int16_t frag_off; u_int8_t ttl; u_int8_t protocol; u_int16_t check; unsigned long saddr; unsigned long daddr; }; struct icmphdr { u_int8_t type; u_int8_t code; u_int16_t checksum; u_int16_t id; u_int16_t sequence; };
また,データに付与されているIPヘッダ,ICMPヘッダを扱うための packet_header も定義しています.packet_header は,(IP ヘッダ + ICMP ヘッダが付与されている) 受信データを引数として指定されると,受信データからヘッダ情報をコピーします.
namespace icmp { class packet_header { public: typedef struct iphdr ip_type; typedef struct icmphdr icmp_type; typedef size_t size_type; packet_header(); explicit packet_header(const char* packet); virtual ~packet_header(); packet_header& operator=(const char* packet); packet_header& assign(const char* packet); void reset(); size_type size() const; size_type ip_size() const; size_type icmp_size() const; ip_type* ip(); icmp_type* icmp(); const ip_type* ip() const; const icmp_type* icmp() const; }; };
ICMP ソケットを用いて通信を行う場合,送信時にはデータの先頭に ICMP ヘッダを付与して送信する必要があります.また,受信されるデータには, IP ヘッダ,および ICMP ヘッダが付与されています.
Example
#include <iostream> #include <string> #include <cstring> #include <memory> #include "clx/icmp.h" #include "clx/timer.h" #include "clx/argument.h" #include "clx/format.h" int main(int argc, char* argv[]) { clx::argument arg(argc, argv); if (arg.head().empty()) { std::cerr << "usage " << argv[0] << " hostname [-l data_bytes]" << std::endl; return -1; } /* * bufは送受信兼用バッファ. * -send()メソッド: payload + ICMPヘッダ長 * -recv()メソッド: payload + IPヘッダ長 + ICMPヘッダ長 * 以上を考慮して大きめに領域を確保する. */ clx::icmp::packet_header hdr; int payload = 56; arg("l,length", payload); int packetsize = payload + hdr.icmp_size(); int buffsize = packetsize + 1024; std::auto_ptr<char> buf(new char[buffsize]); try { clx::icmp::socket s(arg.head().at(0)); std::cout << clx::format("ICMP ECHO %s (%s): %d data bytes") % arg.head().at(0) % s.to().ipaddr() % payload << std::endl; int seq = 0; while (1) { std::memset(buf.get(), 'a', packetsize); // sending ICMP echo request hdr.reset(); hdr.icmp()->type = ICMP_ECHO_REQUEST; hdr.icmp()->sequence = seq; std::memcpy(buf.get(), (char*)hdr.icmp(), hdr.icmp_size()); s.send(buf.get(), packetsize); clx::timer t; // receiving ICMP echo packet std::memset(buf.get(), 0, buffsize); int len = s.recv(buf.get(), buffsize); if (len < 0) return -1; double recv = t.total_elapsed(); // print information hdr = buf.get(); if (hdr.icmp()->type == ICMP_ECHO_REPLY) { std::cout << clx::format("%d bytes from %s: icmp_seq=%d ttl=%d time=%d ms") % len % s.from().ipaddr() % static_cast<int>(hdr.icmp()->sequence) % static_cast<int>(hdr.ip()->ttl) % static_cast<int>(recv * 1000) << std::endl; seq = hdr.icmp()->sequence + 1; } else { std::cerr << clx::format("received ICMP packet (type: %d) from %s") % static_cast<int>(hdr.icmp()->type) % s.from().ipaddr() << std::endl; } clx::sleep(1.0); } } catch (clx::socket_error& e) { std::cerr << e.what() << std::endl; return -1; } catch (clx::sockaddress_error& e) { std::cerr << e.what() << std::endl; return -1; } return 0; }
Result $ ./test yahoo.com -l 1000 ICMP ECHO yahoo.com (68.180.206.184): 1000 data bytes 1028 bytes from 68.180.206.184: icmp_seq=0 ttl=46 time=156 ms 1028 bytes from 68.180.206.184: icmp_seq=1 ttl=46 time=159 ms 1028 bytes from 68.180.206.184: icmp_seq=2 ttl=46 time=168 ms 1028 bytes from 68.180.206.184: icmp_seq=3 ttl=46 time=171 ms 1028 bytes from 68.180.206.184: icmp_seq=4 ttl=46 time=162 ms 1028 bytes from 68.180.206.184: icmp_seq=5 ttl=46 time=158 ms
ping のような動きをします.指定されたホスト名に対して,
- ICMP ECHO REQUEST を送信する.
- ICMP ECHO REPLY を受信する.
- 1 秒スリープする.
と言う動きを繰り返します.RTT の計測にはタイムスタンプは使わずに,send() メソッドが成功した直後から recv() メソッドが成功した直後までの時間を表示しています.
Template Parameters
- Family
- プロトコルファミリーを指定します.
Related Types
typedef basic_sockaddress<Family, IPPROTO_ICMP> address_type; typedef char char_type; typedef std::basic_string<char_type> string_type;
Construction and Member Functions
basic_socket(); basic_socket(const basic_socket& cp); basic_soket& operator=(const basic_socket& cp); explicit basic_socket(socket_int s, const address_type& addr); explicit basic_socket(const char_type* host); explicit basic_socket(const string_type& host); virtual ~basic_socket(); basic_socket& connect(const char_type* host); basic_socket& connect(const string_type& host); int send_to(const char_type* src, int n, const address_type& addr); int send_to(const string_type& src, const address_type& addr); int send_to(const char_type* src, int n, const char_type* host); int send_to(const string_type& src, const string_type& host); int send(const char_type* src, int n); int send(const string_type& src); int recv(char_type* dest, int n); const address_type& from() const; const address_type& to() const;
send() メソッドは,コンストラクタ,または connect() メソッドで指定したホストに対して, 通信を試みます.from() メソッドは,直近の recv() メソッドを用いて受信したデータの送り先を返します.