3

I try to connect to socket to send icmp echo request with modified boost/pinger.cpp return

Operation not permitted

even I am root user.

In Ubuntu 20.04, I am runnig the program with sudo there is no problem it works. But if I run at ARM with poky OS (Yocto) (with root of course) it return the error as I said.

Error occured when I try to

mSocket.async_connect(mDestination, boost::bind(&Pinger::startSend, this, boost::placeholders::_1));

Here is my code

pinger.cpp

#include "pinger.h"

Pinger::Pinger(boost::asio::io_context &io_context, const char *destination)
    : mResolver(io_context), mSocket(io_context),
      mTimeoutTimer(io_context), mSequenceNumber(1)
{
    mDestination = *mResolver.resolve(boost::asio::ip::icmp::v4(), destination, "");
}

void Pinger::start()
{
    startConnect();
}

void Pinger::stop()
{
    mSocket.close();
    mTimeoutTimer.cancel();
}

void Pinger::startConnect()
{
    mSocket.async_connect(mDestination, boost::bind(&Pinger::startSend, this, boost::placeholders::_1));
}

void Pinger::startSend(const boost::system::error_code &ec)
{
    if (ec)
        return;

    std::string body("\"Hello!\" from pinger.");

    // Create an ICMP header for an echo request.
    icmp_header echo_request;
    echo_request.type(icmp_header::echo_request);
    echo_request.code(0);
    echo_request.identifier(getIdentifier());
    echo_request.sequence_number(mSequenceNumber);
    compute_checksum(echo_request, body.begin(), body.end());

    // Encode the request packet.
    boost::asio::streambuf request_buffer;
    std::ostream os(&request_buffer);
    os << echo_request << body;

    // Send the request.
    mTimeSent = boost::asio::steady_timer::clock_type::now();
    mSocket.async_send(request_buffer.data(), boost::bind(&Pinger::startReceive, this, boost::placeholders::_1));

    // Wait up to ten seconds for a reply.
    mTimeoutTimer.expires_at(mTimeSent + boost::asio::chrono::seconds(15));
    mTimeoutTimer.async_wait(boost::bind(&Pinger::handleTimeout, this, boost::placeholders::_1));
}

void Pinger::startReceive(const boost::system::error_code &ec)
{
    if (ec)
        return;

    // Discard any data already in the buffer.
    mReplyBuffer.consume(mReplyBuffer.size());

    // Wait for a reply. We prepare the buffer to receive up to 64KB.
    mSocket.async_receive(mReplyBuffer.prepare(65536),
                          boost::bind(&Pinger::handleReceive, this, boost::placeholders::_1, _2));
}

void Pinger::handleReceive(const boost::system::error_code &ec, std::size_t length)
{
    if (ec)
        return;

    // The actual number of bytes received is committed to the buffer so that we
    // can extract it using a std::istream object.
    mReplyBuffer.commit(length);

    // Decode the reply packet.
    std::istream is(&mReplyBuffer);
    ipv4_header ipv4_hdr;
    icmp_header icmp_hdr;
    is >> ipv4_hdr >> icmp_hdr;

    // We can receive all ICMP packets received by the host, so we need to
    // filter that match the our identifier.
    if (is && icmp_hdr.identifier() == getIdentifier() && icmp_hdr.sequence_number() == mSequenceNumber)
    {
        if (!mResultSent)
        {
            mResultSent = true;
            stop();
            onResult(true);
            return;
        }
    }
}

void Pinger::handleTimeout(const boost::system::error_code &ec)
{
    if (ec == boost::asio::error::operation_aborted)
        return;

    if (!mResultSent)
    {
        mResultSent = true;
        stop();
        onResult(false);
    }
}

unsigned short Pinger::getIdentifier()
{
#if defined(BOOST_ASIO_WINDOWS)
    return static_cast<unsigned short>(::GetCurrentProcessId());
#else
    return static_cast<unsigned short>(::getpid());
#endif
}

pinger.h


#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/signals2.hpp>
#include <istream>
#include <iostream>
#include <ostream>

#include "icmp_header.hpp"
#include "ipv4_header.hpp"

class Pinger
{
public:
    Pinger(boost::asio::io_context &io_context, const char *destination);

    void start();
    void stop();

    //signals
    boost::signals2::signal<void(bool isAlive)> onResult;

private:
    void startConnect();
    void startSend(const boost::system::error_code &ec);
    void startReceive(const boost::system::error_code &ec);
    void handleReceive(const boost::system::error_code &ec, std::size_t length);
    void handleTimeout(const boost::system::error_code &ec);
    static unsigned short getIdentifier();

    boost::asio::ip::icmp::resolver mResolver;
    boost::asio::ip::icmp::endpoint mDestination;
    boost::asio::ip::icmp::socket mSocket;
    boost::asio::steady_timer mTimeoutTimer;
    unsigned short mSequenceNumber;
    boost::asio::chrono::steady_clock::time_point mTimeSent;
    boost::asio::streambuf mReplyBuffer;
    bool mResultSent = false;
};


furkan
  • 79
  • 5

1 Answers1

1

Out on a limb

setcap cap_net_raw,cap_net_admin=eip ./your_exeutable

might help.

boost::asio::ip::icmp::socket mSocket;

looks like it is going to be using SOCK_RAW under the hood

sehe
  • 374,641
  • 47
  • 450
  • 633
  • thank you for your answer. I was try setcap but did not work and yes it use raw sokcet. Actually I think I missed add some thing to OS. Is there any lib or dependencies to run raw socket or send icmp request? – furkan Jul 03 '20 at 10:36
  • @sehe A bad news is `setcap` still needs `root` permission. – John Jun 21 '22 at 08:44
  • OP already had `root`, so that's not bad news – sehe Jun 21 '22 at 10:08