raw socket in linux

来源:互联网 发布:网络通信介质 编辑:程序博客网 时间:2024/05/21 06:59

0 前言

http://www.binarytides.com/raw-sockets-c-code-linux/

In normal sockets when any data is send over the network, the kernel of the operating system adds some headers to it like IP header and TCP header. So an application only needs to take care of what data it is sending and what reply it is expecting.
But there are other cases when an application needs to set its own headers. Raw sockets are used in security related applications like nmap , packets sniffer etc.

通常情况下,我们使用socket只关心载荷部分,协议的头部信息由操作系统自动填充。有些时候程序需要自己处理header,比如安全相关的程序nmap,packet sniffer等等,此时需要使用raw socket

1 Raw TCP packets

一个 TCP packet结构为:
Packet = IP Header + TCP Header + Data

2 Raw tcp sockets

创建一个tcp raw socket:

int s = socket (AF_INET, SOCK_RAW, IPPROTO_TCP);

程序需要提供tcp header, 而ip header 由操作系统的网络协议栈提供。

如果程序也想要接管ip header,可以通过以下两种方法:

  • 使用IPPROTO_RAW :
int s = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);   
  • 设置IP_HDRINCL socket选项为1
int s = socket (AF_INET, SOCK_RAW, IPPROTO_TCP);int one = 1;const int *val = &one;if (setsockopt (s, IPPROTO_IP, IP_HDRINCL, val, sizeof (one)) < 0){    printf ("Error setting IP_HDRINCL. Error number : %d . Error message : %s \n" , errno , strerror(errno));    exit(0);} // 当使用IP_HDRINCL, 函数socket的参数protocol不再起作用了

3 Example

/*    Raw TCP packets    Silver Moon (m00n.silv3r@gmail.com)*/#include<stdio.h>   //for printf#include<string.h> //memset#include<sys/socket.h>  //for socket ofcourse#include<stdlib.h> //for exit(0);#include<errno.h> //For errno - the error number#include<netinet/tcp.h> //Provides declarations for tcp header#include<netinet/ip.h>  //Provides declarations for ip header/*     96 bit (12 bytes) pseudo header needed for tcp header checksum calculation */struct pseudo_header{    u_int32_t source_address;    u_int32_t dest_address;    u_int8_t placeholder;    u_int8_t protocol;    u_int16_t tcp_length;};/*    Generic checksum calculation function*/unsigned short csum(unsigned short *ptr,int nbytes) {    register long sum;    unsigned short oddbyte;    register short answer;    sum=0;    while(nbytes>1) {        sum+=*ptr++;        nbytes-=2;    }    if(nbytes==1) {        oddbyte=0;        *((u_char*)&oddbyte)=*(u_char*)ptr;        sum+=oddbyte;    }    sum = (sum>>16)+(sum & 0xffff);    sum = sum + (sum>>16);    answer=(short)~sum;    return(answer);}int main (void){    //Create a raw socket    int s = socket (PF_INET, SOCK_RAW, IPPROTO_TCP);    if(s == -1)    {        //socket creation failed, may be because of non-root privileges        perror("Failed to create socket");        exit(1);    }    //Datagram to represent the packet    char datagram[4096] , source_ip[32] , *data , *pseudogram;    //zero out the packet buffer    memset (datagram, 0, 4096);    //IP header    struct iphdr *iph = (struct iphdr *) datagram;    //TCP header    struct tcphdr *tcph = (struct tcphdr *) (datagram + sizeof (struct ip));    struct sockaddr_in sin;    struct pseudo_header psh;    //Data part    data = datagram + sizeof(struct iphdr) + sizeof(struct tcphdr);    strcpy(data , "ABCDEFGHIJKLMNOPQRSTUVWXYZ");    //some address resolution    strcpy(source_ip , "192.168.1.2");    sin.sin_family = AF_INET;    sin.sin_port = htons(80);    sin.sin_addr.s_addr = inet_addr ("1.2.3.4");    //Fill in the IP Header    iph->ihl = 5;    iph->version = 4;    iph->tos = 0;    iph->tot_len = sizeof (struct iphdr) + sizeof (struct tcphdr) + strlen(data);    iph->id = htonl (54321);    //Id of this packet    iph->frag_off = 0;    iph->ttl = 255;    iph->protocol = IPPROTO_TCP;    iph->check = 0;     //Set to 0 before calculating checksum    iph->saddr = inet_addr ( source_ip );   //Spoof the source ip address    iph->daddr = sin.sin_addr.s_addr;    //Ip checksum    iph->check = csum ((unsigned short *) datagram, iph->tot_len);    //TCP Header    tcph->source = htons (1234);    tcph->dest = htons (80);    tcph->seq = 0;    tcph->ack_seq = 0;    tcph->doff = 5; //tcp header size    tcph->fin=0;    tcph->syn=1;    tcph->rst=0;    tcph->psh=0;    tcph->ack=0;    tcph->urg=0;    tcph->window = htons (5840);    /* maximum allowed window size */    tcph->check = 0;    //leave checksum 0 now, filled later by pseudo header    tcph->urg_ptr = 0;    //Now the TCP checksum    psh.source_address = inet_addr( source_ip );    psh.dest_address = sin.sin_addr.s_addr;    psh.placeholder = 0;    psh.protocol = IPPROTO_TCP;    psh.tcp_length = htons(sizeof(struct tcphdr) + strlen(data) );    int psize = sizeof(struct pseudo_header) + sizeof(struct tcphdr) + strlen(data);    pseudogram = malloc(psize);    memcpy(pseudogram , (char*) &psh , sizeof (struct pseudo_header));    memcpy(pseudogram + sizeof(struct pseudo_header) , tcph , sizeof(struct tcphdr) + strlen(data));    tcph->check = csum( (unsigned short*) pseudogram , psize);    //IP_HDRINCL to tell the kernel that headers are included in the packet    int one = 1;    const int *val = &one;    if (setsockopt (s, IPPROTO_IP, IP_HDRINCL, val, sizeof (one)) < 0)    {        perror("Error setting IP_HDRINCL");        exit(0);    }    //loop if you want to flood :)    while (1)    {        //Send the packet        if (sendto (s, datagram, iph->tot_len , 0, (struct sockaddr *) &sin, sizeof (sin)) < 0)        {            perror("sendto failed");        }        //Data send successfully        else        {            printf ("Packet Send. Length : %d \n" , iph->tot_len);        }    }    return 0;}//Complete

4 编译&运行

使用gcc编译,raw socket运行需要root权限。

5 其他参考

http://opensourceforu.com/2015/03/a-guide-to-using-raw-sockets/