使用boost asio raw socket创建第2层/ ethernet套接字(在C ++中)

本文档介绍了如何在C++中利用Boost.Asio库创建第二层(Layer 2)或以太网套接字,以便进行原始的网络通信。通过这种方法,开发者可以直接操作数据链路层的包,实现更底层的网络编程。
// #include <boost/asio/detail/config.hpp>
// #include <boost/asio/detail/socket_types.hpp>
// #include <boost/asio/basic_raw_socket.hpp>
// #include <boost/asio/ip/basic_endpoint.hpp>
// #include <boost/asio/ip/basic_resolver.hpp>
// #include <boost/asio/ip/basic_resolver_iterator.hpp>
// #include <boost/asio/ip/basic_resolver_query.hpp>
// #include <boost/asio/detail/push_options.hpp>
// #include <boost/asio.hpp>
#include <netinet/if_ether.h>
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <sys/socket.h>
#include <linux/if_packet.h>
#include <cstddef>
#include <iostream>

#include <boost/asio.hpp>
#include <boost/bind.hpp>

class ipv4_header
{
public:
    ipv4_header() { std::fill(rep_, rep_ + sizeof(rep_), 0); }

    unsigned char  version() const { return (rep_[0] >> 4) & 0xF; }
    unsigned short header_length() const { return (rep_[0] & 0xF) * 4; }
    unsigned char  type_of_service() const { return rep_[1]; }
    unsigned short total_length() const { return decode(2, 3); }
    unsigned short identification() const { return decode(4, 5); }
    bool           dont_fragment() const { return (rep_[6] & 0x40) != 0; }
    bool           more_fragments() const { return (rep_[6] & 0x20) != 0; }
    unsigned short fragment_offset() const { return decode(6, 7) & 0x1FFF; }
    unsigned int   time_to_live() const { return rep_[8]; }
    unsigned char  protocol() const { return rep_[9]; }
    unsigned short header_checksum() const { return decode(10, 11); }

    boost::asio::ip::address_v4 source_address() const
    {
        boost::asio::ip::address_v4::bytes_type bytes = {{rep_[12], rep_[13], rep_[14], rep_[15]}};
        return boost::asio::ip::address_v4(bytes);
    }

    boost::asio::ip::address_v4 destination_address() const
    {
        boost::asio::ip::address_v4::bytes_type bytes = {{rep_[16], rep_[17], rep_[18], rep_[19]}};
        return boost::asio::ip::address_v4(bytes);
    }

    friend std::istream& operator>>(std::istream& is, ipv4_header& header)
    {
        is.read(reinterpret_cast<char*>(header.rep_), 20);
        if (header.version() != 4) is.setstate(std::ios::failbit);
        std::streamsize options_length = header.header_length() - 20;
        if (options_length < 0 || options_length > 40)
            is.setstate(std::ios::failbit);
        else
            is.read(reinterpret_cast<char*>(header.rep_) + 20, options_length);
        return is;
    }

private:
    unsigned short decode(int a, int b) const { return (rep_[a] << 8) + rep_[b]; }

    unsigned char rep_[60];
};

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;
    }

    ll_endpoint() {}

    /// 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 (struct sockaddr*)&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.sll_family == e2.sockaddr.sll_family) &&
               (e1.sockaddr.sll_protocol == e2.sockaddr.sll_protocol) &&
               (e1.sockaddr.sll_ifindex == e2.sockaddr.sll_ifindex) &&
               (e1.sockaddr.sll_hatype == e2.sockaddr.sll_hatype);
    }

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

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

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

    /// 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); }
};

/// 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_;
};

boost::asio::io_service io_service;
ll_protocol::socket     socket_(io_service);
boost::asio::streambuf  reply_buffer_;

void start_receive();
void handle_receive(const boost::system::error_code& error, std::size_t length);

char buff[2048];
void start_receive()
{
   // Wait for a reply. We prepare the buffer to receive up to 64KB.
    socket_.async_receive(boost::asio::buffer(buff,
          2048), boost::bind(handle_receive, _1, _2));
}

void handle_receive(const boost::system::error_code& error, std::size_t length)
{
    if (error)
    {
        std::cout << "async_receive error:" << error.message() << std::endl;
        return;
    }

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

    // // Print out some information about the reply packet.
    // std::cout << "protocol:" << (iipv4_hdr.protocol()
    //     << " " <<  length - ipv4_hdr.header_length() 
    //     << " bytes from " << ipv4_hdr.source_address()
    //     << " to " << ipv4_hdr.destination_address() << std::endl;

    struct iphdr* ip = (struct iphdr*)(buff + ETH_HLEN);
    int           ip_header_length = ip->ihl * 4;
    int           eth_ip_hdr_len = ETH_HLEN + ip_header_length;

    // printf("IP packet size: %d\n", htons(ip->tot_len));

    struct sockaddr_in source;
    struct sockaddr_in dest;
    memset(&source, 0, sizeof(source));
    source.sin_addr.s_addr = ip->saddr;
    memset(&dest, 0, sizeof(dest));
    dest.sin_addr.s_addr = ip->daddr;

    printf("%s -> ", inet_ntoa(source.sin_addr));
    printf("%s   ", inet_ntoa(dest.sin_addr));

    std::string srcIp(inet_ntoa(source.sin_addr));
    std::string dstIp(inet_ntoa(dest.sin_addr));

    u_char protocol = ip->protocol;
    if (protocol == IPPROTO_TCP)
    {
        struct tcphdr* tcp = (struct tcphdr*)(buff + eth_ip_hdr_len);
        int            dstPort = ntohs(tcp->dest);
        int tcp_header_length = tcp->doff * 4;
        printf("%d -> %d\n", ntohs(tcp->source), ntohs(tcp->dest));
        printf("TCP seq: %u\n", ntohl(tcp->seq));
        printf("payload size: %d\n", htons(ip->tot_len) - ip_header_length - tcp_header_length);
       
    }
    else if (protocol == IPPROTO_UDP)
    {
        struct udphdr* udp = (struct udphdr*)(buff + eth_ip_hdr_len);
        int            dstPort = ntohs(udp->dest);
        int            udp_header_length = 8;
        printf("%d -> %d\n", ntohs(udp->source), ntohs(udp->dest));
        printf("UDP\n");
        printf("payload size: %d\n", ntohs(udp->len) - udp_header_length);
       
    }
    else
    {
        printf("Unknown protocol:%d\n", protocol + 0);
    }

    start_receive();
}

int main()
{
    socket_.open(ll_protocol());
    std::string              ifname("em1");
    ll_endpoint<ll_protocol> ep((const char*)ifname.c_str());
    std::cout << ">>>" << ((sockaddr_ll*)ep.data())->sll_ifindex << std::endl;
    socket_.bind(ep);

    start_receive();

    io_service.run();

    while (1)
    {
        sleep(5);
        std::cout << "runing..." << std::endl;
    }
}

参考:
https://www.thinbug.com/q/26212014

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值