My raw socket usage example with Boost::Asio.
/* * ping.cpp * * Author, Copyright: Oleg Borodin <onborodin@gmail.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. * */ #include <boost/asio.hpp> #include <boost/bind.hpp> #include <boost/array.hpp> #include <istream> #include <iostream> #include <ostream> namespace ping { namespace ip { const std::size_t min_header_size = 20; const std::size_t option_max_size = 40; const std::size_t reply_buffer_size = 65536; } namespace icmp { uint16_t id = 1234; uint16_t seq = 4567; namespace type { const uint16_t echo_reply = 0; //const uint16_t destination_unreachable = 3; //const uint16_t source_quench = 4; //const uint16_t redirect = 5; const uint16_t echo_request = 8; const uint16_t time_exceeded = 11; //const uint16_t parameter_problem = 12; //const uint16_t timestamp_request = 13; //const uint16_t timestamp_reply = 14; //const uint16_t info_request = 15; //const uint16_t info_reply = 16; //const uint16_t address_request = 17; //const uint16_t address_reply = 18; } } } using namespace boost::asio; class ip_packet { public: struct { std::uint8_t ip_hl:4; /* header length */ std::uint8_t ip_v:4; /* version */ std::uint8_t ip_tos; /* type of service */ std::uint16_t ip_len; /* total length */ std::uint16_t ip_id; /* identification */ std::uint16_t ip_off; /* fragment offset field */ std::uint8_t ip_ttl; /* time to live */ std::uint8_t ip_p; /* protocol */ std::uint16_t ip_sum; /* checksum */ ip::address_v4::bytes_type ip_src; /* source address */ ip::address_v4::bytes_type ip_dst; /* dest address */ std::uint8_t options[ping::ip::option_max_size]; /* options */ } __packed __aligned(2) header; ip_packet() { std::memset(&header, 0, sizeof(header)); } uint16_t ip_ttl() { return header.ip_ttl; } uint16_t ip_v() { return header.ip_v; } uint16_t ip_hl() { return (header.ip_hl * sizeof(uint32_t)); } friend std::istream& operator >> (std::istream& istream, ip_packet& packet) { istream.read((char *)(&packet.header), ping::ip::min_header_size); std::size_t packet_options_size = packet.ip_hl() - ping::ip::min_header_size; istream.read((char *)(&packet.header.options), packet_options_size); return istream; } }; class icmp_packet { public: struct { uint8_t icmp_type; uint8_t icmp_code; uint16_t icmp_cksum; uint16_t icmp_id; uint16_t icmp_seq; } header; icmp_packet() { std::memset(&header, 0, sizeof(header)); } friend std::ostream& operator << (std::ostream& ostream, icmp_packet& packet) { return ostream.write((char *)(&packet.header), sizeof(packet.header)); } friend std::istream& operator >> (std::istream& istream, icmp_packet& packet) { return istream.read((char *)(&packet.header), sizeof(packet.header)); } void icmp_type(const uint16_t type) { header.icmp_type = type; } uint16_t icmp_type() { return header.icmp_type; } uint16_t icmp_id() { return ntohs(header.icmp_id); } void icmp_id(uint16_t id) { header.icmp_id = htons(id); } uint16_t icmp_seq() { return ntohs(header.icmp_seq); } void icmp_seq(uint16_t seq) { header.icmp_seq = htons(seq); } void calc_checksum() { header.icmp_cksum = 0; const uint16_t * const data = (uint16_t *)&header; std::size_t header_size = sizeof(header); uint32_t accu = 0; for (std::size_t i = 0; i < (header_size >> 1); ++i) { accu = accu + data[i]; } while (accu >> 16) { accu = (accu & 0xffff) + (accu >> 16); } header.icmp_cksum = ~accu; } }; class pinger { public: std::string hostname = "localhost"; boost::asio::streambuf request_buffer; boost::asio::streambuf reply_buffer; ip::icmp::endpoint destination; ip::icmp::endpoint source; pinger(std::string host) : hostname(host) { } void start() { std::uint8_t max_ttl = 12; for (std::uint8_t ttl = 1; ttl < max_ttl; ttl++) { ping(ttl); } } void ping(uint8_t ttl) { io_service io_service; ip::icmp::resolver resolver(io_service); ip::icmp::resolver::query query(ip::icmp::v4(), hostname, ""); ip::icmp::resolver::iterator iter = resolver.resolve(query); destination = *iter; source = ip::icmp::endpoint(ip::icmp::v4(), 0); try { ip::icmp::socket socket(io_service, source); icmp_packet packet; packet.icmp_type(ping::icmp::type::echo_request); packet.icmp_id(ping::icmp::id); packet.icmp_seq(ping::icmp::seq); packet.calc_checksum(); std::ostream ostream(&request_buffer); ostream << packet; const ip::unicast::hops option(ttl); socket.set_option(option); boost::posix_time::ptime time_sent = boost::posix_time::microsec_clock::universal_time(); //socket.non_blocking(true); int timeout = 3000; deadline_timer timer(socket.get_io_service()); timer.expires_from_now(boost::posix_time::milliseconds(timeout)); timer.async_wait(boost::bind(&pinger::handle_timeout, this, ttl, &socket)); //std::cout << "# send icmp echo request to " << destination.address().to_string() << " with ttl " << (uint16_t)ttl <<"\n"; socket.send_to(request_buffer.data(), destination); reply_buffer.consume(reply_buffer.size()); socket.async_receive( reply_buffer.prepare(ping::ip::reply_buffer_size), boost::bind(&pinger::handle_receive, this, _1, _2, ttl, time_sent, &timer) ); io_service.run(); } catch (std::exception& e) { std::cout << "# exception: " << e.what() << "\n"; return; } } void handle_timeout(uint8_t ttl, ip::icmp::socket *socket) { std::cout << "# timeout ttl " << (uint16_t)ttl <<"\n"; socket->cancel(); socket->close(); } void handle_receive( boost::system::error_code error, std::size_t length, uint8_t ttl, boost::posix_time::ptime time_sent, deadline_timer *timer ) { reply_buffer.commit(length); timer->cancel(); if (error != boost::system::errc::success) { std::cout << "# error " << error << "\n"; return; } boost::posix_time::ptime time_receive = boost::posix_time::microsec_clock::universal_time(); //std::cout << "# receive " << length << " bytes\n"; std::istream istream(&reply_buffer); //std::cout << "# reply buffer size " << reply_buffer.size() << "\n"; ip_packet ip; icmp_packet icmp; istream >> ip >> icmp; ip::address_v4 source_address(ip.header.ip_src); std::string source_address_str = source_address.to_string(); //std::cout << "# receive ip packet from " << source_address_str << "\n"; //std::cout << "# ip ver " << ip.ip_v() << "\n"; //std::cout << "# ip hdr len " << ip.ip_hl() << "\n"; //std::cout << "# ip ttl " << ip.ip_ttl() << "\n"; auto time_diff = time_receive - time_sent; uint16_t icmp_type = icmp.icmp_type(); switch(icmp_type) { case ping::icmp::type::echo_reply: std::cout << "# ttl " << uint16_t(ttl) << " receive icmp echo reply packet from " << source_address_str << "\n"; std::cout << "# icmp id " << icmp.icmp_id() << "\n"; std::cout << "# icmp seq " << icmp.icmp_seq() << "\n"; std::cout << "# time " << time_diff.total_milliseconds() << " ms\n"; break; case ping::icmp::type::time_exceeded: std::cout << "# ttl " << uint16_t(ttl) << " receive icmp time exceeded packet from " << source_address_str << " after " << time_diff.total_milliseconds() << " ms\n"; break; default: std::cout << "# ttl " << uint16_t(ttl) << " receive icmp packet type " << icmp_type << " from " << source_address_str << "\n"; break; } } }; int main(int argc, char **argv) { pinger p("www.gnu.org"); p.start(); return 0; }
$ sudo ./ping # ttl 1 receive icmp time exceeded packet from 192.168.56.1 after 2 ms # timeout ttl 1 # ttl 2 receive icmp time exceeded packet from 212.48.195.247 after 12 ms # timeout ttl 2 # ttl 3 receive icmp time exceeded packet from 212.48.198.234 after 66 ms # timeout ttl 3 # ttl 4 receive icmp time exceeded packet from 87.226.133.254 after 76 ms # timeout ttl 4 # ttl 5 receive icmp time exceeded packet from 194.68.123.187 after 126 ms # timeout ttl 5 # ttl 6 receive icmp time exceeded packet from 184.105.65.125 after 96 ms # timeout ttl 6 # ttl 7 receive icmp time exceeded packet from 72.52.92.213 after 80 ms # timeout ttl 7 # ttl 8 receive icmp time exceeded packet from 72.52.92.166 after 154 ms # timeout ttl 8 # ttl 9 receive icmp time exceeded packet from 184.105.64.54 after 166 ms # timeout ttl 9 # timeout ttl 10 # error system:85 # ttl 11 receive icmp echo reply packet from 209.51.188.148 # icmp id 1234 # icmp seq 4567 # time 148 ms # timeout ttl 11