计算UDP校验和时出现Bad Checksum的技术求助
Hey there! Let's dig into why your UDP checksum is showing up as "Bad Checksum" in Wireshark—this is a super common gotcha, and I’ve banged my head against it more times than I’d like to admit. Let’s break down the most likely issues and how to fix them:
1. Pseudo-Header Mistakes (The #1 Culprit)
UDP校验和依赖一个伪头部(Pseudo Header)来计算,这个头部包含IP层的关键信息,必须和实际IP包的内容完全匹配:
- Double-check your
create_pseudo_hdrfunction:- Are you using network byte order (big-endian) for the source/destination IP addresses? IP headers are stored in network byte order, so you shouldn’t convert them back to host order here.
- Is the UDP length field set correctly? It should be
htons(UDP_SIZE + data_len)(UDP header size plus your data length), not just the data length alone. - Did you hardcode the protocol number to
17(UDP’s assigned protocol number)? That field can’t be left blank or set to the wrong value.
2. Byte Alignment & Padding Errors
Your code handles odd-length buffers by adding 1 to the size, but make sure:
- The padding byte (for odd-length data) is a virtual zero—you don’t need to actually append it to the real packet, just include it in the checksum calculation buffer.
- You’re iterating over the entire buffer in 16-bit chunks. If you’re reading bytes individually and manually combining them, you might be messing up the byte order (e.g., low byte first instead of high byte first).
3. Forgetting to Zero the UDP Checksum Field
Before calculating the checksum, you must set the UDP header’s checksum field to 0. If you leave it with a garbage value or a previous checksum, your calculation will be wrong every time. Check if you’re doing this before passing the UDP *u struct to your function.
4. Incorrect Sum Reduction
The checksum calculation requires folding a 32-bit sum down to 16 bits:
- After accumulating all 16-bit values into your
uint32_t sum, you need to add the high 16 bits of the sum to the low 16 bits repeatedly until only a 16-bit value remains. - Finally, you take the one’s complement of that 16-bit value to get the final checksum.
Quick Fix Example Code Snippet
Here’s a simplified version of the calculation that avoids common pitfalls:
#include <string.h> #include <arpa/inet.h> // Assume PSEUDO_HDR, IP, UDP structs are defined correctly #define PS_SIZE sizeof(PSEUDO_HDR) #define UDP_SIZE sizeof(UDP) uint16_t compute_udp_checksum(IP *ip, UDP *u, void *data, int data_len) { uint32_t sum = 0; int total_len = PS_SIZE + UDP_SIZE + data_len; // Allocate buffer (use stack or dynamic, just ensure it's aligned) uint8_t *buffer = malloc(total_len + 1); // +1 for padding if needed if (!buffer) { perror("malloc"); return 0; } // 1. Fill pseudo-header (use network byte order for all fields) PSEUDO_HDR *ps = (PSEUDO_HDR*)buffer; ps->src_ip = ip->src; // Should already be network byte order ps->dst_ip = ip->dst; ps->zero = 0; ps->proto = IPPROTO_UDP; ps->udp_len = htons(UDP_SIZE + data_len); // 2. Fill UDP header (reset checksum to 0 first!) UDP *udp = (UDP*)(buffer + PS_SIZE); memcpy(udp, u, UDP_SIZE); udp->checksum = 0; // 3. Fill data memcpy(buffer + PS_SIZE + UDP_SIZE, data, data_len); // 4. Handle odd-length padding if (total_len % 2 != 0) { buffer[total_len] = 0; total_len++; } // 5. Accumulate all 16-bit words uint16_t *words = (uint16_t*)buffer; for (int i = 0; i < total_len / 2; i++) { sum += ntohs(words[i]); // Convert to host order for sum, or skip if buffer is network order // Fold overflow back into sum if (sum > 0xFFFF) { sum = (sum & 0xFFFF) + (sum >> 16); } } // 6. Compute final checksum (one's complement) uint16_t checksum = ~sum; free(buffer); return htons(checksum); // Convert back to network byte order }
One Last Thing to Check in Wireshark
Sometimes Wireshark flags a checksum as bad because your network card is doing hardware checksum offloading—it calculates the checksum after Wireshark captures the packet. To rule this out:
- Go to Wireshark’s preferences → Protocols → UDP → Uncheck "Validate UDP checksum if possible".
内容的提问来源于stack exchange,提问作者user9583381

