我遇到的问题是示例程序生成的tcp校验和(下面复制)与wireshark计算的校验和不匹配。有人可以指出我哪里出错了。 在这里我尝试了两种方式
同时使用这两个值,得到两个不同的值,两者都不匹配wireshark值。
我在这里复制IP和TCP标题详细信息。
IP标头:
<00> 0000 60 00 00 00 00 2a 06 80 10 80 a2 b1 00 00 00 00 <00> 0010 00 00 00 00 00 1e 00 00 ff 00 00 00 00 00 00 00 <00> 00 00 00 00 00 00 00 00 24TCP标头:
<00> 0000 04 22 00 50 00 01 e0 dd 00 01 42 74 50 14 22 380010 eb 10 00 00
我的理解是,添加psuedo头和TCP头值将给出校验和。手动添加值会给出完全不同的值。以编程方式,当我尝试时,它是(38 eb)。 wireshark显示正确的值应为 0xb348
我做错了什么?有人可以建议我如何手动完成它?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // close()
#include <string.h> // strcpy, memset(), and memcpy()
#include <netdb.h> // struct addrinfo
#include <sys/types.h> // needed for socket(), uint8_t, uint16_t
#include <sys/socket.h> // needed for socket()
#include <netinet/in.h> // IPPROTO_TCP, INET6_ADDRSTRLEN
#include <netinet/ip.h> // IP_MAXPACKET (which is 65535)
#include <netinet/ip6.h> // struct ip6_hdr
#define __FAVOR_BSD // Use BSD format of tcp header
#include <netinet/tcp.h> // struct tcphdr
#include <arpa/inet.h> // inet_pton() and inet_ntop()
#include <sys/ioctl.h> // macro ioctl is defined
#include <bits/ioctls.h> // defines values for argument "request" of ioctl.
#include <net/if.h> // struct ifreq
#include <linux/if_ether.h> // ETH_P_IP = 0x0800, ETH_P_IPV6 = 0x86DD
#include <linux/if_packet.h> // struct sockaddr_ll (see man 7 packet)
#include <net/ethernet.h>
#include <errno.h> // errno, perror()
void ipv6_to_str_unexpanded(char *str, const struct in6_addr * addr) {
sprintf(str, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
(int)addr->s6_addr[0], (int)addr->s6_addr[1],
(int)addr->s6_addr[2], (int)addr->s6_addr[3],
(int)addr->s6_addr[4], (int)addr->s6_addr[5],
(int)addr->s6_addr[6], (int)addr->s6_addr[7],
(int)addr->s6_addr[8], (int)addr->s6_addr[9],
(int)addr->s6_addr[10], (int)addr->s6_addr[11],
(int)addr->s6_addr[12], (int)addr->s6_addr[13],
(int)addr->s6_addr[14], (int)addr->s6_addr[15]);
printf("addr:[%s]\n",str);
}
static inline uint16_t
get_16b_sum(uint16_t *ptr16, uint32_t nr)
{
uint32_t sum = 0;
while (nr > 1)
{
sum +=*ptr16;
nr -= sizeof(uint16_t);
ptr16++;
if (sum > UINT16_MAX)
sum -= UINT16_MAX;
}
/* If length is in odd bytes */
if (nr)
sum += *((uint8_t*)ptr16);
sum = ((sum & 0xffff0000) >> 16) + (sum & 0xffff);
sum &= 0x0ffff;
return (uint16_t)sum;
}
static inline uint16_t
get_ipv6_psd_sum (struct ip6_hdr * ip_hdr)
{
/* Pseudo Header for IPv6/UDP/TCP checksum */
union ipv6_psd_header {
struct {
uint8_t src_addr[16]; /* IP address of source host. */
uint8_t dst_addr[16]; /* IP address of destination host(s). */
uint32_t len; /* L4 length. */
uint32_t proto; /* L4 protocol - top 3 bytes must be zero */
} __attribute__((__packed__));
uint16_t u16_arr[0]; /* allow use as 16-bit values with safe aliasing */
} psd_hdr;
memcpy(&psd_hdr.src_addr, &ip_hdr->ip6_src,
(sizeof(ip_hdr->ip6_src) + sizeof(ip_hdr->ip6_dst)));
//psd_hdr.len = ip_hdr->payload_len;
psd_hdr.len = ip_hdr->ip6_plen;
psd_hdr.proto = IPPROTO_TCP;//(ip_hdr->proto << 24);
return get_16b_sum(psd_hdr.u16_arr, sizeof(psd_hdr));
}
static inline uint16_t
get_ipv6_udptcp_checksum(struct ip6_hdr *ipv6_hdr, uint16_t *l4_hdr)
{
uint32_t cksum;
uint32_t l4_len;
l4_len = (ipv6_hdr->ip6_plen);
cksum = get_16b_sum(l4_hdr, l4_len);
cksum += get_ipv6_psd_sum(ipv6_hdr);
cksum = ((cksum & 0xffff0000) >> 16) + (cksum & 0xffff);
cksum = (~cksum) & 0xffff;
if (cksum == 0)
cksum = 0xffff;
return (uint16_t)cksum;
}
//! \brief Calculate the TCP checksum.
//! \param buff The TCP packet.
//! \param len The size of the TCP packet.
//! \param src_addr The IP source address (in network format).
//! \param dest_addr The IP destination address (in network format).
//! \return The result of the checksum.
uint16_t tcp_checksum(const void *buff, size_t len, struct in6_addr src_addr, struct in6_addr dest_addr)
{
const uint16_t *buf=buff;
uint16_t *ip_src=(void *)&src_addr, *ip_dst=(void *)&dest_addr;
uint32_t sum;
size_t length=len;
// Calculate the sum //
sum = 0;
while (len > 1)
{
sum += *buf++;
if (sum & 0x80000000)
sum = (sum & 0xFFFF) + (sum >> 16);
len -= 2;
}
if ( len & 1 )
// Add the padding if the packet lenght is odd //
sum += *((uint8_t *)buf);
// Add the pseudo-header //
sum += *(ip_src++);
sum += *ip_src;
sum += *(ip_dst++);
sum += *ip_dst;
sum += htons(IPPROTO_TCP);
sum += htons(length);
// Add the carries //
while (sum >> 16)
sum = (sum & 0xFFFF) + (sum >> 16);
// Return the one's complement of sum //
return ( (uint16_t)(~sum) );
}
// Define some constants.
#define ETH_HDRLEN 14 // Ethernet header length
#define IP6_HDRLEN 40 // IPv6 header length
#define TCP_HDRLEN 20 // TCP header length, excludes options data
// Function prototypes
uint16_t checksum (uint16_t *, int);
uint16_t tcp6_checksum (struct ip6_hdr, struct tcphdr, uint8_t *, int);
char *allocate_strmem (int);
uint8_t *allocate_ustrmem (int);
int *allocate_intmem (int);
int
main (int argc, char **argv)
{
int i, status, frame_length, sd, bytes, *tcp_flags, opt_len;
char *interface, *target, *src_ip, *dst_ip;
struct ip6_hdr iphdr;
struct tcphdr tcphdr;
uint8_t *src_mac, *dst_mac, *ether_frame;
uint8_t *options;
struct addrinfo hints, *res;
struct sockaddr_in6 *ipv6;
struct sockaddr_ll device;
struct ifreq ifr;
void *tmp;
// Allocate memory for various arrays.
src_mac = allocate_ustrmem (6);
dst_mac = allocate_ustrmem (6);
ether_frame = allocate_ustrmem (IP_MAXPACKET);
interface = allocate_strmem (40);
target = allocate_strmem (INET6_ADDRSTRLEN);
src_ip = allocate_strmem (INET6_ADDRSTRLEN);
dst_ip = allocate_strmem (INET6_ADDRSTRLEN);
tcp_flags = allocate_intmem (8);
options = allocate_ustrmem (40);
// Interface to send packet through.
strcpy (interface, "eth0");
// Source IPv6 address: you need to fill this out
strcpy (src_ip,"1080:a2b1::1e:0");
strcpy (dst_ip,"ff00::24");
// IPv6 header
// IPv6 version (4 bits), Traffic class (8 bits), Flow label (20 bits)
iphdr.ip6_flow = htonl ((6 << 28) | (0 << 20) | 0);
// Payload length (16 bits): TCP header + TCP options
//iphdr.ip6_plen = htons (TCP_HDRLEN + opt_len);
//iphdr.ip6_plen = htons (TCP_HDRLEN);
iphdr.ip6_plen = htons(TCP_HDRLEN);
// Next header (8 bits): 6 for TCP
iphdr.ip6_nxt = IPPROTO_TCP;
// Hop limit (8 bits): default to maximum value
iphdr.ip6_hops = 128;
// Source IPv6 address (128 bits)
if ((status = inet_pton (AF_INET6, src_ip, &(iphdr.ip6_src))) != 1) {
fprintf (stderr, "inet_pton() failed.\nError message: %s", strerror (status));
exit (EXIT_FAILURE);
}
char srcAddr[32];
memset(srcAddr,0,32);
printf("src ip addr:");
ipv6_to_str_unexpanded(srcAddr,&(iphdr.ip6_src));
// Destination IPv6 address (128 bits)
if ((status = inet_pton (AF_INET6, dst_ip, &(iphdr.ip6_dst))) != 1) {
fprintf (stderr, "inet_pton() failed.\nError message: %s", strerror (status));
exit (EXIT_FAILURE);
}
char dstAddr[32];
memset(dstAddr,0,32);
printf("dst ip addr:");
ipv6_to_str_unexpanded(dstAddr,&(iphdr.ip6_dst));
// TCP header
// Source port number (16 bits)
tcphdr.th_sport = htons (1058);
printf("src port:[%d]\n",tcphdr.th_sport);
// Destination port number (16 bits)
tcphdr.th_dport = htons (80);
// Sequence number (32 bits)
tcphdr.th_seq = htonl (1);
printf("seq :[%d]\n",tcphdr.th_seq);
// Acknowledgement number (32 bits): 0 in first packet of SYN/ACK process
tcphdr.th_ack = htonl (1);
// Reserved (4 bits): should be 0
tcphdr.th_x2 = 0;
// Data offset (4 bits): size of TCP header + length of options, in 32-bit words
//tcphdr.th_off = (TCP_HDRLEN + opt_len) / 4;
tcphdr.th_off = TCP_HDRLEN/4;
// Flags (8 bits)
// FIN flag (1 bit)
tcp_flags[0] = 0;
// SYN flag (1 bit): set to 1
tcp_flags[1] = 0;
// RST flag (1 bit)
tcp_flags[2] = 1;
// PSH flag (1 bit)
tcp_flags[3] = 0;
// ACK flag (1 bit)
tcp_flags[4] = 1;
// URG flag (1 bit)
tcp_flags[5] = 0;
// ECE flag (1 bit)
tcp_flags[6] = 0;
// CWR flag (1 bit)
tcp_flags[7] = 0;
tcphdr.th_flags = 0;
for (i=0; i<8; i++) {
tcphdr.th_flags += (tcp_flags[i] << i);
}
// Window size (16 bits)
tcphdr.th_win = htons (8760);
// Urgent pointer (16 bits): 0 (only valid if URG flag is set)
tcphdr.th_urp = htons (0);
tcphdr.th_sum = 0;
//tcphdr.th_sum = get_ipv6_udptcp_checksum(&iphdr, (uint16_t *)&tcphdr);
tcphdr.th_sum = tcp_checksum((void *)&tcphdr, htons(20), iphdr.ip6_src, iphdr.ip6_dst);
printf("TCP Checksum:[%x]\n",tcphdr.th_sum);
return 0;
}
char *
allocate_strmem (int len)
{
void *tmp;
if (len <= 0) {
fprintf (stderr, "ERROR: Cannot allocate memory because len = %i in allocate_strmem().\n", len);
exit (EXIT_FAILURE);
}
tmp = (char *) malloc (len * sizeof (char));
if (tmp != NULL) {
memset (tmp, 0, len * sizeof (char));
return (tmp);
} else {
fprintf (stderr, "ERROR: Cannot allocate memory for array allocate_strmem().\n");
exit (EXIT_FAILURE);
}
}
答案 0 :(得分:9)
TCP / UDP / IP的校验和计算相当简单。什么叫做#16; 16 bit one&#39;算术只是一个概念,即在添加两个16位数字时,无论从16位传送的是什么,都会从位0添加回来。例如,
0x8000 + 0x8000 = 0x10000 => 0x1 + 0x0000 = 0x0001.
该算法的一个特性是通过简单的二进制反演产生负值。此算法中的0有2个二进制值:0x0000和0xffff
-0x0001 = ~0x0001 = 0xfffe;
0xfffe + 0x8000 + 0x8000 = 0x1fffe => 0x1 + 0xfffe = 0xffff = 0x0000
关于16位补码的另一个好处是你在进行16位加法时不必担心字节序,你必须正确地转换最终结果。发生这种情况是因为进位总是从一个字节传到另一个字节并且永远不会丢失。这里的示例与在小端机器中读取数据的示例相同:
0x0080 + 0x0080 = 0x0100 => htons(0x0100) = 0x0001
这就是为什么所有校验和计算算法都不会将每个16位值从网络转换为主机字节顺序的原因。
考虑到所有这些,您只需将数据块分解为16位工作,以常规方式将它们全部加在一起,然后将较高的16位添加到较低的16位并反转结果,然后再将其写回数据包。
在您的示例中,TCP标头校验和将计算为:
0x0422 + 0x0050 + 0x0001 + 0xe0dd + 0x0001 + 0x4274 + 0x5014 + 0x2238 +
0x0000 + 0x0000 = 0x19a11 = 0x1 + 0x9a11 = 0x9a12
^^^^^^ // <- this is the place for the TCP checksum
如TCP checksum calculation中所述,您需要在TCP数据包中添加伪标头,以便源和目标IP地址和端口也参与校验和计算。此伪标头对于IPv4和IPv6是不同的。在您的IPv6示例中,它将是:
0x1080 + 0xa2b1 + 0x0000 + 0x0000 + // source IPv6 address
0x0000 + 0x0000 + 0x001e + 0x0000 +
0xff00 + 0x0000 + 0x0000 + 0x0000 + // destination IPv6 address
0x0000 + 0x0000 + 0x0000 + 0x0024 +
0x0016 + // IP payload (TCP packet) lenght
0x0006 // Next Header value for TCP
= 0x1b28f = 0x1 + 0xb28f = 0xb290
现在组合的TCP和IP伪头校验和将是:
0x9a12 + 0xb290 = 0x14ca2 = 0x1 + 0x4ca2 = 0x4ca3
在将校验和写回标题之前取消校验和:
~0x4ca3 = 0xb35c
注意:此校验和仍然与您声称Wireshark计算的大不相同,因为您提供的数据包根据IP标头有20个字节的TCP有效负载数据,TCP负载也用于校验和计算。在我的例子中,我只使用TCP头而没有任何其他有效载荷。
在提供的代码中发现了许多问题。
此函数计算IPv4校验和。要为IPv6修改它,您需要将计算中使用的IP地址大小从4个字节扩展到16个。
围绕ip_src
和ip_dst
初始化的代码是错误的,应该是:
uint16_t *ip_src=(uint16_t *)&src_addr->in_addr;
uint16_t *ip_dst=(uint16_t *)&dest_addr->in_addr;
l4_len
未从网络字节顺序转换。它应该是:
l4_len = ntohs(ipv6_hdr->ip6_plen);
计算的校验和不会转换为网络字节顺序,因为它应该是:
tcphdr.th_sum = htons(get_ipv6_udptcp_checksum(&iphdr, (uint16_t *)&tcphdr));
答案 1 :(得分:0)
带有Pseudo标头的UDP帧的校验和计算,并且:已解决! (包括C-Code,示例输出和Wireshark数据帧以进行验证)
你好,我一直在研究同样的问题: 请找到一个C程序,它根据伪标题和帧内容生成正确的UDP CHECK。包括现实生活中同一帧的程序输出和wireshark结果。
unsigned long sum=0, sum2=0, term=0;
CString s;
#define DATABYTES 3
// pseudo header bytes:
unsigned char ip1[]={192,168,11,25}; // ip 1
unsigned char ip2[]={192,168,11,20}; // ip 2
unsigned char pr []= {0,17}; // udp protocol
unsigned char len[]= {0,8+DATABYTES}; // UDP entire message length entire
// acutal message bytes:
unsigned char port1[] ={0x22,0xb8}; // port 1
unsigned char port2[] ={0x22,0xb8}; // port 1
unsigned char msglen[]={0,8+DATABYTES}; // UDP entire message length entire
unsigned char check[] = {0,0}; // check set to 0
unsigned char msg[DATABYTES]= {0x11,0x22,0x33}; // 10 bytes message length
// add artificial pseudo header bytes to sum
term = ((unsigned short) ip1[0] << 8) + ip1 [1];
sum+=term;
s.Format("Add ip1 = 0x%04x Sum = 0x%04x\n",term,sum); AfxTrace(s);
term = ((unsigned short) ip1[2] << 8) + ip1 [3];
sum+=term;
s.Format("Add ip1 = 0x%04x Sum = 0x%04x\n",term,sum); AfxTrace(s);
term = ((unsigned short) ip2[0] << 8) + ip2 [1];
sum+=term;
s.Format("Add ip2 = 0x%04x Sum = 0x%04x\n",term,sum); AfxTrace(s);
term = ((unsigned short) ip2[2] << 8) + ip2 [3];
sum+=term;
s.Format("Add ip2 = 0x%04x Sum = 0x%04x\n",term,sum); AfxTrace(s);
term = ((unsigned short) pr[0] << 8) + pr [1];
sum+=term;
s.Format("Add pr = 0x%04x Sum = 0x%04x\n",term,sum); AfxTrace(s);
term = ((unsigned short) len[0] << 8) + len [1];
sum+=term;
s.Format("Add len = 0x%04x Sum = 0x%04x\n",term,sum); AfxTrace(s);
// ----------------------------------------------------------------
// add real udp header bytes to sum (ports ....)
term = ((unsigned short) port1[0] << 8) + port1[1];
sum+=term;
s.Format("Add port1 = 0x%04x Sum = 0x%04x\n",term,sum); AfxTrace(s);
term = ((unsigned short) port2[0] << 8) + port2[1];
sum+=term;
s.Format("Add port2 = 0x%04x Sum = 0x%04x\n",term,sum); AfxTrace(s);
term = ((unsigned short) msglen[0] << 8) + msglen [1];
sum+=term;
s.Format("Add msglen = 0x%04x Sum = 0x%04x\n",term,sum); AfxTrace(s);
// ----------------------------------------------------------------
// add data bytes info:
for (int i=0;i<DATABYTES/2;i++)
{
// add message data bytes to sum sum (ports ....)
term = ((unsigned short) msg[i*2] << 8) + msg [1+i*2];
sum+=term;
s.Format("Add msg = 0x%04x sum = 0x%04x\n",term,sum); AfxTrace(s);
}
if (DATABYTES % 2 != 0)
{
term = ((unsigned short) msg[DATABYTES-1] << 8);
sum+=term;
s.Format("Add msg = 0x%04x sum = 0x%04x\n",term,sum); AfxTrace(s);
}
sum= (sum >> 16) + (sum & 0xFFFF);
s.Format("Clean sum = 0x%04x \n",sum); AfxTrace(s);
sum= (sum >> 16) + (sum & 0xFFFF);
s.Format("Clean sum = 0x%04x \n",sum); AfxTrace(s);
sum= (~sum & 0xFFFF);
s.Format("not sum = 0x%04x \n",sum); AfxTrace(s);
计划的输出:
Add ip1 = 0xc0a8 Sum = 0xc0a8
Add ip1 = 0x0b19 Sum = 0xcbc1
Add ip2 = 0xc0a8 Sum = 0x18c69
Add ip2 = 0x0b14 Sum = 0x1977d
Add pr = 0x0011 Sum = 0x1978e
Add len = 0x000b Sum = 0x19799
Add port1 = 0x22b8 Sum = 0x1ba51
Add port2 = 0x22b8 Sum = 0x1dd09
Add msglen = 0x000b Sum = 0x1dd14
Add msg = 0x1122 sum = 0x1ee36
Add msg = 0x3300 sum = 0x22136
Add msg = 0x0000 sum = 0x22136
Add msg = 0x0000 sum = 0x22136
Clean sum = 0x2138
Clean sum = 0x2138
not sum = 0xdec7
还有wireshark框架:
Ethernet II, Src: Intel_af:ef:6f (00:03:47:af:ef:6f), Dst: aa:aa:bb:bb:cc:cd (aa:aa:bb:bb:cc:cd)
Destination: aa:aa:bb:bb:cc:cd (aa:aa:bb:bb:cc:cd)
Address: aa:aa:bb:bb:cc:cd (aa:aa:bb:bb:cc:cd)
.... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
.... ...0 .... .... .... .... = IG bit: Individual address (unicast)
Source: Intel_af:ef:6f (00:03:47:af:ef:6f)
Address: Intel_af:ef:6f (00:03:47:af:ef:6f)
.... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
.... ...0 .... .... .... .... = IG bit: Individual address (unicast)
Type: IP (0x0800)
Internet Protocol Version 4, Src: 192.168.11.25 (192.168.11.25), Dst: 192.168.11.20 (192.168.11.20)
Version: 4
Header length: 20 bytes
Differentiated Services Field: 0x00 (DSCP 0x00: Default; ECN: 0x00: Not-ECT (Not ECN-Capable Transport))
0000 00.. = Differentiated Services Codepoint: Default (0x00)
.... ..00 = Explicit Congestion Notification: Not-ECT (Not ECN-Capable Transport) (0x00)
Total Length: 31
Identification: 0xebe3 (60387)
Flags: 0x00
0... .... = Reserved bit: Not set
.0.. .... = Don't fragment: Not set
..0. .... = More fragments: Not set
Fragment offset: 0
Time to live: 128
Protocol: UDP (17)
Header checksum: 0xb76c [correct]
[Good: True]
[Bad: False]
Source: 192.168.11.25 (192.168.11.25)
Destination: 192.168.11.20 (192.168.11.20)
[Source GeoIP: Unknown]
[Destination GeoIP: Unknown]
User Datagram Protocol, Src Port: ddi-udp-1 (8888), Dst Port: ddi-udp-1 (8888)
Source port: ddi-udp-1 (8888)
Destination port: ddi-udp-1 (8888)
Length: 11
Checksum: 0xdec7 [correct]
[Good Checksum: True]
[Bad Checksum: False]
Data (3 bytes)
Data: 112233
[Length: 3]
0000 aa aa bb bb cc cd 00 03 47 af ef 6f 08 00 45 00 ........G..o..E.
0010 00 1f eb e3 00 00 80 11 b7 6c c0 a8 0b 19 c0 a8 .........l......
0020 0b 14 22 b8 22 b8 00 0b de c7 11 22 33 .."."......"3
缺少一件事:如果输出为0000,则需要将其反转为FFFF以发出计算检查信号,而不是检查不存在。 祝你好运,JOHI。