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 *)&ether_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 *)&ether_ipv4_udp_pkt.pkt.iphdr.check), 0x9535);
67     EXPECT_EQ(read_be16((uint8_t *)&ether_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 *)&ether_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 *)&ether_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 *)&ether_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 *)&ether_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 *)&ether_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 *)&ether_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 *)&ether_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 *)&ether_ipv4_igmpv2_pkt.pkt.iphdr.check), 0x8853);
237 }
238 
239 }  // namespace apf
240