VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/checksum/ipv4.cpp@ 10982

Last change on this file since 10982 was 10982, checked in by vboxsync, 17 years ago

iprt: More IPv4 checksum calculation.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.4 KB
Line 
1/* $Id: ipv4.cpp 10982 2008-07-30 13:33:48Z vboxsync $ */
2/** @file
3 * IPRT - IPv4 Checksum calculation and validation.
4 */
5
6/*
7 * Copyright (C) 2008 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 *
26 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
27 * Clara, CA 95054 USA or visit http://www.sun.com if you need
28 * additional information or have any questions.
29 */
30
31/*******************************************************************************
32* Header Files *
33*******************************************************************************/
34#include <iprt/net.h>
35#include <iprt/assert.h>
36
37
38/**
39 * Calculates the checksum of the IPv4 header.
40 *
41 * @returns Checksum.
42 * @param pIpHdr Pointer to the IPv4 header to checksum, network endian (big).
43 * Assumes the caller already checked the minimum size requirement.
44 */
45RTDECL(uint16_t) RTNetIPv4HdrChecksum(PCRTNETIPV4 pIpHdr)
46{
47 uint16_t const *paw = (uint16_t const *)pIpHdr;
48 int32_t iSum = paw[0] /* ip_hl */
49 + paw[1] /* ip_len */
50 + paw[2] /* ip_id */
51 + paw[3] /* ip_off */
52 + paw[4] /* ip_ttl */
53 /*+ paw[5] == 0 */ /* ip_sum */
54 + paw[6] /* ip_src */
55 + paw[7] /* ip_src:16 */
56 + paw[8] /* ip_dst */
57 + paw[9]; /* ip_dst:16 */
58 /* any options */
59 if (pIpHdr->ip_hl > 20 / 4)
60 {
61 /* this is a bit insane... (identical to the TCP header) */
62 switch (pIpHdr->ip_hl)
63 {
64 case 6: iSum += paw[10] + paw[11]; break;
65 case 7: iSum += paw[10] + paw[11] + paw[12] + paw[13]; break;
66 case 8: iSum += paw[10] + paw[11] + paw[12] + paw[13] + paw[14] + paw[15]; break;
67 case 9: iSum += paw[10] + paw[11] + paw[12] + paw[13] + paw[14] + paw[15] + paw[16] + paw[17]; break;
68 case 10: iSum += paw[10] + paw[11] + paw[12] + paw[13] + paw[14] + paw[15] + paw[16] + paw[17] + paw[18] + paw[19]; break;
69 case 11: iSum += paw[10] + paw[11] + paw[12] + paw[13] + paw[14] + paw[15] + paw[16] + paw[17] + paw[18] + paw[19] + paw[20] + paw[21]; break;
70 case 12: iSum += paw[10] + paw[11] + paw[12] + paw[13] + paw[14] + paw[15] + paw[16] + paw[17] + paw[18] + paw[19] + paw[20] + paw[21] + paw[22] + paw[23]; break;
71 case 13: iSum += paw[10] + paw[11] + paw[12] + paw[13] + paw[14] + paw[15] + paw[16] + paw[17] + paw[18] + paw[19] + paw[20] + paw[21] + paw[22] + paw[23] + paw[24] + paw[25]; break;
72 case 14: iSum += paw[10] + paw[11] + paw[12] + paw[13] + paw[14] + paw[15] + paw[16] + paw[17] + paw[18] + paw[19] + paw[20] + paw[21] + paw[22] + paw[23] + paw[24] + paw[25] + paw[26] + paw[27]; break;
73 case 15: iSum += paw[10] + paw[11] + paw[12] + paw[13] + paw[14] + paw[15] + paw[16] + paw[17] + paw[18] + paw[19] + paw[20] + paw[21] + paw[22] + paw[23] + paw[24] + paw[25] + paw[26] + paw[27] + paw[28] + paw[29]; break;
74 default:
75 AssertFailed();
76 }
77 }
78
79 /* 16-bit one complement fun */
80 iSum = (iSum >> 16) + (iSum & 0xffff); /* hi + low words */
81 iSum += iSum >> 16; /* carry */
82 return (uint16_t)~iSum;
83}
84
85
86
87/**
88 * Verifies the header version, header size, packet size, and header checksum
89 * of the specified IPv4 header.
90 *
91 * @returns true if valid, false if invalid.
92 * @param pIpHdr Pointer to the IPv4 header to validate. Network endian (big).
93 * @param cbHdrMax The max header size, or the max size of what pIpHdr points
94 * to if you like. Note that an IPv4 header can be up to 60 bytes.
95 * @param cbPktMax The max IP packet size, IP header and payload. This doesn't have
96 * to be mapped following pIpHdr.
97 */
98RTDECL(bool) RTNetIPv4IsHdrsValid(PCRTNETIPV4 pIpHdr, size_t cbHdrMax, size_t cbPktMax)
99{
100 Assert(cbPktMax >= cbHdrMax);
101 if (RT_UNLIKELY(cbHdrMax < RTNETIPV4_MIN_LEN))
102 return false;
103 if (RT_UNLIKELY(pIpHdr->ip_hl * 4 < RTNETIPV4_MIN_LEN))
104 return false;
105 if (RT_UNLIKELY(pIpHdr->ip_hl * 4 > cbHdrMax))
106 {
107 Assert(pIpHdr->ip_hl * 4 > cbPktMax); /* You'll hit this if you mapped/copy too little of the header! */
108 return false;
109 }
110 if (RT_UNLIKELY(pIpHdr->ip_v != 4))
111 return false;
112 if (RT_UNLIKELY(RT_BE2H_U16(pIpHdr->ip_len) > cbPktMax))
113 return false;
114 uint16_t u16Sum = RTNetIPv4HdrChecksum(pIpHdr);
115 if (RT_UNLIKELY(RT_BE2H_U16(pIpHdr->ip_sum) != u16Sum))
116 return false;
117 return true;
118}
119
120
121/**
122 * Calculates the checksum of a pseudo header given an IPv4 header [inlined].
123 *
124 * @returns 32-bit intermediary checksum value.
125 * @param pIpHdr The IP header (network endian (big)).
126 */
127DECLINLINE(uint32_t) rtNetIPv4PseudoChecksum(PCRTNETIPV4 pIpHdr)
128{
129 uint16_t cbPayload = RT_BE2H_U16(pIpHdr->ip_len) - pIpHdr->ip_hl * 4;
130 uint32_t iSum = pIpHdr->ip_src.au16[0]
131 + pIpHdr->ip_src.au16[1]
132 + pIpHdr->ip_dst.au16[0]
133 + pIpHdr->ip_dst.au16[1]
134#ifdef RT_BIG_ENDIAN
135 + pIpHdr->ip_p
136#else
137 + ((uint32_t)pIpHdr->ip_p << 8)
138#endif
139 + RT_H2BE_U16(cbPayload);
140 return iSum;
141}
142
143
144/**
145 * Calculates the checksum of a pseudo header given an IPv4 header.
146 *
147 * @returns 32-bit intermediary checksum value.
148 * @param pIpHdr The IP header (network endian (big)).
149 */
150RTDECL(uint32_t) RTNetIPv4PseudoChecksum(PCRTNETIPV4 pIpHdr)
151{
152 return rtNetIPv4PseudoChecksum(pIpHdr);
153}
154
155
156/**
157 * Calculates the checksum of a pseudo header given the individual components.
158 *
159 * @returns 32-bit intermediary checksum value.
160 * @param SrcAddr The source address in host endian.
161 * @param DstAddr The destination address in host endian.
162 * @param bProtocol The protocol number.
163 * @param cbPkt The packet size (host endian of course) (no IPv4 header).
164 */
165RTDECL(uint32_t) RTNetIPv4PseudoChecksumBits(RTNETADDRIPV4 SrcAddr, RTNETADDRIPV4 DstAddr, uint8_t bProtocol, uint16_t cbPkt)
166{
167 uint32_t iSum = RT_H2BE_U16(SrcAddr.au16[0])
168 + RT_H2BE_U16(SrcAddr.au16[1])
169 + RT_H2BE_U16(DstAddr.au16[0])
170 + RT_H2BE_U16(DstAddr.au16[1])
171#ifdef RT_BIG_ENDIAN
172 + bProtocol
173#else
174 + ((uint32_t)bProtocol << 8)
175#endif
176 + RT_H2BE_U16(cbPkt);
177 return iSum;
178}
179
180
181/**
182 * Adds the checksum of the UDP header to the intermediate checksum value [inlined].
183 *
184 * @returns 32-bit intermediary checksum value.
185 * @param pUdpHdr Pointer to the UDP header to checksum, network endian (big).
186 * @param iSum The 32-bit intermediate checksum value.
187 */
188DECLINLINE(uint32_t) rtNetIPv4AddUDPChecksum(PCRTNETUDP pUdpHdr, uint32_t iSum)
189{
190 iSum += pUdpHdr->uh_sport
191 + pUdpHdr->uh_dport
192 /*+ pUdpHdr->uh_sum = 0 */
193 + pUdpHdr->uh_ulen;
194 return iSum;
195}
196
197
198/**
199 * Adds the checksum of the UDP header to the intermediate checksum value.
200 *
201 * @returns 32-bit intermediary checksum value.
202 * @param pUdpHdr Pointer to the UDP header to checksum, network endian (big).
203 * @param iSum The 32-bit intermediate checksum value.
204 */
205RTDECL(uint32_t) RTNetIPv4AddUDPChecksum(PCRTNETUDP pUdpHdr, uint32_t iSum)
206{
207 return rtNetIPv4AddUDPChecksum(pUdpHdr,iSum);
208}
209
210
211/**
212 * Adds the checksum of the TCP header to the intermediate checksum value [inlined].
213 *
214 * @returns 32-bit intermediary checksum value.
215 * @param pUdpHdr Pointer to the TCP header to checksum, network endian (big).
216 * Assums the caller has already validate it and made sure the
217 * entire header is present.
218 * @param iSum The 32-bit intermediate checksum value.
219 */
220DECLINLINE(uint32_t) rtNetIPv4AddTCPChecksum(PCRTNETTCP pTcpHdr, uint32_t iSum)
221{
222 uint16_t const *paw = (uint16_t const *)pTcpHdr;
223 iSum += paw[0] /* th_sport */
224 + paw[1] /* th_dport */
225 + paw[2] /* th_seq */
226 + paw[3] /* th_seq:16 */
227 + paw[4] /* th_ack */
228 + paw[5] /* th_ack:16 */
229 + paw[6] /* th_off, th_x2, th_flags */
230 + paw[7] /* th_win */
231 /*+ paw[8] == 0 */ /* th_sum */
232 + paw[9]; /* th_urp */
233 if (pTcpHdr->th_off > RTNETTCP_MIN_LEN / 4)
234 {
235 /* this is a bit insane... (identical to the IPv4 header) */
236 switch (pTcpHdr->th_off)
237 {
238 case 6: iSum += paw[10] + paw[11]; break;
239 case 7: iSum += paw[10] + paw[11] + paw[12] + paw[13]; break;
240 case 8: iSum += paw[10] + paw[11] + paw[12] + paw[13] + paw[14] + paw[15]; break;
241 case 9: iSum += paw[10] + paw[11] + paw[12] + paw[13] + paw[14] + paw[15] + paw[16] + paw[17]; break;
242 case 10: iSum += paw[10] + paw[11] + paw[12] + paw[13] + paw[14] + paw[15] + paw[16] + paw[17] + paw[18] + paw[19]; break;
243 case 11: iSum += paw[10] + paw[11] + paw[12] + paw[13] + paw[14] + paw[15] + paw[16] + paw[17] + paw[18] + paw[19] + paw[20] + paw[21]; break;
244 case 12: iSum += paw[10] + paw[11] + paw[12] + paw[13] + paw[14] + paw[15] + paw[16] + paw[17] + paw[18] + paw[19] + paw[20] + paw[21] + paw[22] + paw[23]; break;
245 case 13: iSum += paw[10] + paw[11] + paw[12] + paw[13] + paw[14] + paw[15] + paw[16] + paw[17] + paw[18] + paw[19] + paw[20] + paw[21] + paw[22] + paw[23] + paw[24] + paw[25]; break;
246 case 14: iSum += paw[10] + paw[11] + paw[12] + paw[13] + paw[14] + paw[15] + paw[16] + paw[17] + paw[18] + paw[19] + paw[20] + paw[21] + paw[22] + paw[23] + paw[24] + paw[25] + paw[26] + paw[27]; break;
247 case 15: iSum += paw[10] + paw[11] + paw[12] + paw[13] + paw[14] + paw[15] + paw[16] + paw[17] + paw[18] + paw[19] + paw[20] + paw[21] + paw[22] + paw[23] + paw[24] + paw[25] + paw[26] + paw[27] + paw[28] + paw[29]; break;
248 default:
249 AssertFailed();
250 }
251 }
252
253 return iSum;
254}
255
256
257/**
258 * Adds the checksum of the TCP header to the intermediate checksum value.
259 *
260 * @returns 32-bit intermediary checksum value.
261 * @param pUdpHdr Pointer to the TCP header to checksum, network endian (big).
262 * Assums the caller has already validate it and made sure the
263 * entire header is present.
264 * @param iSum The 32-bit intermediate checksum value.
265 */
266RTDECL(uint32_t) RTNetIPv4AddTCPChecksum(PCRTNETTCP pTcpHdr, uint32_t iSum)
267{
268 return rtNetIPv4AddTCPChecksum(pTcpHdr, iSum);
269}
270
271
272/**
273 * Adds the checksum of the specified data segment to the intermediate checksum value [inlined].
274 *
275 * @returns 32-bit intermediary checksum value.
276 * @param pUdpHdr Pointer to the UDP header to checksum, network endian (big).
277 * @param iSum The 32-bit intermediate checksum value.
278 * @param pfOdd This is used to keep track of odd bits, initialize to false
279 * when starting to checksum the data (aka text) after a TCP
280 * or UDP header (data never start at an odd offset).
281 */
282DECLINLINE(uint32_t) rtNetIPv4AddDataChecksum(void const *pvData, size_t cbData, uint32_t iSum, bool *pfOdd)
283{
284 if (*pfOdd)
285 {
286#ifdef RT_BIG_ENDIAN
287 /* there was an odd byte in the previous chunk, add the lower byte. */
288 iSum += *(uint8_t *)pvData;
289#else
290 /* there was an odd byte in the previous chunk, add the upper byte. */
291 iSum += (uint32_t)*(uint8_t *)pvData << 8;
292#endif
293 /* skip the byte. */
294 cbData--;
295 if (!cbData)
296 return iSum;
297 pvData = (uint8_t const *)pvData + 1;
298 }
299
300 /* iterate the data. */
301 uint16_t const *pw = (uint16_t const *)pvData;
302 while (cbData > 1)
303 {
304 iSum += *pw;
305 pw++;
306 cbData -= 2;
307 }
308
309 /* handle odd byte. */
310 if (cbData)
311 {
312#ifdef RT_BIG_ENDIAN
313 iSum += (uint32_t)*(uint8_t *)pw << 8;
314#else
315 iSum += *(uint8_t *)pw;
316#endif
317 *pfOdd = true;
318 }
319 else
320 *pfOdd = false;
321 return iSum;
322}
323
324/**
325 * Adds the checksum of the specified data segment to the intermediate checksum value.
326 *
327 * @returns 32-bit intermediary checksum value.
328 * @param pUdpHdr Pointer to the UDP header to checksum, network endian (big).
329 * @param iSum The 32-bit intermediate checksum value.
330 * @param pfOdd This is used to keep track of odd bits, initialize to false
331 * when starting to checksum the data (aka text) after a TCP
332 * or UDP header (data never start at an odd offset).
333 */
334RTDECL(uint32_t) RTNetIPv4AddDataChecksum(void const *pvData, size_t cbData, uint32_t iSum, bool *pfOdd)
335{
336 return rtNetIPv4AddDataChecksum(pvData, cbData, iSum, pfOdd);
337}
338
339
340/**
341 * Finalizes a IPv4 checksum [inlined].
342 *
343 * @returns The checksum.
344 * @param iSum The 32-bit intermediate checksum value.
345 */
346DECLINLINE(uint16_t) rtNetIPv4FinalizeChecksum(uint32_t iSum)
347{
348 /* 16-bit one complement fun */
349 iSum = (iSum >> 16) + (iSum & 0xffff); /* hi + low words */
350 iSum += iSum >> 16; /* carry */
351 return (uint16_t)~iSum;
352}
353
354
355/**
356 * Finalizes a IPv4 checksum.
357 *
358 * @returns The checksum.
359 * @param iSum The 32-bit intermediate checksum value.
360 */
361RTDECL(uint16_t) RTNetIPv4FinalizeChecksum(uint32_t iSum)
362{
363 return rtNetIPv4FinalizeChecksum(iSum);
364}
365
366
367
368/**
369 * Calculates the checksum for the UDP header given the IP header,
370 * UDP header and payload.
371 *
372 * @returns The checksum.
373 * @param pIpHdr Pointer to the IPv4 header, in network endian (big).
374 * @param pUdpHdr Pointer to the UDP header, in network endian (big).
375 * @param pvData Pointer to the UDP payload. The size is taken from the
376 * UDP header and the caller is supposed to have validated
377 * this before calling.
378 */
379RTDECL(uint16_t) RTNetIPv4UDPChecksum(PCRTNETIPV4 pIpHdr, PCRTNETUDP pUdpHdr, void const *pvData)
380{
381 uint32_t iSum = RTNetIPv4PseudoChecksum(pIpHdr);
382 iSum = RTNetIPv4AddUDPChecksum(pUdpHdr, iSum);
383 bool fOdd = false;
384 iSum = RTNetIPv4AddDataChecksum(pvData, RT_BE2H_U16(pUdpHdr->uh_ulen) - sizeof(*pUdpHdr), iSum, &fOdd);
385 iSum = RTNetIPv4FinalizeChecksum(iSum);
386 return iSum;
387}
388
389
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette