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