Linux下用RAW socket发送syn包


源码编译方法:gcc -o syn syn.c

结果:在CentOS 6上成功运行,用tcpdump抓包分析,发送的对端有syn,ack包返回,一切正常。

过程:写代码时忘记了对tcph->protocol赋值,计算出得checksum老不对,数据包是成功发出去了,但是对端没syn,ack包回,查了几个小时,郁闷死我~~~

疑问:ip->check为0是内核会计算ip头的checksum,但是计算出得结果和我用ip_fast_csum得到的结果不一致,这是为何?标记一下,有结论再附上。

  1. #include <stdio.h>   
  2. #include <stdlib.h>   
  3. #include <string.h>   
  4. #include <getopt.h>   
  5.   
  6. #include <netinet/ip.h>   
  7. #include <netinet/tcp.h>   
  8.   
  9. /* 数据包最大长度,无负载 */  
  10. #define MAX_PKG_SIZE 80 /* ip header = 20 ~ 40, tcp header = 20 ~ 40; max = 80 */   
  11. /* 计算TCP校验和的最大使用长度 www.bkjia.com*/  
  12. #define MAX_PSD_SIZE 52 /* psd header = 12, tcp header = 20 ~ 40; max = 52 */   
  13. /* 数据包构造参数 */  
  14. struct st_args{  
  15.     struct sockaddr_in saddr; /* 源地址 */  
  16.     struct sockaddr_in daddr; /* 目的地址 */  
  17. };  
  18.   
  19. /* 用于计算TCP校验和的伪头部 */  
  20. struct psdhdr{  
  21.     uint32_t saddr; /* ip头部源地址 */  
  22.     uint32_t daddr; /* ip头部目的地址 */  
  23.     uint8_t mbz; /* 补全字段,需为0 */  
  24.     uint8_t protocol; /* ip头部协议 */  
  25.     uint16_t tcpl; /* tcp长度,包括头部和数据部分 */  
  26. };  
  27.   
  28.   
  29. /* 
  30.  * 采用汇编计算ip头部校验和 
  31.  * @param[in]: iph, ip头指针;  
  32.  * @param[in]: ihl, ip头长度(4的倍数) 
  33.  * 
  34.  * @return 16位校验和 
  35.  * */  
  36. static inline uint16_t ip_fast_csum(const void *iph, unsigned int ihl)  
  37. {  
  38.     unsigned int sum;  
  39.   
  40.     asm("  movl (%1), %0\n"  
  41.         "  subl $4, %2\n"  
  42.         "  jbe 2f\n"  
  43.         "  addl 4(%1), %0\n"  
  44.         "  adcl 8(%1), %0\n"  
  45.         "  adcl 12(%1), %0\n"  
  46.         "1: adcl 16(%1), %0\n"  
  47.         "  lea 4(%1), %1\n"  
  48.         "  decl %2\n"  
  49.         "  jne  1b\n"  
  50.         "  adcl $0, %0\n"  
  51.         "  movl %0, %2\n"  
  52.         "  shrl $16, %0\n"  
  53.         "  addw %w2, %w0\n"  
  54.         "  adcl $0, %0\n"  
  55.         "  notl %0\n"  
  56.         "2:"  
  57.     /* Since the input registers which are loaded with iph and ihl 
  58.        are modified, we must also specify them as outputs, or gcc 
  59.        will assume they contain their original values. */  
  60.         : "=r" (sum), "=r" (iph), "=r" (ihl)  
  61.         : "1" (iph), "2" (ihl)  
  62.         : "memory");  
  63.     return (uint16_t)sum;  
  64. }  
  65.   
  66. /* 
  67.  * 计算校验和 
  68.  * @param[in]: buffer, 待计算数据指针 
  69.  * @param[in]: size, 数据长度 
  70.  * 
  71.  * @return 校验和 
  72.  * */  
  73. uint16_t csum(uint16_t *buffer, int size)  
  74. {  
  75.     unsigned long cksum = 0;  
  76.   
  77.     while(size>1)  
  78.     {  
  79.         cksum += *buffer++;  
  80.         size -= sizeof(uint16_t);  
  81.     }  
  82.   
  83.     if(size)  
  84.     {  
  85.         cksum += *(unsigned char*)buffer;  
  86.     }  
  87.   
  88.     cksum = (cksum>>16) + (cksum&0xffff);  
  89.     cksum += (cksum>>16);   
  90.       
  91.     return (uint16_t)(~cksum);  
  92. }  
  93. /* 
  94.  * 调试用的函数,用于输出数据 
  95.  * */  
  96. void data_dump(uint8_t *pdata, int len)  
  97. {  
  98.     int i = 0;  
  99.     printf("len = %d\n", len);  
  100.     for(i = 0;  i < len; ++i)  
  101.     {  
  102.         printf("%02X ", *(pdata + i));  
  103.         if((i + 1) % 4 == 0)  
  104.             printf("\n");  
  105.     }  
  106. }  
  107.   
  108. /* 
  109.  * 数据包发送函数,www.bkjia.com只构造了ip头+tcp头大小的长度; 
  110.  * ip头和tcp都无选项部分,tcp无负载数据 
  111.  * @param[in]: parg, 构造数据包是采用的一些参数 
  112.  *  
  113.  * @return -1, 发送失败;0, 发送成功 
  114.  * */  
  115. int send_pkg(struct st_args* parg)  
  116. {  
  117.     uint8_t datagram[MAX_PKG_SIZE] = {0};  
  118.     uint8_t psdheader[MAX_PSD_SIZE] = {0};  
  119.       
  120.     struct iphdr *iph = (struct iphdr*)datagram;  
  121.     struct tcphdr *tcph = (struct tcphdr*)(datagram + sizeof(struct iphdr));  
  122.     struct tcp_options *tcpopt = (struct tcp_options*)(datagram + sizeof(struct iphdr)  
  123.                                  + sizeof(struct tcphdr));  
  124.     struct psdhdr *psdh = (struct psdhdr*)psdheader;  
  125.     struct tcphdr *tcph_psd = (struct tcphdr*)(psdheader + sizeof(struct psdhdr));  
  126.       
  127.     int sockfd = -1, ret = 0;  
  128.     int optval = 1;  
  129.     const int *poptval = &optval;  
  130.       
  131.     sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP);  
  132.     if(sockfd < 0)  
  133.     {  
  134.         perror("create socket failed!\n");  
  135.         goto err_out;  
  136.     }  
  137.       
  138.     iph->ihl = 5; /* header length, 5 * 4 = 20 Bytes */  
  139.     iph->version = 4;   /* version, ipv4 */  
  140.     iph->tos = 0; /* type of service, gernarel */  
  141.     iph->tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr); /* total length, iph + tcph = 32 Bytes */  
  142.     iph->id = htons(54321); /* identifcation */  
  143.     iph->frag_off = htons(0x02 << 13); /* fragment offset field */  
  144.     iph->ttl = 64; /* time to live */  
  145.     iph->protocol = 6; /* protocol, tcp */  
  146.     iph->check = 0; /* checksum */  
  147.     iph->saddr = parg->saddr.sin_addr.s_addr; /* source address */  
  148.     iph->daddr = parg->daddr.sin_addr.s_addr; /* dest address */  
  149.       
  150.     tcph->source = parg->saddr.sin_port; /* source port */  
  151.     tcph->dest = parg->daddr.sin_port; /* dest port */  
  152.     tcph->seq = random(); /* current sended packet sequence number */  
  153.     tcph->ack_seq = 0; /* expect received next packet sequence number */  
  154.     tcph->doff = sizeof(struct tcphdr) / 4; /* data position in the packet */  
  155.     tcph->syn = 1; /* syn packet */  
  156.     tcph->window = htons(65535); /* size of tcp window, FreeBSD uses this value */  
  157.     tcph->check = 0; /* checksum */  
  158.     tcph->urg_ptr = 0; /* urgent data position */  
  159.       
  160.     psdh->saddr = iph->saddr;  
  161.     psdh->daddr = iph->daddr;  
  162.     psdh->mbz = 0;  
  163.     psdh->protocol = iph->protocol;  
  164.     psdh->tcpl = htons(sizeof(struct tcphdr));  
  165.     //data_dump(psdheader, sizeof(struct psdhdr));   
  166.   
  167.     memcpy(tcph_psd, tcph, sizeof(struct tcphdr));  
  168.       
  169.     tcph->check = csum((uint16_t*)psdheader, sizeof(struct psdhdr) + sizeof(struct tcphdr));  
  170.     /* iph->check == 0时, 内核会自动计算校验和 */  
  171.     //iph->check = ip_fast_csum(datagram, iph->ihl);   
  172.   
  173.     if(setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, poptval, sizeof(optval)) < 0)  
  174.     {  
  175.         perror("setsockopt failed!");  
  176.         goto err_out;  
  177.     }  
  178.       
  179.     ret = sendto(sockfd, datagram, iph->tot_len, 0,   
  180.             (struct sockaddr*)&(parg->daddr), sizeof(parg->daddr));  
  181.     if(ret < 0)  
  182.     {  
  183.         perror("sendto socket failed!");  
  184.         goto err_out;  
  185.     }  
  186.   
  187.     //data_dump(datagram, 40);   
  188.     close(sockfd);  
  189.     return 0;  
  190.       
  191. err_out:  
  192.     if(sockfd != -1)  
  193.         close(sockfd);  
  194.     return -1;  
  195. }  
  196.   
  197. int main(int argc, char **argv)  
  198. {  
  199.     struct st_args args;  
  200.       
  201. #define MAX_IP_SIZE 16   
  202.     uint8_t sip[MAX_IP_SIZE] = "192.168.1.107";  
  203.     uint8_t dip[MAX_IP_SIZE] = "192.168.1.1";  
  204.     uint16_t sport = 55555;  
  205.     uint16_t dport = 80;  
  206.     int8_t arg = 0;  
  207.   
  208.     struct option lopts[] = {  
  209.         {"saddr", required_argument, 0, 's'},  
  210.         {"sport", required_argument, 0, 'p'},  
  211.         {"daddr", required_argument, 0, 'd'},  
  212.         {"dport", required_argument, 0, 'f'}  
  213.     };  
  214.     while((arg = getopt_long(argc, argv, "s:p:d:f:", lopts, NULL)) != -1)  
  215.     {  
  216.         switch(arg)  
  217.         {  
  218.         case 's':  
  219.             memcpy(sip, optarg, MAX_IP_SIZE);  
  220.             break;  
  221.         case 'p':  
  222.             sport = atoi(optarg);  
  223.             break;  
  224.         case 'd':  
  225.             memcpy(dip, optarg, MAX_IP_SIZE);  
  226.             break;  
  227.         case 'f':  
  228.             dport = atoi(optarg);  
  229.             break;  
  230.         default:  
  231.             break;  
  232.         }  
  233.     }  
  234.       
  235.     memset(&args, 0, sizeof(struct st_args));  
  236.     inet_pton(AF_INET, sip, (void*)&args.saddr.sin_addr);  
  237.     args.saddr.sin_port = htons(sport);  
  238.   
  239.     inet_pton(AF_INET, dip, (void*)&args.daddr.sin_addr);  
  240.     args.daddr.sin_port = htons(dport);  
  241.   
  242.     send_pkg(&args);  
  243.     return 0;  
  244. }  

相关内容