[英]Why does not the server respond with syn-ack packets when I send syn-packets with raw sockets?
I am experimenting with raw sockets and I have just written a small program that sends TCP packets with the syn
flag set. 我正在尝试使用原始套接字,我刚刚编写了一个小程序,它发送带有
syn
标志集的TCP数据包。 I can see the packets coming with Wireshark on the server side and they look good, but the server never responds with any syn-ack
packets. 我可以在服务器端看到与Wireshark一起发送的数据包,它们看起来不错,但是服务器永远不会响应任何
syn-ack
数据包。
I have compared the syn
packets that my program constructs (see code below) with the ones that hping3
sends (because the packets of hping3 always get a syn-ack
). 我已经将我的程序构造的
syn
数据包(见下面的代码)与hping3
发送的数据包进行了比较(因为hping3的数据包总是得到一个syn-ack
)。 The only that differs between my syn packets and hping3's syn packets are the ip identification
number, tcp source port
(which is randomized in hping3), tcp sequence number
(which is also randomized in hping3) and the ip checksum
field. 我的syn包和hping3的syn包之间唯一不同的是
ip identification
号, tcp source port
(在hping3中随机化), tcp sequence number
(在hping3中也随机化)和ip checksum
字段。 All these four fields are based on some random numbers, that is why they differ. 所有这四个字段都基于一些随机数,这就是它们不同的原因。 All other fields are equal!
所有其他领域都是平等的! But my program does not get any syn-acks but hping3 does!
但我的程序没有得到任何同步,但hping3呢!
I am using Kali Linux for sending the packets (of course as root) and CentOS for the server. 我正在使用Kali Linux发送数据包(当然是root用户)和CentOS用于服务器。
Have I missed something essential or just missunderstood anything? 我错过了必要的东西或者只是误解了什么吗?
Removed code 删除了代码
EDIT 编辑
Here is the entire packet captured by Wireshark on the client side (divided into 4 images below). 这是Wireshark在客户端捕获的整个数据包(下面分为4个图像)。 Note that the packets sent by hping3 are totally equal except the values for ip identification, source port, sequence number and checksum:
请注意,除了ip标识,源端口,序列号和校验和的值之外,hping3发送的数据包完全相同:
Images removed 图像已删除
Here is the hex dump of the packet. 这是数据包的十六进制转储。
Hexdump removed Hexdump已移除
EDIT 2 编辑2
Ok, now I have created the pseudo header according to RFC793 . 好的,现在我已根据RFC793创建了伪标头。 The pseudo header is just used for the tcp checksum calculation.
伪标头仅用于tcp校验和计算。 Now the IP header seems to be correct, but Wireshark complains about that the packet does not contain a full TCP header and it really seems corrupted because some of the fields contains strange values that I have not set.
现在IP头似乎是正确的,但Wireshark抱怨该数据包不包含一个完整的TCP头,它真的好像已经损坏,因为一些字段包含我没有设置的奇怪值。
First I allocate a buffer (called tcp_header
) with space for the tcp header and the pseudo header. 首先,我为tcp头和伪头分配一个带空格的缓冲区(称为
tcp_header
)。 Second, I create a buffer for the ip header containing space for ip, tcp and pseudo headers. 其次,我为包含ip,tcp和伪标头空间的ip头创建了一个缓冲区。
First I fill the tcp_header
with its data and then I copy it to the ip_header
before sending it with the sendto
function. 首先,我用
tcp_header
填充其数据,然后将其复制到ip_header
然后再发送sendto
函数。
Does something go wrong when I copy the contents of tcp_packet
to ip_packet
or am I doing something else wrong? 当我将
tcp_packet
的内容tcp_packet
到ip_packet
或者我做错了什么时,会出现问题吗?
#include <cstdlib>
#include <stdio.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#define __FAVOR_BSD 1
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <sys/ioctl.h>
#include <string.h>
#include <iostream>
#include <net/ethernet.h>
#include <time.h>
#define PCKT_LEN 1024
struct pseudohdr
{
__u32 saddr;
__u32 daddr;
__u8 zero;
__u8 protocol;
__u16 lenght;
};
#define PSEUDOHDR_SIZE sizeof(struct pseudohdr)
unsigned short csum(unsigned short *buf, int len) {
unsigned long sum;
for(sum=0; len>0; len-=2)
sum += *buf++;
sum = (sum >> 16) + (sum &0xffff);
sum += (sum >> 16);
return (unsigned short)(~sum);
}
int main(int argc, char** argv) {
srand(time(NULL));
char *ip_packet = new char[sizeof(struct iphdr) +
sizeof(struct tcphdr)]();
char *tcp_packet = new char[sizeof(struct pseudohdr) +
sizeof(struct tcphdr)]();
struct pseudohdr *pseudoheader = (struct pseudohdr*) tcp_packet;
class tcphdr *tcp = (struct tcphdr *) (tcp_packet + sizeof(struct pseudohdr));
class iphdr *ip = (struct iphdr *) ip_packet;
class sockaddr_in sin, din;
int sd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if(sd < 0) {
perror("socket() error");
exit(-1);
} else {
printf("socket()-SOCK_RAW and tcp protocol is OK.\n");
}
// Randomize src port
int srcport = rand()%100+25000;
sin.sin_family = AF_INET; // Address family
sin.sin_addr.s_addr = inet_addr("192.168.2.80");
sin.sin_port = htons(srcport); // Source port
din.sin_family = AF_INET;
din.sin_addr.s_addr = inet_addr("192.168.2.6");
din.sin_port = htons(80); // Destination port
/* tcp pseudo header */
memcpy(&pseudoheader->saddr, &sin.sin_addr.s_addr, 4);
memcpy(&pseudoheader->daddr, &din.sin_addr.s_addr, 4);
pseudoheader->protocol = 6; /* tcp */
pseudoheader->lenght = htons(sizeof(struct pseudohdr) + sizeof(struct tcphdr));
ip->ihl = 5;
ip->version = 4;
ip->tos = 0;
ip->tot_len = sizeof(class iphdr) + sizeof(class tcphdr);
ip->id = htons((getpid() & 255) + rand()%100+30000);
ip->frag_off = 0;
ip->ttl = 32;
ip->protocol = 6; // TCP
ip->check = 0; // Done by kernel
memcpy(&ip->saddr, (char*)&sin.sin_addr, sizeof(ip->saddr));
memcpy(&ip->daddr, (char*)&din.sin_addr, sizeof(ip->daddr));
// The TCP structure
tcp->th_sport = htons(srcport);
tcp->th_dport = htons(80); // Destination port
tcp->th_seq = htonl(rand()%100+1000);
tcp->th_ack = htonl(rand()%30);
tcp->th_off = 5;
tcp->th_flags = TH_SYN;
tcp->th_win = htons(1024);
tcp->th_urp = 0;
// Now calculate tcp checksum
tcp->th_sum = csum((unsigned short *) tcp_packet, sizeof(struct pseudohdr) + sizeof(struct tcphdr));
// Copy tcp_packet to ip_packet
memcpy(ip_packet + sizeof(struct iphdr), tcp_packet+sizeof(struct pseudohdr), sizeof(struct tcphdr));
// Bind socket to interface
int one = 1;
const int *val = &one;
const char opt[] = "eth0";
if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, (char *)&one, sizeof(one)) < 0) {
perror("setsockopt() error");
exit(-1);
}
else
printf("setsockopt() is OK\n");
if(sendto(sd, ip_packet, ip->tot_len, 0, (sockaddr*)&din, sizeof(din)) < 0) {
perror("sendto() error");
exit(-1);
}
else
printf("Send OK!");
close(sd);
return 0;
}
The tcp contents of the packet: 数据包的tcp内容:
Images removed 图像已删除
Edit 3 编辑3
Now I have found something interesting. 现在我发现了一些有趣的东西。 Study the cheksums on this picture:
研究这张照片上的cheksums:
The checksum is in network order and shall thus be read in reversed order, as 0x06c0
(and not is as it is stated above as 0xc006). 校验和按网络顺序排列,因此应按相反的顺序读取,如
0x06c0
(而不是如上所述为0xc006)。 That is equal to the decimal value of 1728
. 这等于
1728
的十进制值。 Wireshark says the correct cheksum should be 0x12c0
which gives a decimal value of 4800
. Wireshark说正确的cheksum应该是
0x12c0
,它给出一个4800
的十进制值。
4800-1728=3072
. 4800-1728=3072
。 That is the difference between the actual checksum and the correct checksum calculated by Wireshark in all packets that is sent by my program. 这是我的程序发送的所有数据包中Wireshark计算的实际校验和与正确校验和之间的差异。
So, if I simply add that value to the cheksum result: 所以,如果我只是将该值添加到cheksum结果中:
tcp->th_sum = csum((unsigned short *) tcp_packet, sizeof(struct pseudohdr) + sizeof(struct tcphdr)) + 3072;
...then all packets get the correct checksum and receives a corresponding SYN-ACK
. ...然后所有数据包都获得正确的校验和并收到相应的
SYN-ACK
。
Why the magic number 3072??? 为什么神奇数字3072 ???
I am not content with the check sum algorithm you are using. 我不满足于您使用的校验和算法。 The one suggested by Stevens:
史蒂文斯建议的那个:
uint16_t
in_cksum(uint16_t *addr, int len)
{
int nleft = len;
uint32_t sum = 0;
uint16_t *w = addr;
uint16_t answer = 0;
/*
* Our algorithm is simple, using a 32 bit accumulator (sum), we add
* sequential 16 bit words to it, and at the end, fold back all the
* carry bits from the top 16 bits into the lower 16 bits.
*/
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
/* 4mop up an odd byte, if necessary */
if (nleft == 1) {
*(unsigned char *)(&answer) = *(unsigned char *)w ;
sum += answer;
}
/* 4add back carry outs from top 16 bits to low 16 bits */
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* truncate to 16 bits */
return(answer);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.