DPDK patches and discussions
 help / color / mirror / Atom feed
From: "Mário Kuka" <kuka@cesnet.cz>
To: Reshma Pattan <reshma.pattan@intel.com>,
	Stephen Hemminger <stephen@networkplumber.org>,
	Ray Kinsella <mdr@ashroe.eu>
Cc: dev@dpdk.org, "Mário Kuka" <kuka@cesnet.cz>
Subject: [PATCH] pcapng: fix write more packets than IOV_MAX limit
Date: Mon, 25 Jul 2022 17:28:11 +0200	[thread overview]
Message-ID: <20220725152811.409447-1-kuka@cesnet.cz> (raw)

The rte_pcapng_write_packets() function fails when we try to write more
packets than the IOV_MAX limit. The error is caused by the writev()
system call, which is limited by the IOV_MAX limit. The iovcnt argument
is valid if it is greater than 0 and less than or equal to IOV_MAX as
defined in <limits.h>.

To avoid this problem, we can split the iovec buffer into smaller
chunks with a maximum size of IOV_MAX and write them sequentially by
calling the writev() repeatedly.

Fixes: 8d23ce8f5ee9 ("pcapng: add new library for writing pcapng files")
Cc: stephen@networkplumber.org

Signed-off-by: Mário Kuka <kuka@cesnet.cz>
---
 app/test/test_pcapng.c  | 42 ++++++++++++++++++++++++++++-
 lib/pcapng/rte_pcapng.c | 58 ++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 98 insertions(+), 2 deletions(-)

diff --git a/app/test/test_pcapng.c b/app/test/test_pcapng.c
index 320dacea34..7f51946fff 100644
--- a/app/test/test_pcapng.c
+++ b/app/test/test_pcapng.c
@@ -110,7 +110,7 @@ test_setup(void)
 	}
 
 	/* Make a pool for cloned packets */
-	mp = rte_pktmbuf_pool_create_by_ops("pcapng_test_pool", NUM_PACKETS,
+	mp = rte_pktmbuf_pool_create_by_ops("pcapng_test_pool", IOV_MAX + NUM_PACKETS,
 					    0, 0,
 					    rte_pcapng_mbuf_size(pkt_len),
 					    SOCKET_ID_ANY, "ring_mp_sc");
@@ -237,6 +237,45 @@ test_validate(void)
 	return ret;
 }
 
+static int
+test_write_over_limit_iov_max(void)
+{
+	struct rte_mbuf *orig;
+	struct rte_mbuf *clones[IOV_MAX + NUM_PACKETS] = { };
+	struct dummy_mbuf mbfs;
+	unsigned int i;
+	ssize_t len;
+
+	/* make a dummy packet */
+	mbuf1_prepare(&mbfs, pkt_len);
+
+	/* clone them */
+	orig  = &mbfs.mb[0];
+	for (i = 0; i < IOV_MAX + NUM_PACKETS; i++) {
+		struct rte_mbuf *mc;
+
+		mc = rte_pcapng_copy(port_id, 0, orig, mp, pkt_len,
+				rte_get_tsc_cycles(), 0);
+		if (mc == NULL) {
+			fprintf(stderr, "Cannot copy packet\n");
+			return -1;
+		}
+		clones[i] = mc;
+	}
+
+	/* write it to capture file */
+	len = rte_pcapng_write_packets(pcapng, clones, IOV_MAX + NUM_PACKETS);
+
+	rte_pktmbuf_free_bulk(clones, IOV_MAX + NUM_PACKETS);
+
+	if (len <= 0) {
+		fprintf(stderr, "Write of packets failed\n");
+		return -1;
+	}
+
+	return 0;
+}
+
 static void
 test_cleanup(void)
 {
@@ -256,6 +295,7 @@ unit_test_suite test_pcapng_suite  = {
 		TEST_CASE(test_write_packets),
 		TEST_CASE(test_write_stats),
 		TEST_CASE(test_validate),
+		TEST_CASE(test_write_over_limit_iov_max),
 		TEST_CASES_END()
 	}
 };
diff --git a/lib/pcapng/rte_pcapng.c b/lib/pcapng/rte_pcapng.c
index 06ad712bd1..5762f89cb9 100644
--- a/lib/pcapng/rte_pcapng.c
+++ b/lib/pcapng/rte_pcapng.c
@@ -567,6 +567,62 @@ mbuf_burst_segs(struct rte_mbuf *pkts[], unsigned int n)
 	return iovcnt;
 }
 
+/*
+ * Update iov after writev() has returned written. We must find how many iov
+ * buffers (from beginning) have been written. The first buffer that was not
+ * written fully is to be updated accordingly.
+ *
+ * Returns offset of buffer that was not written fully.
+ */
+static int
+pcapng_update_iov(struct iovec *iov, const int count, size_t written)
+{
+	for (int i = 0; written > 0 && i < count; ++i) {
+		if (written < iov[i].iov_len) {
+			/* found buffer that was not written fully */
+			iov[i].iov_base = RTE_PTR_ADD(iov[i].iov_base, written);
+			iov[i].iov_len -= written;
+
+			return i;
+		}
+
+		/* buffer fully written, zero it and skip */
+		written -= iov[i].iov_len;
+
+		iov[i].iov_base = NULL;
+		iov[i].iov_len = 0;
+	}
+
+	return count;
+}
+
+/*
+ * Writes all iovcnt buffers of data described by iov to the file associated with
+ * the file descriptor fd.
+ *
+ * Note: POSIX.1-2001 allows an implementation to place a limit on the number
+ *       of items that can be passed in iov. An implementation can advertise
+ *       its limit by defining IOV_MAX in <limits.h>.
+ */
+static ssize_t
+pcapng_writev(int fd, struct iovec *iov, const int count)
+{
+	size_t total = 0;
+	int at = 0;
+
+	while (at < count) {
+		const int iov_cnt = RTE_MIN(count - at, IOV_MAX);
+		ssize_t wlen = writev(fd, &iov[at], iov_cnt);
+		if (unlikely(wlen < 0))
+			return wlen;
+
+		total += wlen;
+		at += pcapng_update_iov(&iov[at], iov_cnt, wlen);
+	}
+
+	return total;
+}
+
 /* Write pre-formatted packets to file. */
 ssize_t
 rte_pcapng_write_packets(rte_pcapng_t *self,
@@ -601,7 +657,7 @@ rte_pcapng_write_packets(rte_pcapng_t *self,
 		} while ((m = m->next));
 	}
 
-	ret = writev(self->outfd, iov, iovcnt);
+	ret = pcapng_writev(self->outfd, iov, iovcnt);
 	if (unlikely(ret < 0))
 		rte_errno = errno;
 	return ret;
-- 
2.31.1


             reply	other threads:[~2022-07-25 19:43 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-07-25 15:28 Mário Kuka [this message]
2022-07-25 15:57 ` Stephen Hemminger
2022-07-25 16:10 ` Stephen Hemminger
2022-07-29  7:18 ` [PATCH v2 0/2] pcapng: fix some issues with writing packets Mário Kuka
2022-07-29  7:18   ` [PATCH v2 1/2] pcapng: fix write more packets than IOV_MAX limit Mário Kuka
2022-07-29  7:18   ` [PATCH v2 2/2] pcapng: check if writev() returns a partial write Mário Kuka
2022-07-29 16:00     ` Stephen Hemminger
2022-07-29 17:08       ` Mário Kuka
2022-07-29 18:14         ` Stephen Hemminger
2022-08-01  8:42           ` Mário Kuka
2022-07-29 15:58   ` [PATCH v2 0/2] pcapng: fix some issues with writing packets Stephen Hemminger
2022-07-29 17:33     ` Mário Kuka
2022-08-01  8:40   ` [PATCH v3] pcapng: fix write more packets than IOV_MAX limit Mário Kuka
2022-08-01 15:33     ` Stephen Hemminger
2022-10-10  0:40       ` Thomas Monjalon

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220725152811.409447-1-kuka@cesnet.cz \
    --to=kuka@cesnet.cz \
    --cc=dev@dpdk.org \
    --cc=mdr@ashroe.eu \
    --cc=reshma.pattan@intel.com \
    --cc=stephen@networkplumber.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).