2013-05-18 18 views
2

我有以下代碼:PACKET_TX_RING只發送出第一分組,則沒有做任何事情了

#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環功能。但是,只有第一個數據包被髮送(並且它被正確發送100%)。 網絡層上似乎沒有其他事情發生(tcpdump -vvv -e顯示沒有任何網絡通信發生!) 但是,sendto()調用得到正確處理。

+0

這可能不是主要的錯誤,但會發生什麼ring_offset,如果輪詢超時且返回值爲0,且_TP_STATUS_AVAILABLE_尚不存在?我想你已經看過[http://wiki.ipxwarzone.com/index.php5?title=Linux_packet_mmap](http://wiki.ipxwarzone.com/index.php5?title=Linux_packet_mmap) – thuovila

+1

我似乎有通過使偏移256(ring_offset * 256)而不是1024來修復這些東西。我只是不明白爲什麼,因爲我總是告訴它,數據包/幀的大小是1024?! – Doridian

+0

@Doridian我有同樣的問題,但移動關閉設置沒有幫助。你有沒有弄清楚爲什麼會發生這種情況? – jwbensley

回答

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); 
+0

你也可以注意到沒有必要強制轉換mmap的值。 – thuovila

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