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 29 graph_pcap_enable(bool val) 30 { 31 pcap_enable = val; 32 } 33 34 int 35 graph_pcap_is_enable(void) 36 { 37 return pcap_enable; 38 } 39 40 void 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 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 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 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 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 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