我正在尝试使用packet_mmap发送原始以太网帧。我找到的一个很好的例子是gist。但是,此示例使用的是链路级原始套接字,因此发送的以太网帧的源以太网地址固定为接口的地址。我需要的是能够发送任意以太网帧(源以太网地址可以是任意的)。
所以我通过将fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
更改为fd = socket(AF_INET,SOCK_PACKET,htons(ETH_P_ALL));
来稍微采用要点。
但是,这导致setsockopt(fd, SOL_PACKET, ringtype, (void*) &tp, sizeof(tp))
失败,导致Operation not supported
失败。
我的linux是3.19.0。
任何实现任意帧发送的想法?非常感谢!
以下是我运行的代码:
// Example asynchronous packet socket reading with PACKET_TX_RING
// From http://codemonkeytips.blogspot.com/2011/07/asynchronous-packet-socket-writing-with.html
#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>
/// The number of frames in the ring
// This number is not set in stone. Nor are block_size, block_nr or frame_size
#define CONF_RING_FRAMES 128
#define CONF_DEVICE "eth0"
/// Offset of data from start of frame
#define PKT_OFFSET (TPACKET_ALIGN(sizeof(struct tpacket_hdr)) + \
TPACKET_ALIGN(sizeof(struct sockaddr_ll)))
/// (unimportant) macro for loud failure
#define RETURN_ERROR(lvl, msg) \
do { \
fprintf(stderr, msg); \
return lvl; \
} while(0);
static struct sockaddr_ll txring_daddr;
/// create a linklayer destination address
// @param ringdev is a link layer device name, such as "eth0"
static int
init_ring_daddr(int fd, const char *ringdev)
{
struct ifreq ifreq;
// get device index
strcpy(ifreq.ifr_name, ringdev);
if (ioctl(fd, SIOCGIFINDEX, &ifreq)) {
perror("ioctl");
return -1;
}
txring_daddr.sll_family = AF_PACKET;
txring_daddr.sll_protocol = htons(ETH_P_IP);
txring_daddr.sll_ifindex = ifreq.ifr_ifindex;
// set the linklayer destination address
// NOTE: this should be a real address, not ff.ff....
txring_daddr.sll_halen = ETH_ALEN;
memset(&txring_daddr.sll_addr, 0xff, ETH_ALEN);
return 0;
}
/// Initialize a packet socket ring buffer
// @param ringtype is one of PACKET_RX_RING or PACKET_TX_RING
static char *
init_packetsock_ring(int fd, int ringtype)
{
struct tpacket_req tp;
char *ring;
// tell kernel to export data through mmap()ped ring
tp.tp_block_size = CONF_RING_FRAMES * getpagesize();
tp.tp_block_nr = 1;
tp.tp_frame_size = getpagesize();
tp.tp_frame_nr = CONF_RING_FRAMES;
if (setsockopt(fd, SOL_PACKET, ringtype, (void*) &tp, sizeof(tp))) {
perror("setting up ring");
RETURN_ERROR(NULL, "setsockopt() ring\n");
}
#ifdef TPACKET_V2
printf("it's TPACKET_V2\n");
val = TPACKET_V1;
setsockopt(fd, SOL_PACKET, PACKET_HDRLEN, &val, sizeof(val));
#endif
// open ring
ring = mmap(0, tp.tp_block_size * tp.tp_block_nr,
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (!ring)
RETURN_ERROR(NULL, "mmap()\n");
if (init_ring_daddr(fd, CONF_DEVICE))
return NULL;
return ring;
}
/// Create a packet socket. If param ring is not NULL, the buffer is mapped
// @param ring will, if set, point to the mapped ring on return
// @return the socket fd
static int
init_packetsock(char **ring, int ringtype)
{
int fd;
// open packet socket
//fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
fd = socket(AF_INET,SOCK_PACKET,htons(3)); //ETH_P_ALL = 3
if (fd < 0) {
RETURN_ERROR(-1, "Root priliveges are required\nsocket() rx. \n");
}
if (ring) {
*ring = init_packetsock_ring(fd, ringtype);
if (!*ring) {
close(fd);
return -1;
}
}
return fd;
}
static int
exit_packetsock(int fd, char *ring)
{
if (munmap(ring, CONF_RING_FRAMES * getpagesize())) {
perror("munmap");
return 1;
}
if (close(fd)) {
perror("close");
return 1;
}
return 0;
}
/// 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 int process_tx(int fd, char *ring, const char *pkt, int pktlen)
{
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 = (void *) ring + (ring_offset * getpagesize());
assert((((unsigned long) header) & (getpagesize() - 1)) == 0);
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;
}
}
// fill data
off = ((void *) 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 = (ring_offset + 1) & (CONF_RING_FRAMES - 1);
// notify kernel
if (sendto(fd, NULL, 0, 0, (void *) &txring_daddr, sizeof(txring_daddr)) < 0) {
perror("sendto");
return -1;
}
return 0;
}
/// Example application that opens a packet socket with rx_ring
int main(int argc, char **argv)
{
char *ring;
char pkt[125] = {0x00,0x0c,0x29,0xa4,0xff,0xbc,0x40,0x25,0xc2,0xd9,0xfb,0x8c,0x08,0x00,0x45,0x00,0x00,0x6f,0x24,0x1b,0x40,0x00,0x40,0x06,0x02,0x4b,0x0a,0x00,0x00,0x07,0x0a,0x00,0x00,0x1d,0xb8,0x64,0x01,0xbb,0x80,0x9e,0xaa,0x77,0x17,0x6d,0xa2,0x04,0x80,0x18,0x00,0x73,0x03,0xa0,0x00,0x00,0x01,0x01,0x08,0x0a,0x01,0x27,0x8e,0xaf,0x00,0x01,0xe8,0x71,0x16,0x03,0x01,0x00,0x36,0x01,0x00,0x00,0x32,0x03,0x02,0x55,0xf5,0x01,0xa9,0xc0,0xca,0xae,0xd6,0xd2,0x9b,0x6a,0x79,0x6d,0x9a,0xe8,0x9d,0x78,0xe2,0x64,0x98,0xf0,0xac,0xcb,0x2c,0x0d,0x51,0xa5,0xf8,0xc4,0x0f,0x93,0x87,0x00,0x00,0x04,0x00,0x35,0x00,0xff,0x01,0x00,0x00,0x05,0x00,0x0f,0x00,0x01,0x01};
int fd;
printf("page size %x\n", getpagesize());
fd = init_packetsock(&ring, PACKET_TX_RING);
if (fd < 0)
return 1;
// TODO: make correct IP packet out of pkt
process_tx(fd, ring, pkt, 125);
pkt[1] ++;
process_tx(fd, ring, pkt, 125);
if (exit_packetsock(fd, ring))
return 1;
printf("OK\n");
return 0;
}
答案 0 :(得分:2)
好吧,我没有运行你的代码,但是SOCK_PACKET
已被弃用。来自man 2 socket
:
SOCK_PACKET:已过时,不应在新程序中使用;看到 分组(7)。
接口访问的最低级别实际上是SOCK_RAW
:它将允许您创建任意数据包,包括伪造以太网层的数据。尝试:
fd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
或
fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));