linux raw socket

来源:互联网 发布:网络安利是什么意思 编辑:程序博客网 时间:2024/05/16 17:20

raw socket即原始套接字,可以通过创建socket时,设置参数SOCK_RAW来创建。相比平时使用SOCK_STREAM创建的应用层的套接字,raw socket可以直接处理ip首部和tcp首部,并且可以监听抓取经过本机的ip数据包和mac帧,可以处理ICMP和IGMP等网络控制报文。通常有两种方式:

//发送接收ip数据包socket(PF_INET,SOCK_RAW,IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP)//发送接收以太网数据帧socket(PF_PACKET,SOCK_RAW,htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))

PF_INET可以处理和获取ip层级以上的数据,而PF_PACKET可以处理和获取数据链路层及以上的数据。

IP 首部结构
这里写图片描述

对应的结构体,头文件netinet/ip.h

struct iphdr  {#if __BYTE_ORDER == __LITTLE_ENDIAN    unsigned int ihl:4;    unsigned int version:4;#elif __BYTE_ORDER == __BIG_ENDIAN    unsigned int version:4;    unsigned int ihl:4;#else# error "Please fix <bits/endian.h>"#endif    u_int8_t tos;    u_int16_t tot_len;    u_int16_t id;    u_int16_t frag_off;    u_int8_t ttl;    u_int8_t protocol;    u_int16_t check;    u_int32_t saddr;    u_int32_t daddr;    /*The options start here. */  };

tcp首部结构
这里写图片描述
对应结构体,头文件netinet/tcp.h

struct tcphdr  {    __extension__ union    {      struct      {    u_int16_t th_sport;     /* source port */    u_int16_t th_dport;     /* destination port */    tcp_seq th_seq;     /* sequence number */    tcp_seq th_ack;     /* acknowledgement number */# if __BYTE_ORDER == __LITTLE_ENDIAN    u_int8_t th_x2:4;       /* (unused) */    u_int8_t th_off:4;      /* data offset */# endif# if __BYTE_ORDER == __BIG_ENDIAN    u_int8_t th_off:4;      /* data offset */    u_int8_t th_x2:4;       /* (unused) */# endif    u_int8_t th_flags;# define TH_FIN 0x01# define TH_SYN 0x02# define TH_RST 0x04# define TH_PUSH    0x08# define TH_ACK 0x10# define TH_URG 0x20    u_int16_t th_win;       /* window */    u_int16_t th_sum;       /* checksum */    u_int16_t th_urp;       /* urgent pointer */      };      struct      {    u_int16_t source;    u_int16_t dest;    u_int32_t seq;    u_int32_t ack_seq;# if __BYTE_ORDER == __LITTLE_ENDIAN    u_int16_t res1:4;    u_int16_t doff:4;    u_int16_t fin:1;    u_int16_t syn:1;    u_int16_t rst:1;    u_int16_t psh:1;    u_int16_t ack:1;    u_int16_t urg:1;    u_int16_t res2:2;# elif __BYTE_ORDER == __BIG_ENDIAN    u_int16_t doff:4;    u_int16_t res1:4;    u_int16_t res2:2;    u_int16_t urg:1;    u_int16_t ack:1;    u_int16_t psh:1;    u_int16_t rst:1;    u_int16_t syn:1;    u_int16_t fin:1;# else#  error "Adjust your <bits/endian.h> defines"# endif    u_int16_t window;    u_int16_t check;    u_int16_t urg_ptr;      };    };};

udp首部结构
这里写图片描述
对应结构体,头文件netinet/udp.h

struct udphdr{  __extension__ union  {    struct    {      u_int16_t uh_sport;       /* source port */      u_int16_t uh_dport;       /* destination port */      u_int16_t uh_ulen;        /* udp length */      u_int16_t uh_sum;     /* udp checksum */    };    struct    {      u_int16_t source;      u_int16_t dest;      u_int16_t len;      u_int16_t check;    };  };};

下面是tcp raw socket的一个栗子
通过填写ip首部和tcp首部向服务器发送SYN请求包,模拟tcp三次握手的过程

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

创建一个tcp raw socket,并通过设置IP_HDRINCL,通知内核数据包中已经包含了ip首部,内核不用填充。

    int one=1;    const int *val=&one;    if(setsockopt(s,IPPROTO_IP,IP_HDRINCL,val,sizeof(int))<0)    {        perror("Error setting IP_HDRINCL\n");        exit(0);    }

rawsock.cpp具体代码如下:

/*  raw tcp packets */#include<unistd.h>#include<stdio.h>#include<string.h>#include<sys/socket.h>#include<stdlib.h>#include<errno.h>#include<netinet/tcp.h>#include<netinet/ip.h>#include <arpa/inet.h>#define DEST_PORT 8888#define SRC_PORT 9999#define SRC_ADDRESS "192.168.0.105"#define DEST_ADDRESS "192.168.0.174"//tcp伪首部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(){    //Create a raw socket    int s=socket(PF_INET,SOCK_RAW,IPPROTO_TCP);    if(s==-1)    {        perror("Failed to create socket\n");        exit(1);    }    //Datagram to represent the packet    char datagram[4096],source_ip[32],*data,*pseudogram;    memset(datagram,0,sizeof(datagram));    //IP Header    struct iphdr *iph=(struct iphdr*)datagram;    //TCP Header    struct tcphdr *tcph=(struct tcphdr*)(datagram+sizeof(struct iphdr));    struct sockaddr_in sin;    struct pseudo_header psh;    //Data part    data=datagram+sizeof(struct iphdr)+sizeof(struct tcphdr);    strcpy(data , "ABCDEFGHIJKLMNOPQRSTUVWXYZ");    strcpy(source_ip,SRC_ADDRESS);//源ip地址    sin.sin_family=AF_INET;    sin.sin_port=htons(DEST_PORT);    sin.sin_addr.s_addr=inet_addr(DEST_ADDRESS);    //Fill in the IP Header    iph->ihl=5;// 20 byte    iph->version=4;//ipv4    iph->tos=0;    iph->tot_len=sizeof(struct iphdr)+sizeof(struct tcphdr)+strlen(data);    iph->id=htonl(54321);//ID    iph->frag_off=0;    iph->ttl=255;    iph->protocol=IPPROTO_TCP;    iph->check=0;    iph->saddr=inet_addr(source_ip);    iph->daddr=sin.sin_addr.s_addr;    //IP checksum    iph->check=csum((unsigned short*)datagram,sizeof(struct iphdr));    //TCP header    tcph->source = htons(SRC_PORT);    tcph->dest = htons(DEST_PORT);    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=(char*)malloc(psize);    memcpy(pseudogram , (char*) &psh , sizeof (struct pseudo_header));    memcpy(pseudogram + sizeof(struct pseudo_header) , tcph , sizeof(struct tcphdr) + strlen(data));    //tcp CRC校验包括:12字节伪首部+tcp首部+数据包    tcph->check = csum((unsigned short*)pseudogram,psize);    //TP_HDRINCL to tell the kernel that headers are included in the pakcet    int one=1;    const int *val=&one;    if(setsockopt(s,IPPROTO_IP,IP_HDRINCL,val,sizeof(int))<0)    {        perror("Error setting IP_HDRINCL\n");        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("send to failed\n");        }        else        {            printf("Packet Send Length:%d \n",iph->tot_len);        }        //sleep(2);    }    return 0;}

测试服务端sev.cpp代码如下:

#include <stdio.h>#include <string.h>#include <stdlib.h>#include <sys/socket.h>#include <arpa/inet.h>#include <errno.h>#include <unistd.h>#define PORT 8888int main(){    int fd = socket(PF_INET,SOCK_STREAM,0);    if(fd<0)    {        perror("create socket error");        exit(0);    }    struct sockaddr_in sin;    sin.sin_family=AF_INET;    sin.sin_port=htons(PORT);    sin.sin_addr.s_addr=INADDR_ANY;    if(bind(fd,(struct sockaddr*)&sin,sizeof(struct sockaddr))<0)    {        perror("socket bind error");        exit(0);    }    if(listen(fd,100)<0)    {        perror("socket listen error");        exit(0);    }    printf("start listening\n");    int clifd;    struct sockaddr_in client;    socklen_t len;    while(1)    {        if((clifd=accept(fd,(struct sockaddr*)&client,&len))<0)        {            perror("accept error");        }        else        {            printf("accept success\n");        }    }}

测试结果如下:
运行服务端,bind的端口号为8888
这里写图片描述
启动raw socket发送SYN包,目的ip地址“192.168.0.174”(这里不知道威为什么我电脑上使用“127.0.0.1”一直失败),源ip地址为“192.168.0.105”(随意填写),端口号9999,注意使用root权限启动
这里写图片描述
结果服务端收到SYN请求,状态变为SYN_RECV
这里写图片描述

参考:
https://en.wikipedia.org/wiki/IPv4#Packet_structure
https://en.wikipedia.org/wiki/Transmission_Control_Protocol
https://en.wikipedia.org/wiki/User_Datagram_Protocol
http://www.binarytides.com/raw-sockets-c-code-linux/
http://blog.chinaunix.net/uid-23069658-id-3280895.html

0 0
原创粉丝点击