
這是我幾乎完整的代碼,我認為它為 TCP 資料包發送了有效的 IP 標頭。但是我的代碼未通過 Wireshark 測驗,無法識別為 TCP 資料包;相反,Wireshark 只是將其標記為可怕的原始資料包,沒有任何源和目標 IP。這確實是一個簡單的 IP 資料包,我確信它可以通過 Wireshark 測驗,但它沒有。
我究竟做錯了什么?目前沒有線索進一步了解如何除錯它,我還可以在我的 tcp 或 ip 標頭中設定什么。之前它在 Wireshark Fragmented TCP 資料包中給我訊息這甚至意味著什么,然后開始說它只是格式錯誤的資料包現在它給我的訊息是這些回應資料包回應只是從我的這個代碼發送的原始資料包,沒有源或目標 ip,考慮對于 TCP 甚至沒有顯示該窗格
void * receiver(void *data)
{
int recvlen = -1;
int writelen = -1;
while (!_do_exit) {
char buf[VPN_MAX_MTU] = {0};
char buf_1[VPN_MAX_MTU] = {0};
memset(buf,0,VPN_MAX_MTU);
memset(buf_1,0,VPN_MAX_MTU);
char *str_source=malloc(18);
char *str_dest=malloc(18);
memset(str_source,0,18);
memset(str_dest,0,18);
recvlen=read(_tun_fd,buf,VPN_MAX_MTU);
debug("SR:d\n", recvlen);
struct iphdr *iph=(struct iphdr *)buf;
struct iphdr *ip=(struct iphdr *)buf_1;
char str_src[18]={0};
char str_dest_t[18]={0};
memcpy(&ip->saddr,&iph->daddr,sizeof(uint32_t));
memcpy(&ip->daddr,&iph->saddr,sizeof(uint32_t));
printf("IN %s %s\n",get_ip_str_1(iph->saddr,str_src),get_ip_str_1(iph->daddr,str_dest_t));
//clear the str_str and str_dest_t with memset(,0)
printf("OUT %s %s\n",get_ip_str_1(ip->saddr,str_src),get_ip_str_1(ip->daddr,str_dest_t));
ip->ihl = 5;
ip->version = 4;
ip->tot_len = sizeof(struct iphdr) sizeof(struct tcphdr);
ip->protocol = IPPROTO_TCP;
ip->check = in_cksum((unsigned short *)ip, sizeof(struct iphdr));
uint16_t k=csum(ip,sizeof(*ip));
ip->check=k;
printf("checksum %d | %d\n",iph->check,ip->check);
int i=iph->ihl*4;
struct tcphdr *tcph=(struct tcphdr *)(buf i);
int j=(struct iphdr *)(ip->ihl*4);
struct tcphdr *tcp=(struct tcphdr *)(buf_1 j);
populate_tcp_some(tcph,tcp);
printf("received syn = %d\n",tcph->syn);
if(tcph->syn==1)
{
populate_tcp_some(tcph,tcp);
tcp->syn=1;
tcp->ack=1;
tcp->dest=tcph->source;
tcp->source=htons(80);
printf("received tcp syn = %d\n",tcph->syn);
}
else{
populate_tcp_some(tcph,tcp);
tcp->syn=0;
tcp->ack=1;
tcp->dest=tcph->source;
tcp->source=htons(80);
printf("sending tcp syn = %d ack = %d\n",tcp->syn,tcp->ack);
}
tcp->check=0;
tcp->check=tcp_chksum(ip,tcp);
printf("checksums %d | %d\n",tcph->check,tcp->check);
if (recvlen > 0) {
writelen = write(_tun_fd, buf_1, sizeof(*ip));
debug("TW:d\n", writelen);
if (writelen < 0) {
debug("%s: rwrite() %s [%d]\n", _progname, strerror(errno), errno);
}
} else if (recvlen < 0) {
debug("%s: rrecvfrom() %s\n", _progname, strerror(errno));
} else if (recvlen == 0) {
}
}
debug("** Receiver ending.\n");
pthread_exit(NULL);
}
uj5u.com熱心網友回復:
部分答案
到目前為止的聊天討論摘要。
原來問題出在打開TUN界面的代碼中,沒有顯示。
打開 TUN 界面顯示在此鏈接中。在“基本用法”部分有以下代碼片段
int fd = open("/dev/net/tun", O_RDWR);
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
strncpy(ifr.ifr_name, "tun0", IFNAMSIZ);
ioctl(fd, TUNSETIFF, &ifr);
IFF_NO_PI下面的“TUN 介面”部分解釋了的用法。如果未設定此標志,則資料包(即寫入緩沖區中的內容recvlen=read(_tun_fd,buf,VPN_MAX_MTU);)以struct tun_pi( link )為前綴,其中包含一些標志和協議。協議是通常寫在以太網報頭中的 ethertype 的值。
由于 OP 沒有設定IFF_NO_PI. 變數buf中的 IP 標頭包含 4 個位元組struct tun_pi,從代碼中可以看出,它被忽略了。由于 OP 試圖在沒有 IP 標頭的情況下決議 IP 標頭,因此代碼不起作用。
寫入描述符也忽略了struct tun_pi,我認為這會導致內核無法正確解釋資料包。
除錯程序
- 有一個來自wireshark的截圖。可以看到,它包含一些奇怪的結構,而不是 IP 資料包,以幾個零和亂數開頭。IP 標頭應以 bytes 開頭
45。所以下一步是弄清楚buf和中的值是什么buf_1。 - 列印出緩沖區的內容后,結果是
buf開始于00 80然后45 ...(... - IP 資料包的其余部分)。顯然,資料包 inbuf沒有正確決議,buf_1填充了一些來自 buf 的隨機值而不是預期值。尋找tun描述符回傳的格式,我找到了上面的鏈接。 - 目前,資料包在wireshark中被決議為IP頭,但在wireshark
protocol中IP頭欄位被決議為ICMP。
代碼問題
(1) , 是決議包的代碼沒有檢查代碼是否正確。
首先,在該行之后,在recvlen=read(_tun_fd,buf,VPN_MAX_MTU);不讀取 recvlen 的值的情況下決議資料包。稍后某處有一條if (recvlen > 0) {線,實際上應該沿著這read條線。
此外,在轉換buf到iphdr一個之前需要檢查 buf 是否至少為 20 位元組(或sizeof(struct iphdr)長。在將下一個標頭轉換為 TCP 之前,需要檢查協議確實是 TCP(按 的值iphdr->protocol)并且 buf 足夠長以包含 TCP標題
(2) :ip->tot_len = sizeof(struct iphdr) sizeof(struct tcphdr);除非tcp header中沒有內容(從代碼中看不到),長度應該是包的總長度,而不是兩個header的長度
(3) : 行writelen = write(_tun_fd, buf_1, sizeof(*ip));最后一個引數應該是包的長度,也就是應該寫入的值ip->tot_len
(4) : 有些值可能需要從主機轉換為網路位元組順序。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/398083.html
