dpdk/app/test-pmd/icmpecho.c
<<
>>
Prefs
   1/* SPDX-License-Identifier: BSD-3-Clause
   2 * Copyright(c) 2013 6WIND S.A.
   3 */
   4
   5#include <stdarg.h>
   6#include <string.h>
   7#include <stdio.h>
   8#include <errno.h>
   9#include <stdint.h>
  10#include <unistd.h>
  11#include <inttypes.h>
  12
  13#include <sys/queue.h>
  14#include <sys/stat.h>
  15
  16#include <rte_common.h>
  17#include <rte_byteorder.h>
  18#include <rte_log.h>
  19#include <rte_debug.h>
  20#include <rte_cycles.h>
  21#include <rte_per_lcore.h>
  22#include <rte_lcore.h>
  23#include <rte_atomic.h>
  24#include <rte_branch_prediction.h>
  25#include <rte_memory.h>
  26#include <rte_mempool.h>
  27#include <rte_mbuf.h>
  28#include <rte_ether.h>
  29#include <rte_ethdev.h>
  30#include <rte_arp.h>
  31#include <rte_ip.h>
  32#include <rte_icmp.h>
  33#include <rte_string_fns.h>
  34#include <rte_flow.h>
  35
  36#include "testpmd.h"
  37
  38static const char *
  39arp_op_name(uint16_t arp_op)
  40{
  41        switch (arp_op) {
  42        case RTE_ARP_OP_REQUEST:
  43                return "ARP Request";
  44        case RTE_ARP_OP_REPLY:
  45                return "ARP Reply";
  46        case RTE_ARP_OP_REVREQUEST:
  47                return "Reverse ARP Request";
  48        case RTE_ARP_OP_REVREPLY:
  49                return "Reverse ARP Reply";
  50        case RTE_ARP_OP_INVREQUEST:
  51                return "Peer Identify Request";
  52        case RTE_ARP_OP_INVREPLY:
  53                return "Peer Identify Reply";
  54        default:
  55                break;
  56        }
  57        return "Unkwown ARP op";
  58}
  59
  60static const char *
  61ip_proto_name(uint16_t ip_proto)
  62{
  63        static const char * ip_proto_names[] = {
  64                "IP6HOPOPTS", /**< IP6 hop-by-hop options */
  65                "ICMP",       /**< control message protocol */
  66                "IGMP",       /**< group mgmt protocol */
  67                "GGP",        /**< gateway^2 (deprecated) */
  68                "IPv4",       /**< IPv4 encapsulation */
  69
  70                "UNASSIGNED",
  71                "TCP",        /**< transport control protocol */
  72                "ST",         /**< Stream protocol II */
  73                "EGP",        /**< exterior gateway protocol */
  74                "PIGP",       /**< private interior gateway */
  75
  76                "RCC_MON",    /**< BBN RCC Monitoring */
  77                "NVPII",      /**< network voice protocol*/
  78                "PUP",        /**< pup */
  79                "ARGUS",      /**< Argus */
  80                "EMCON",      /**< EMCON */
  81
  82                "XNET",       /**< Cross Net Debugger */
  83                "CHAOS",      /**< Chaos*/
  84                "UDP",        /**< user datagram protocol */
  85                "MUX",        /**< Multiplexing */
  86                "DCN_MEAS",   /**< DCN Measurement Subsystems */
  87
  88                "HMP",        /**< Host Monitoring */
  89                "PRM",        /**< Packet Radio Measurement */
  90                "XNS_IDP",    /**< xns idp */
  91                "TRUNK1",     /**< Trunk-1 */
  92                "TRUNK2",     /**< Trunk-2 */
  93
  94                "LEAF1",      /**< Leaf-1 */
  95                "LEAF2",      /**< Leaf-2 */
  96                "RDP",        /**< Reliable Data */
  97                "IRTP",       /**< Reliable Transaction */
  98                "TP4",        /**< tp-4 w/ class negotiation */
  99
 100                "BLT",        /**< Bulk Data Transfer */
 101                "NSP",        /**< Network Services */
 102                "INP",        /**< Merit Internodal */
 103                "SEP",        /**< Sequential Exchange */
 104                "3PC",        /**< Third Party Connect */
 105
 106                "IDPR",       /**< InterDomain Policy Routing */
 107                "XTP",        /**< XTP */
 108                "DDP",        /**< Datagram Delivery */
 109                "CMTP",       /**< Control Message Transport */
 110                "TPXX",       /**< TP++ Transport */
 111
 112                "ILTP",       /**< IL transport protocol */
 113                "IPv6_HDR",   /**< IP6 header */
 114                "SDRP",       /**< Source Demand Routing */
 115                "IPv6_RTG",   /**< IP6 routing header */
 116                "IPv6_FRAG",  /**< IP6 fragmentation header */
 117
 118                "IDRP",       /**< InterDomain Routing*/
 119                "RSVP",       /**< resource reservation */
 120                "GRE",        /**< General Routing Encap. */
 121                "MHRP",       /**< Mobile Host Routing */
 122                "BHA",        /**< BHA */
 123
 124                "ESP",        /**< IP6 Encap Sec. Payload */
 125                "AH",         /**< IP6 Auth Header */
 126                "INLSP",      /**< Integ. Net Layer Security */
 127                "SWIPE",      /**< IP with encryption */
 128                "NHRP",       /**< Next Hop Resolution */
 129
 130                "UNASSIGNED",
 131                "UNASSIGNED",
 132                "UNASSIGNED",
 133                "ICMPv6",     /**< ICMP6 */
 134                "IPv6NONEXT", /**< IP6 no next header */
 135
 136                "Ipv6DSTOPTS",/**< IP6 destination option */
 137                "AHIP",       /**< any host internal protocol */
 138                "CFTP",       /**< CFTP */
 139                "HELLO",      /**< "hello" routing protocol */
 140                "SATEXPAK",   /**< SATNET/Backroom EXPAK */
 141
 142                "KRYPTOLAN",  /**< Kryptolan */
 143                "RVD",        /**< Remote Virtual Disk */
 144                "IPPC",       /**< Pluribus Packet Core */
 145                "ADFS",       /**< Any distributed FS */
 146                "SATMON",     /**< Satnet Monitoring */
 147
 148                "VISA",       /**< VISA Protocol */
 149                "IPCV",       /**< Packet Core Utility */
 150                "CPNX",       /**< Comp. Prot. Net. Executive */
 151                "CPHB",       /**< Comp. Prot. HeartBeat */
 152                "WSN",        /**< Wang Span Network */
 153
 154                "PVP",        /**< Packet Video Protocol */
 155                "BRSATMON",   /**< BackRoom SATNET Monitoring */
 156                "ND",         /**< Sun net disk proto (temp.) */
 157                "WBMON",      /**< WIDEBAND Monitoring */
 158                "WBEXPAK",    /**< WIDEBAND EXPAK */
 159
 160                "EON",        /**< ISO cnlp */
 161                "VMTP",       /**< VMTP */
 162                "SVMTP",      /**< Secure VMTP */
 163                "VINES",      /**< Banyon VINES */
 164                "TTP",        /**< TTP */
 165
 166                "IGP",        /**< NSFNET-IGP */
 167                "DGP",        /**< dissimilar gateway prot. */
 168                "TCF",        /**< TCF */
 169                "IGRP",       /**< Cisco/GXS IGRP */
 170                "OSPFIGP",    /**< OSPFIGP */
 171
 172                "SRPC",       /**< Strite RPC protocol */
 173                "LARP",       /**< Locus Address Resolution */
 174                "MTP",        /**< Multicast Transport */
 175                "AX25",       /**< AX.25 Frames */
 176                "4IN4",       /**< IP encapsulated in IP */
 177
 178                "MICP",       /**< Mobile Int.ing control */
 179                "SCCSP",      /**< Semaphore Comm. security */
 180                "ETHERIP",    /**< Ethernet IP encapsulation */
 181                "ENCAP",      /**< encapsulation header */
 182                "AES",        /**< any private encr. scheme */
 183
 184                "GMTP",       /**< GMTP */
 185                "IPCOMP",     /**< payload compression (IPComp) */
 186                "UNASSIGNED",
 187                "UNASSIGNED",
 188                "PIM",        /**< Protocol Independent Mcast */
 189        };
 190
 191        if (ip_proto < RTE_DIM(ip_proto_names))
 192                return ip_proto_names[ip_proto];
 193        switch (ip_proto) {
 194#ifdef IPPROTO_PGM
 195        case IPPROTO_PGM:  /**< PGM */
 196                return "PGM";
 197#endif
 198        case IPPROTO_SCTP:  /**< Stream Control Transport Protocol */
 199                return "SCTP";
 200#ifdef IPPROTO_DIVERT
 201        case IPPROTO_DIVERT: /**< divert pseudo-protocol */
 202                return "DIVERT";
 203#endif
 204        case IPPROTO_RAW: /**< raw IP packet */
 205                return "RAW";
 206        default:
 207                break;
 208        }
 209        return "UNASSIGNED";
 210}
 211
 212static void
 213ipv4_addr_to_dot(uint32_t be_ipv4_addr, char *buf)
 214{
 215        uint32_t ipv4_addr;
 216
 217        ipv4_addr = rte_be_to_cpu_32(be_ipv4_addr);
 218        sprintf(buf, "%d.%d.%d.%d", (ipv4_addr >> 24) & 0xFF,
 219                (ipv4_addr >> 16) & 0xFF, (ipv4_addr >> 8) & 0xFF,
 220                ipv4_addr & 0xFF);
 221}
 222
 223static void
 224ether_addr_dump(const char *what, const struct rte_ether_addr *ea)
 225{
 226        char buf[RTE_ETHER_ADDR_FMT_SIZE];
 227
 228        rte_ether_format_addr(buf, RTE_ETHER_ADDR_FMT_SIZE, ea);
 229        if (what)
 230                printf("%s", what);
 231        printf("%s", buf);
 232}
 233
 234static void
 235ipv4_addr_dump(const char *what, uint32_t be_ipv4_addr)
 236{
 237        char buf[16];
 238
 239        ipv4_addr_to_dot(be_ipv4_addr, buf);
 240        if (what)
 241                printf("%s", what);
 242        printf("%s", buf);
 243}
 244
 245static uint16_t
 246ipv4_hdr_cksum(struct rte_ipv4_hdr *ip_h)
 247{
 248        uint16_t *v16_h;
 249        uint32_t ip_cksum;
 250
 251        /*
 252         * Compute the sum of successive 16-bit words of the IPv4 header,
 253         * skipping the checksum field of the header.
 254         */
 255        v16_h = (unaligned_uint16_t *) ip_h;
 256        ip_cksum = v16_h[0] + v16_h[1] + v16_h[2] + v16_h[3] +
 257                v16_h[4] + v16_h[6] + v16_h[7] + v16_h[8] + v16_h[9];
 258
 259        /* reduce 32 bit checksum to 16 bits and complement it */
 260        ip_cksum = (ip_cksum & 0xffff) + (ip_cksum >> 16);
 261        ip_cksum = (ip_cksum & 0xffff) + (ip_cksum >> 16);
 262        ip_cksum = (~ip_cksum) & 0x0000FFFF;
 263        return (ip_cksum == 0) ? 0xFFFF : (uint16_t) ip_cksum;
 264}
 265
 266#define is_multicast_ipv4_addr(ipv4_addr) \
 267        (((rte_be_to_cpu_32((ipv4_addr)) >> 24) & 0x000000FF) == 0xE0)
 268
 269/*
 270 * Receive a burst of packets, lookup for ICMP echo requests, and, if any,
 271 * send back ICMP echo replies.
 272 */
 273static void
 274reply_to_icmp_echo_rqsts(struct fwd_stream *fs)
 275{
 276        struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
 277        struct rte_mbuf *pkt;
 278        struct rte_ether_hdr *eth_h;
 279        struct rte_vlan_hdr *vlan_h;
 280        struct rte_arp_hdr  *arp_h;
 281        struct rte_ipv4_hdr *ip_h;
 282        struct rte_icmp_hdr *icmp_h;
 283        struct rte_ether_addr eth_addr;
 284        uint32_t retry;
 285        uint32_t ip_addr;
 286        uint16_t nb_rx;
 287        uint16_t nb_tx;
 288        uint16_t nb_replies;
 289        uint16_t eth_type;
 290        uint16_t vlan_id;
 291        uint16_t arp_op;
 292        uint16_t arp_pro;
 293        uint32_t cksum;
 294        uint8_t  i;
 295        int l2_len;
 296        uint64_t start_tsc = 0;
 297
 298        get_start_cycles(&start_tsc);
 299
 300        /*
 301         * First, receive a burst of packets.
 302         */
 303        nb_rx = rte_eth_rx_burst(fs->rx_port, fs->rx_queue, pkts_burst,
 304                                 nb_pkt_per_burst);
 305        inc_rx_burst_stats(fs, nb_rx);
 306        if (unlikely(nb_rx == 0))
 307                return;
 308
 309        fs->rx_packets += nb_rx;
 310        nb_replies = 0;
 311        for (i = 0; i < nb_rx; i++) {
 312                if (likely(i < nb_rx - 1))
 313                        rte_prefetch0(rte_pktmbuf_mtod(pkts_burst[i + 1],
 314                                                       void *));
 315                pkt = pkts_burst[i];
 316                eth_h = rte_pktmbuf_mtod(pkt, struct rte_ether_hdr *);
 317                eth_type = RTE_BE_TO_CPU_16(eth_h->ether_type);
 318                l2_len = sizeof(struct rte_ether_hdr);
 319                if (verbose_level > 0) {
 320                        printf("\nPort %d pkt-len=%u nb-segs=%u\n",
 321                               fs->rx_port, pkt->pkt_len, pkt->nb_segs);
 322                        ether_addr_dump("  ETH:  src=", &eth_h->s_addr);
 323                        ether_addr_dump(" dst=", &eth_h->d_addr);
 324                }
 325                if (eth_type == RTE_ETHER_TYPE_VLAN) {
 326                        vlan_h = (struct rte_vlan_hdr *)
 327                                ((char *)eth_h + sizeof(struct rte_ether_hdr));
 328                        l2_len  += sizeof(struct rte_vlan_hdr);
 329                        eth_type = rte_be_to_cpu_16(vlan_h->eth_proto);
 330                        if (verbose_level > 0) {
 331                                vlan_id = rte_be_to_cpu_16(vlan_h->vlan_tci)
 332                                        & 0xFFF;
 333                                printf(" [vlan id=%u]", vlan_id);
 334                        }
 335                }
 336                if (verbose_level > 0) {
 337                        printf(" type=0x%04x\n", eth_type);
 338                }
 339
 340                /* Reply to ARP requests */
 341                if (eth_type == RTE_ETHER_TYPE_ARP) {
 342                        arp_h = (struct rte_arp_hdr *) ((char *)eth_h + l2_len);
 343                        arp_op = RTE_BE_TO_CPU_16(arp_h->arp_opcode);
 344                        arp_pro = RTE_BE_TO_CPU_16(arp_h->arp_protocol);
 345                        if (verbose_level > 0) {
 346                                printf("  ARP:  hrd=%d proto=0x%04x hln=%d "
 347                                       "pln=%d op=%u (%s)\n",
 348                                       RTE_BE_TO_CPU_16(arp_h->arp_hardware),
 349                                       arp_pro, arp_h->arp_hlen,
 350                                       arp_h->arp_plen, arp_op,
 351                                       arp_op_name(arp_op));
 352                        }
 353                        if ((RTE_BE_TO_CPU_16(arp_h->arp_hardware) !=
 354                             RTE_ARP_HRD_ETHER) ||
 355                            (arp_pro != RTE_ETHER_TYPE_IPV4) ||
 356                            (arp_h->arp_hlen != 6) ||
 357                            (arp_h->arp_plen != 4)
 358                            ) {
 359                                rte_pktmbuf_free(pkt);
 360                                if (verbose_level > 0)
 361                                        printf("\n");
 362                                continue;
 363                        }
 364                        if (verbose_level > 0) {
 365                                rte_ether_addr_copy(&arp_h->arp_data.arp_sha,
 366                                                &eth_addr);
 367                                ether_addr_dump("        sha=", &eth_addr);
 368                                ip_addr = arp_h->arp_data.arp_sip;
 369                                ipv4_addr_dump(" sip=", ip_addr);
 370                                printf("\n");
 371                                rte_ether_addr_copy(&arp_h->arp_data.arp_tha,
 372                                                &eth_addr);
 373                                ether_addr_dump("        tha=", &eth_addr);
 374                                ip_addr = arp_h->arp_data.arp_tip;
 375                                ipv4_addr_dump(" tip=", ip_addr);
 376                                printf("\n");
 377                        }
 378                        if (arp_op != RTE_ARP_OP_REQUEST) {
 379                                rte_pktmbuf_free(pkt);
 380                                continue;
 381                        }
 382
 383                        /*
 384                         * Build ARP reply.
 385                         */
 386
 387                        /* Use source MAC address as destination MAC address. */
 388                        rte_ether_addr_copy(&eth_h->s_addr, &eth_h->d_addr);
 389                        /* Set source MAC address with MAC address of TX port */
 390                        rte_ether_addr_copy(&ports[fs->tx_port].eth_addr,
 391                                        &eth_h->s_addr);
 392
 393                        arp_h->arp_opcode = rte_cpu_to_be_16(RTE_ARP_OP_REPLY);
 394                        rte_ether_addr_copy(&arp_h->arp_data.arp_tha,
 395                                        &eth_addr);
 396                        rte_ether_addr_copy(&arp_h->arp_data.arp_sha,
 397                                        &arp_h->arp_data.arp_tha);
 398                        rte_ether_addr_copy(&eth_h->s_addr,
 399                                        &arp_h->arp_data.arp_sha);
 400
 401                        /* Swap IP addresses in ARP payload */
 402                        ip_addr = arp_h->arp_data.arp_sip;
 403                        arp_h->arp_data.arp_sip = arp_h->arp_data.arp_tip;
 404                        arp_h->arp_data.arp_tip = ip_addr;
 405                        pkts_burst[nb_replies++] = pkt;
 406                        continue;
 407                }
 408
 409                if (eth_type != RTE_ETHER_TYPE_IPV4) {
 410                        rte_pktmbuf_free(pkt);
 411                        continue;
 412                }
 413                ip_h = (struct rte_ipv4_hdr *) ((char *)eth_h + l2_len);
 414                if (verbose_level > 0) {
 415                        ipv4_addr_dump("  IPV4: src=", ip_h->src_addr);
 416                        ipv4_addr_dump(" dst=", ip_h->dst_addr);
 417                        printf(" proto=%d (%s)\n",
 418                               ip_h->next_proto_id,
 419                               ip_proto_name(ip_h->next_proto_id));
 420                }
 421
 422                /*
 423                 * Check if packet is a ICMP echo request.
 424                 */
 425                icmp_h = (struct rte_icmp_hdr *) ((char *)ip_h +
 426                                              sizeof(struct rte_ipv4_hdr));
 427                if (! ((ip_h->next_proto_id == IPPROTO_ICMP) &&
 428                       (icmp_h->icmp_type == RTE_IP_ICMP_ECHO_REQUEST) &&
 429                       (icmp_h->icmp_code == 0))) {
 430                        rte_pktmbuf_free(pkt);
 431                        continue;
 432                }
 433
 434                if (verbose_level > 0)
 435                        printf("  ICMP: echo request seq id=%d\n",
 436                               rte_be_to_cpu_16(icmp_h->icmp_seq_nb));
 437
 438                /*
 439                 * Prepare ICMP echo reply to be sent back.
 440                 * - switch ethernet source and destinations addresses,
 441                 * - use the request IP source address as the reply IP
 442                 *    destination address,
 443                 * - if the request IP destination address is a multicast
 444                 *   address:
 445                 *     - choose a reply IP source address different from the
 446                 *       request IP source address,
 447                 *     - re-compute the IP header checksum.
 448                 *   Otherwise:
 449                 *     - switch the request IP source and destination
 450                 *       addresses in the reply IP header,
 451                 *     - keep the IP header checksum unchanged.
 452                 * - set RTE_IP_ICMP_ECHO_REPLY in ICMP header.
 453                 * ICMP checksum is computed by assuming it is valid in the
 454                 * echo request and not verified.
 455                 */
 456                rte_ether_addr_copy(&eth_h->s_addr, &eth_addr);
 457                rte_ether_addr_copy(&eth_h->d_addr, &eth_h->s_addr);
 458                rte_ether_addr_copy(&eth_addr, &eth_h->d_addr);
 459                ip_addr = ip_h->src_addr;
 460                if (is_multicast_ipv4_addr(ip_h->dst_addr)) {
 461                        uint32_t ip_src;
 462
 463                        ip_src = rte_be_to_cpu_32(ip_addr);
 464                        if ((ip_src & 0x00000003) == 1)
 465                                ip_src = (ip_src & 0xFFFFFFFC) | 0x00000002;
 466                        else
 467                                ip_src = (ip_src & 0xFFFFFFFC) | 0x00000001;
 468                        ip_h->src_addr = rte_cpu_to_be_32(ip_src);
 469                        ip_h->dst_addr = ip_addr;
 470                        ip_h->hdr_checksum = ipv4_hdr_cksum(ip_h);
 471                } else {
 472                        ip_h->src_addr = ip_h->dst_addr;
 473                        ip_h->dst_addr = ip_addr;
 474                }
 475                icmp_h->icmp_type = RTE_IP_ICMP_ECHO_REPLY;
 476                cksum = ~icmp_h->icmp_cksum & 0xffff;
 477                cksum += ~RTE_BE16(RTE_IP_ICMP_ECHO_REQUEST << 8) & 0xffff;
 478                cksum += RTE_BE16(RTE_IP_ICMP_ECHO_REPLY << 8);
 479                cksum = (cksum & 0xffff) + (cksum >> 16);
 480                cksum = (cksum & 0xffff) + (cksum >> 16);
 481                icmp_h->icmp_cksum = ~cksum;
 482                pkts_burst[nb_replies++] = pkt;
 483        }
 484
 485        /* Send back ICMP echo replies, if any. */
 486        if (nb_replies > 0) {
 487                nb_tx = rte_eth_tx_burst(fs->tx_port, fs->tx_queue, pkts_burst,
 488                                         nb_replies);
 489                /*
 490                 * Retry if necessary
 491                 */
 492                if (unlikely(nb_tx < nb_replies) && fs->retry_enabled) {
 493                        retry = 0;
 494                        while (nb_tx < nb_replies &&
 495                                        retry++ < burst_tx_retry_num) {
 496                                rte_delay_us(burst_tx_delay_time);
 497                                nb_tx += rte_eth_tx_burst(fs->tx_port,
 498                                                fs->tx_queue,
 499                                                &pkts_burst[nb_tx],
 500                                                nb_replies - nb_tx);
 501                        }
 502                }
 503                fs->tx_packets += nb_tx;
 504                inc_tx_burst_stats(fs, nb_tx);
 505                if (unlikely(nb_tx < nb_replies)) {
 506                        fs->fwd_dropped += (nb_replies - nb_tx);
 507                        do {
 508                                rte_pktmbuf_free(pkts_burst[nb_tx]);
 509                        } while (++nb_tx < nb_replies);
 510                }
 511        }
 512
 513        get_end_cycles(fs, start_tsc);
 514}
 515
 516struct fwd_engine icmp_echo_engine = {
 517        .fwd_mode_name  = "icmpecho",
 518        .port_fwd_begin = NULL,
 519        .port_fwd_end   = NULL,
 520        .packet_fwd     = reply_to_icmp_echo_rqsts,
 521};
 522