【问题标题】:PACKET_TX_RING only sending out first packet, then not doing anything anymorePACKET_TX_RING 只发送第一个数据包,然后不再做任何事情
【发布时间】:2013-05-13 13:21:31
【问题描述】:

我有以下代码:

#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() 调用得到了正确处理。

【问题讨论】:

  • 这可能不是主要错误,但是如果 poll 超时并返回 0 并且 TP_STATUS_AVAILABLE 还不存在,ring_offset 会发生什么?我猜你已经看过http://wiki.ipxwarzone.com/index.php5?title=Linux_packet_mmap
  • 我似乎通过将偏移量设为 256 (ring_offset * 256) 而不是 1024 来修复这些问题。我只是不明白为什么,因为我总是告诉它数据包/帧的大小为 1024?!
  • @Doridian 我遇到了同样的问题,但移动偏移没有帮助。你有没有弄清楚为什么会发生这种情况?
  • 我现在已经在我的代码中解决了这个问题;如果使用 tpacket_req v2,则必须使用 PACKET_ALIGN(TPACKET2_HDRLEN);;如果使用 tpacket_req v1,则必须使用 TPACKET_HDRLEN。这将使您与 16 字节边界对齐。就我而言,在他们使用的示例中(对于 v2)TPACKET2_HDRLEN - sizeof(struct sockaddr_ll) 是 32,但它不起作用。请参阅下面的答案。

标签: c++ c linux sockets


【解决方案1】:

(我知道这有点晚了,但是这方面的文档仍然很差,示例很少,所以希望这会对某人有所帮助):

根据我上面的 cmets,这是我现在的工作代码(没有错误检查,只是一个粗略的概念证明):

struct tpacket2_hdr *hdr;
for (uint16_t i = 0; i < tpacket_req.tp_frame_nr; i += 1) {

    hdr = (void*)(mmapped_buffer + (tpacket_req.tp_frame_size * i));

    uint8_t *data = (uint8_t*)(hdr + TPACKET_ALIGN(TPACKET2_HDRLEN));

    memcpy(data, tx_buffer, frame_size);

    hdr->tp_len = frame_size;

    hdr->tp_status = TP_STATUS_SEND_REQUEST;

}
int32_t send_ret = sendto(sock_fd, NULL, 0, 0, NULL, 0); 

【讨论】:

    【解决方案2】:

    我自己没有测试此功能,但我认为您在配置 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);
    

    【讨论】:

    • 另外你可以注意到不需要转换 mmap 的值。
    猜你喜欢
    • 2022-11-13
    • 2011-08-11
    • 2015-11-01
    • 2012-02-17
    • 1970-01-01
    • 2021-08-29
    • 1970-01-01
    • 2016-09-22
    • 2011-09-21
    相关资源
    最近更新 更多