]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /*- |
2 | * BSD LICENSE | |
3 | * | |
4 | * Copyright(c) 2010-2016 Intel Corporation. All rights reserved. | |
5 | * Copyright(c) 2014 6WIND S.A. | |
6 | * All rights reserved. | |
7 | * | |
8 | * Redistribution and use in source and binary forms, with or without | |
9 | * modification, are permitted provided that the following conditions | |
10 | * are met: | |
11 | * | |
12 | * * Redistributions of source code must retain the above copyright | |
13 | * notice, this list of conditions and the following disclaimer. | |
14 | * * Redistributions in binary form must reproduce the above copyright | |
15 | * notice, this list of conditions and the following disclaimer in | |
16 | * the documentation and/or other materials provided with the | |
17 | * distribution. | |
18 | * * Neither the name of Intel Corporation nor the names of its | |
19 | * contributors may be used to endorse or promote products derived | |
20 | * from this software without specific prior written permission. | |
21 | * | |
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
23 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
24 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
25 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
26 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
27 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
28 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
30 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
31 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
32 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
33 | */ | |
34 | ||
35 | #include <time.h> | |
36 | ||
37 | #include <net/if.h> | |
38 | ||
39 | #include <pcap.h> | |
40 | ||
41 | #include <rte_cycles.h> | |
42 | #include <rte_ethdev.h> | |
43 | #include <rte_kvargs.h> | |
44 | #include <rte_malloc.h> | |
45 | #include <rte_mbuf.h> | |
46 | #include <rte_vdev.h> | |
47 | ||
48 | #define RTE_ETH_PCAP_SNAPSHOT_LEN 65535 | |
49 | #define RTE_ETH_PCAP_SNAPLEN ETHER_MAX_JUMBO_FRAME_LEN | |
50 | #define RTE_ETH_PCAP_PROMISC 1 | |
51 | #define RTE_ETH_PCAP_TIMEOUT -1 | |
52 | ||
53 | #define ETH_PCAP_RX_PCAP_ARG "rx_pcap" | |
54 | #define ETH_PCAP_TX_PCAP_ARG "tx_pcap" | |
55 | #define ETH_PCAP_RX_IFACE_ARG "rx_iface" | |
56 | #define ETH_PCAP_TX_IFACE_ARG "tx_iface" | |
57 | #define ETH_PCAP_IFACE_ARG "iface" | |
58 | ||
59 | #define ETH_PCAP_ARG_MAXLEN 64 | |
60 | ||
61 | #define RTE_PMD_PCAP_MAX_QUEUES 16 | |
62 | ||
63 | static char errbuf[PCAP_ERRBUF_SIZE]; | |
64 | static unsigned char tx_pcap_data[RTE_ETH_PCAP_SNAPLEN]; | |
65 | static struct timeval start_time; | |
66 | static uint64_t start_cycles; | |
67 | static uint64_t hz; | |
68 | ||
69 | struct queue_stat { | |
70 | volatile unsigned long pkts; | |
71 | volatile unsigned long bytes; | |
72 | volatile unsigned long err_pkts; | |
73 | }; | |
74 | ||
75 | struct pcap_rx_queue { | |
76 | pcap_t *pcap; | |
77 | uint8_t in_port; | |
78 | struct rte_mempool *mb_pool; | |
79 | struct queue_stat rx_stat; | |
80 | char name[PATH_MAX]; | |
81 | char type[ETH_PCAP_ARG_MAXLEN]; | |
82 | }; | |
83 | ||
84 | struct pcap_tx_queue { | |
85 | pcap_dumper_t *dumper; | |
86 | pcap_t *pcap; | |
87 | struct queue_stat tx_stat; | |
88 | char name[PATH_MAX]; | |
89 | char type[ETH_PCAP_ARG_MAXLEN]; | |
90 | }; | |
91 | ||
92 | struct pmd_internals { | |
93 | struct pcap_rx_queue rx_queue[RTE_PMD_PCAP_MAX_QUEUES]; | |
94 | struct pcap_tx_queue tx_queue[RTE_PMD_PCAP_MAX_QUEUES]; | |
95 | int if_index; | |
96 | int single_iface; | |
97 | }; | |
98 | ||
99 | struct pmd_devargs { | |
100 | unsigned int num_of_queue; | |
101 | struct devargs_queue { | |
102 | pcap_dumper_t *dumper; | |
103 | pcap_t *pcap; | |
104 | const char *name; | |
105 | const char *type; | |
106 | } queue[RTE_PMD_PCAP_MAX_QUEUES]; | |
107 | }; | |
108 | ||
109 | static const char *valid_arguments[] = { | |
110 | ETH_PCAP_RX_PCAP_ARG, | |
111 | ETH_PCAP_TX_PCAP_ARG, | |
112 | ETH_PCAP_RX_IFACE_ARG, | |
113 | ETH_PCAP_TX_IFACE_ARG, | |
114 | ETH_PCAP_IFACE_ARG, | |
115 | NULL | |
116 | }; | |
117 | ||
118 | static struct ether_addr eth_addr = { | |
119 | .addr_bytes = { 0, 0, 0, 0x1, 0x2, 0x3 } | |
120 | }; | |
121 | ||
122 | static const char *drivername = "Pcap PMD"; | |
123 | static struct rte_eth_link pmd_link = { | |
124 | .link_speed = ETH_SPEED_NUM_10G, | |
125 | .link_duplex = ETH_LINK_FULL_DUPLEX, | |
126 | .link_status = ETH_LINK_DOWN, | |
127 | .link_autoneg = ETH_LINK_SPEED_FIXED, | |
128 | }; | |
129 | ||
130 | static int | |
131 | eth_pcap_rx_jumbo(struct rte_mempool *mb_pool, struct rte_mbuf *mbuf, | |
132 | const u_char *data, uint16_t data_len) | |
133 | { | |
134 | /* Copy the first segment. */ | |
135 | uint16_t len = rte_pktmbuf_tailroom(mbuf); | |
136 | struct rte_mbuf *m = mbuf; | |
137 | ||
138 | rte_memcpy(rte_pktmbuf_append(mbuf, len), data, len); | |
139 | data_len -= len; | |
140 | data += len; | |
141 | ||
142 | while (data_len > 0) { | |
143 | /* Allocate next mbuf and point to that. */ | |
144 | m->next = rte_pktmbuf_alloc(mb_pool); | |
145 | ||
146 | if (unlikely(!m->next)) | |
147 | return -1; | |
148 | ||
149 | m = m->next; | |
150 | ||
151 | /* Headroom is not needed in chained mbufs. */ | |
152 | rte_pktmbuf_prepend(m, rte_pktmbuf_headroom(m)); | |
153 | m->pkt_len = 0; | |
154 | m->data_len = 0; | |
155 | ||
156 | /* Copy next segment. */ | |
157 | len = RTE_MIN(rte_pktmbuf_tailroom(m), data_len); | |
158 | rte_memcpy(rte_pktmbuf_append(m, len), data, len); | |
159 | ||
160 | mbuf->nb_segs++; | |
161 | data_len -= len; | |
162 | data += len; | |
163 | } | |
164 | ||
165 | return mbuf->nb_segs; | |
166 | } | |
167 | ||
168 | /* Copy data from mbuf chain to a buffer suitable for writing to a PCAP file. */ | |
169 | static void | |
170 | eth_pcap_gather_data(unsigned char *data, struct rte_mbuf *mbuf) | |
171 | { | |
172 | uint16_t data_len = 0; | |
173 | ||
174 | while (mbuf) { | |
175 | rte_memcpy(data + data_len, rte_pktmbuf_mtod(mbuf, void *), | |
176 | mbuf->data_len); | |
177 | ||
178 | data_len += mbuf->data_len; | |
179 | mbuf = mbuf->next; | |
180 | } | |
181 | } | |
182 | ||
183 | static uint16_t | |
184 | eth_pcap_rx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) | |
185 | { | |
186 | unsigned int i; | |
187 | struct pcap_pkthdr header; | |
188 | const u_char *packet; | |
189 | struct rte_mbuf *mbuf; | |
190 | struct pcap_rx_queue *pcap_q = queue; | |
191 | uint16_t num_rx = 0; | |
192 | uint16_t buf_size; | |
193 | uint32_t rx_bytes = 0; | |
194 | ||
195 | if (unlikely(pcap_q->pcap == NULL || nb_pkts == 0)) | |
196 | return 0; | |
197 | ||
198 | /* Reads the given number of packets from the pcap file one by one | |
199 | * and copies the packet data into a newly allocated mbuf to return. | |
200 | */ | |
201 | for (i = 0; i < nb_pkts; i++) { | |
202 | /* Get the next PCAP packet */ | |
203 | packet = pcap_next(pcap_q->pcap, &header); | |
204 | if (unlikely(packet == NULL)) | |
205 | break; | |
206 | ||
207 | mbuf = rte_pktmbuf_alloc(pcap_q->mb_pool); | |
208 | if (unlikely(mbuf == NULL)) | |
209 | break; | |
210 | ||
211 | /* Now get the space available for data in the mbuf */ | |
212 | buf_size = rte_pktmbuf_data_room_size(pcap_q->mb_pool) - | |
213 | RTE_PKTMBUF_HEADROOM; | |
214 | ||
215 | if (header.caplen <= buf_size) { | |
216 | /* pcap packet will fit in the mbuf, can copy it */ | |
217 | rte_memcpy(rte_pktmbuf_mtod(mbuf, void *), packet, | |
218 | header.caplen); | |
219 | mbuf->data_len = (uint16_t)header.caplen; | |
220 | } else { | |
221 | /* Try read jumbo frame into multi mbufs. */ | |
222 | if (unlikely(eth_pcap_rx_jumbo(pcap_q->mb_pool, | |
223 | mbuf, | |
224 | packet, | |
225 | header.caplen) == -1)) { | |
226 | rte_pktmbuf_free(mbuf); | |
227 | break; | |
228 | } | |
229 | } | |
230 | ||
231 | mbuf->pkt_len = (uint16_t)header.caplen; | |
232 | mbuf->port = pcap_q->in_port; | |
233 | bufs[num_rx] = mbuf; | |
234 | num_rx++; | |
235 | rx_bytes += header.caplen; | |
236 | } | |
237 | pcap_q->rx_stat.pkts += num_rx; | |
238 | pcap_q->rx_stat.bytes += rx_bytes; | |
239 | ||
240 | return num_rx; | |
241 | } | |
242 | ||
243 | static inline void | |
244 | calculate_timestamp(struct timeval *ts) { | |
245 | uint64_t cycles; | |
246 | struct timeval cur_time; | |
247 | ||
248 | cycles = rte_get_timer_cycles() - start_cycles; | |
249 | cur_time.tv_sec = cycles / hz; | |
250 | cur_time.tv_usec = (cycles % hz) * 10e6 / hz; | |
251 | timeradd(&start_time, &cur_time, ts); | |
252 | } | |
253 | ||
254 | /* | |
255 | * Callback to handle writing packets to a pcap file. | |
256 | */ | |
257 | static uint16_t | |
258 | eth_pcap_tx_dumper(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) | |
259 | { | |
260 | unsigned int i; | |
261 | struct rte_mbuf *mbuf; | |
262 | struct pcap_tx_queue *dumper_q = queue; | |
263 | uint16_t num_tx = 0; | |
264 | uint32_t tx_bytes = 0; | |
265 | struct pcap_pkthdr header; | |
266 | ||
267 | if (dumper_q->dumper == NULL || nb_pkts == 0) | |
268 | return 0; | |
269 | ||
270 | /* writes the nb_pkts packets to the previously opened pcap file | |
271 | * dumper */ | |
272 | for (i = 0; i < nb_pkts; i++) { | |
273 | mbuf = bufs[i]; | |
274 | calculate_timestamp(&header.ts); | |
275 | header.len = mbuf->pkt_len; | |
276 | header.caplen = header.len; | |
277 | ||
278 | if (likely(mbuf->nb_segs == 1)) { | |
279 | pcap_dump((u_char *)dumper_q->dumper, &header, | |
280 | rte_pktmbuf_mtod(mbuf, void*)); | |
281 | } else { | |
282 | if (mbuf->pkt_len <= ETHER_MAX_JUMBO_FRAME_LEN) { | |
283 | eth_pcap_gather_data(tx_pcap_data, mbuf); | |
284 | pcap_dump((u_char *)dumper_q->dumper, &header, | |
285 | tx_pcap_data); | |
286 | } else { | |
287 | RTE_LOG(ERR, PMD, | |
288 | "Dropping PCAP packet. Size (%d) > max jumbo size (%d).\n", | |
289 | mbuf->pkt_len, | |
290 | ETHER_MAX_JUMBO_FRAME_LEN); | |
291 | ||
292 | rte_pktmbuf_free(mbuf); | |
293 | break; | |
294 | } | |
295 | } | |
296 | ||
297 | rte_pktmbuf_free(mbuf); | |
298 | num_tx++; | |
299 | tx_bytes += mbuf->pkt_len; | |
300 | } | |
301 | ||
302 | /* | |
303 | * Since there's no place to hook a callback when the forwarding | |
304 | * process stops and to make sure the pcap file is actually written, | |
305 | * we flush the pcap dumper within each burst. | |
306 | */ | |
307 | pcap_dump_flush(dumper_q->dumper); | |
308 | dumper_q->tx_stat.pkts += num_tx; | |
309 | dumper_q->tx_stat.bytes += tx_bytes; | |
310 | dumper_q->tx_stat.err_pkts += nb_pkts - num_tx; | |
311 | ||
312 | return num_tx; | |
313 | } | |
314 | ||
315 | /* | |
316 | * Callback to handle sending packets through a real NIC. | |
317 | */ | |
318 | static uint16_t | |
319 | eth_pcap_tx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) | |
320 | { | |
321 | unsigned int i; | |
322 | int ret; | |
323 | struct rte_mbuf *mbuf; | |
324 | struct pcap_tx_queue *tx_queue = queue; | |
325 | uint16_t num_tx = 0; | |
326 | uint32_t tx_bytes = 0; | |
327 | ||
328 | if (unlikely(nb_pkts == 0 || tx_queue->pcap == NULL)) | |
329 | return 0; | |
330 | ||
331 | for (i = 0; i < nb_pkts; i++) { | |
332 | mbuf = bufs[i]; | |
333 | ||
334 | if (likely(mbuf->nb_segs == 1)) { | |
335 | ret = pcap_sendpacket(tx_queue->pcap, | |
336 | rte_pktmbuf_mtod(mbuf, u_char *), | |
337 | mbuf->pkt_len); | |
338 | } else { | |
339 | if (mbuf->pkt_len <= ETHER_MAX_JUMBO_FRAME_LEN) { | |
340 | eth_pcap_gather_data(tx_pcap_data, mbuf); | |
341 | ret = pcap_sendpacket(tx_queue->pcap, | |
342 | tx_pcap_data, mbuf->pkt_len); | |
343 | } else { | |
344 | RTE_LOG(ERR, PMD, | |
345 | "Dropping PCAP packet. Size (%d) > max jumbo size (%d).\n", | |
346 | mbuf->pkt_len, | |
347 | ETHER_MAX_JUMBO_FRAME_LEN); | |
348 | ||
349 | rte_pktmbuf_free(mbuf); | |
350 | break; | |
351 | } | |
352 | } | |
353 | ||
354 | if (unlikely(ret != 0)) | |
355 | break; | |
356 | num_tx++; | |
357 | tx_bytes += mbuf->pkt_len; | |
358 | rte_pktmbuf_free(mbuf); | |
359 | } | |
360 | ||
361 | tx_queue->tx_stat.pkts += num_tx; | |
362 | tx_queue->tx_stat.bytes += tx_bytes; | |
363 | tx_queue->tx_stat.err_pkts += nb_pkts - num_tx; | |
364 | ||
365 | return num_tx; | |
366 | } | |
367 | ||
368 | /* | |
369 | * pcap_open_live wrapper function | |
370 | */ | |
371 | static inline int | |
372 | open_iface_live(const char *iface, pcap_t **pcap) { | |
373 | *pcap = pcap_open_live(iface, RTE_ETH_PCAP_SNAPLEN, | |
374 | RTE_ETH_PCAP_PROMISC, RTE_ETH_PCAP_TIMEOUT, errbuf); | |
375 | ||
376 | if (*pcap == NULL) { | |
377 | RTE_LOG(ERR, PMD, "Couldn't open %s: %s\n", iface, errbuf); | |
378 | return -1; | |
379 | } | |
380 | ||
381 | return 0; | |
382 | } | |
383 | ||
384 | static int | |
385 | open_single_iface(const char *iface, pcap_t **pcap) | |
386 | { | |
387 | if (open_iface_live(iface, pcap) < 0) { | |
388 | RTE_LOG(ERR, PMD, "Couldn't open interface %s\n", iface); | |
389 | return -1; | |
390 | } | |
391 | ||
392 | return 0; | |
393 | } | |
394 | ||
395 | static int | |
396 | open_single_tx_pcap(const char *pcap_filename, pcap_dumper_t **dumper) | |
397 | { | |
398 | pcap_t *tx_pcap; | |
399 | ||
400 | /* | |
401 | * We need to create a dummy empty pcap_t to use it | |
402 | * with pcap_dump_open(). We create big enough an Ethernet | |
403 | * pcap holder. | |
404 | */ | |
405 | tx_pcap = pcap_open_dead(DLT_EN10MB, RTE_ETH_PCAP_SNAPSHOT_LEN); | |
406 | if (tx_pcap == NULL) { | |
407 | RTE_LOG(ERR, PMD, "Couldn't create dead pcap\n"); | |
408 | return -1; | |
409 | } | |
410 | ||
411 | /* The dumper is created using the previous pcap_t reference */ | |
412 | *dumper = pcap_dump_open(tx_pcap, pcap_filename); | |
413 | if (*dumper == NULL) { | |
414 | RTE_LOG(ERR, PMD, "Couldn't open %s for writing.\n", | |
415 | pcap_filename); | |
416 | return -1; | |
417 | } | |
418 | ||
419 | return 0; | |
420 | } | |
421 | ||
422 | static int | |
423 | open_single_rx_pcap(const char *pcap_filename, pcap_t **pcap) | |
424 | { | |
425 | *pcap = pcap_open_offline(pcap_filename, errbuf); | |
426 | if (*pcap == NULL) { | |
427 | RTE_LOG(ERR, PMD, "Couldn't open %s: %s\n", pcap_filename, | |
428 | errbuf); | |
429 | return -1; | |
430 | } | |
431 | ||
432 | return 0; | |
433 | } | |
434 | ||
435 | static int | |
436 | eth_dev_start(struct rte_eth_dev *dev) | |
437 | { | |
438 | unsigned int i; | |
439 | struct pmd_internals *internals = dev->data->dev_private; | |
440 | struct pcap_tx_queue *tx; | |
441 | struct pcap_rx_queue *rx; | |
442 | ||
443 | /* Special iface case. Single pcap is open and shared between tx/rx. */ | |
444 | if (internals->single_iface) { | |
445 | tx = &internals->tx_queue[0]; | |
446 | rx = &internals->rx_queue[0]; | |
447 | ||
448 | if (!tx->pcap && strcmp(tx->type, ETH_PCAP_IFACE_ARG) == 0) { | |
449 | if (open_single_iface(tx->name, &tx->pcap) < 0) | |
450 | return -1; | |
451 | rx->pcap = tx->pcap; | |
452 | } | |
453 | goto status_up; | |
454 | } | |
455 | ||
456 | /* If not open already, open tx pcaps/dumpers */ | |
457 | for (i = 0; i < dev->data->nb_tx_queues; i++) { | |
458 | tx = &internals->tx_queue[i]; | |
459 | ||
460 | if (!tx->dumper && | |
461 | strcmp(tx->type, ETH_PCAP_TX_PCAP_ARG) == 0) { | |
462 | if (open_single_tx_pcap(tx->name, &tx->dumper) < 0) | |
463 | return -1; | |
464 | } else if (!tx->pcap && | |
465 | strcmp(tx->type, ETH_PCAP_TX_IFACE_ARG) == 0) { | |
466 | if (open_single_iface(tx->name, &tx->pcap) < 0) | |
467 | return -1; | |
468 | } | |
469 | } | |
470 | ||
471 | /* If not open already, open rx pcaps */ | |
472 | for (i = 0; i < dev->data->nb_rx_queues; i++) { | |
473 | rx = &internals->rx_queue[i]; | |
474 | ||
475 | if (rx->pcap != NULL) | |
476 | continue; | |
477 | ||
478 | if (strcmp(rx->type, ETH_PCAP_RX_PCAP_ARG) == 0) { | |
479 | if (open_single_rx_pcap(rx->name, &rx->pcap) < 0) | |
480 | return -1; | |
481 | } else if (strcmp(rx->type, ETH_PCAP_RX_IFACE_ARG) == 0) { | |
482 | if (open_single_iface(rx->name, &rx->pcap) < 0) | |
483 | return -1; | |
484 | } | |
485 | } | |
486 | ||
487 | status_up: | |
488 | dev->data->dev_link.link_status = ETH_LINK_UP; | |
489 | ||
490 | return 0; | |
491 | } | |
492 | ||
493 | /* | |
494 | * This function gets called when the current port gets stopped. | |
495 | * Is the only place for us to close all the tx streams dumpers. | |
496 | * If not called the dumpers will be flushed within each tx burst. | |
497 | */ | |
498 | static void | |
499 | eth_dev_stop(struct rte_eth_dev *dev) | |
500 | { | |
501 | unsigned int i; | |
502 | struct pmd_internals *internals = dev->data->dev_private; | |
503 | struct pcap_tx_queue *tx; | |
504 | struct pcap_rx_queue *rx; | |
505 | ||
506 | /* Special iface case. Single pcap is open and shared between tx/rx. */ | |
507 | if (internals->single_iface) { | |
508 | tx = &internals->tx_queue[0]; | |
509 | rx = &internals->rx_queue[0]; | |
510 | pcap_close(tx->pcap); | |
511 | tx->pcap = NULL; | |
512 | rx->pcap = NULL; | |
513 | goto status_down; | |
514 | } | |
515 | ||
516 | for (i = 0; i < dev->data->nb_tx_queues; i++) { | |
517 | tx = &internals->tx_queue[i]; | |
518 | ||
519 | if (tx->dumper != NULL) { | |
520 | pcap_dump_close(tx->dumper); | |
521 | tx->dumper = NULL; | |
522 | } | |
523 | ||
524 | if (tx->pcap != NULL) { | |
525 | pcap_close(tx->pcap); | |
526 | tx->pcap = NULL; | |
527 | } | |
528 | } | |
529 | ||
530 | for (i = 0; i < dev->data->nb_rx_queues; i++) { | |
531 | rx = &internals->rx_queue[i]; | |
532 | ||
533 | if (rx->pcap != NULL) { | |
534 | pcap_close(rx->pcap); | |
535 | rx->pcap = NULL; | |
536 | } | |
537 | } | |
538 | ||
539 | status_down: | |
540 | dev->data->dev_link.link_status = ETH_LINK_DOWN; | |
541 | } | |
542 | ||
543 | static int | |
544 | eth_dev_configure(struct rte_eth_dev *dev __rte_unused) | |
545 | { | |
546 | return 0; | |
547 | } | |
548 | ||
549 | static void | |
550 | eth_dev_info(struct rte_eth_dev *dev, | |
551 | struct rte_eth_dev_info *dev_info) | |
552 | { | |
553 | struct pmd_internals *internals = dev->data->dev_private; | |
554 | ||
555 | dev_info->driver_name = drivername; | |
556 | dev_info->if_index = internals->if_index; | |
557 | dev_info->max_mac_addrs = 1; | |
558 | dev_info->max_rx_pktlen = (uint32_t) -1; | |
559 | dev_info->max_rx_queues = dev->data->nb_rx_queues; | |
560 | dev_info->max_tx_queues = dev->data->nb_tx_queues; | |
561 | dev_info->min_rx_bufsize = 0; | |
562 | dev_info->pci_dev = NULL; | |
563 | } | |
564 | ||
565 | static void | |
566 | eth_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) | |
567 | { | |
568 | unsigned int i; | |
569 | unsigned long rx_packets_total = 0, rx_bytes_total = 0; | |
570 | unsigned long tx_packets_total = 0, tx_bytes_total = 0; | |
571 | unsigned long tx_packets_err_total = 0; | |
572 | const struct pmd_internals *internal = dev->data->dev_private; | |
573 | ||
574 | for (i = 0; i < RTE_ETHDEV_QUEUE_STAT_CNTRS && | |
575 | i < dev->data->nb_rx_queues; i++) { | |
576 | stats->q_ipackets[i] = internal->rx_queue[i].rx_stat.pkts; | |
577 | stats->q_ibytes[i] = internal->rx_queue[i].rx_stat.bytes; | |
578 | rx_packets_total += stats->q_ipackets[i]; | |
579 | rx_bytes_total += stats->q_ibytes[i]; | |
580 | } | |
581 | ||
582 | for (i = 0; i < RTE_ETHDEV_QUEUE_STAT_CNTRS && | |
583 | i < dev->data->nb_tx_queues; i++) { | |
584 | stats->q_opackets[i] = internal->tx_queue[i].tx_stat.pkts; | |
585 | stats->q_obytes[i] = internal->tx_queue[i].tx_stat.bytes; | |
586 | stats->q_errors[i] = internal->tx_queue[i].tx_stat.err_pkts; | |
587 | tx_packets_total += stats->q_opackets[i]; | |
588 | tx_bytes_total += stats->q_obytes[i]; | |
589 | tx_packets_err_total += stats->q_errors[i]; | |
590 | } | |
591 | ||
592 | stats->ipackets = rx_packets_total; | |
593 | stats->ibytes = rx_bytes_total; | |
594 | stats->opackets = tx_packets_total; | |
595 | stats->obytes = tx_bytes_total; | |
596 | stats->oerrors = tx_packets_err_total; | |
597 | } | |
598 | ||
599 | static void | |
600 | eth_stats_reset(struct rte_eth_dev *dev) | |
601 | { | |
602 | unsigned int i; | |
603 | struct pmd_internals *internal = dev->data->dev_private; | |
604 | ||
605 | for (i = 0; i < dev->data->nb_rx_queues; i++) { | |
606 | internal->rx_queue[i].rx_stat.pkts = 0; | |
607 | internal->rx_queue[i].rx_stat.bytes = 0; | |
608 | } | |
609 | ||
610 | for (i = 0; i < dev->data->nb_tx_queues; i++) { | |
611 | internal->tx_queue[i].tx_stat.pkts = 0; | |
612 | internal->tx_queue[i].tx_stat.bytes = 0; | |
613 | internal->tx_queue[i].tx_stat.err_pkts = 0; | |
614 | } | |
615 | } | |
616 | ||
617 | static void | |
618 | eth_dev_close(struct rte_eth_dev *dev __rte_unused) | |
619 | { | |
620 | } | |
621 | ||
622 | static void | |
623 | eth_queue_release(void *q __rte_unused) | |
624 | { | |
625 | } | |
626 | ||
627 | static int | |
628 | eth_link_update(struct rte_eth_dev *dev __rte_unused, | |
629 | int wait_to_complete __rte_unused) | |
630 | { | |
631 | return 0; | |
632 | } | |
633 | ||
634 | static int | |
635 | eth_rx_queue_setup(struct rte_eth_dev *dev, | |
636 | uint16_t rx_queue_id, | |
637 | uint16_t nb_rx_desc __rte_unused, | |
638 | unsigned int socket_id __rte_unused, | |
639 | const struct rte_eth_rxconf *rx_conf __rte_unused, | |
640 | struct rte_mempool *mb_pool) | |
641 | { | |
642 | struct pmd_internals *internals = dev->data->dev_private; | |
643 | struct pcap_rx_queue *pcap_q = &internals->rx_queue[rx_queue_id]; | |
644 | ||
645 | pcap_q->mb_pool = mb_pool; | |
646 | dev->data->rx_queues[rx_queue_id] = pcap_q; | |
647 | pcap_q->in_port = dev->data->port_id; | |
648 | ||
649 | return 0; | |
650 | } | |
651 | ||
652 | static int | |
653 | eth_tx_queue_setup(struct rte_eth_dev *dev, | |
654 | uint16_t tx_queue_id, | |
655 | uint16_t nb_tx_desc __rte_unused, | |
656 | unsigned int socket_id __rte_unused, | |
657 | const struct rte_eth_txconf *tx_conf __rte_unused) | |
658 | { | |
659 | struct pmd_internals *internals = dev->data->dev_private; | |
660 | ||
661 | dev->data->tx_queues[tx_queue_id] = &internals->tx_queue[tx_queue_id]; | |
662 | ||
663 | return 0; | |
664 | } | |
665 | ||
666 | static const struct eth_dev_ops ops = { | |
667 | .dev_start = eth_dev_start, | |
668 | .dev_stop = eth_dev_stop, | |
669 | .dev_close = eth_dev_close, | |
670 | .dev_configure = eth_dev_configure, | |
671 | .dev_infos_get = eth_dev_info, | |
672 | .rx_queue_setup = eth_rx_queue_setup, | |
673 | .tx_queue_setup = eth_tx_queue_setup, | |
674 | .rx_queue_release = eth_queue_release, | |
675 | .tx_queue_release = eth_queue_release, | |
676 | .link_update = eth_link_update, | |
677 | .stats_get = eth_stats_get, | |
678 | .stats_reset = eth_stats_reset, | |
679 | }; | |
680 | ||
681 | /* | |
682 | * Function handler that opens the pcap file for reading a stores a | |
683 | * reference of it for use it later on. | |
684 | */ | |
685 | static int | |
686 | open_rx_pcap(const char *key, const char *value, void *extra_args) | |
687 | { | |
688 | unsigned int i; | |
689 | const char *pcap_filename = value; | |
690 | struct pmd_devargs *rx = extra_args; | |
691 | pcap_t *pcap = NULL; | |
692 | ||
693 | for (i = 0; i < rx->num_of_queue; i++) { | |
694 | if (open_single_rx_pcap(pcap_filename, &pcap) < 0) | |
695 | return -1; | |
696 | ||
697 | rx->queue[i].pcap = pcap; | |
698 | rx->queue[i].name = pcap_filename; | |
699 | rx->queue[i].type = key; | |
700 | } | |
701 | ||
702 | return 0; | |
703 | } | |
704 | ||
705 | /* | |
706 | * Opens a pcap file for writing and stores a reference to it | |
707 | * for use it later on. | |
708 | */ | |
709 | static int | |
710 | open_tx_pcap(const char *key, const char *value, void *extra_args) | |
711 | { | |
712 | unsigned int i; | |
713 | const char *pcap_filename = value; | |
714 | struct pmd_devargs *dumpers = extra_args; | |
715 | pcap_dumper_t *dumper; | |
716 | ||
717 | for (i = 0; i < dumpers->num_of_queue; i++) { | |
718 | if (open_single_tx_pcap(pcap_filename, &dumper) < 0) | |
719 | return -1; | |
720 | ||
721 | dumpers->queue[i].dumper = dumper; | |
722 | dumpers->queue[i].name = pcap_filename; | |
723 | dumpers->queue[i].type = key; | |
724 | } | |
725 | ||
726 | return 0; | |
727 | } | |
728 | ||
729 | /* | |
730 | * Opens an interface for reading and writing | |
731 | */ | |
732 | static inline int | |
733 | open_rx_tx_iface(const char *key, const char *value, void *extra_args) | |
734 | { | |
735 | const char *iface = value; | |
736 | struct pmd_devargs *tx = extra_args; | |
737 | pcap_t *pcap = NULL; | |
738 | ||
739 | if (open_single_iface(iface, &pcap) < 0) | |
740 | return -1; | |
741 | ||
742 | tx->queue[0].pcap = pcap; | |
743 | tx->queue[0].name = iface; | |
744 | tx->queue[0].type = key; | |
745 | ||
746 | return 0; | |
747 | } | |
748 | ||
749 | /* | |
750 | * Opens a NIC for reading packets from it | |
751 | */ | |
752 | static inline int | |
753 | open_rx_iface(const char *key, const char *value, void *extra_args) | |
754 | { | |
755 | unsigned int i; | |
756 | const char *iface = value; | |
757 | struct pmd_devargs *rx = extra_args; | |
758 | pcap_t *pcap = NULL; | |
759 | ||
760 | for (i = 0; i < rx->num_of_queue; i++) { | |
761 | if (open_single_iface(iface, &pcap) < 0) | |
762 | return -1; | |
763 | rx->queue[i].pcap = pcap; | |
764 | rx->queue[i].name = iface; | |
765 | rx->queue[i].type = key; | |
766 | } | |
767 | ||
768 | return 0; | |
769 | } | |
770 | ||
771 | /* | |
772 | * Opens a NIC for writing packets to it | |
773 | */ | |
774 | static int | |
775 | open_tx_iface(const char *key, const char *value, void *extra_args) | |
776 | { | |
777 | unsigned int i; | |
778 | const char *iface = value; | |
779 | struct pmd_devargs *tx = extra_args; | |
780 | pcap_t *pcap; | |
781 | ||
782 | for (i = 0; i < tx->num_of_queue; i++) { | |
783 | if (open_single_iface(iface, &pcap) < 0) | |
784 | return -1; | |
785 | tx->queue[i].pcap = pcap; | |
786 | tx->queue[i].name = iface; | |
787 | tx->queue[i].type = key; | |
788 | } | |
789 | ||
790 | return 0; | |
791 | } | |
792 | ||
793 | static int | |
794 | pmd_init_internals(const char *name, const unsigned int nb_rx_queues, | |
795 | const unsigned int nb_tx_queues, | |
796 | struct pmd_internals **internals, | |
797 | struct rte_eth_dev **eth_dev) | |
798 | { | |
799 | struct rte_eth_dev_data *data = NULL; | |
800 | unsigned int numa_node = rte_socket_id(); | |
801 | ||
802 | RTE_LOG(INFO, PMD, "Creating pcap-backed ethdev on numa socket %u\n", | |
803 | numa_node); | |
804 | ||
805 | /* now do all data allocation - for eth_dev structure | |
806 | * and internal (private) data | |
807 | */ | |
808 | data = rte_zmalloc_socket(name, sizeof(*data), 0, numa_node); | |
809 | if (data == NULL) | |
810 | goto error; | |
811 | ||
812 | *internals = rte_zmalloc_socket(name, sizeof(**internals), 0, | |
813 | numa_node); | |
814 | if (*internals == NULL) | |
815 | goto error; | |
816 | ||
817 | /* reserve an ethdev entry */ | |
818 | *eth_dev = rte_eth_dev_allocate(name); | |
819 | if (*eth_dev == NULL) | |
820 | goto error; | |
821 | ||
822 | /* now put it all together | |
823 | * - store queue data in internals, | |
824 | * - store numa_node info in eth_dev | |
825 | * - point eth_dev_data to internals | |
826 | * - and point eth_dev structure to new eth_dev_data structure | |
827 | */ | |
828 | data->dev_private = *internals; | |
829 | data->port_id = (*eth_dev)->data->port_id; | |
830 | snprintf(data->name, sizeof(data->name), "%s", (*eth_dev)->data->name); | |
831 | data->nb_rx_queues = (uint16_t)nb_rx_queues; | |
832 | data->nb_tx_queues = (uint16_t)nb_tx_queues; | |
833 | data->dev_link = pmd_link; | |
834 | data->mac_addrs = ð_addr; | |
835 | ||
836 | /* | |
837 | * NOTE: we'll replace the data element, of originally allocated | |
838 | * eth_dev so the rings are local per-process | |
839 | */ | |
840 | (*eth_dev)->data = data; | |
841 | (*eth_dev)->dev_ops = &ops; | |
842 | (*eth_dev)->driver = NULL; | |
843 | data->dev_flags = RTE_ETH_DEV_DETACHABLE; | |
844 | data->kdrv = RTE_KDRV_NONE; | |
845 | data->drv_name = drivername; | |
846 | data->numa_node = numa_node; | |
847 | ||
848 | return 0; | |
849 | ||
850 | error: | |
851 | rte_free(data); | |
852 | rte_free(*internals); | |
853 | ||
854 | return -1; | |
855 | } | |
856 | ||
857 | static int | |
858 | eth_from_pcaps_common(const char *name, struct pmd_devargs *rx_queues, | |
859 | const unsigned int nb_rx_queues, struct pmd_devargs *tx_queues, | |
860 | const unsigned int nb_tx_queues, struct rte_kvargs *kvlist, | |
861 | struct pmd_internals **internals, struct rte_eth_dev **eth_dev) | |
862 | { | |
863 | struct rte_kvargs_pair *pair = NULL; | |
864 | unsigned int k_idx; | |
865 | unsigned int i; | |
866 | ||
867 | /* do some parameter checking */ | |
868 | if (rx_queues == NULL && nb_rx_queues > 0) | |
869 | return -1; | |
870 | if (tx_queues == NULL && nb_tx_queues > 0) | |
871 | return -1; | |
872 | ||
873 | if (pmd_init_internals(name, nb_rx_queues, nb_tx_queues, internals, | |
874 | eth_dev) < 0) | |
875 | return -1; | |
876 | ||
877 | for (i = 0; i < nb_rx_queues; i++) { | |
878 | struct pcap_rx_queue *rx = &(*internals)->rx_queue[i]; | |
879 | struct devargs_queue *queue = &rx_queues->queue[i]; | |
880 | ||
881 | rx->pcap = queue->pcap; | |
882 | snprintf(rx->name, sizeof(rx->name), "%s", queue->name); | |
883 | snprintf(rx->type, sizeof(rx->type), "%s", queue->type); | |
884 | } | |
885 | ||
886 | for (i = 0; i < nb_tx_queues; i++) { | |
887 | struct pcap_tx_queue *tx = &(*internals)->tx_queue[i]; | |
888 | struct devargs_queue *queue = &tx_queues->queue[i]; | |
889 | ||
890 | tx->dumper = queue->dumper; | |
891 | tx->pcap = queue->pcap; | |
892 | snprintf(tx->name, sizeof(tx->name), "%s", queue->name); | |
893 | snprintf(tx->type, sizeof(tx->type), "%s", queue->type); | |
894 | } | |
895 | ||
896 | for (k_idx = 0; k_idx < kvlist->count; k_idx++) { | |
897 | pair = &kvlist->pairs[k_idx]; | |
898 | if (strstr(pair->key, ETH_PCAP_IFACE_ARG) != NULL) | |
899 | break; | |
900 | } | |
901 | ||
902 | if (pair == NULL) | |
903 | (*internals)->if_index = 0; | |
904 | else | |
905 | (*internals)->if_index = if_nametoindex(pair->value); | |
906 | ||
907 | return 0; | |
908 | } | |
909 | ||
910 | static int | |
911 | eth_from_pcaps(const char *name, struct pmd_devargs *rx_queues, | |
912 | const unsigned int nb_rx_queues, struct pmd_devargs *tx_queues, | |
913 | const unsigned int nb_tx_queues, struct rte_kvargs *kvlist, | |
914 | int single_iface, unsigned int using_dumpers) | |
915 | { | |
916 | struct pmd_internals *internals = NULL; | |
917 | struct rte_eth_dev *eth_dev = NULL; | |
918 | int ret; | |
919 | ||
920 | ret = eth_from_pcaps_common(name, rx_queues, nb_rx_queues, | |
921 | tx_queues, nb_tx_queues, kvlist, &internals, ð_dev); | |
922 | ||
923 | if (ret < 0) | |
924 | return ret; | |
925 | ||
926 | /* store weather we are using a single interface for rx/tx or not */ | |
927 | internals->single_iface = single_iface; | |
928 | ||
929 | eth_dev->rx_pkt_burst = eth_pcap_rx; | |
930 | ||
931 | if (using_dumpers) | |
932 | eth_dev->tx_pkt_burst = eth_pcap_tx_dumper; | |
933 | else | |
934 | eth_dev->tx_pkt_burst = eth_pcap_tx; | |
935 | ||
936 | return 0; | |
937 | } | |
938 | ||
939 | static int | |
940 | pmd_pcap_probe(const char *name, const char *params) | |
941 | { | |
942 | unsigned int is_rx_pcap = 0, is_tx_pcap = 0; | |
943 | struct rte_kvargs *kvlist; | |
944 | struct pmd_devargs pcaps = {0}; | |
945 | struct pmd_devargs dumpers = {0}; | |
946 | int single_iface = 0; | |
947 | int ret; | |
948 | ||
949 | RTE_LOG(INFO, PMD, "Initializing pmd_pcap for %s\n", name); | |
950 | ||
951 | gettimeofday(&start_time, NULL); | |
952 | start_cycles = rte_get_timer_cycles(); | |
953 | hz = rte_get_timer_hz(); | |
954 | ||
955 | kvlist = rte_kvargs_parse(params, valid_arguments); | |
956 | if (kvlist == NULL) | |
957 | return -1; | |
958 | ||
959 | /* | |
960 | * If iface argument is passed we open the NICs and use them for | |
961 | * reading / writing | |
962 | */ | |
963 | if (rte_kvargs_count(kvlist, ETH_PCAP_IFACE_ARG) == 1) { | |
964 | ||
965 | ret = rte_kvargs_process(kvlist, ETH_PCAP_IFACE_ARG, | |
966 | &open_rx_tx_iface, &pcaps); | |
967 | ||
968 | if (ret < 0) | |
969 | goto free_kvlist; | |
970 | ||
971 | dumpers.queue[0] = pcaps.queue[0]; | |
972 | ||
973 | single_iface = 1; | |
974 | pcaps.num_of_queue = 1; | |
975 | dumpers.num_of_queue = 1; | |
976 | ||
977 | goto create_eth; | |
978 | } | |
979 | ||
980 | /* | |
981 | * We check whether we want to open a RX stream from a real NIC or a | |
982 | * pcap file | |
983 | */ | |
984 | pcaps.num_of_queue = rte_kvargs_count(kvlist, ETH_PCAP_RX_PCAP_ARG); | |
985 | if (pcaps.num_of_queue) | |
986 | is_rx_pcap = 1; | |
987 | else | |
988 | pcaps.num_of_queue = rte_kvargs_count(kvlist, | |
989 | ETH_PCAP_RX_IFACE_ARG); | |
990 | ||
991 | if (pcaps.num_of_queue > RTE_PMD_PCAP_MAX_QUEUES) | |
992 | pcaps.num_of_queue = RTE_PMD_PCAP_MAX_QUEUES; | |
993 | ||
994 | if (is_rx_pcap) | |
995 | ret = rte_kvargs_process(kvlist, ETH_PCAP_RX_PCAP_ARG, | |
996 | &open_rx_pcap, &pcaps); | |
997 | else | |
998 | ret = rte_kvargs_process(kvlist, ETH_PCAP_RX_IFACE_ARG, | |
999 | &open_rx_iface, &pcaps); | |
1000 | ||
1001 | if (ret < 0) | |
1002 | goto free_kvlist; | |
1003 | ||
1004 | /* | |
1005 | * We check whether we want to open a TX stream to a real NIC or a | |
1006 | * pcap file | |
1007 | */ | |
1008 | dumpers.num_of_queue = rte_kvargs_count(kvlist, ETH_PCAP_TX_PCAP_ARG); | |
1009 | if (dumpers.num_of_queue) | |
1010 | is_tx_pcap = 1; | |
1011 | else | |
1012 | dumpers.num_of_queue = rte_kvargs_count(kvlist, | |
1013 | ETH_PCAP_TX_IFACE_ARG); | |
1014 | ||
1015 | if (dumpers.num_of_queue > RTE_PMD_PCAP_MAX_QUEUES) | |
1016 | dumpers.num_of_queue = RTE_PMD_PCAP_MAX_QUEUES; | |
1017 | ||
1018 | if (is_tx_pcap) | |
1019 | ret = rte_kvargs_process(kvlist, ETH_PCAP_TX_PCAP_ARG, | |
1020 | &open_tx_pcap, &dumpers); | |
1021 | else | |
1022 | ret = rte_kvargs_process(kvlist, ETH_PCAP_TX_IFACE_ARG, | |
1023 | &open_tx_iface, &dumpers); | |
1024 | ||
1025 | if (ret < 0) | |
1026 | goto free_kvlist; | |
1027 | ||
1028 | create_eth: | |
1029 | ret = eth_from_pcaps(name, &pcaps, pcaps.num_of_queue, &dumpers, | |
1030 | dumpers.num_of_queue, kvlist, single_iface, is_tx_pcap); | |
1031 | ||
1032 | free_kvlist: | |
1033 | rte_kvargs_free(kvlist); | |
1034 | ||
1035 | return ret; | |
1036 | } | |
1037 | ||
1038 | static int | |
1039 | pmd_pcap_remove(const char *name) | |
1040 | { | |
1041 | struct rte_eth_dev *eth_dev = NULL; | |
1042 | ||
1043 | RTE_LOG(INFO, PMD, "Closing pcap ethdev on numa socket %u\n", | |
1044 | rte_socket_id()); | |
1045 | ||
1046 | if (name == NULL) | |
1047 | return -1; | |
1048 | ||
1049 | /* reserve an ethdev entry */ | |
1050 | eth_dev = rte_eth_dev_allocated(name); | |
1051 | if (eth_dev == NULL) | |
1052 | return -1; | |
1053 | ||
1054 | rte_free(eth_dev->data->dev_private); | |
1055 | rte_free(eth_dev->data); | |
1056 | ||
1057 | rte_eth_dev_release_port(eth_dev); | |
1058 | ||
1059 | return 0; | |
1060 | } | |
1061 | ||
1062 | static struct rte_vdev_driver pmd_pcap_drv = { | |
1063 | .probe = pmd_pcap_probe, | |
1064 | .remove = pmd_pcap_remove, | |
1065 | }; | |
1066 | ||
1067 | RTE_PMD_REGISTER_VDEV(net_pcap, pmd_pcap_drv); | |
1068 | RTE_PMD_REGISTER_ALIAS(net_pcap, eth_pcap); | |
1069 | RTE_PMD_REGISTER_PARAM_STRING(net_pcap, | |
1070 | ETH_PCAP_RX_PCAP_ARG "=<string> " | |
1071 | ETH_PCAP_TX_PCAP_ARG "=<string> " | |
1072 | ETH_PCAP_RX_IFACE_ARG "=<ifc> " | |
1073 | ETH_PCAP_TX_IFACE_ARG "=<ifc> " | |
1074 | ETH_PCAP_IFACE_ARG "=<ifc>"); |