1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (c) 2021 Microsoft Corporation 3 */ 4 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <unistd.h> 8 9 #include <rte_ethdev.h> 10 #include <rte_ether.h> 11 #include <rte_mbuf.h> 12 #include <rte_mempool.h> 13 #include <rte_net.h> 14 #include <rte_pcapng.h> 15 16 #include <pcap/pcap.h> 17 18 #include "test.h" 19 20 #define NUM_PACKETS 10 21 #define DUMMY_MBUF_NUM 3 22 23 static rte_pcapng_t *pcapng; 24 static struct rte_mempool *mp; 25 static const uint32_t pkt_len = 200; 26 static uint16_t port_id; 27 static char file_name[] = "/tmp/pcapng_test_XXXXXX.pcapng"; 28 29 /* first mbuf in the packet, should always be at offset 0 */ 30 struct dummy_mbuf { 31 struct rte_mbuf mb[DUMMY_MBUF_NUM]; 32 uint8_t buf[DUMMY_MBUF_NUM][RTE_MBUF_DEFAULT_BUF_SIZE]; 33 }; 34 35 static void 36 dummy_mbuf_prep(struct rte_mbuf *mb, uint8_t buf[], uint32_t buf_len, 37 uint32_t data_len) 38 { 39 uint32_t i; 40 uint8_t *db; 41 42 mb->buf_addr = buf; 43 mb->buf_iova = (uintptr_t)buf; 44 mb->buf_len = buf_len; 45 rte_mbuf_refcnt_set(mb, 1); 46 47 /* set pool pointer to dummy value, test doesn't use it */ 48 mb->pool = (void *)buf; 49 50 rte_pktmbuf_reset(mb); 51 db = (uint8_t *)rte_pktmbuf_append(mb, data_len); 52 53 for (i = 0; i != data_len; i++) 54 db[i] = i; 55 } 56 57 /* Make an IP packet consisting of chain of one packets */ 58 static void 59 mbuf1_prepare(struct dummy_mbuf *dm, uint32_t plen) 60 { 61 struct { 62 struct rte_ether_hdr eth; 63 struct rte_ipv4_hdr ip; 64 } pkt = { 65 .eth = { 66 .dst_addr.addr_bytes = "\xff\xff\xff\xff\xff\xff", 67 .ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4), 68 }, 69 .ip = { 70 .version_ihl = RTE_IPV4_VHL_DEF, 71 .total_length = rte_cpu_to_be_16(plen), 72 .time_to_live = IPDEFTTL, 73 .next_proto_id = IPPROTO_RAW, 74 .src_addr = rte_cpu_to_be_32(RTE_IPV4_LOOPBACK), 75 .dst_addr = rte_cpu_to_be_32(RTE_IPV4_BROADCAST), 76 } 77 }; 78 79 memset(dm, 0, sizeof(*dm)); 80 dummy_mbuf_prep(&dm->mb[0], dm->buf[0], sizeof(dm->buf[0]), plen); 81 82 rte_eth_random_addr(pkt.eth.src_addr.addr_bytes); 83 memcpy(rte_pktmbuf_mtod(dm->mb, void *), &pkt, RTE_MIN(sizeof(pkt), plen)); 84 } 85 86 static int 87 test_setup(void) 88 { 89 int tmp_fd; 90 91 port_id = rte_eth_find_next(0); 92 if (port_id >= RTE_MAX_ETHPORTS) { 93 fprintf(stderr, "No valid Ether port\n"); 94 return -1; 95 } 96 97 tmp_fd = mkstemps(file_name, strlen(".pcapng")); 98 if (tmp_fd == -1) { 99 perror("mkstemps() failure"); 100 return -1; 101 } 102 printf("pcapng: output file %s\n", file_name); 103 104 /* open a test capture file */ 105 pcapng = rte_pcapng_fdopen(tmp_fd, NULL, NULL, "pcapng_test", NULL); 106 if (pcapng == NULL) { 107 fprintf(stderr, "rte_pcapng_fdopen failed\n"); 108 close(tmp_fd); 109 return -1; 110 } 111 112 /* Make a pool for cloned packeets */ 113 mp = rte_pktmbuf_pool_create_by_ops("pcapng_test_pool", NUM_PACKETS, 114 0, 0, 115 rte_pcapng_mbuf_size(pkt_len), 116 SOCKET_ID_ANY, "ring_mp_sc"); 117 if (mp == NULL) { 118 fprintf(stderr, "Cannot create mempool\n"); 119 return -1; 120 } 121 return 0; 122 } 123 124 static int 125 test_write_packets(void) 126 { 127 struct rte_mbuf *orig; 128 struct rte_mbuf *clones[NUM_PACKETS] = { }; 129 struct dummy_mbuf mbfs; 130 unsigned int i; 131 ssize_t len; 132 133 /* make a dummy packet */ 134 mbuf1_prepare(&mbfs, pkt_len); 135 136 /* clone them */ 137 orig = &mbfs.mb[0]; 138 for (i = 0; i < NUM_PACKETS; i++) { 139 struct rte_mbuf *mc; 140 141 mc = rte_pcapng_copy(port_id, 0, orig, mp, pkt_len, 142 rte_get_tsc_cycles(), 0); 143 if (mc == NULL) { 144 fprintf(stderr, "Cannot copy packet\n"); 145 return -1; 146 } 147 clones[i] = mc; 148 } 149 150 /* write it to capture file */ 151 len = rte_pcapng_write_packets(pcapng, clones, NUM_PACKETS); 152 153 rte_pktmbuf_free_bulk(clones, NUM_PACKETS); 154 155 if (len <= 0) { 156 fprintf(stderr, "Write of packets failed\n"); 157 return -1; 158 } 159 160 return 0; 161 } 162 163 static int 164 test_write_stats(void) 165 { 166 ssize_t len; 167 168 /* write a statistics block */ 169 len = rte_pcapng_write_stats(pcapng, port_id, 170 NULL, 0, 0, 171 NUM_PACKETS, 0); 172 if (len <= 0) { 173 fprintf(stderr, "Write of statistics failed\n"); 174 return -1; 175 } 176 return 0; 177 } 178 179 static void 180 pkt_print(u_char *user, const struct pcap_pkthdr *h, 181 const u_char *bytes) 182 { 183 unsigned int *countp = (unsigned int *)user; 184 const struct rte_ether_hdr *eh; 185 struct tm *tm; 186 char tbuf[128], src[64], dst[64]; 187 188 tm = localtime(&h->ts.tv_sec); 189 if (tm == NULL) { 190 perror("localtime"); 191 return; 192 } 193 194 if (strftime(tbuf, sizeof(tbuf), "%X", tm) == 0) { 195 fprintf(stderr, "strftime returned 0!\n"); 196 return; 197 } 198 199 eh = (const struct rte_ether_hdr *)bytes; 200 rte_ether_format_addr(dst, sizeof(dst), &eh->dst_addr); 201 rte_ether_format_addr(src, sizeof(src), &eh->src_addr); 202 printf("%s.%06lu: %s -> %s type %x length %u\n", 203 tbuf, (unsigned long)h->ts.tv_usec, 204 src, dst, rte_be_to_cpu_16(eh->ether_type), h->len); 205 206 *countp += 1; 207 } 208 209 /* 210 * Open the resulting pcapng file with libpcap 211 * Would be better to use capinfos from wireshark 212 * but that creates an unwanted dependency. 213 */ 214 static int 215 test_validate(void) 216 { 217 char errbuf[PCAP_ERRBUF_SIZE]; 218 unsigned int count = 0; 219 pcap_t *pcap; 220 int ret; 221 222 pcap = pcap_open_offline(file_name, errbuf); 223 if (pcap == NULL) { 224 fprintf(stderr, "pcap_open_offline('%s') failed: %s\n", 225 file_name, errbuf); 226 return -1; 227 } 228 229 ret = pcap_loop(pcap, 0, pkt_print, (u_char *)&count); 230 if (ret == 0) 231 printf("Saw %u packets\n", count); 232 else 233 fprintf(stderr, "pcap_dispatch: failed: %s\n", 234 pcap_geterr(pcap)); 235 pcap_close(pcap); 236 237 return ret; 238 } 239 240 static void 241 test_cleanup(void) 242 { 243 if (mp) 244 rte_mempool_free(mp); 245 246 if (pcapng) 247 rte_pcapng_close(pcapng); 248 249 } 250 251 static struct 252 unit_test_suite test_pcapng_suite = { 253 .setup = test_setup, 254 .teardown = test_cleanup, 255 .suite_name = "Test Pcapng Unit Test Suite", 256 .unit_test_cases = { 257 TEST_CASE(test_write_packets), 258 TEST_CASE(test_write_stats), 259 TEST_CASE(test_validate), 260 TEST_CASES_END() 261 } 262 }; 263 264 static int 265 test_pcapng(void) 266 { 267 return unit_test_suite_runner(&test_pcapng_suite); 268 } 269 270 REGISTER_TEST_COMMAND(pcapng_autotest, test_pcapng); 271