c++ - PACKET_TX_RING 只发送第一个数据包,然后不再做任何事情

标签 c++ c linux sockets

我有以下代码:

#ifndef RAWSOCKET_H
#define RAWSOCKET_H

#include <stdio.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>

#include <arpa/inet.h>
#include <netinet/if_ether.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>

#include <linux/if.h>
#include <linux/if_packet.h>

#include "IPPacket.h"

#define CONF_RING_FRAMES        128

/// Initialize a packet socket ring buffer
//  @param ringtype is one of PACKET_RX_RING or PACKET_TX_RING
static inline char *
init_packetsock_ring(int fd, int ringtype)
{
  tpacket_req tp;
  char *ring;

  // tell kernel to export data through mmap()ped ring
  tp.tp_block_size = 1024 * 8;
  tp.tp_block_nr = 1024;
  tp.tp_frame_size = 1024 * 8;
  tp.tp_frame_nr = 1024;
  setsockopt(fd, SOL_PACKET, ringtype, (void*) &tp, sizeof(tp));

  int val = TPACKET_V1;
  setsockopt(fd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val));

  // open ring
  ring = (char*)mmap(0, tp.tp_block_size * tp.tp_block_nr,
               PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

  if (!ring)
    return NULL;

  return ring;
}

/// transmit a packet using packet ring
//  NOTE: for high rate processing try to batch system calls,
//        by writing multiple packets to the ring before calling send()
//
//  @param pkt is a packet from the network layer up (e.g., IP)
//  @return 0 on success, -1 on failure
static inline int
process_tx(int fd, char *ring, const char *pkt, int pktlen, sockaddr_ll *txring_daddr)
{
  static int ring_offset = 0;

  struct tpacket_hdr *header;
  struct pollfd pollset;
  char *off;
  int ret;

  // fetch a frame
  // like in the PACKET_RX_RING case, we define frames to be a page long,
  // including their header. This explains the use of getpagesize().
  header = (tpacket_hdr*)(void *) ring + (ring_offset * 1024);

  while (header->tp_status != TP_STATUS_AVAILABLE) {

    // if none available: wait on more data
    pollset.fd = fd;
    pollset.events = POLLOUT;
    pollset.revents = 0;
    ret = poll(&pollset, 1, 1000 /* don't hang */);
    if (ret < 0) {
      if (errno != EINTR) {
        perror("poll");
        return -1;
      }
      return 0;
    }

    ring_offset++;
    if(ring_offset >= 1024 * 8) ring_offset = 0;
    header = (tpacket_hdr*)(void *) ring + (ring_offset * 1024);
  }

  // fill data
  off = (char*)(((char*) header) + (TPACKET_HDRLEN - sizeof(struct sockaddr_ll)));
  memcpy(off, pkt, pktlen);

  // fill header
  header->tp_len = pktlen;
  header->tp_status = TP_STATUS_SEND_REQUEST;

  // increase consumer ring pointer
  /*ring_offset++;
  if(ring_offset >= 1024 * 8) ring_offset = 0;*/

  // notify kernel
  if (sendto(fd, NULL, 0, 0, (sockaddr*)txring_daddr, sizeof(sockaddr_ll)) < 0) {
    perror("sendto");
    return -1;
  }

  return 0;
}

class RawSocket
{
    public:
        inline RawSocket() { }
        inline void initialize() {
            sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP));

            ring = init_packetsock_ring(sockfd, PACKET_TX_RING);

            ifreq ifr;
            memset (&ifr, 0, sizeof (ifr));
            strncpy((char *) ifr.ifr_name, "eth0", IFNAMSIZ);
            ioctl(sockfd, SIOCGIFINDEX, &ifr);
            int index = ifr.ifr_ifindex;
            ioctl(sockfd, SIOCGIFHWADDR, &ifr);

            sll = new sockaddr_ll();
            sll->sll_family = AF_PACKET;
            sll->sll_ifindex = index;
            sll->sll_protocol = htons(ETH_P_IP);
            sll->sll_halen = htons(6);

            memcpy(IPPacket::our_mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
            memcpy(sll->sll_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);

            /*struct packet_mreq mr;
            memset (&mr, 0, sizeof (mr));
            mr.mr_ifindex = ifr.ifr_ifindex;
            mr.mr_type = PACKET_MR_PROMISC;
            setsockopt(sockfd, SOL_PACKET,PACKET_ADD_MEMBERSHIP, &mr, sizeof (mr));*/
            //setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &optval, sizeof(int));
        }

        inline ~RawSocket() {
            close(sockfd);
        }

        inline void send(const IPPacket* ip) const {
            process_tx(sockfd, ring, ip->packet_ptr, ip->tot_len, sll);
            printf("TX\n");
        }
    protected:
        char *ring;
        int sockfd;
        sockaddr_ll *sll;
};

#endif // RAWSOCKET_H

ip->packet_ptr 是指向包含 ethhdr 和 iphdr 等的数据包的指针。 数据包通过“正常”PF_PACKET 套接字正确发送。 现在我尝试使用 TX Ring 功能。但是,只有第一个数据包被发送(并且 100% 正确发送)。 网络层似乎没有发生任何其他事情(tcpdump -vvv -e 显示根本没有网络流量发生!) 但是,sendto() 调用得到正确处理。

最佳答案

我自己没有测试这个功能,但我认为你在配置 struct tpacket_req 字段时有错误。 _nr 字段非常大。请参阅此示例代码(链接到 wiki ):

/* Setup the fd for mmap() ring buffer */
req.tp_block_size=4096;
req.tp_frame_size=1024;
req.tp_block_nr=64;
req.tp_frame_nr=4*64;
if ( (setsockopt(fd,
    SOL_PACKET,
    PACKET_RX_RING,
    (char *)&req,
    sizeof(req))) != 0 ) {
    perror("setsockopt()");
    close(fd);
    return 1;
};

/* mmap() the sucker */
map=mmap(NULL,
    req.tp_block_size * req.tp_block_nr,
    PROT_READ|PROT_WRITE|PROT_EXEC, MAP_SHARED, fd, 0);

关于c++ - PACKET_TX_RING 只发送第一个数据包,然后不再做任何事情,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16624879/

相关文章:

linux - 为什么使用 uImage 而不是 zImage

c++ - 通过指向 int 的指针为 char 数组起别名合法吗?

c++ - R - ggplot2 使用什么作为其绘图后端?

c++ - 在异常中不嵌入 std::string 的规则是否仍然适用于移动构造函数?

c - C 中合并两个链表时遇到问题

linux - 使用绝对路径覆盖 exec 系统调用

c++ - Qt/C++ - qDebug << 类

c - 为 2D 数组分配内存时出现段错误(核心转储)

c - C语言中任意非线性方程的辛普森1/3积分法积分?

linux - Bash for 循环不能正确理解变量?