app/testpmd: fix reply to a multicast ICMP request
authorIvan Boule <ivan.boule@6wind.com>
Thu, 28 May 2015 15:05:23 +0000 (17:05 +0200)
committerThomas Monjalon <thomas.monjalon@6wind.com>
Fri, 12 Jun 2015 13:55:38 +0000 (15:55 +0200)
Set the IP source and destination addresses in the IP header of the
ICMP reply as follows:
  - Use the request IP source address as the reply IP destination address
  - If the request IP destination address is a multicast IP address
      - choose a reply IP source address different from the request IP
        source address,
      - re-compute the IP header checksum.
    Otherwise
      - switch the request IP source and destination addresses in the
        reply,
      - keep the IP header checksum unchanged.

Signed-off-by: Ivan Boule <ivan.boule@6wind.com>
app/test-pmd/icmpecho.c

index e249e71..29aef71 100644 (file)
@@ -272,6 +272,30 @@ ipv4_addr_dump(const char *what, uint32_t be_ipv4_addr)
        printf("%s", buf);
 }
 
+static uint16_t
+ipv4_hdr_cksum(struct ipv4_hdr *ip_h)
+{
+       uint16_t *v16_h;
+       uint32_t ip_cksum;
+
+       /*
+        * Compute the sum of successive 16-bit words of the IPv4 header,
+        * skipping the checksum field of the header.
+        */
+       v16_h = (uint16_t *) ip_h;
+       ip_cksum = v16_h[0] + v16_h[1] + v16_h[2] + v16_h[3] +
+               v16_h[4] + v16_h[6] + v16_h[7] + v16_h[8] + v16_h[9];
+
+       /* reduce 32 bit checksum to 16 bits and complement it */
+       ip_cksum = (ip_cksum & 0xffff) + (ip_cksum >> 16);
+       ip_cksum = (ip_cksum & 0xffff) + (ip_cksum >> 16);
+       ip_cksum = (~ip_cksum) & 0x0000FFFF;
+       return (ip_cksum == 0) ? 0xFFFF : (uint16_t) ip_cksum;
+}
+
+#define is_multicast_ipv4_addr(ipv4_addr) \
+       (((rte_be_to_cpu_32((ipv4_addr)) >> 24) & 0x000000FF) == 0xE0)
+
 /*
  * Receive a burst of packets, lookup for ICMP echo requets, and, if any,
  * send back ICMP echo replies.
@@ -443,9 +467,18 @@ reply_to_icmp_echo_rqsts(struct fwd_stream *fs)
                /*
                 * Prepare ICMP echo reply to be sent back.
                 * - switch ethernet source and destinations addresses,
-                * - switch IPv4 source and destinations addresses,
+                * - use the request IP source address as the reply IP
+                *    destination address,
+                * - if the request IP destination address is a multicast
+                *   address:
+                *     - choose a reply IP source address different from the
+                *       request IP source address,
+                *     - re-compute the IP header checksum.
+                *   Otherwise:
+                *     - switch the request IP source and destination
+                *       addresses in the reply IP header,
+                *     - keep the IP header checksum unchanged.
                 * - set IP_ICMP_ECHO_REPLY in ICMP header.
-                * No need to re-compute the IP header checksum.
                 * ICMP checksum is computed by assuming it is valid in the
                 * echo request and not verified.
                 */
@@ -453,8 +486,21 @@ reply_to_icmp_echo_rqsts(struct fwd_stream *fs)
                ether_addr_copy(&eth_h->d_addr, &eth_h->s_addr);
                ether_addr_copy(&eth_addr, &eth_h->d_addr);
                ip_addr = ip_h->src_addr;
-               ip_h->src_addr = ip_h->dst_addr;
-               ip_h->dst_addr = ip_addr;
+               if (is_multicast_ipv4_addr(ip_h->dst_addr)) {
+                       uint32_t ip_src;
+
+                       ip_src = rte_be_to_cpu_32(ip_addr);
+                       if ((ip_src & 0x00000003) == 1)
+                               ip_src = (ip_src & 0xFFFFFFFC) | 0x00000002;
+                       else
+                               ip_src = (ip_src & 0xFFFFFFFC) | 0x00000001;
+                       ip_h->src_addr = rte_cpu_to_be_32(ip_src);
+                       ip_h->dst_addr = ip_addr;
+                       ip_h->hdr_checksum = ipv4_hdr_cksum(ip_h);
+               } else {
+                       ip_h->src_addr = ip_h->dst_addr;
+                       ip_h->dst_addr = ip_addr;
+               }
                icmp_h->icmp_type = IP_ICMP_ECHO_REPLY;
                cksum = ~icmp_h->icmp_cksum & 0xffff;
                cksum += ~htons(IP_ICMP_ECHO_REQUEST << 8) & 0xffff;