2

我有以下代码:

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

4

2 回答 2

1

我自己没有测试此功能,但我认为您在配置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);
于 2013-05-20T11:04:38.787 回答
0

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

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

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); 
于 2017-03-22T17:38:48.823 回答