Vlan id is set to 0 when TPACKET_V2 is used

2019-02-27 10:28发布

问题:

I have a problem about the usage of this TPACKET_V2 .

My problem is that after setting of this type of packet on socket, when I try to receive some packets I can't read the vlan id from the packet (of course from the header of the packet) the vlan_tci is ever 0.

Now I'm using open suse sp1 and when I run my program on sless sp2 I 'm able to get the vlan id with the same program that doesn't work on sless sp1 but the weird thing is that tcpdump is able to get the vlan id (on this sless) and tcpdump set the TPACKET_V2 (so this means that TPACKET_2 is supported)

My simple project is based on these functions , all called by the function createSocket , then there is a simple method that is reading packets on the socket and there I try to get informations on vlan id (there there is also the relative part used before with the 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);
}

and in my function for receive packets and I try to read informations and in particular h_vlan_TCI from but I receive ever 0x00 !!! Any suggestions?

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()

}

回答1:

When the kernel removes the vlan it also changes eth_h.h_proto to the protocol after de vlan tag so ETH_P_8021Q == ntohs(frame->eth_h.h_proto) will most probably be false.

Also, if you are listening in the tagged interface (ie. eth0.100) instead of the physical interface (eth0) you will not see the tags.