windows 10 中的原始 UDP 套接字导致网络问题
Raw UDP socket in windows 10 cause network issue
在我的客户端套接字代码中,我需要获取 IP 层 header 用于 UDP 数据报的接收数据包。
所以我决定使用以下系统调用将套接字更改为 RAW:
int optval=1;
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2,2),&wsaData) == SOCKET_ERROR) {
int err = WSAGetLastError();
my_err("WSAStartup error %d\n",err);
return 0;
}
if((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == INVALID_SOCKET) {
perror("socket()");
return 0;
}
if (setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, (char *)&optval, sizeof optval) != 0) {
perror("setsockopt(IPPROTO_IP,IP_HDRINCL)");
return 0;
}
然后我使用以下方式发送第一个数据:
//content is a structure containing data and its length
uint16_t all_length = content->len;
size_t IP_HL = sizeof (IPHEADER);
//get_interface_ip returns interface ip address
uint32_t src_addr = get_interface_ip();
IPHEADER *iph = NULL;
size_t all_length = IP_HL + sizeof (UDPHEADER) + total_len;
UDPHEADER *udph = NULL;
BUFFER buf;
make_buf(&buf, 0);
iph = (IPHEADER *) BPTR(&buf);
iph->ip_hl = 5;
iph->ip_v = 4;
iph->ip_tos = 0;
iph->ip_len = all_length;
//get_uniq_id creates ip id field
iph->ip_id = htonl (get_uniq_id()); //Id of this packet
iph->ip_off = 64; //DONT fragment
iph->ip_ttl = 255;
iph->ip_p = IPPROTO_UDP;
iph->ip_sum = 0; //Set to 0 before calculating checksum
iph->ip_src = src_addr;
iph->ip_dst = remote.sin_addr.s_addr;
iph->ip_sum = csum((uint16_t *)iph, iph->ip_len);
udph = (UDPHEADER *) (iph + 1);
memcpy((udph + 1), (char *)content->data, total_len);
//get_src_port gets a fixed source port number randomly created in program start time
udph->uh_sport = get_src_port();
udph->uh_dport = get_server_port();
udph->uh_len = htons(sizeof (UDPHEADER) + total_len);
udph->uh_check = 0; //leave checksum 0 now, filled later by pseudo header
{
PSESUDO_HEADER psh;
int psize = 0;
char *pseudogram = NULL;
psh.source_address = src_addr;
psh.dest_address = remote.sin_addr.s_addr;
psh.placeholder = 0;
psh.protocol = IPPROTO_UDP;
psh.udp_length = udph->uh_len;
psize = sizeof(PSESUDO_HEADER) + sizeof(UDPHEADER) + total_len;
pseudogram = (char *)malloc(psize);
memcpy(pseudogram , (char*) &psh , sizeof (PSESUDO_HEADER));
memcpy(pseudogram + sizeof(PSESUDO_HEADER) , udph , sizeof(UDPHEADER) + total_len);
udph->uh_check = csum((uint16_t *)pseudogram, psize);
}
es->wsabuf.len = all_length;
es->wsabuf.buf = buf.data;
if(WSASendTo(sock, &wsabuf, 1, NULL, 0, (SOCKADDR *)&remote, sizeof(remote), &io_ovl, NULL) == SOCKET_ERROR) {
int err = WSAGetLastError();
if (err != WSA_IO_PENDING)
return 0;
}
上面的代码,创建一个 IP header 然后是 UDP header 然后计算 IP 和 UDP 校验和。我对 linux 代码使用相同的逻辑并且工作得很好。
当我 运行 程序并发送第一个数据时,有线连接发生了!这个程序导致我的旧 ADSL 调制解调器崩溃!
我唯一能想到的就是它生成的数据包格式。
我认为它一定是生成了一个格式错误的 IP 或 UDP headers,导致旧调制解调器崩溃。
我启动了 Wireshark 并捕获了两次数据包,第一次使用相同的代码,但没有使用 RAW 套接字(常规发送),第二次使用 RAW 套接字。
然后比较结果。令我惊讶的是,我找不到任何不同之处!
Wireshark 在没有 RAW 套接字的情况下发送捕获:
Frame 1: 105 bytes on wire (840 bits), 105 bytes captured (840 bits) on interface 0
Interface id: 0 (\Device\NPF_{UUID})
Encapsulation type: Ethernet (1)
Arrival Time: Jan 30, 2017 21:53:46.500050000 Iran Standard Time
[Time shift for this packet: 0.000000000 seconds]
Epoch Time: 1485800626.500050000 seconds
[Time delta from previous captured frame: 0.000000000 seconds]
[Time delta from previous displayed frame: 0.000000000 seconds]
[Time since reference or first frame: 0.000000000 seconds]
Frame Number: 1
Frame Length: 105 bytes (840 bits)
Capture Length: 105 bytes (840 bits)
[Frame is marked: False]
[Frame is ignored: False]
[Protocols in frame: eth:ethertype:ip:udp:data]
[Coloring Rule Name: UDP]
[Coloring Rule String: udp]
Ethernet II, Src: IntelCor_MAC:ADDR (SRC:MAC:ADDR), Dst: AlphaNet_MAC:ADDR (DST:MAC:ADDR)
Destination: AlphaNet_MAC:ADDR (DST:MAC:ADDR)
Source: IntelCor_MAC:ADDR (SRC:MAC:ADDR)
Type: IPv4 (0x0800)
Internet Protocol Version 4, Src: 192.168.1.4, Dst: server_ip
0100 .... = Version: 4
.... 0101 = Header Length: 20 bytes (5)
Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)
Total Length: 91
Identification: 0x6a10 (27152)
Flags: 0x02 (Don't Fragment)
Fragment offset: 0
Time to live: 128
Protocol: UDP (17)
Header checksum: 0x70a6 [correct]
[Header checksum status: Good]
[Calculated Checksum: 0x70a6]
Source: 192.168.1.4
Destination: server_ip
[Source GeoIP: Unknown]
[Destination GeoIP: Unknown]
User Datagram Protocol, Src Port: 60594, Dst Port: 7554
Source Port: 60594
Destination Port: 7554
Length: 71
Checksum: 0x755e [correct]
[Checksum Status: Good]
[Stream index: 0]
Data (63 bytes)
Data: 002523a38308884051a46d6c5135778f0a32db3027df1576...
[Length: 63]
并且使用 RAW 套接字:
Frame 2: 105 bytes on wire (840 bits), 105 bytes captured (840 bits) on interface 0
Interface id: 0 (\Device\NPF_{UUID})
Encapsulation type: Ethernet (1)
Arrival Time: Jan 30, 2017 21:39:38.086166000 Iran Standard Time
[Time shift for this packet: 0.000000000 seconds]
Epoch Time: 1485799778.086166000 seconds
[Time delta from previous captured frame: 17.305889000 seconds]
[Time delta from previous displayed frame: 17.305889000 seconds]
[Time since reference or first frame: 17.305889000 seconds]
Frame Number: 2
Frame Length: 105 bytes (840 bits)
Capture Length: 105 bytes (840 bits)
[Frame is marked: False]
[Frame is ignored: False]
[Protocols in frame: eth:ethertype:ip:udp:data]
[Coloring Rule Name: UDP]
[Coloring Rule String: udp]
Ethernet II, Src: IntelCor_MAC:ADDR (SRC:MAC:ADDR), Dst: AlphaNet_MAC:ADDR (DST:MAC:ADDR)
Destination: AlphaNet_MAC:ADDR (DST:MAC:ADDR)
Source: IntelCor_MAC:ADDR (SRC:MAC:ADDR)
Type: IPv4 (0x0800)
Internet Protocol Version 4, Src: 192.168.1.4, Dst: server_ip
0100 .... = Version: 4
.... 0101 = Header Length: 20 bytes (5)
Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)
Total Length: 91
Identification: 0x58ef (22767)
Flags: 0x02 (Don't Fragment)
Fragment offset: 0
Time to live: 255
Protocol: UDP (17)
Header checksum: 0x02c7 [correct]
[Header checksum status: Good]
[Calculated Checksum: 0x02c7]
Source: 192.168.1.4
Destination: server_ip
[Source GeoIP: Unknown]
[Destination GeoIP: Unknown]
User Datagram Protocol, Src Port: 16383, Dst Port: 7554
Source Port: 16383
Destination Port: 7554
Length: 71
Checksum: 0x2faf [correct]
[Checksum Status: Good]
[Stream index: 0]
Data (63 bytes)
Data: 00250d80dd1b561e4d81c22c2914f49f1bb492f78d6a3ee8...
[Length: 63]
有什么想法吗?
对不起,这是我的错误。这段代码是我的旧代码之一,包含很多文件。最近我有一些空闲时间,决定做一些 TODO 任务。
自从我将套接字更改为 RAW 后,写入次数增加了 (data_length + udp_header + ip_header) 但在其他地方(另一个文件) 包含以下内容:
if(number_of_written != data_length) {
repeat_socket_process();
}
有了这段代码,我居然是DOS(拒绝服务攻击)我自己的路由器!
在几分之一秒内生成了太多数据包。所以它与原始套接字和数据包无关headers。
我需要做的就是检查
if (number_of_written != data_length + udp_header + ip_header)
无论如何感谢你们的跟进,希望这能帮助其他人在类似情况下追踪他们代码的其他地方
在我的客户端套接字代码中,我需要获取 IP 层 header 用于 UDP 数据报的接收数据包。
所以我决定使用以下系统调用将套接字更改为 RAW:
int optval=1;
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2,2),&wsaData) == SOCKET_ERROR) {
int err = WSAGetLastError();
my_err("WSAStartup error %d\n",err);
return 0;
}
if((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == INVALID_SOCKET) {
perror("socket()");
return 0;
}
if (setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, (char *)&optval, sizeof optval) != 0) {
perror("setsockopt(IPPROTO_IP,IP_HDRINCL)");
return 0;
}
然后我使用以下方式发送第一个数据:
//content is a structure containing data and its length
uint16_t all_length = content->len;
size_t IP_HL = sizeof (IPHEADER);
//get_interface_ip returns interface ip address
uint32_t src_addr = get_interface_ip();
IPHEADER *iph = NULL;
size_t all_length = IP_HL + sizeof (UDPHEADER) + total_len;
UDPHEADER *udph = NULL;
BUFFER buf;
make_buf(&buf, 0);
iph = (IPHEADER *) BPTR(&buf);
iph->ip_hl = 5;
iph->ip_v = 4;
iph->ip_tos = 0;
iph->ip_len = all_length;
//get_uniq_id creates ip id field
iph->ip_id = htonl (get_uniq_id()); //Id of this packet
iph->ip_off = 64; //DONT fragment
iph->ip_ttl = 255;
iph->ip_p = IPPROTO_UDP;
iph->ip_sum = 0; //Set to 0 before calculating checksum
iph->ip_src = src_addr;
iph->ip_dst = remote.sin_addr.s_addr;
iph->ip_sum = csum((uint16_t *)iph, iph->ip_len);
udph = (UDPHEADER *) (iph + 1);
memcpy((udph + 1), (char *)content->data, total_len);
//get_src_port gets a fixed source port number randomly created in program start time
udph->uh_sport = get_src_port();
udph->uh_dport = get_server_port();
udph->uh_len = htons(sizeof (UDPHEADER) + total_len);
udph->uh_check = 0; //leave checksum 0 now, filled later by pseudo header
{
PSESUDO_HEADER psh;
int psize = 0;
char *pseudogram = NULL;
psh.source_address = src_addr;
psh.dest_address = remote.sin_addr.s_addr;
psh.placeholder = 0;
psh.protocol = IPPROTO_UDP;
psh.udp_length = udph->uh_len;
psize = sizeof(PSESUDO_HEADER) + sizeof(UDPHEADER) + total_len;
pseudogram = (char *)malloc(psize);
memcpy(pseudogram , (char*) &psh , sizeof (PSESUDO_HEADER));
memcpy(pseudogram + sizeof(PSESUDO_HEADER) , udph , sizeof(UDPHEADER) + total_len);
udph->uh_check = csum((uint16_t *)pseudogram, psize);
}
es->wsabuf.len = all_length;
es->wsabuf.buf = buf.data;
if(WSASendTo(sock, &wsabuf, 1, NULL, 0, (SOCKADDR *)&remote, sizeof(remote), &io_ovl, NULL) == SOCKET_ERROR) {
int err = WSAGetLastError();
if (err != WSA_IO_PENDING)
return 0;
}
上面的代码,创建一个 IP header 然后是 UDP header 然后计算 IP 和 UDP 校验和。我对 linux 代码使用相同的逻辑并且工作得很好。
当我 运行 程序并发送第一个数据时,有线连接发生了!这个程序导致我的旧 ADSL 调制解调器崩溃!
我唯一能想到的就是它生成的数据包格式。
我认为它一定是生成了一个格式错误的 IP 或 UDP headers,导致旧调制解调器崩溃。
我启动了 Wireshark 并捕获了两次数据包,第一次使用相同的代码,但没有使用 RAW 套接字(常规发送),第二次使用 RAW 套接字。
然后比较结果。令我惊讶的是,我找不到任何不同之处!
Wireshark 在没有 RAW 套接字的情况下发送捕获:
Frame 1: 105 bytes on wire (840 bits), 105 bytes captured (840 bits) on interface 0
Interface id: 0 (\Device\NPF_{UUID})
Encapsulation type: Ethernet (1)
Arrival Time: Jan 30, 2017 21:53:46.500050000 Iran Standard Time
[Time shift for this packet: 0.000000000 seconds]
Epoch Time: 1485800626.500050000 seconds
[Time delta from previous captured frame: 0.000000000 seconds]
[Time delta from previous displayed frame: 0.000000000 seconds]
[Time since reference or first frame: 0.000000000 seconds]
Frame Number: 1
Frame Length: 105 bytes (840 bits)
Capture Length: 105 bytes (840 bits)
[Frame is marked: False]
[Frame is ignored: False]
[Protocols in frame: eth:ethertype:ip:udp:data]
[Coloring Rule Name: UDP]
[Coloring Rule String: udp]
Ethernet II, Src: IntelCor_MAC:ADDR (SRC:MAC:ADDR), Dst: AlphaNet_MAC:ADDR (DST:MAC:ADDR)
Destination: AlphaNet_MAC:ADDR (DST:MAC:ADDR)
Source: IntelCor_MAC:ADDR (SRC:MAC:ADDR)
Type: IPv4 (0x0800)
Internet Protocol Version 4, Src: 192.168.1.4, Dst: server_ip
0100 .... = Version: 4
.... 0101 = Header Length: 20 bytes (5)
Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)
Total Length: 91
Identification: 0x6a10 (27152)
Flags: 0x02 (Don't Fragment)
Fragment offset: 0
Time to live: 128
Protocol: UDP (17)
Header checksum: 0x70a6 [correct]
[Header checksum status: Good]
[Calculated Checksum: 0x70a6]
Source: 192.168.1.4
Destination: server_ip
[Source GeoIP: Unknown]
[Destination GeoIP: Unknown]
User Datagram Protocol, Src Port: 60594, Dst Port: 7554
Source Port: 60594
Destination Port: 7554
Length: 71
Checksum: 0x755e [correct]
[Checksum Status: Good]
[Stream index: 0]
Data (63 bytes)
Data: 002523a38308884051a46d6c5135778f0a32db3027df1576...
[Length: 63]
并且使用 RAW 套接字:
Frame 2: 105 bytes on wire (840 bits), 105 bytes captured (840 bits) on interface 0
Interface id: 0 (\Device\NPF_{UUID})
Encapsulation type: Ethernet (1)
Arrival Time: Jan 30, 2017 21:39:38.086166000 Iran Standard Time
[Time shift for this packet: 0.000000000 seconds]
Epoch Time: 1485799778.086166000 seconds
[Time delta from previous captured frame: 17.305889000 seconds]
[Time delta from previous displayed frame: 17.305889000 seconds]
[Time since reference or first frame: 17.305889000 seconds]
Frame Number: 2
Frame Length: 105 bytes (840 bits)
Capture Length: 105 bytes (840 bits)
[Frame is marked: False]
[Frame is ignored: False]
[Protocols in frame: eth:ethertype:ip:udp:data]
[Coloring Rule Name: UDP]
[Coloring Rule String: udp]
Ethernet II, Src: IntelCor_MAC:ADDR (SRC:MAC:ADDR), Dst: AlphaNet_MAC:ADDR (DST:MAC:ADDR)
Destination: AlphaNet_MAC:ADDR (DST:MAC:ADDR)
Source: IntelCor_MAC:ADDR (SRC:MAC:ADDR)
Type: IPv4 (0x0800)
Internet Protocol Version 4, Src: 192.168.1.4, Dst: server_ip
0100 .... = Version: 4
.... 0101 = Header Length: 20 bytes (5)
Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)
Total Length: 91
Identification: 0x58ef (22767)
Flags: 0x02 (Don't Fragment)
Fragment offset: 0
Time to live: 255
Protocol: UDP (17)
Header checksum: 0x02c7 [correct]
[Header checksum status: Good]
[Calculated Checksum: 0x02c7]
Source: 192.168.1.4
Destination: server_ip
[Source GeoIP: Unknown]
[Destination GeoIP: Unknown]
User Datagram Protocol, Src Port: 16383, Dst Port: 7554
Source Port: 16383
Destination Port: 7554
Length: 71
Checksum: 0x2faf [correct]
[Checksum Status: Good]
[Stream index: 0]
Data (63 bytes)
Data: 00250d80dd1b561e4d81c22c2914f49f1bb492f78d6a3ee8...
[Length: 63]
有什么想法吗?
对不起,这是我的错误。这段代码是我的旧代码之一,包含很多文件。最近我有一些空闲时间,决定做一些 TODO 任务。
自从我将套接字更改为 RAW 后,写入次数增加了 (data_length + udp_header + ip_header) 但在其他地方(另一个文件) 包含以下内容:
if(number_of_written != data_length) {
repeat_socket_process();
}
有了这段代码,我居然是DOS(拒绝服务攻击)我自己的路由器!
在几分之一秒内生成了太多数据包。所以它与原始套接字和数据包无关headers。
我需要做的就是检查
if (number_of_written != data_length + udp_header + ip_header)
无论如何感谢你们的跟进,希望这能帮助其他人在类似情况下追踪他们代码的其他地方