@@ -57,10 +57,27 @@ int xdp_vlan_swap_func(struct xdp_md *ctx)
5757 return XDP_PASS ;
5858}
5959
60- static __always_inline __u16 csum16_add ( __u16 csum , __u16 addend )
60+ static __always_inline __u16 csum_fold_helper ( __u32 csum )
6161{
62- csum += addend ;
63- return csum + (csum < addend );
62+ return ~((csum & 0xffff ) + (csum >> 16 ));
63+ }
64+
65+ /*
66+ * The icmp_checksum_diff function takes pointers to old and new structures and
67+ * the old checksum and returns the new checksum. It uses the bpf_csum_diff
68+ * helper to compute the checksum difference. Note that the sizes passed to the
69+ * bpf_csum_diff helper should be multiples of 4, as it operates on 32-bit
70+ * words.
71+ */
72+ static __always_inline __u16 icmp_checksum_diff (
73+ __u16 seed ,
74+ struct icmphdr_common * icmphdr_new ,
75+ struct icmphdr_common * icmphdr_old )
76+ {
77+ __u32 csum , size = sizeof (struct icmphdr_common );
78+
79+ csum = bpf_csum_diff (icmphdr_old , size , icmphdr_new , size , seed );
80+ return csum_fold_helper (csum );
6481}
6582
6683/* Solution to packet03/assignment-1 */
@@ -76,8 +93,9 @@ int xdp_icmp_echo_func(struct xdp_md *ctx)
7693 int icmp_type ;
7794 struct iphdr * iphdr ;
7895 struct ipv6hdr * ipv6hdr ;
79- __u16 echo_reply , m0 , m1 ;
96+ __u16 echo_reply , old_csum ;
8097 struct icmphdr_common * icmphdr ;
98+ struct icmphdr_common icmphdr_old ;
8199 __u32 action = XDP_PASS ;
82100
83101 /* These keep track of the next header type and iterator pointer */
@@ -119,11 +137,34 @@ int xdp_icmp_echo_func(struct xdp_md *ctx)
119137 /* Swap Ethernet source and destination */
120138 swap_src_dst_mac (eth );
121139
122- /* Patch the packet and update the checksum */
123- m0 = * (__u16 * ) icmphdr ;
140+
141+ /* Patch the packet and update the checksum.*/
142+ old_csum = icmphdr -> cksum ;
143+ icmphdr -> cksum = 0 ;
144+ icmphdr_old = * icmphdr ;
124145 icmphdr -> type = echo_reply ;
125- m1 = * (__u16 * ) icmphdr ;
126- icmphdr -> checksum = ~(csum16_add (csum16_add (~icmphdr -> checksum , ~m0 ), m1 ));
146+ icmphdr -> cksum = icmp_checksum_diff (~old_csum , icmphdr , & icmphdr_old );
147+
148+ /* Another, less generic, but a bit more efficient way to update the
149+ * checksum is listed below. As only one 16-bit word changed, the sum
150+ * can be patched using this formula: sum' = ~(~sum + ~m0 + m1), where
151+ * sum' is a new sum, sum is an old sum, m0 and m1 are the old and new
152+ * 16-bit words, correspondingly. In the formula above the + operation
153+ * is defined as the following function:
154+ *
155+ * static __always_inline __u16 csum16_add(__u16 csum, __u16 addend)
156+ * {
157+ * csum += addend;
158+ * return csum + (csum < addend);
159+ * }
160+ *
161+ * So an alternative code to update the checksum might look like this:
162+ *
163+ * __u16 m0 = * (__u16 *) icmphdr;
164+ * icmphdr->type = echo_reply;
165+ * __u16 m1 = * (__u16 *) icmphdr;
166+ * icmphdr->checksum = ~(csum16_add(csum16_add(~icmphdr->checksum, ~m0), m1));
167+ */
127168
128169 action = XDP_TX ;
129170
0 commit comments