【问题标题】:Create a layer 2 / ethernet socket with boost asio raw socket (in C++)使用 boost asio 原始套接字创建第 2 层/以太网套接字(在 C++ 中)
【发布时间】:2014-11-30 11:07:59
【问题描述】:

使用boost::asio 库创建 IP、TCP 或 UDP 套接字相当容易。但是以以太网套接字为例,您需要实现boost/asio/basic_raw_socket.hpp

由于互联网上没有这样的例子,而且我花了很长时间才找到答案,所以我会把我的解决方法放在这里。

我找到的最有用的资源是:AF_NETLINK (netlink) sockets using boost::asio

【问题讨论】:

    标签: c++ sockets boost boost-asio ethernet


    【解决方案1】:

    可以使用 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)));
    

    【讨论】:

    • 您的意思是创建 ll_endpoint 模板而不是?很高兴知道。 :)
    【解决方案2】:

    首先要做的是创建一个基于 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()));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-02-16
      • 1970-01-01
      • 1970-01-01
      • 2012-01-16
      • 2011-10-15
      • 2011-12-05
      • 2014-02-21
      • 1970-01-01
      相关资源
      最近更新 更多