-
Notifications
You must be signed in to change notification settings - Fork 36
/
Copy pathudpChecksum.py
166 lines (145 loc) · 8.18 KB
/
udpChecksum.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# Calculation for UDP checksum
#
# This module calculates the UDP checksum in case of IPv6 usage.
#
from helpers import showAsHex
# A valid ethernet frame, containing IPv6 and UDP, to verify the checksum algorithm.
testethernetframe = [
0x33 , 0x33 , 0x00 , 0x00 , 0x00 , 0x01,
0x04 , 0x65 , 0x65 , 0x00 , 0x64 , 0xc3 ,
0x86 , 0xdd ,
0x60 , 0x00
, 0x00 , 0x00 , 0x00 , 0x12 , 0x11 , 0x0a , 0xfe , 0x80 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x06 , 0x65
, 0x65 , 0xff , 0xfe , 0x00 , 0x64 , 0xc3 , 0xff , 0x02 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00
, 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 , 0xcc , 0xaf , 0x3b , 0x0e , 0x00 , 0x12 , 0x89 , 0x5e , 0x01 , 0xfe
, 0x90 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 , 0x10 , 0x00 ]
def calculateUdpChecksumForIPv6(udpframe, ipv6source, ipv6dest):
# Parameters:
# udpframe: the udp frame including udp header and udppayload
# ipv6source: the 16 byte IPv6 source address. Must be the same, which is used later for the transmission.
# ipv6source: the 16 byte IPv6 destination address. Must be the same, which is used later for the transmission.
udpframe[6] = 0 # at the beginning, set the checksum in the udp header to 00 00.
udpframe[7] = 0
# construct an array, consisting of a 40-byte-pseudo-ipv6-header, and the udp frame (consisting of udp header and udppayload)
bufferlen = 40+len(udpframe)
if ((bufferlen & 1)!=0):
# if we have an odd buffer length, we need to add a padding byte in the end, because the sum calculation
# will need 16-bit-aligned data.
bufferlen+=1
buffer = bytearray(bufferlen)
for i in range(0, len(buffer)):
buffer[i] = 0 # everything 0 for clean initialization
# fill the pseudo-ipv6-header
for i in range(0, 16): # copy 16 bytes IPv6 addresses
buffer[i] = ipv6source[i] # IPv6 source address
buffer[16+i] = ipv6dest[i] # IPv6 destination address
udplen = len(udpframe)
nxt = 0x11 # should be 0x11 in case of udp
buffer[32] = 0 # high byte of the FOUR byte length is always 0
buffer[33] = 0 # 2nd byte of the FOUR byte length is always 0
buffer[34] = udplen >> 8 # 3rd
buffer[35] = udplen & 0xFF # low byte of the FOUR byte length
buffer[36] = 0 # 3 padding bytes with 0x00
buffer[37] = 0
buffer[38] = 0
buffer[39] = nxt # the nxt is at the end of the pseudo header
# pseudo-ipv6-header finished. Now lets put the udpframe into the buffer. (Containing udp header and udppayload)
for i in range(0, len(udpframe)):
buffer[40+i] = udpframe[i]
# showAsHex(buffer, "buffer ")
# buffer is prepared. Run the checksum over the complete buffer.
totalSum = 0
for i in range(0, len(buffer)>>1): # running through the complete buffer, in 2-byte-steps
value16 = buffer[2*i] * 256 + buffer[2*i+1] # take the current 16-bit-word
totalSum += value16 # we start with a normal addition of the value to the totalSum
# But we do not want normal addition, we want a 16 bit one's complement sum,
# see https://en.wikipedia.org/wiki/User_Datagram_Protocol
if (totalSum>=65536): # On each addition, if a carry-out (17th bit) is produced,
totalSum-=65536 # swing that 17th carry bit around
totalSum+=1 # and add it to the least significant bit of the running total.
# Finally, the sum is then one's complemented to yield the value of the UDP checksum field.
checksum = totalSum ^ 0xffff
return checksum
if __name__ == "__main__":
print("Testing the udp checksum calculation...")
showAsHex(testethernetframe, "testethernetframe ")
ipv6frame = bytearray(len(testethernetframe)-6-6-2) # without the ethernet header (MAC, MAC, ethertype)
for i in range(0, len(ipv6frame)):
ipv6frame[i] = testethernetframe[14+i]
showAsHex(ipv6frame, "ipv6frame ")
# checksum calculation see https://en.wikipedia.org/wiki/User_Datagram_Protocol
# We want to calculate the UDP checksum. This needs to include also some data from "lower level" IP header, so we need the complete IP frame, not
# only the UDP part.
#
# From wikipedia.org: Checksum is the 16-bit one's complement
# of the one's complement sum of a pseudo header of information from
# * the IP header,
# * the UDP header,
# * and the data,
# padded with zero octets at the end (if necessary) to make a multiple of two octets.
# The pseudoIPv6 header for checksum calculation has a DIFFERENT format then a normal IPv6 header. So we cannot use the original frame,
# instead, we copy the relevant information out of it into a dedicated buffer.
# We have IPv6, so the IP header is
ipv6pseudoheader = bytearray(40)
for i in range(0, 16):
ipv6pseudoheader[i] = ipv6frame[8+i] # IPv6 source address
ipv6pseudoheader[16+i] = ipv6frame[24+i] # IPv6 destination address
udplen = ipv6frame[4]*256 + ipv6frame[5] # the real IPv6 header has a two byte length info
nxt = ipv6frame[6] # the real IPv6 header next-protocol information (should be 0x11 in case of udp)
print("udplen=" + str(udplen))
print("nxt=" + str(nxt))
ipv6pseudoheader[32] = 0 # high byte of the FOUR byte length is always 0
ipv6pseudoheader[33] = 0 # 2nd byte of the FOUR byte length is always 0
ipv6pseudoheader[34] = udplen >> 8 # 3rd
ipv6pseudoheader[35] = udplen & 0xFF # low byte of the FOUR byte length
ipv6pseudoheader[36] = 0 # 3 padding bytes with 0x00
ipv6pseudoheader[37] = 0
ipv6pseudoheader[38] = 0
ipv6pseudoheader[39] = nxt # the nxt is at the end of the pseudo header
showAsHex(ipv6pseudoheader, "ipv6pseudoheader ")
udpHeader = bytearray(8)
for i in range(0, 8):
udpHeader[i] = ipv6frame[40+i] # in the real IPv6, we have also 40 byte IPv6 header, and afterwards the 8 byte UDP header
showAsHex(udpHeader, "udpHeader ")
udpPayload = bytearray(udplen-8) # payload size is the announced udp size minus udpHeaderSize
for i in range(0, len(udpPayload)):
udpPayload[i] = ipv6frame[40+8+i]
showAsHex(udpPayload, "udpPayload ")
transmittedChecksum = udpHeader[6] * 256 + udpHeader[7]
print("The transmitted checksum is " + hex(transmittedChecksum))
# Checksum algorithm:
# 1. Set the checksum in the udp header to 00 00
udpHeader[6] = 0
udpHeader[7] = 0
runningTotal = 0 # startvalue zero
for i in range(0, len(ipv6pseudoheader)>>1):
value16 = ipv6pseudoheader[2*i] * 256 + ipv6pseudoheader[2*i+1]
runningTotal += value16
if (runningTotal>=65536): # On each addition, if a carry-out (17th bit) is produced,
runningTotal-=65536 # swing that 17th carry bit around
runningTotal+=1 # and add it to the least significant bit of the running total.
for i in range(0, len(udpHeader)>>1):
value16 = udpHeader[2*i] * 256 + udpHeader[2*i+1]
runningTotal += value16
if (runningTotal>=65536): # On each addition, if a carry-out (17th bit) is produced,
runningTotal-=65536 # swing that 17th carry bit around
runningTotal+=1 # and add it to the least significant bit of the running total.
for i in range(0, len(udpPayload)>>1):
value16 = udpPayload[2*i] * 256 + udpPayload[2*i+1]
runningTotal += value16
if (runningTotal>=65536): # On each addition, if a carry-out (17th bit) is produced,
runningTotal-=65536 # swing that 17th carry bit around
runningTotal+=1 # and add it to the least significant bit of the running total.
# Finally, the sum is then one's complemented to yield the value of the UDP checksum field.
checksum = runningTotal ^ 0xffff
print("calculated checksum=" + hex(checksum))
# Same test, using the function
myUdpFrame = bytearray(udplen) # payload size is the announced udp size
for i in range(0, len(myUdpFrame)):
myUdpFrame[i] = ipv6frame[40+i]
myIpv6Source = bytearray(16)
myIpv6Dest = bytearray(16)
for i in range(0, 16):
myIpv6Source[i] = ipv6frame[8+i] # IPv6 source address
myIpv6Dest[i] = ipv6frame[24+i] # IPv6 destination address
print("calculated with the function call: " + hex(calculateUdpChecksumForIPv6(myUdpFrame, myIpv6Source, myIpv6Dest)))