test/bpf: skip test if libpcap is unavailable
[dpdk.git] / app / test / test_pcapng.c
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 packets */
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         rte_mempool_free(mp);
244
245         if (pcapng)
246                 rte_pcapng_close(pcapng);
247
248 }
249
250 static struct
251 unit_test_suite test_pcapng_suite  = {
252         .setup = test_setup,
253         .teardown = test_cleanup,
254         .suite_name = "Test Pcapng Unit Test Suite",
255         .unit_test_cases = {
256                 TEST_CASE(test_write_packets),
257                 TEST_CASE(test_write_stats),
258                 TEST_CASE(test_validate),
259                 TEST_CASES_END()
260         }
261 };
262
263 static int
264 test_pcapng(void)
265 {
266         return unit_test_suite_runner(&test_pcapng_suite);
267 }
268
269 REGISTER_TEST_COMMAND(pcapng_autotest, test_pcapng);