1 #include <cstdint>
2 #include <cstddef>
3 #include <gtest/gtest.h>
4 #include <linux/icmpv6.h>
5 #include <linux/if_ether.h>
6 #include <linux/in.h>
7 #include <linux/in6.h>
8 #include <linux/ip.h>
9 #include <linux/ipv6.h>
10 #include <linux/udp.h>
11 #include <linux/igmp.h>
12 #include "apf_defs.h"
13 #include "apf_utils.h"
14 #include "apf_checksum.h"
15
16 namespace apf {
17
18 #define htons(x) __builtin_bswap16(x)
19 #define packed __attribute__((packed))
20
21
22
TEST(ApfChecksumTest,CalcIPv4UDPChecksum)23 TEST(ApfChecksumTest, CalcIPv4UDPChecksum) {
24 // An IPv4 UDP packet with IPv4 header checksum and UDP checksum set to 0
25 union packed {
26 uint8_t data[77];
27 struct packed {
28 struct ethhdr ethhdr;
29 struct iphdr iphdr;
30 struct udphdr udphdr;
31 uint8_t udp_payload[];
32 } pkt;
33 } ether_ipv4_udp_pkt = {{
34 0x01, 0x00, 0x5e, 0x00, 0x00, 0xfb,
35 0x38, 0xca, 0x84, 0xb7, 0x7f, 0x16,
36 0x08, 0x00, // end of ethernet header
37 0x45,
38 0x04,
39 0x00, 0x3f,
40 0x43, 0xcd,
41 0x40, 0x00,
42 0xff,
43 0x11,
44 0x00, 0x00,
45 0xc0, 0xa8, 0x01, 0x03,
46 0xe0, 0x00, 0x00, 0xfb, // end of ipv4 header
47 0x14, 0xe9,
48 0x14, 0xe9,
49 0x00, 0x2b,
50 0x00, 0x00, // end of udp header
51 0x00, 0x00, 0x84, 0x00, 0x00, 0x00,
52 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x62, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00, 0x00,
53 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x04, 0xc0, 0xa8, 0x01, 0x09,
54 }};
55 // Reset IPv4 header checksum to 0
56 ether_ipv4_udp_pkt.pkt.iphdr.check = 0;
57 // Set the UDP checksum to UDP payload size
58 ether_ipv4_udp_pkt.pkt.udphdr.check = htons(sizeof(ether_ipv4_udp_pkt) - IPV4_HLEN - ETH_HLEN);
59 uint8_t dscp = csum_and_return_dscp((uint8_t *)ðer_ipv4_udp_pkt, sizeof(ether_ipv4_udp_pkt),
60 ETH_HLEN /* ip_ofs */, IPPROTO_UDP /* partial_csum */,
61 ETH_HLEN + offsetof(iphdr, saddr) /* csum_start */,
62 ETH_HLEN + IPV4_HLEN + offsetof(udphdr, check) /* csum_ofs */,
63 true /* udp */);
64 EXPECT_EQ(dscp, 1);
65 // Verify IPv4 header checksum
66 EXPECT_EQ(read_be16((uint8_t *)ðer_ipv4_udp_pkt.pkt.iphdr.check), 0x9535);
67 EXPECT_EQ(read_be16((uint8_t *)ðer_ipv4_udp_pkt.pkt.udphdr.check), 0xa73d);
68 }
69
TEST(ApfChecksumTest,CalcIPv6UDPChecksum)70 TEST(ApfChecksumTest, CalcIPv6UDPChecksum) {
71 // An IPv6 UDP packet with UDP checksum set to 0
72 union packed {
73 uint8_t data[97];
74 struct packed {
75 struct ethhdr ethhdr;
76 struct ipv6hdr ipv6hdr;
77 struct udphdr udphdr;
78 uint8_t udp_payload[];
79 } pkt;
80 } ether_ipv6_udp_pkt = {{
81 0x33, 0x33, 0x00, 0x00, 0x00, 0xfb,
82 0x38, 0xca, 0x84, 0xb7, 0x7f, 0x16,
83 0x86, 0xdd, // end of ethernet header
84 0x61, 0x89, 0xf4, 0x6b,
85 0x00, 0x2b,
86 0x11,
87 0xff,
88 0x24, 0x0d, 0x00, 0x1a, 0x03, 0xa6, 0xc4, 0x00, 0xb7, 0x5a, 0xb4, 0x85, 0x28, 0x10, 0xad, 0x6b,
89 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb, // end of ipv6 header
90 0x14, 0xe9,
91 0x14, 0xe9,
92 0x00, 0x2b,
93 0x00, 0x00, // end of udp header
94 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x62, 0x05, 0x6c,
95 0x6f, 0x63, 0x61, 0x6c, 0x00, 0x00, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x04, 0xc0,
96 0xa8, 0x01, 0x09
97 }};
98 // Set the UDP checksum to UDP payload size
99 ether_ipv6_udp_pkt.pkt.udphdr.check = htons(sizeof(ether_ipv6_udp_pkt) - IPV6_HLEN - ETH_HLEN);
100 uint8_t dscp = csum_and_return_dscp((uint8_t *)ðer_ipv6_udp_pkt, sizeof(ether_ipv6_udp_pkt),
101 ETH_HLEN /* ip_ofs */, IPPROTO_UDP /* partial_csum */,
102 ETH_HLEN + offsetof(ipv6hdr, saddr) /* csum_start */,
103 ETH_HLEN + IPV6_HLEN + offsetof(udphdr, check) /* csum_ofs */,
104 true /* udp */);
105 EXPECT_EQ(dscp, 6);
106 // verify UDP checksum
107 EXPECT_EQ(read_be16((uint8_t *)ðer_ipv6_udp_pkt.pkt.udphdr.check), 0x1cbd);
108 }
109
TEST(ApfChecksumTest,CalcICMPv6Checksum)110 TEST(ApfChecksumTest, CalcICMPv6Checksum) {
111 // An ICMPv6 packet with checksum field set to 0
112 union packed {
113 uint8_t data[78];
114 struct packed {
115 struct ethhdr ethhdr;
116 struct ipv6hdr ipv6hdr;
117 struct icmp6hdr icmp6hdr;
118 uint8_t icmpv6_payload[];
119 } pkt;
120 } ether_ipv6_icmp6_pkt = {{
121 0xcc, 0x1a, 0xfa, 0xc7, 0xd2, 0xd8,
122 0xbc, 0xd0, 0x74, 0x58, 0xf1, 0x4f,
123 0x86, 0xdd, // end of ethernet header
124 0x61, 0x80, 0x00, 0x00,
125 0x00, 0x18,
126 0x3a,
127 0xff,
128 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x12, 0x11, 0x2c, 0xdc, 0x04, 0x35, 0x11,
129 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // end of ipv6 header
130 0x88,
131 0x00,
132 0x00, 0x00, // end of icmpv6 header
133 0x40, 0x00, 0x00, 0x00, 0x24, 0x0d, 0x00, 0x1a, 0x03, 0xa6, 0xc4, 0x00, 0xfd, 0x3d, 0x12, 0xb7,
134 0x90, 0xb6, 0xe9, 0xd2
135 }};
136 // Set the ICMPv6 checksum to ICMPv6 payload size
137 ether_ipv6_icmp6_pkt.pkt.icmp6hdr.icmp6_cksum = htons(sizeof(ether_ipv6_icmp6_pkt) - IPV6_HLEN - ETH_HLEN);
138 uint8_t dscp = csum_and_return_dscp((uint8_t *)ðer_ipv6_icmp6_pkt, sizeof(ether_ipv6_icmp6_pkt),
139 ETH_HLEN /* ip_ofs */, IPPROTO_ICMPV6 /* partial_csum */,
140 ETH_HLEN + offsetof(ipv6hdr, saddr) /* csum_start */,
141 ETH_HLEN + IPV6_HLEN + offsetof(icmp6hdr, icmp6_cksum) /* csum_ofs */,
142 false /* udp */);
143 EXPECT_EQ(dscp, 6);
144 // verify layer 4 checksum
145 EXPECT_EQ(read_be16((uint8_t *)ðer_ipv6_icmp6_pkt.pkt.icmp6hdr.icmp6_cksum), 0x8a09);
146 }
147
TEST(ApfChecksumTest,CalcICMPv6ChecksumWithHopByHopOption)148 TEST(ApfChecksumTest, CalcICMPv6ChecksumWithHopByHopOption) {
149 // An ICMPv6 packet(including hop-by-hop option) with checksum field set to 0
150 union packed {
151 uint8_t data[90];
152 struct packed {
153 struct ethhdr ethhdr;
154 struct ipv6hdr ipv6hdr;
155 uint8_t hopopts[8];
156 struct icmp6hdr icmp6hdr;
157 uint8_t icmpv6_payload[];
158 } pkt;
159 } ether_ipv6_hopopts_icmp6_pkt = {{
160 0x33, 0x33, 0x00, 0x00, 0x00, 0x16,
161 0xe0, 0x4f, 0x43, 0xe6, 0xfb, 0xcf,
162 0x86, 0xdd, // end of ethernet header
163 0x60, 0x00, 0x00, 0x00,
164 0x00, 0x24,
165 0x00,
166 0x01,
167 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x6b, 0xe2, 0xfe, 0xd6, 0x53, 0x4e, 0xe0,
168 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, // end of ipv6 header
169 0x3a, 0x00, 0x05, 0x02, 0x00, 0x00, 0x01, 0x00, // end of hop-by-hop option
170 0x8f,
171 0x00,
172 0x00, 0x00, // end of icmpv6 header
173 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
174 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c
175 }};
176
177 // Set the ICMPv6 checksum to ICMPv6 (header + payload) size + ~{16-bit sum of hop-by-hop header}
178 ether_ipv6_hopopts_icmp6_pkt.pkt.icmp6hdr.icmp6_cksum =
179 htons(sizeof(ether_ipv6_hopopts_icmp6_pkt) - IPV6_HLEN - ETH_HLEN
180 - sizeof(ether_ipv6_hopopts_icmp6_pkt.pkt.hopopts)
181 + 0xbffd); // 0xffff - (0x3a00 + 0x0502 + 0x0000 + 0x0100) = 0xbffd
182 uint8_t dscp = csum_and_return_dscp((uint8_t *)ðer_ipv6_hopopts_icmp6_pkt,
183 sizeof(ether_ipv6_hopopts_icmp6_pkt),
184 ETH_HLEN /* ip_ofs */, IPPROTO_ICMPV6 /* partial_csum */,
185 ETH_HLEN + offsetof(ipv6hdr, saddr) /* csum_start */,
186 ETH_HLEN + IPV6_HLEN
187 + sizeof(ether_ipv6_hopopts_icmp6_pkt.pkt.hopopts)
188 + offsetof(icmp6hdr, icmp6_cksum) /* csum_ofs */,
189 false /* udp */);
190 EXPECT_EQ(dscp, 0);
191 // verify layer 4 checksum
192 EXPECT_EQ(read_be16((uint8_t *)ðer_ipv6_hopopts_icmp6_pkt.pkt.icmp6hdr.icmp6_cksum), 0xf760);
193 }
194
TEST(ApfChecksumTest,CalcIGMPv2Checksum)195 TEST(ApfChecksumTest, CalcIGMPv2Checksum) {
196 // An IGMPv2 packet with ip checksum field set to 0
197 union packed {
198 uint8_t data[46];
199 struct packed {
200 struct ethhdr ethhdr;
201 struct iphdr iphdr;
202 uint8_t router_alert_option[4];
203 struct igmphdr igmphdr;
204 } pkt;
205 } ether_ipv4_igmpv2_pkt = {{
206 0x01, 0x00, 0x5e, 0x00, 0x00, 0xfb,
207 0xa2, 0x29, 0xae, 0xb3, 0x56, 0x6b,
208 0x08, 0x00, // end of ethernet header
209 0x46,
210 0x00,
211 0x00, 0x20,
212 0xf8, 0xf3,
213 0x00, 0x00,
214 0x01,
215 0x02,
216 0x00, 0x00,
217 0xc0, 0xa8, 0x01, 0xed,
218 0xe0, 0x00, 0x00, 0xfb, // end of ipv4 header without option
219 0x94, 0x04, 0x00, 0x00, // router alert option
220 0x16,
221 0x00,
222 0x09, 0x04,
223 0xe0, 0x00, 0x00, 0xfb // end of igmp payload
224 }};
225
226 // Set IPv4 checksum to 0x9404 + 0x0000 = 0x9404
227 ether_ipv4_igmpv2_pkt.pkt.iphdr.check = htons(0x9404);
228 uint8_t dscp = csum_and_return_dscp((uint8_t *)ðer_ipv4_igmpv2_pkt,
229 sizeof(ether_ipv4_igmpv2_pkt),
230 ETH_HLEN /* ip_ofs */, IPPROTO_IGMP /* partial_csum */,
231 0 /* csum_start */,
232 255 /* csum_ofs */,
233 false /* udp */);
234 EXPECT_EQ(dscp, 0);
235 // Verify IPv4 header checksum
236 EXPECT_EQ(read_be16((uint8_t *)ðer_ipv4_igmpv2_pkt.pkt.iphdr.check), 0x8853);
237 }
238
239 } // namespace apf
240