2012-06-07 69 views
2

我是網絡編程的新手。在過去的幾天裏,我正在學習這一點。今天我試圖玩原始插座。一切順利。但是Ip校驗並沒有幫到我。不管我手動給iph->檢查的值是多少,而接收到的校驗和都不同。IP校驗和自動更改

其實我所做的是我創建了一個原始套接字C程序,它將使用原始套接字創建一個發送到環回接口的TCP數據包。我用wireshark捕獲了這個包。但是當我解釋接收到的數據包時,我發現無論我在程序中給出的校驗和值如何,它都顯示出一些不同的常數值。

這裏是我的代碼

#include <stdio.h> 
#include <string.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/ip.h> 
#include <netinet/tcp.h> 
#include <arpa/inet.h> 
#include <errno.h> 
//#include <linux/in.h> 

unsigned short calculate_check_sum (unsigned short *buf, int nwords) { 
unsigned long sum; 
for (sum = 0; nwords > 0; nwords--) { 
    sum += *buf++; 
} 
sum = (sum >> 16) + (sum & 0xffff); 
sum += (sum >> 16); 
return ~sum; 
} 

int main() { 
int sockfd = socket (PF_INET, SOCK_RAW, 6); 
if (sockfd < 0) { 
    perror ("socket failed: "); 
    return -1; 
} 
char packet[4096]; 

struct iphdr *iph = (struct iphdr *) packet; 
struct tcphdr *tcph = (struct tcphdr *) packet + sizeof (struct iphdr); 
//char *payload = packet + sizeof (struct iphdr) + sizeof (struct tcphdr); 

memset (packet, 0, 4096); 

//strcpy (payload, "helllo"); 

struct sockaddr_in sin; 
sin.sin_family = AF_INET; 
sin.sin_port = htons (1330); 
inet_pton (AF_INET, "127.0.0.1", &(sin.sin_addr)); 

/* TESTING 
char var[123]; 
inet_ntop (AF_INET, &(sin.sin_addr), var, INET_ADDRSTRLEN); 
printf ("%s\n", var); 
*/ 

//WRITING ON IP HEADER 
iph->ihl = 5; 
iph->version = 4; 
iph->tos = 0; 
iph->tot_len = sizeof (struct iphdr) + sizeof (struct tcphdr); //+ strlen (payload); 
iph->id = htons (1234); 
iph->frag_off = 0; 
iph->ttl = 225; 
iph->protocol = 6; 
iph->check = 0; 
iph->saddr = inet_addr ("43.88.80.72"); 
iph->daddr = sin.sin_addr.s_addr; 

//WRITING TCP HEADER 
tcph->source = htons (1234); 
tcph->dest = htons (1330); 
tcph->seq = random(); 
tcph->ack_seq = 0; 
tcph->res1 = 0; 
tcph->doff = 0; 
tcph->syn = 1; 
tcph->window = htons (1000); 
tcph->check = 0; 
tcph->urg_ptr = 0; 

//CALCULATE CHECK SUM AND PUT IN THEIR PLACES 
//iph->check = calculate_check_sum ((unsigned short *) packet, iph->tot_len >> 1); 
iph->check = 0xffff; 

int one = 1; 
if (setsockopt (sockfd, 0, IP_HDRINCL, &one, sizeof (one)) < 0) { 
    perror ("setsockopt faild: "); 
    return -1; 
} 

int ret = sendto (sockfd, packet, iph->tot_len, 0, (struct sockaddr *) &sin, sizeof (sin)); 
if (ret < 0) { 
    perror ("sendto failed: "); 
    return -1; 
} 
return 0; 
} 

這是Wireshark的捕獲流 00000000000000000000000008004500002804d20000e106 * da5c * 2b5850487f0000010000000000000000000000000000000000000000

這裏5cda(在astrics之間)是我得到的校驗。

也代替Wireshark的,我已經創建了自己的捕捉程序如下,

#include <stdio.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <unistd.h> 
#include <netinet/tcp.h> 
#include <netinet/ip.h> 
#include <string.h> 
#include <errno.h> 

int main() { 
int fd = socket (PF_INET, SOCK_RAW, 6); 
int len_msg; 
if (fd < 0) 
    perror ("socket failed: "); 
char buf [8192]; 
memset (buf, 0, 8192); 
struct sockaddr_in sock_addr; 
inet_pton (AF_INET, "127.0.0.1", &(sock_addr.sin_addr)); 
int len = sizeof (struct sockaddr); 
printf ("%s\n",inet_ntoa(sock_addr.sin_addr.s_addr)); 
if (bind (fd, (struct sockaddr *) &sock_addr, len)) { 
    perror ("bind failed: "); 
    return -1; 
} 
while ((len_msg = recvfrom (fd, buf, 8192, 0, (struct sockaddr *) &sock_addr, (socklen_t *)&len)) != -1) { 
    //printf ("%s\n", buf+sizeof(struct iphdr)+sizeof(struct tcphdr)); 
    //printf ("%s\n", buf); 
    int i; 
    printf ("%d\n", len_msg); 
    for (i = 0; i < len_msg; i += 1) 
     printf ("%x ", (unsigned char) *(buf + i)); 
    printf ("\n"); 

    printf ("*****************************************\n"); 
    struct iphdr *iph = (struct iphdr *) buf; 
    printf ("checksum: %x\n", iph->check); 
    break; 
} 
printf ("%s\n",inet_ntoa(sock_addr.sin_addr.s_addr)); 
return 0; 
} 

不過我正在檢查的值相同。無論我在第一個程序中輸入什麼值,作爲校驗和,捕獲的數據包只有值0x5cda作爲其校驗和。

我正在使用CentOS。內核本身在數據包中做了什麼改變? 請幫助我。

由於提前

回答

2

從你的描述,它看起來像你是在把一個虛假的IP校驗和您的發送代碼。根據RFC 1122,這是無效的。我懷疑Linux原始套接字處理代碼更寬鬆,並且接受具有無效IP校驗和的數據包將覆蓋您的校驗和。

看看:

http://lxr.linux.no/linux+v3.4.1/net/ipv4/raw.c#L458 (這什麼,當你發送郵件時調用)