Last active
April 7, 2016 09:19
-
-
Save krisk0/5213d414a12b4a3ddcabd152e0cb2e59 to your computer and use it in GitHub Desktop.
send Ethernet frames the fast way (via mmap)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * 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