我有以下代码:
#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() 调用得到了正确处理。