1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(C) 2023 Marvell International Ltd.
3 */
4
5 #include <errno.h>
6 #include <pwd.h>
7 #include <stdlib.h>
8 #include <unistd.h>
9
10 #include <rte_ethdev.h>
11 #include <rte_mbuf.h>
12 #include <rte_pcapng.h>
13
14 #include "rte_graph_worker.h"
15
16 #include "graph_pcap_private.h"
17
18 #define GRAPH_PCAP_BUF_SZ 128
19 #define GRAPH_PCAP_NUM_PACKETS 1024
20 #define GRAPH_PCAP_PKT_POOL "graph_pcap_pkt_pool"
21 #define GRAPH_PCAP_FILE_NAME "dpdk_graph_pcap_capture_XXXXXX.pcapng"
22
23 /* For multi-process, packets are captured in separate files. */
24 static rte_pcapng_t *pcapng_fd;
25 static bool pcap_enable;
26 struct rte_mempool *pkt_mp;
27
28 void
graph_pcap_enable(bool val)29 graph_pcap_enable(bool val)
30 {
31 pcap_enable = val;
32 }
33
34 int
graph_pcap_is_enable(void)35 graph_pcap_is_enable(void)
36 {
37 return pcap_enable;
38 }
39
40 void
graph_pcap_exit(struct rte_graph * graph)41 graph_pcap_exit(struct rte_graph *graph)
42 {
43 if (rte_eal_process_type() == RTE_PROC_PRIMARY)
44 rte_mempool_free(pkt_mp);
45
46 if (pcapng_fd) {
47 rte_pcapng_close(pcapng_fd);
48 pcapng_fd = NULL;
49 }
50
51 /* Disable pcap. */
52 graph->pcap_enable = 0;
53 graph_pcap_enable(0);
54 }
55
56 static int
graph_pcap_default_path_get(char ** dir_path)57 graph_pcap_default_path_get(char **dir_path)
58 {
59 struct passwd *pwd;
60 char *home_dir;
61
62 /* First check for shell environment variable */
63 home_dir = getenv("HOME");
64 if (home_dir == NULL) {
65 graph_warn("Home env not preset.");
66 /* Fallback to password file entry */
67 pwd = getpwuid(getuid());
68 if (pwd == NULL)
69 return -EINVAL;
70
71 home_dir = pwd->pw_dir;
72 }
73
74 /* Append default pcap file to directory */
75 if (asprintf(dir_path, "%s/%s", home_dir, GRAPH_PCAP_FILE_NAME) == -1)
76 return -ENOMEM;
77
78 return 0;
79 }
80
81 int
graph_pcap_file_open(const char * filename)82 graph_pcap_file_open(const char *filename)
83 {
84 int fd, ret;
85 uint16_t portid;
86 char file_name[RTE_GRAPH_PCAP_FILE_SZ];
87 char *pcap_dir;
88
89 if (pcapng_fd)
90 goto done;
91
92 if (!filename || filename[0] == '\0') {
93 if (graph_pcap_default_path_get(&pcap_dir) < 0)
94 return -1;
95 snprintf(file_name, RTE_GRAPH_PCAP_FILE_SZ, "%s", pcap_dir);
96 free(pcap_dir);
97 } else {
98 snprintf(file_name, RTE_GRAPH_PCAP_FILE_SZ, "%s_XXXXXX.pcapng",
99 filename);
100 }
101
102 fd = mkstemps(file_name, strlen(".pcapng"));
103 if (fd < 0) {
104 graph_err("mkstemps() failure");
105 return -1;
106 }
107
108 graph_info("pcap filename: %s", file_name);
109
110 /* Open a capture file */
111 pcapng_fd = rte_pcapng_fdopen(fd, NULL, NULL, "Graph pcap tracer",
112 NULL);
113 if (pcapng_fd == NULL) {
114 graph_err("Graph rte_pcapng_fdopen failed.");
115 goto error;
116 }
117
118 /* Add the configured interfaces as possible capture ports */
119 RTE_ETH_FOREACH_DEV(portid) {
120 ret = rte_pcapng_add_interface(pcapng_fd, portid,
121 NULL, NULL, NULL);
122 if (ret < 0) {
123 graph_err("Graph rte_pcapng_add_interface port %u failed: %d",
124 portid, ret);
125 goto error;
126 }
127 }
128
129 done:
130 return 0;
131 error:
132 if (pcapng_fd != NULL) {
133 rte_pcapng_close(pcapng_fd);
134 pcapng_fd = NULL;
135 }
136 close(fd);
137 return -1;
138 }
139
140 int
graph_pcap_mp_init(void)141 graph_pcap_mp_init(void)
142 {
143 pkt_mp = rte_mempool_lookup(GRAPH_PCAP_PKT_POOL);
144 if (pkt_mp)
145 goto done;
146
147 /* Make a pool for cloned packets */
148 pkt_mp = rte_pktmbuf_pool_create_by_ops(GRAPH_PCAP_PKT_POOL,
149 IOV_MAX + RTE_GRAPH_BURST_SIZE, 0, 0,
150 rte_pcapng_mbuf_size(RTE_MBUF_DEFAULT_BUF_SIZE),
151 SOCKET_ID_ANY, "ring_mp_mc");
152 if (pkt_mp == NULL) {
153 graph_err("Cannot create mempool for graph pcap capture.");
154 return -1;
155 }
156
157 done:
158 return 0;
159 }
160
161 int
graph_pcap_init(struct graph * graph)162 graph_pcap_init(struct graph *graph)
163 {
164 struct rte_graph *graph_data = graph->graph;
165
166 if (graph_pcap_file_open(graph->pcap_filename) < 0)
167 goto error;
168
169 if (graph_pcap_mp_init() < 0)
170 goto error;
171
172 /* User configured number of packets to capture. */
173 if (graph->num_pkt_to_capture)
174 graph_data->nb_pkt_to_capture = graph->num_pkt_to_capture;
175 else
176 graph_data->nb_pkt_to_capture = GRAPH_PCAP_NUM_PACKETS;
177
178 /* All good. Now populate data for secondary process. */
179 rte_strscpy(graph_data->pcap_filename, graph->pcap_filename, RTE_GRAPH_PCAP_FILE_SZ);
180 graph_data->pcap_enable = 1;
181
182 return 0;
183
184 error:
185 graph_pcap_exit(graph_data);
186 graph_pcap_enable(0);
187 graph_err("Graph pcap initialization failed. Disabling pcap trace.");
188 return -1;
189 }
190
191 uint16_t
graph_pcap_dispatch(struct rte_graph * graph,struct rte_node * node,void ** objs,uint16_t nb_objs)192 graph_pcap_dispatch(struct rte_graph *graph,
193 struct rte_node *node, void **objs,
194 uint16_t nb_objs)
195 {
196 struct rte_mbuf *mbuf_clones[RTE_GRAPH_BURST_SIZE];
197 char buffer[GRAPH_PCAP_BUF_SZ];
198 uint64_t i, num_packets;
199 struct rte_mbuf *mbuf;
200 ssize_t len;
201
202 if (!nb_objs || (graph->nb_pkt_captured >= graph->nb_pkt_to_capture))
203 goto done;
204
205 num_packets = graph->nb_pkt_to_capture - graph->nb_pkt_captured;
206 /* nb_objs will never be greater than RTE_GRAPH_BURST_SIZE */
207 if (num_packets > nb_objs)
208 num_packets = nb_objs;
209
210 snprintf(buffer, GRAPH_PCAP_BUF_SZ, "%s: %s", graph->name, node->name);
211
212 for (i = 0; i < num_packets; i++) {
213 struct rte_mbuf *mc;
214 mbuf = (struct rte_mbuf *)objs[i];
215
216 mc = rte_pcapng_copy(mbuf->port, 0, mbuf, pkt_mp, mbuf->pkt_len,
217 0, buffer);
218 if (mc == NULL)
219 break;
220
221 mbuf_clones[i] = mc;
222 }
223
224 /* write it to capture file */
225 len = rte_pcapng_write_packets(pcapng_fd, mbuf_clones, i);
226 rte_pktmbuf_free_bulk(mbuf_clones, i);
227 if (len <= 0)
228 goto done;
229
230 graph->nb_pkt_captured += i;
231
232 done:
233 return node->original_process(graph, node, objs, nb_objs);
234 }
235