使用TPACKET_V2时,Vlan id为0

Vlan id is set to 0 when TPACKET_V2 is used

本文关键字:Vlan id TPACKET V2 使用      更新时间:2023-10-16

我对这个TPACKET_V2的使用有一个问题。

我的问题是,在套接字上设置了这种类型的数据包后,当我试图接收一些数据包时,我无法从数据包(当然是从数据包的头)中读取vlan id, vlan_tci是0。

现在我使用的是开放的suse sp1,当我在sless sp2上运行我的程序时,我能够使用相同的程序获得vlan id,该程序在sless sp1上不起作用,但奇怪的是tcpdump能够获得vlan id(在这个sless上)和tcpdump设置TPACKET_V2(所以这意味着支持TPACKET_2)

我的简单项目是基于这些函数,所有由函数createSocket调用,然后有一个简单的方法是读取套接字上的数据包,在那里我试图获得vlan id的信息(那里也有之前使用的相对部分与TPACKET_V1)

#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <netinet/ether.h>
#include <linux/filter.h>
#include <net/if.h>
#include <arpa/inet.h>
    enum INTERFACE_T
    {
        RX_INTERFACE,
        TX_INTERFACE
    };
    static const char* PKT_TYPE[];
    // BLOCK_NUM*BLOCK_SIZE = FRAME_NUM*FRAME_SIZE
    enum { RX_BLOCK_SIZE = 8192,
        RX_BLOCK_NUM  = 256,
        RX_FRAME_SIZE = 2048,
        RX_FRAME_NUM  = 1024
    };
    enum { TX_BLOCK_SIZE = 8192,
        TX_BLOCK_NUM  = 256,
        TX_FRAME_SIZE = 2048,
        TX_FRAME_NUM  = 1024
    };
    struct RxFrame {
        struct tpacket2_hdr tp_h;  // Packet header
        uint8_t tp_pad[TPACKET_ALIGN(sizeof(tpacket2_hdr))-sizeof(tpacket2_hdr)];
        struct sockaddr_ll sa_ll; // Link level address information
        uint8_t sa_ll_pad[14];    //Alignment padding
        struct ethhdr eth_h;
    } __attribute__((packed));
    struct TxFrame
    {
        struct tpacket_hdr tp_h; // Packet header
        uint8_t tp_pad[TPACKET_ALIGN(sizeof(tpacket_hdr))-sizeof(tpacket_hdr)];
//      struct vlan_ethhdr vlan_eth_h;
//      struct arp arp;
    } __attribute__((packed));

    struct ring_buff {
        struct tpacket_req req;
        size_t  size;  // mmap size
        size_t  cur_frame;
        struct iovec *ring_buffer_;
        void *buffer;  // mmap
    };

int setIfFlags(short int flags)
{
    struct ifreq    ifr;
    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, if_name_.c_str(), sizeof(ifr.ifr_name));
    ifr.ifr_hwaddr.sa_family=getIfArptype();
    ifr.ifr_flags |= flags;
    if ( ioctl(socket_, SIOCSIFFLAGS, &ifr) == -1)
    {
        std::cout << "Error: ioctl(SIOSIFFLAGS) failed!" << std::endl;
        return 1;
    }
    return 0;
}

int bindSocket()
{
    struct sockaddr_ll sll;
    memset(&sll, 0, sizeof(sll));
    sll.sll_family   = AF_PACKET;
    sll.sll_protocol = htons(ETH_P_ALL);
    sll.sll_ifindex  = ifIndex_;
    sll.sll_hatype   = 0;
    sll.sll_pkttype  = 0;
    sll.sll_halen    = 0;
    if (bind(socket_, (struct sockaddr *)&sll, sizeof(sll)) < 0) {
        std::cout << "Error: bind() failed!" << std::endl;
        return 1;
    }
    return 0;
}

int packetMmap(ring_buff * rb)
{
    assert(rb);
    rb->buffer = mmap(0, rb->size, PROT_READ | PROT_WRITE, MAP_SHARED, socket_, 0);
    if (rb->buffer == MAP_FAILED) {
        std::cout << "Error: mmap() failed!" << std::endl;
        return 1; 
    }
    return 0;
}

void packetMunmap(ring_buff * rb)
{
    assert(rb);
    if (rb->buffer)
    {
        munmap(rb->buffer, rb->size);
        rb->buffer = NULL;
        rb->size = 0;
    }
}

int frameBufferCreate(ring_buff * rb)
{
    assert(rb);
    rb->ring_buffer_ =  (struct iovec*) malloc(rb->req.tp_frame_nr * sizeof(*rb->ring_buffer_));
    if (!rb->ring_buffer_) {
        std::cout << "No memory available !!!" << std::endl;
        return 1;
    }
    memset(rb->ring_buffer_, 0, rb->req.tp_frame_nr * sizeof(*rb->ring_buffer_));
    for (unsigned int i = 0; i < rb->req.tp_frame_nr; i++) {
        rb->ring_buffer_[i].iov_base = static_cast<void *>(static_cast<char *>(rb->buffer)+(i*rb->req.tp_frame_size));
        rb->ring_buffer_[i].iov_len = rb->req.tp_frame_size;
    }
    return 0;
}

void setRingBuffer(struct ring_buff *ringbuf) { rb_ = ringbuf; }

int setVlanTaggingStripping()
{
    socklen_t len;
    int val;
    unsigned int sk_type, tp_reserve, maclen, tp_hdrlen, netoff, macoff;
        unsigned int tp_hdr_len;
    unsigned int frame_size = RX_FRAME_SIZE;

    val = TPACKET_V2;
    len = sizeof(val);
    if (getsockopt(socket_, SOL_PACKET, PACKET_HDRLEN, &val, &len) < 0) {
        std::cout << "Error: getsockopt(SOL_PACKET, PACKET_HDRLEN) failed (can't get TPACKET_V2 header len on packet)" << std::endl;
                return 1;
    }
        tp_hdr_len = val;
    std::cout << "TPACKET_V2 header is supported (hdr len is " << val << ")"<< std::endl;
    std::cout << "tpacket2_hdrs header is supported (hdr len is " << sizeof(tpacket2_hdr) << ")"<< std::endl;
    val = TPACKET_V2;
    if (setsockopt(socket_, SOL_PACKET, PACKET_VERSION, &val, sizeof(val)) < 0) {
        std::cout << "Error: setsockopt(SOL_PACKET, PACKET_VERSION) failed (can't activate TPACKET_V2 on packet)" << std::endl;
        return 1;
    }
    std::cout << "TPACKET_V2 version is configured !!! " << std::endl;
    /* Reserve space for VLAN tag reconstruction */
    val = VLAN_TAG_LEN;
    if (setsockopt(socket_, SOL_PACKET, PACKET_RESERVE, &val, sizeof(val)) < 0) {
        std::cout << "Error: setsockopt(SOL_PACKET, PACKET_RESERVE) failed (can't set up reserve on packet)" << std::endl;
        return 1;
    }
    std::cout<< "Reserve space for VLAN tag reconstruction is configured !!! " << std::endl;
    return 0;
}

int setSoBufforce(int optname, int buffSize)
{
    if (setsockopt(socket_, SOL_SOCKET, SO_SNDBUFFORCE, &buffSize, sizeof(buffSize)) == -1)
    {
        std::cout << "Error: setsocketopt("<< (optname == SO_SNDBUFFORCE ? "SO_SNDBUFFORCE" : "SO_RCVBUFFORCE") << ") failed!" << std::endl;
        return 1;
    }
    return 0;
}
 createSocket(std::string ifName, INTERFACE_T ifType)
{
    if (ifName.empty())
    {
        std::cout << "Error: interface is empty!" << std::endl;;
        return NULL;
    }
    //Create the socket
    if ( (socket_ = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1 )
    {
        std::cout << "Error: calling socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)) failed!" << std::endl;
    }
    std::cout << "Creating Socket on interface= " << ifName << " to listen to ETH_P_ALL"<<std::endl;;

    s->setIfFlags(IFF_PROMISC|IFF_BROADCAST);
    //allocate space for ring buffer
    ring_buff *rb = (ring_buff *) malloc(sizeof(ring_buff));
    //  use the same size for RX/TX ring
    //set the version , here I insert the use of TPACKET_V2!
    setVlanTaggingStripping();
    rb->req.tp_block_size = RX_BLOCK_SIZE;
    rb->req.tp_block_nr   = RX_BLOCK_NUM;
    rb->req.tp_frame_size = RX_FRAME_SIZE;
    rb->req.tp_frame_nr   = RX_FRAME_NUM;
    setPacketRing(PACKET_RX_RING,&rb->req);
    rb->size = (rb->req.tp_block_size)*(rb->req.tp_block_nr);
    rb->cur_frame = 0;
    // Tweak send/rcv buffer size
    int sndBufSz = 4194304; // Send buffer in bytes
    int rcvBufSz = 4194304; // Receive buffer in bytes
    if (setSoBufforce(SO_SNDBUFFORCE, sndBufSz))
    {
      //close socket
    }
    if (setSoBufforce(SO_RCVBUFFORCE, rcvBufSz))
    {
        //close socket
    }
    // Add ARP filter so we will only receive ARP packet on this socket
    struct sock_filter BPF_code[6];
    struct sock_fprog filter;
    bindSocket();
    if (packetMmap(rb))
    {
        std::cout << "Error: mmap() failed!" << std::endl;
        //close socket
    }
    frameBufferCreate(rb);
    setRingBuffer(rb);
}

和在我的函数中接收数据包,我尝试读取信息,特别是h_vlan_TCI,但我收到0x00 !!有什么建议吗?

struct vlan_ethhdr* vlan_eth_h = (struct vlan_ethhdr*)&frame->eth_h

void readRawSocket(socket_)
{
    while (*(unsigned long*)rb->ring_buffer_[rb->cur_frame].iov_base)
    {
              RxFrame* frame = (RxFrame *)rb->ring_buffer_[rb->cur_frame].iov_base;
#if 0
                tpacket_hdr* h = &frame->tp_h;
                char buffer[256];
                sprintf (buffer, " -tpacket(v1): status=%ld,len=%d,snaplen=%d,mac=%d,net=%d,sec=%d,usec=%d",
                         h->tp_status, h->tp_len, h->tp_snaplen, h->tp_mac,h->tp_net, h->tp_sec, h->tp_usec);
                std::cout << std::string(buffer) << std::endl;
#else
                tpacket2_hdr* h = &frame->tp_h;
                char buffer[512];
                sprintf (buffer, " -tpacket(v2): status=%d,len=%d,snaplen=%d,mac=%d,net=%d,sec=%d,nsec=%d,vlan_tci=%d (vlan_tci=0x%04x)",
                         h->tp_status, h->tp_len, h->tp_snaplen, h->tp_mac, h->tp_net,  h->tp_sec, h->tp_nsec, h->tp_vlan_tci,  ntohs(h->tp_vlan_tci));
                std::cout << std::string(buffer) << std::endl;
#endif
        if ( ETH_P_8021Q == ntohs(frame->eth_h.h_proto) )
        {
                    struct vlan_ethhdr* vlan_eth_h = (struct vlan_ethhdr*)&frame->eth_h;
                    int vlan_tag = VLAN_TAG(ntohs(vlan_eth_h->h_vlan_TCI));
                   std::cout << " -Vlan " << vlan_tag << " packet to this host received";
                }
        rb->cur_frame = ( rb->cur_frame+1) % rx_socket_->getFrameNum();
    } // while()
}

当内核删除vlan时,它也将eth_h.h_proto更改为de vlan标记后的协议,因此ETH_P_8021Q == ntohs(frame->eth_h.h_proto)很可能为假。

同样,如果你正在监听标记的接口(即。Eth0.100)而不是物理接口(eth0),您将看不到标签。