Skip to content

Instantly share code, notes, and snippets.

@krisk0
Last active April 7, 2016 09:19
Show Gist options
  • Select an option

  • Save krisk0/5213d414a12b4a3ddcabd152e0cb2e59 to your computer and use it in GitHub Desktop.

Select an option

Save krisk0/5213d414a12b4a3ddcabd152e0cb2e59 to your computer and use it in GitHub Desktop.
send Ethernet frames the fast way (via mmap)
/*
* Send 3 malformed ethernet ARP frames the fast way (described in
* linux/Documentation/packet_mmap.txt)
*
* compile with -std=c99 -lnet
*
* The program is known to work under 64-bit Linux and 64-bit Android
* Requires libnet library (>=net-libs/libnet-1.1.6, >=bionic-core/libnet-1.1.6)
*/
typedef unsigned int uint;
#include <libnet.h>
#include <libnet/libnet-functions.h>
#include <string.h>
#include <assert.h>
#include <linux/if_packet.h>
#include <sys/mman.h>
#include <asm-generic/mman.h>
#include <linux/if.h>
#include <linux/if_ether.h>
char* g_ct[2];
struct tpacket2_hdr* g_control[2];
typedef struct
{
void* unmap_me;
int size;
int fd;
} transfer_ring_t;
int get_ifindex(const char* interface)
{
struct ifreq s_ifr;
int s = socket(AF_INET, SOCK_DGRAM, 0);
assert(s>=0);
strncpy(s_ifr.ifr_name, interface, sizeof(s_ifr.ifr_name));
assert( 0==ioctl(s, SIOCGIFINDEX, &s_ifr) );
close(s);
return s_ifr.ifr_ifindex;
}
void setup_transfer(transfer_ring_t* t,const char* interface)
{
int fd=socket(PF_PACKET, SOCK_RAW, 0);
assert(fd>=0);
int val=1;
assert(0<=setsockopt(fd, SOL_PACKET, PACKET_QDISC_BYPASS,
&val, sizeof(val)));
struct tpacket_req layout;
// size must be >= tp_frame_nr*tp_frame_size
// size must divisible by kernel page size which is 4096
layout.tp_block_size=4096;
layout.tp_frame_size=2048; // 2048 < MTU+header=1500+14
layout.tp_block_nr = 1;
layout.tp_frame_nr = 2;
assert(0==setsockopt(fd, SOL_PACKET, PACKET_TX_RING,
&layout, sizeof(layout)));
t->size=layout.tp_block_size * layout.tp_block_nr;
void* me=mmap(NULL, t->size,
PROT_READ|PROT_WRITE, MAP_SHARED|MAP_POPULATE|MAP_LOCKED, fd, 0);
assert( MAP_FAILED != me );
for(int i=2;i--;)
{
g_control[i]=me+i*layout.tp_frame_size;
g_ct[i]=((char*)g_control[i]) + TPACKET_HDRLEN - sizeof(struct sockaddr_ll);
}
// bind socket to interface
struct sockaddr_ll my_addr;
my_addr.sll_ifindex=get_ifindex(interface);
my_addr.sll_family = AF_PACKET;
my_addr.sll_protocol = htons(ETH_P_ALL);
assert(0==bind(fd,(struct sockaddr *)&my_addr, sizeof(my_addr)));
t->fd=fd;
t->unmap_me=me;
}
int sock_qdisc_bypass(void)
{
int s, val=1;
s=socket(PF_PACKET,SOCK_RAW,0);
assert(0<=s);
val = setsockopt(s,SOL_PACKET,PACKET_QDISC_BYPASS,&val,sizeof(val));
// -1 means kernel version < 3.14
if(val<0)
fprintf(stderr,"Kernel does not support PACKET_QDISC_BYPASS\n");
return s;
}
void bye_transfer(transfer_ring_t* t)
{
munmap(t->unmap_me,t->size);
close(t->fd);
}
char* g_warmup_string="мамамыларамы";
char g_header[16]={
0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,
0x08,0x06,0x00,0x10};
void throw_frame(int s,int i,const char* data,int data_length)
{
struct tpacket2_hdr* c=g_control[i];
while(c->tp_status & (TP_STATUS_SEND_REQUEST | TP_STATUS_SENDING))
{
printf(".");
assert( 0<=sendto(s, NULL, 0, MSG_DONTWAIT, NULL, 0) );
}
//printf("tp_status: %d\n",c->tp_status);
assert(TP_STATUS_AVAILABLE == c->tp_status);
memcpy(g_ct[i],g_header,16);
memcpy(g_ct[i]+16,data,data_length);
c->tp_len=c->tp_snaplen = 16+data_length;
c->tp_status=TP_STATUS_SEND_REQUEST;
sendto(s, NULL, 0, 0, NULL, 0);
assert(TP_STATUS_AVAILABLE == c->tp_status);
}
void get_interface(char* i,char* m)
{
char e[LIBNET_ERRBUF_SIZE];
e[0] = 0;
libnet_t* handle = libnet_init(LIBNET_LINK, NULL, e);
if(!handle)
{
fprintf(stderr,"libnet_init() failed\n");
fprintf(stderr,"%s\n",e);
assert(0);
}
char* c=(char*)libnet_getdevice(handle);
assert(c);
assert(1+IFNAMSIZ>strlen(c));
strncpy(i, c, 1+IFNAMSIZ);
c=(char*)libnet_get_hwaddr(handle);
memcpy(m,c,6);
libnet_destroy(handle);
}
char g_interface[1+IFNAMSIZ];
int main()
{
transfer_ring_t r;
get_interface(g_interface,g_header+6);
setup_transfer(&r,g_interface);
printf("unswappable page at %lX\n",(uint64_t)r.unmap_me);
for(int i=0;i<2;i++)
printf("write area no. %d is at %lX\n",i,(uint64_t)g_ct[i]);
int index=0;
int close_me=sock_qdisc_bypass();
for(int i=0;i<3;i++)
{
printf("sending frame no. %d\n",i);
throw_frame(r.fd,index,g_warmup_string+8*i,8);
index ^= 1;
}
close(close_me);
bye_transfer(&r);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment