Создайте сокет уровня 2 / ethernet с boost asio raw socket (в C ++)

Создать сокеты IP, TCP или UDP довольно просто, используя boost::asio библиотека. Но когда дело доходит до сокетов Ethernet, например, вам необходимо реализовать boost/asio/basic_raw_socket.hpp

Поскольку в Интернете нет примеров подобного, и, поскольку я потратил много времени, прежде чем нашел ответ, я приведу здесь свое решение.

Самый полезный ресурс, который я нашел, был: AF_NETLINK (netlink) сокеты с использованием boost :: asio

4

Решение

Необработанный сокет может быть открыт с помощью generic :: raw_protocol:

std::string ifname("eth1");

typedef boost::asio::generic::raw_protocol raw_protocol_t;
typedef boost::asio::generic::basic_endpoint<raw_protocol_t> raw_endpoint_t;

sockaddr_ll sockaddr;
memset(&sockaddr, 0, sizeof(sockaddr));
sockaddr.sll_family = PF_PACKET;
sockaddr.sll_protocol = htons(ETH_P_ALL);
sockaddr.sll_ifindex = if_nametoindex(ifname.c_str());
sockaddr.sll_hatype = 1;

raw_protocol_t::socket socket(io_service, raw_protocol_t(PF_PACKET, SOCK_RAW))
socket.bind(raw_endpoint_t(&sockaddr, sizeof(sockaddr)));
7

Другие решения

Первое, что нужно сделать, это создать протокол Ethernet на основе класса basic_raw_socket.
Вы можете изменить поля протокола (htons (ETH_P_ALL)) и семейства (PF_PACKET) в зависимости от трафика, который вы хотите отправить / получить.

/// Create a link-layer protocol associated with a link-layer endpoint
class ll_protocol
{
public:
/// Obtain an identifier for the type of the protocol.
int type() const
{
return SOCK_RAW;
}

/// Obtain an identifier for the protocol.
int protocol() const
{
return protocol_;
}

/// Obtain an identifier for the protocol family.
int family() const
{
return family_;
}

// Construct with a specific family.
explicit ll_protocol(int protocol, int family) :
protocol_(protocol), family_(family)
{
}
explicit ll_protocol() :
protocol_(htons(ETH_P_ALL)), family_(PF_PACKET)
{
}

typedef boost::asio::basic_raw_socket<ll_protocol> socket;
typedef ll_endpoint<ll_protocol> endpoint;

private:
int protocol_;
int family_;
};

Для привязки сокета к интерфейсу требуется конечная точка. Ключевым моментом является создание структуры sockaddr_ll, в которой можно указать интерфейс для отправки / получения трафика.

#include <net/ethernet.h>
#include <sys/socket.h>
#include <linux/if_packet.h>
#include <cstddef>

template <typename Protocol>
class ll_endpoint
{
private:
sockaddr_ll sockaddr;
public:
/// The protocol type associated with the endpoint.
typedef Protocol protocol_type;
typedef boost::asio::detail::socket_addr_type data_type;

/// Constructor
ll_endpoint(const char* ifname)
{
sockaddr.sll_family = PF_PACKET;
sockaddr.sll_protocol = htons(ETH_P_ALL);
sockaddr.sll_ifindex = if_nametoindex(ifname);
sockaddr.sll_hatype = 1;
}

/// Assign from another endpoint.
ll_endpoint& operator=(const ll_endpoint& other)
{
sockaddr = other.sockaddr;
return *this;
}

/// The protocol associated with the endpoint.
protocol_type protocol() const
{
return protocol_type();
}

/// Get the underlying endpoint in the native type.
data_type* data()
{
return &sockaddr;
}

/// Get the underlying endpoint in the native type.
const data_type* data() const
{
return (struct sockaddr*)&sockaddr;
}

/// Get the underlying size of the endpoint in the native type.
std::size_t size() const
{
return sizeof(sockaddr);
}

/// Set the underlying size of the endpoint in the native type.
void resize(std::size_t size)
{
/* nothing we can do here */
}

/// Get the capacity of the endpoint in the native type.
std::size_t capacity() const
{
return sizeof(sockaddr);
}

/// Compare two endpoints for equality.
friend bool operator==(const ll_endpoint<Protocol>& e1,
const ll_endpoint<Protocol>& e2)
{
return e1.sockaddr == e2.sockaddr;
}

/// Compare two endpoints for inequality.
friend bool operator!=(const ll_endpoint<Protocol>& e1,
const ll_endpoint<Protocol>& e2)
{
return !(e1.sockaddr == e2.sockaddr);
}

/// Compare endpoints for ordering.
friend bool operator<(const ll_endpoint<Protocol>& e1,
const ll_endpoint<Protocol>& e2)
{
return e1.sockaddr < e2.sockaddr;
}

/// Compare endpoints for ordering.
friend bool operator>(const ll_endpoint<Protocol>& e1,
const ll_endpoint<Protocol>& e2)
{
return e2.sockaddr < e1.sockaddr;
}

/// Compare endpoints for ordering.
friend bool operator<=(const ll_endpoint<Protocol>& e1,
const ll_endpoint<Protocol>& e2)
{
return !(e2 < e1);
}

/// Compare endpoints for ordering.
friend bool operator>=(const ll_endpoint<Protocol>& e1,
const ll_endpoint<Protocol>& e2)
{
return !(e1 < e2);
}
};

Наконец, вы можете открыть сокет и подключиться к конечной точке следующим образом:

string ifname("eth1");
ll_protocol::socket socket;
socket.open(ll_protocol());
socket.bind(ll_endpoint<ll_protocol>((const char*)ifname.c_str()));
6

По вопросам рекламы [email protected]