[PATCH v6 00/30] Introduce discontiguous frames management
This series introduces iov_tail to convey frame information between functions. This is only an API change, for the moment the memory pool is only able to store contiguous buffer, so, except for vhost-user in a special case, we only play with iovec array with only one entry. v6: - Replaced iov_slice() with the clearer iov_tail_clone() for creating iovec subsets. - Standardized local header variable names (to *_storage suffix). - Renamed functions for better semantics (e.g., ignore_arp to accept_arp, packet_data to packet_get). - Corrected OPTLEN_MAX definition in TCP. - Addressed minor logic issues (e.g., DHCPv6 FQDN flags, NDP null check). - Updated ipv6_l4hdr() return type to boolean. - Improved comments and documentation across several modules. v5: - store in the pool iovec array with several entries v4: Prepare to introduce iovec array in the pool: - passe iov_tail rather than pool to ndp,icmp, dhcp, dhcpv6 and arp - remove unused pool macros - add memory regions in the pool structure, this will allow us to use the buf pointer to store the iovec array for vhost-user v3: Address comments from David Laurent Vivier (30): arp: Don't mix incoming and outgoing buffers iov: Introduce iov_tail_clone() and iov_tail_drop(). iov: Update IOV_REMOVE_HEADER() and IOV_PEEK_HEADER() tap: Use iov_tail with tap_add_packet() packet: Use iov_tail with packet_add() packet: Add packet_data() arp: Convert to iov_tail ndp: Convert to iov_tail icmp: Convert to iov_tail udp: Convert to iov_tail tcp: Convert tcp_tap_handler() to use iov_tail tcp: Convert tcp_data_from_tap() to use iov_tail dhcpv6: move offset initialization out of dhcpv6_opt() dhcpv6: Extract sending of NotOnLink status dhcpv6: Convert to iov_tail dhcpv6: Use iov_tail in dhcpv6_opt() dhcp: Convert to iov_tail ip: Use iov_tail in ipv6_l4hdr() tap: Convert tap4_handler() to iov_tail tap: Convert tap6_handler() to iov_tail packet: rename packet_data() to packet_get() arp: use iov_tail rather than pool dhcp: use iov_tail rather than pool dhcpv6: use iov_tail rather than pool icmp: use iov_tail rather than pool ndp: use iov_tail rather than pool packet: remove PACKET_POOL() and PACKET_POOL_P() packet: remove unused parameter from PACKET_POOL_DECL() packet: add memory regions information into pool packet: use buf to store iovec array arp.c | 86 ++++++++++++------- arp.h | 2 +- dhcp.c | 47 ++++++----- dhcp.h | 2 +- dhcpv6.c | 222 +++++++++++++++++++++++++++++-------------------- dhcpv6.h | 2 +- icmp.c | 41 ++++++---- icmp.h | 2 +- iov.c | 102 +++++++++++++++++++++-- iov.h | 58 +++++++++---- ip.c | 32 ++++---- ip.h | 3 +- ndp.c | 16 +++- ndp.h | 4 +- packet.c | 227 ++++++++++++++++++++++++++++++++++++++------------- packet.h | 57 ++++++------- pcap.c | 1 + tap.c | 134 +++++++++++++++++------------- tap.h | 4 +- tcp.c | 61 ++++++++++---- tcp_buf.c | 2 +- udp.c | 33 +++++--- vhost_user.c | 28 +++---- virtio.c | 4 +- virtio.h | 18 +++- vu_common.c | 48 ++++------- 26 files changed, 799 insertions(+), 437 deletions(-) -- 2.49.0
Don't use the memory of the incoming packet to build the outgoing buffer
as it can be memory of the TX queue in the case of vhost-user.
Moreover with vhost-user, the packet can be split across several
iovec and it's easier to rebuild it in a buffer than updating an
existing iovec array.
Signed-off-by: Laurent Vivier
On Wed, 4 Jun 2025 15:08:05 +0200
Laurent Vivier
Don't use the memory of the incoming packet to build the outgoing buffer as it can be memory of the TX queue in the case of vhost-user.
Moreover with vhost-user, the packet can be split across several iovec and it's easier to rebuild it in a buffer than updating an existing iovec array.
Signed-off-by: Laurent Vivier
Reviewed-by: David Gibson --- arp.c | 84 ++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 55 insertions(+), 29 deletions(-) diff --git a/arp.c b/arp.c index fc482bbd9938..721ff2d529b5 100644 --- a/arp.c +++ b/arp.c @@ -31,56 +31,82 @@ #include "tap.h"
/** - * arp() - Check if this is a supported ARP message, reply as needed + * accept_arp() - Check if we should accept this ARP message * @c: Execution context - * @p: Packet pool, single packet with Ethernet buffer + * @ah: ARP header + * @am: ARP message * - * Return: 1 if handled, -1 on failure + * Return: true if the message is supported, false otherwise.
...wait, the function name is reversed now, but this isn't. You actually return true if the message should be discarded, not if the message "is supported". On v5, I mentioned I'd change the name of the function to accept_arp() rather than reversing the return value... but I missed this part, sorry. I guess if it's accept_arp(), it should return true *if we want to do something with the message*. If it's ignore_arp(), the opposite. I don't have a particular preference, as long as the function doesn't return the opposite of what the verb in its name says. :)
*/ -int arp(const struct ctx *c, const struct pool *p) +static bool accept_arp(const struct ctx *c, + const struct arphdr *ah, const struct arpmsg *am) { - unsigned char swap[4]; - struct ethhdr *eh; - struct arphdr *ah; - struct arpmsg *am; - size_t l2len; - - eh = packet_get(p, 0, 0, sizeof(*eh), NULL); - ah = packet_get(p, 0, sizeof(*eh), sizeof(*ah), NULL); - am = packet_get(p, 0, sizeof(*eh) + sizeof(*ah), sizeof(*am), NULL); - - if (!eh || !ah || !am) - return -1; - if (ah->ar_hrd != htons(ARPHRD_ETHER) || ah->ar_pro != htons(ETH_P_IP) || ah->ar_hln != ETH_ALEN || ah->ar_pln != 4 || ah->ar_op != htons(ARPOP_REQUEST)) - return 1; + return true;
/* Discard announcements, but not 0.0.0.0 "probes" */ if (memcmp(am->sip, &in4addr_any, sizeof(am->sip)) && !memcmp(am->sip, am->tip, sizeof(am->sip))) - return 1; + return true;
/* Don't resolve the guest's assigned address, either. */ if (!memcmp(am->tip, &c->ip4.addr, sizeof(am->tip))) + return true; + + return false; +} + +/** + * arp() - Check if this is a supported ARP message, reply as needed + * @c: Execution context + * @p: Packet pool, single packet with Ethernet buffer + * + * Return: 1 if handled, -1 on failure + */ +int arp(const struct ctx *c, const struct pool *p) +{ + struct { + struct ethhdr eh; + struct arphdr ah; + struct arpmsg am; + } __attribute__((__packed__)) resp; + const struct ethhdr *eh; + const struct arphdr *ah; + const struct arpmsg *am; + + eh = packet_get(p, 0, 0, sizeof(*eh), NULL); + ah = packet_get(p, 0, sizeof(*eh), sizeof(*ah), NULL); + am = packet_get(p, 0, sizeof(*eh) + sizeof(*ah), sizeof(*am), NULL); + + if (!eh || !ah || !am) + return -1; + + if (accept_arp(c, ah, am)) return 1;
- ah->ar_op = htons(ARPOP_REPLY); - memcpy(am->tha, am->sha, sizeof(am->tha)); - memcpy(am->sha, c->our_tap_mac, sizeof(am->sha)); + /* Ethernet header */ + resp.eh.h_proto = htons(ETH_P_ARP); + memcpy(resp.eh.h_dest, eh->h_source, sizeof(resp.eh.h_dest)); + memcpy(resp.eh.h_source, c->our_tap_mac, sizeof(resp.eh.h_source));
- memcpy(swap, am->tip, sizeof(am->tip)); - memcpy(am->tip, am->sip, sizeof(am->tip)); - memcpy(am->sip, swap, sizeof(am->sip)); + /* ARP header */ + resp.ah.ar_op = htons(ARPOP_REPLY); + resp.ah.ar_hrd = ah->ar_hrd; + resp.ah.ar_pro = ah->ar_pro; + resp.ah.ar_hln = ah->ar_hln; + resp.ah.ar_pln = ah->ar_pln;
- l2len = sizeof(*eh) + sizeof(*ah) + sizeof(*am); - memcpy(eh->h_dest, eh->h_source, sizeof(eh->h_dest)); - memcpy(eh->h_source, c->our_tap_mac, sizeof(eh->h_source)); + /* ARP message */ + memcpy(resp.am.sha, c->our_tap_mac, sizeof(resp.am.sha)); + memcpy(resp.am.sip, am->tip, sizeof(resp.am.sip)); + memcpy(resp.am.tha, am->sha, sizeof(resp.am.tha)); + memcpy(resp.am.tip, am->sip, sizeof(resp.am.tip));
- tap_send_single(c, eh, l2len); + tap_send_single(c, &resp, sizeof(resp));
return 1; }
-- Stefano
These utilities enhance iov_tail manipulation, useful for
efficient packet processing by enabling iovec array cloning and
header stripping without data copies.
- iov_tail_drop(): Discards a specified number of bytes from the
beginning of an iov_tail by advancing its internal offset and pruning
consumed elements.
- iov_tail_clone(): Clone an iov_tail into an iovec array, adjusting the
first iovec entry to remove the iov_tail offset.
Signed-off-by: Laurent Vivier
On Wed, 4 Jun 2025 15:08:06 +0200
Laurent Vivier
These utilities enhance iov_tail manipulation, useful for efficient packet processing by enabling iovec array cloning and header stripping without data copies.
- iov_tail_drop(): Discards a specified number of bytes from the beginning of an iov_tail by advancing its internal offset and pruning consumed elements.
- iov_tail_clone(): Clone an iov_tail into an iovec array, adjusting the first iovec entry to remove the iov_tail offset.
Signed-off-by: Laurent Vivier
--- iov.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ iov.h | 3 +++ 2 files changed, 55 insertions(+) diff --git a/iov.c b/iov.c index 91e87a740f0a..3b89f8126ee7 100644 --- a/iov.c +++ b/iov.c @@ -192,6 +192,21 @@ size_t iov_tail_size(struct iov_tail *tail) return iov_size(tail->iov, tail->cnt) - tail->off; }
+/** + * iov_tail_drop() - Discard a header from an IOV tail
Same as v5:
---
On Mon, 26 May 2025 16:19:03 +0200
Stefano Brivio
Mixing 'header' with 'tail' makes this a bit confusing. Yes, these vectors are used for headers, but what about calling it simply 'item', to match the context of this function?
+ * @tail: IO vector tail + * @len: length to move the head of the tail + * + * Returns: true if the item still contains any bytes, otherwise false
Nit: we always use the imperative mood here, and no tabs, "Return: true if...".
+ */ +/* cppcheck-suppress unusedFunction */ +bool iov_tail_drop(struct iov_tail *tail, size_t len) +{ + tail->off = tail->off + len; + + return iov_tail_prune(tail); +} + /** * iov_peek_header_() - Get pointer to a header from an IOV tail * @tail: IOV tail to get header from @@ -248,3 +263,40 @@ void *iov_remove_header_(struct iov_tail *tail, size_t len, size_t align) tail->off = tail->off + len; return p; } + +/** + * iov_tail_clone() - Assign iov references referencing a subset of the data
Nit (same as for iov_slice() in v5): "iov_tail_clone() - Assign ..."
+ * in an iov_tail + * + * @dst_iov: Pointer to the destination array of struct iovec describing + * the scatter/gather I/O vector to copy to.
Same as v5: perhaps "...to shallow copy to"?
+ * @dst_iov_cnt: Maximum number of elements in the destination iov array. + * @tail: Pointer to the source iov_tail + * + * Returns: The number of elements successfully copied to the destination
"referenced from the destination iov array"? Same as above: "Return: the number of..."
+ * iov array, a negative value if there is not enough room in the + * destination iov array + */ +/* cppcheck-suppress unusedFunction */ +ssize_t iov_tail_clone(struct iovec *dst_iov, size_t dst_iov_cnt, + struct iov_tail *tail) +{ + const struct iovec *iov = &tail->iov[0]; + size_t iov_cnt = tail->cnt; + size_t offset = tail->off; + unsigned int i, j; + + i = iov_skip_bytes(iov, iov_cnt, offset, &offset); + + /* assign iov references referencing a subset of the source one */
Nit: excess whitespace.
+ for (j = 0; i < iov_cnt && j < dst_iov_cnt; i++, j++) { + dst_iov[j].iov_base = (char *)iov[i].iov_base + offset; + dst_iov[j].iov_len = iov[i].iov_len - offset; + offset = 0; + }
Nice, this is much less scary than the previous iov_slice().
+ + if (j == dst_iov_cnt && i != iov_cnt) + return -1; + + return j; +} diff --git a/iov.h b/iov.h index 9855bf0c0c32..11a8567aa183 100644 --- a/iov.h +++ b/iov.h @@ -72,8 +72,11 @@ struct iov_tail {
bool iov_tail_prune(struct iov_tail *tail); size_t iov_tail_size(struct iov_tail *tail); +bool iov_tail_drop(struct iov_tail *tail, size_t len); void *iov_peek_header_(struct iov_tail *tail, size_t len, size_t align); void *iov_remove_header_(struct iov_tail *tail, size_t len, size_t align); +ssize_t iov_tail_clone(struct iovec *dst_iov, size_t dst_iov_cnt, + struct iov_tail *tail);
/** * IOV_PEEK_HEADER() - Get typed pointer to a header from an IOV tail
-- Stefano
Provide a temporary variable of the wanted type to store
the header if the memory in the iovec array is not contiguous.
Signed-off-by: Laurent Vivier
On Wed, 4 Jun 2025 15:08:07 +0200
Laurent Vivier
Provide a temporary variable of the wanted type to store the header if the memory in the iovec array is not contiguous.
Signed-off-by: Laurent Vivier
--- iov.c | 53 ++++++++++++++++++++++++++++++++++++++++++++--------- iov.h | 55 +++++++++++++++++++++++++++++++++++++++++-------------- tcp_buf.c | 2 +- 3 files changed, 86 insertions(+), 24 deletions(-) diff --git a/iov.c b/iov.c index 3b89f8126ee7..541ca8aa9119 100644 --- a/iov.c +++ b/iov.c @@ -109,7 +109,7 @@ size_t iov_from_buf(const struct iovec *iov, size_t iov_cnt, * * Returns: The number of bytes successfully copied. */ -/* cppcheck-suppress unusedFunction */ +/* cppcheck-suppress [staticFunction] */ size_t iov_to_buf(const struct iovec *iov, size_t iov_cnt, size_t offset, void *buf, size_t bytes) { @@ -127,6 +127,7 @@ size_t iov_to_buf(const struct iovec *iov, size_t iov_cnt, /* copying data */ for (copied = 0; copied < bytes && i < iov_cnt; i++) { size_t len = MIN(iov[i].iov_len - offset, bytes - copied); + /* NOLINTNEXTLINE(clang-analyzer-core.NonNullParamChecker) */ memcpy((char *)buf + copied, (char *)iov[i].iov_base + offset, len); copied += len; @@ -208,7 +209,7 @@ bool iov_tail_drop(struct iov_tail *tail, size_t len) }
/** - * iov_peek_header_() - Get pointer to a header from an IOV tail + * iov_check_header() - Check if a header can be accessed * @tail: IOV tail to get header from * @len: Length of header to get, in bytes * @align: Required alignment of header, in bytes @@ -219,8 +220,7 @@ bool iov_tail_drop(struct iov_tail *tail, size_t len) * overruns the IO vector, is not contiguous or doesn't have the * requested alignment. */ -/* cppcheck-suppress [staticFunction,unmatchedSuppression] */ -void *iov_peek_header_(struct iov_tail *tail, size_t len, size_t align) +static void *iov_check_header(struct iov_tail *tail, size_t len, size_t align) { char *p;
@@ -240,27 +240,62 @@ void *iov_peek_header_(struct iov_tail *tail, size_t len, size_t align) return p; }
+/** + * iov_peek_header_() - Get pointer to a header from an IOV tail + * @tail: IOV tail to get header from + * @v: Temporary memory to use if the memory in @tail + * is discontinuous + * @len: Length of header to get, in bytes + * @align: Required alignment of header, in bytes + * + * @tail may be pruned, but will represent the same bytes as before. + * + * Returns: Pointer to the first @len logical bytes of the tail, or to
Nit: Return: pointer to ...
+ * a copy if that overruns the IO vector, is not contiguous or + * doesn't have the requested alignment. NULL if that overruns the + * IO vector. + */ +/* cppcheck-suppress [staticFunction,unmatchedSuppression] */ +void *iov_peek_header_(struct iov_tail *tail, void *v, size_t len, size_t align) +{ + char *p = iov_check_header(tail, len, align); + size_t l; + + if (p) + return p; + + l = iov_to_buf(tail->iov, tail->cnt, tail->off, v, len); + if (l != len) + return NULL; + + return v; +} + /** * iov_remove_header_() - Remove a header from an IOV tail * @tail: IOV tail to remove header from (modified) + * @v: Temporary memory to use if the memory in @tail + * is discontinuous * @len: Length of header to remove, in bytes * @align: Required alignment of header, in bytes * * On success, @tail is updated so that it longer includes the bytes of the * returned header. * - * Returns: Pointer to the first @len logical bytes of the tail, NULL if that - * overruns the IO vector, is not contiguous or doesn't have the - * requested alignment. + * Returns: Pointer to the first @len logical bytes of the tail, or to + * a copy if that overruns the IO vector, is not contiguous or + * doesn't have the requested alignment. NULL if that overruns the + * IO vector. */ -void *iov_remove_header_(struct iov_tail *tail, size_t len, size_t align) +void *iov_remove_header_(struct iov_tail *tail, void *v, size_t len, size_t align) { - char *p = iov_peek_header_(tail, len, align); + char *p = iov_peek_header_(tail, v, len, align);
if (!p) return NULL;
tail->off = tail->off + len; + return p; }
diff --git a/iov.h b/iov.h index 11a8567aa183..7d5bce2f841c 100644 --- a/iov.h +++ b/iov.h @@ -70,41 +70,68 @@ struct iov_tail { #define IOV_TAIL(iov_, cnt_, off_) \ (struct iov_tail){ .iov = (iov_), .cnt = (cnt_), .off = (off_) }
+/** + * IOV_TAIL_FROM_BUF() - Create a new IOV tail from a buffer + * @buf_: Buffer address to use in the iovec + * @len_: Buffer size + * @off_: Byte offset in the buffer where the tail begins + */ +#define IOV_TAIL_FROM_BUF(buf_, len_, off_) \ + IOV_TAIL((&(const struct iovec){ .iov_base = (buf_), \ + .iov_len = (len_) }), \ + 1, \ + (off_)) + bool iov_tail_prune(struct iov_tail *tail); size_t iov_tail_size(struct iov_tail *tail); bool iov_tail_drop(struct iov_tail *tail, size_t len); -void *iov_peek_header_(struct iov_tail *tail, size_t len, size_t align); -void *iov_remove_header_(struct iov_tail *tail, size_t len, size_t align); +void *iov_peek_header_(struct iov_tail *tail, void *v, size_t len, size_t align); +void *iov_remove_header_(struct iov_tail *tail, void *v, size_t len, size_t align); ssize_t iov_tail_clone(struct iovec *dst_iov, size_t dst_iov_cnt, struct iov_tail *tail);
/** * IOV_PEEK_HEADER() - Get typed pointer to a header from an IOV tail * @tail_: IOV tail to get header from - * @type_: Data type of the header + * @var_: Temporary buffer of the type of the header to use if + * the memory in the iovec array is not contiguous. * * @tail_ may be pruned, but will represent the same bytes as before. * - * Returns: Pointer of type (@type_ *) located at the start of @tail_, NULL if - * we can't get a contiguous and aligned pointer. + * Returns: Pointer of type (@type_ *) located at the start of @tail_ + * or to @var_ if iovec memory is not contiguous, NULL if + * that overruns the iovec. */ -#define IOV_PEEK_HEADER(tail_, type_) \ - ((type_ *)(iov_peek_header_((tail_), \ - sizeof(type_), __alignof__(type_)))) + +#define IOV_PEEK_HEADER(tail_, var_) \ + ((__typeof__(var_) *)(iov_peek_header_((tail_), &(var_), \ + sizeof(var_), \ + __alignof__(var_))))
/** * IOV_REMOVE_HEADER() - Remove and return typed header from an IOV tail * @tail_: IOV tail to remove header from (modified) - * @type_: Data type of the header to remove + * @var_: Temporary buffer of the type of the header to use if + * the memory in the iovec array is not contiguous. * * On success, @tail_ is updated so that it longer includes the bytes of the * returned header. * - * Returns: Pointer of type (@type_ *) located at the old start of @tail_, NULL - * if we can't get a contiguous and aligned pointer. + * Returns: Pointer of type (@type_ *) located at the start of @tail_ + * or to @var_ if iovec memory is not contiguous, NULL if + * that overruns the iovec. + */ + +#define IOV_REMOVE_HEADER(tail_, var_) \ + ((__typeof__(var_) *)(iov_remove_header_((tail_), &(var_), \ + sizeof(var_), __alignof__(var_)))) + +/** IOV_DROP_HEADER() - Remove a typed header from an IOV tail + * @tail_: IOV tail to remove header from (modified) + * @type_: Data type of the header to remove + * + * Returns: true if the tail still contains any bytes, otherwise false */ -#define IOV_REMOVE_HEADER(tail_, type_) \ - ((type_ *)(iov_remove_header_((tail_), \ - sizeof(type_), __alignof__(type_)))) +#define IOV_DROP_HEADER(tail_, type_) iov_tail_drop((tail_), sizeof(type_))
#endif /* IOVEC_H */ diff --git a/tcp_buf.c b/tcp_buf.c index 05305636b503..ac6d73130f55 100644 --- a/tcp_buf.c +++ b/tcp_buf.c @@ -160,7 +160,7 @@ static void tcp_l2_buf_fill_headers(const struct tcp_tap_conn *conn, uint32_t seq, bool no_tcp_csum) { struct iov_tail tail = IOV_TAIL(&iov[TCP_IOV_PAYLOAD], 1, 0); - struct tcphdr *th = IOV_REMOVE_HEADER(&tail, struct tcphdr); + struct tcphdr th_storage, *th = IOV_REMOVE_HEADER(&tail, th_storage); struct tap_hdr *taph = iov[TCP_IOV_TAP].iov_base; const struct flowside *tapside = TAPFLOW(conn); const struct in_addr *a4 = inany_v4(&tapside->oaddr);
-- Stefano
Use IOV_PEEK_HEADER() to get the ethernet header from the iovec.
Move the workaround about multiple iovec array from vu_handle_tx() to
tap_add_packet(). Removing the offset out of the iovec array should
reduce the iovec count to 1.
Signed-off-by: Laurent Vivier
Modify the interface of packet_add_do() to take an iov_tail
rather than a memory pointer and length.
Internally it only supports iovec array with only one entry,
after being pruned. We can accept iovec array with several
entries if the offset allows the function to reduce the number
of entries to 1.
tap4_handler() is updated to create an iov_tail value using
IOV_TAIL_FROM_BUF() from the buffer and the length.
Signed-off-by: Laurent Vivier
packet_data() gets the data range from a packet descriptor from a
given pool.
It uses iov_tail to return the packet memory.
Signed-off-by: Laurent Vivier
Use packet_data() and extract headers using IOV_REMOVE_HEADER()
rather than packet_get().
Signed-off-by: Laurent Vivier
Use packet_data() and extract headers using IOV_REMOVE_HEADER()
rather than packet_get().
Signed-off-by: Laurent Vivier
Use packet_data() and extract headers using IOV_PEEK_HEADER()
rather than packet_get().
Introduce iov_tail_msghdr() to convert iov_tail to msghdr
Signed-off-by: Laurent Vivier
Use packet_data() and extract headers using IOV_REMOVE_HEADER()
and IOV_PEEK_HEADER() rather than packet_get().
Signed-off-by: Laurent Vivier
Use packet_data() and extract headers using IOV_REMOVE_HEADER()
and iov_remove_header_() rather than packet_get().
Signed-off-by: Laurent Vivier
Use packet_data() and extract headers using IOV_PEEK_HEADER()
rather than packet_get().
Signed-off-by: Laurent Vivier
No functional change.
Currently, if dhcpv6_opt() is called with offset set to 0, it will set the
offset to point to DHCPv6 options offset.
To simplify the use of iovec_tail in a later patch, move the initialization
out of the function. Replace all the call using 0 by a call using
the offset of the DHCPv6 options.
Signed-off-by: Laurent Vivier
Extract code from dhcpv6() into a new function, dhcpv6_send_ia_notonlink()
Signed-off-by: Laurent Vivier
Use packet_data() and extract headers using IOV_REMOVE_HEADER()
and IOV_PEEK_HEADER() rather than packet_get().
Signed-off-by: Laurent Vivier
dhcpv6_opt() and its callers are refactored for iov_tail option parsing,
replacing direct offset management for improved robustness.
Its signature is now `bool dhcpv6_opt(iov_tail *data, type)`. `*data` (in/out)
points to a found option on `true` return or is restored on `false`.
The main dhcpv6() function uses IOV_REMOVE_HEADER for the msg_hdr, then
passes the iov_tail (now at options start) to the new dhcpv6_opt().
Signed-off-by: Laurent Vivier
On Wed, 4 Jun 2025 15:08:20 +0200
Laurent Vivier
dhcpv6_opt() and its callers are refactored for iov_tail option parsing, replacing direct offset management for improved robustness.
Its signature is now `bool dhcpv6_opt(iov_tail *data, type)`. `*data` (in/out) points to a found option on `true` return or is restored on `false`. The main dhcpv6() function uses IOV_REMOVE_HEADER for the msg_hdr, then passes the iov_tail (now at options start) to the new dhcpv6_opt().
Signed-off-by: Laurent Vivier
--- dhcpv6.c | 178 +++++++++++++++++++++++++++++++------------------------ iov.c | 1 - 2 files changed, 102 insertions(+), 77 deletions(-) diff --git a/dhcpv6.c b/dhcpv6.c index ae06e646f92f..bd6268804d95 100644 --- a/dhcpv6.c +++ b/dhcpv6.c @@ -280,112 +280,122 @@ static struct resp_not_on_link_t {
/** * dhcpv6_opt() - Get option from DHCPv6 message - * @p: Packet pool, single packet with UDP header - * @offset: Offset to look at, 0: end of header, set to option start + * @data: Buffer with options, set to matching option on return * @type: Option type to look up, network order * - * Return: pointer to option header, or NULL on malformed or missing option + * Return: true if found and @data points to the option header, + * or false on malformed or missing option and @data is + * unmodified. */ -static struct opt_hdr *dhcpv6_opt(const struct pool *p, size_t *offset, - uint16_t type) +static bool dhcpv6_opt(struct iov_tail *data, uint16_t type) { - struct opt_hdr *o; - size_t left; + struct iov_tail head = *data; + struct opt_hdr o_storage; + const struct opt_hdr *o;
- ASSERT(*offset >= UDP_MSG_HDR_SIZE); - - while ((o = packet_get_try(p, 0, *offset, sizeof(*o), &left))) { + while ((o = IOV_PEEK_HEADER(data, o_storage))) { unsigned int opt_len = ntohs(o->l) + sizeof(*o);
- if (ntohs(o->l) > left) - return NULL; + if (opt_len > iov_tail_size(data)) + break;
if (o->t == type) - return o; + return true;
- *offset += opt_len; + iov_tail_drop(data, opt_len); }
- return NULL; + *data = head; + return false; }
/** * dhcpv6_ia_notonlink() - Check if any IA contains non-appropriate addresses - * @p: Packet pool, single packet starting from UDP header + * @data: Data to look at, packet starting from UDP header (input/output) * @la: Address we want to lease to the client * - * Return: pointer to non-appropriate IA_NA or IA_TA, if any, NULL otherwise + * Return: true and @data points to non-appropriate IA_NA or IA_TA, if any, + * false otherwise and @data is unmodified */ -static struct opt_hdr *dhcpv6_ia_notonlink(const struct pool *p, - struct in6_addr *la) +static bool dhcpv6_ia_notonlink(struct iov_tail *data, + struct in6_addr *la) { int ia_types[2] = { OPT_IA_NA, OPT_IA_TA }, *ia_type; + struct opt_ia_addr opt_addr_storage; const struct opt_ia_addr *opt_addr; + struct iov_tail current, ia_base; + struct opt_ia_na ia_storage; char buf[INET6_ADDRSTRLEN]; + const struct opt_ia_na *ia; struct in6_addr req_addr; + struct opt_hdr h_storage; const struct opt_hdr *h; - struct opt_hdr *ia; - size_t offset;
foreach(ia_type, ia_types) { - offset = UDP_MSG_HDR_SIZE; - while ((ia = dhcpv6_opt(p, &offset, *ia_type))) { - if (ntohs(ia->l) < OPT_VSIZE(ia_na)) - return NULL; - - offset += sizeof(struct opt_ia_na); - - while ((h = dhcpv6_opt(p, &offset, OPT_IAAADR))) { - if (ntohs(h->l) != OPT_VSIZE(ia_addr)) - return NULL; - - opt_addr = (const struct opt_ia_addr *)h; + current = *data; + while (dhcpv6_opt(¤t, *ia_type)) { + ia_base = current; + ia = IOV_REMOVE_HEADER(¤t, ia_storage); + if (!ia || ntohs(ia->hdr.l) < OPT_VSIZE(ia_na)) + goto notfound; + + while (dhcpv6_opt(¤t, OPT_IAAADR)) { + h = IOV_PEEK_HEADER(¤t, h_storage); + if (!h || ntohs(h->l) != OPT_VSIZE(ia_addr)) + goto notfound; + + opt_addr = IOV_REMOVE_HEADER(¤t, + opt_addr_storage);
This still comes with the Coverity warning I reported for v5, but now it's a single occurrence: /home/sbrivio/passt/dhcpv6.c:349:5: Type: Dereference null return value (NULL_RETURNS) /home/sbrivio/passt/dhcpv6.c:334:2: Call to null-returning function 1. path: Condition "ia_type - ia_types < 2L /* (int)(sizeof (ia_types) / sizeof (ia_types[0])) */", taking true branch. /home/sbrivio/passt/dhcpv6.c:336:3: 2. path: Condition "dhcpv6_opt(¤t, *ia_type)", taking true branch. /home/sbrivio/passt/dhcpv6.c:339:4: 3. path: Condition "!ia", taking false branch. /home/sbrivio/passt/dhcpv6.c:339:4: 4. path: Condition "__bswap_16(ia->hdr.l) < 12UL /* sizeof (struct opt_ia_na) - sizeof (struct opt_hdr) */", taking false branch. /home/sbrivio/passt/dhcpv6.c:342:4: 5. path: Condition "dhcpv6_opt(¤t, 1280 /* (__uint16_t)(((5 >> 8) & 0xff) | ((5 & 0xff) << 8)) */)", taking true branch. /home/sbrivio/passt/dhcpv6.c:344:5: 6. path: Condition "!h", taking false branch. /home/sbrivio/passt/dhcpv6.c:344:5: 7. path: Condition "__bswap_16(h->l) != 24UL /* sizeof (struct opt_ia_addr) - sizeof (struct opt_hdr) */", taking false branch. /home/sbrivio/passt/dhcpv6.c:347:5: 8. returned_null: "iov_remove_header_" returns "NULL" (checked 9 out of 11 times). /home/sbrivio/passt/iov.c:292:2: Call to null-returning function 8.1. path: Condition "!p", taking true branch. /home/sbrivio/passt/iov.c:293:3: 8.2. return_null: Explicitly returning null. /home/sbrivio/passt/dhcpv6.c:347:5: 9. var_assigned: Assigning: "opt_addr" = "NULL" return value from "iov_remove_header_". /home/sbrivio/passt/dhcpv6.c:349:5: 10. dereference: Dereferencing "opt_addr", which is known to be "NULL". /home/sbrivio/passt/arp.c:84:2: Examples where return value was checked for null 11. example_assign: Example 1: Assigning: "eh" = return value from "iov_remove_header_(data, &eh_storage, 14UL, 1UL)". /home/sbrivio/passt/arp.c:87:2: 12. example_checked: Example 1 (cont.): "eh" has its value checked in "eh". /home/sbrivio/passt/arp.c:86:2: Examples where return value was checked for null 13. example_assign: Example 2: Assigning: "am" = return value from "iov_remove_header_(data, &am_storage, 20UL, 1UL)". /home/sbrivio/passt/arp.c:87:2: 14. example_checked: Example 2 (cont.): "am" has its value checked in "am". /home/sbrivio/passt/dhcp.c:319:2: Examples where return value was checked for null 15. example_assign: Example 3: Assigning: "eh" = return value from "iov_remove_header_(data, &eh_storage, 14UL, 1UL)". /home/sbrivio/passt/dhcp.c:321:2: 16. example_checked: Example 3 (cont.): "eh" has its value checked in "eh". /home/sbrivio/passt/dhcp.c:338:2: Examples where return value was checked for null 17. example_assign: Example 4: Assigning: "m" = return value from "iov_remove_header_(data, &mc, 240UL, 1UL)". /home/sbrivio/passt/dhcp.c:340:2: 18. example_checked: Example 4 (cont.): "m" has its value checked in "m". /home/sbrivio/passt/dhcp.c:371:3: Examples where return value was checked for null 19. example_assign: Example 5: Assigning: "type" = return value from "iov_remove_header_(data, &type_storage, 1UL, 1UL)". /home/sbrivio/passt/dhcp.c:373:3: 20. example_checked: Example 5 (cont.): "type" has its value checked in "type".
req_addr = opt_addr->addr; if (!IN6_ARE_ADDR_EQUAL(la, &req_addr)) - goto err; - - offset += sizeof(struct opt_ia_addr); + goto notonlink; } } }
- return NULL; +notfound: + return false;
-err: +notonlink: info("DHCPv6: requested address %s not on link", inet_ntop(AF_INET6, &req_addr, buf, sizeof(buf))); - return ia; + *data = ia_base; + return true; }
/** * dhcpv6_send_ia_notonlink() - Send NotOnLink status - * @c: Execution context - * @ia: Pointer to non-appropriate IA_NA or IA_TA - * @client_id: Client ID message option - * xid: Transaction ID for message exchange + * @c: Execution context + * @ia_base: Non-appropriate IA_NA or IA_TA base + * @client_id_base: Client ID message option base + * @len: Client ID length + * @xid: Transaction ID for message exchange */ -static void dhcpv6_send_ia_notonlink(struct ctx *c, struct opt_hdr *ia, - const struct opt_hdr *client_id, - uint32_t xid) +static void dhcpv6_send_ia_notonlink(struct ctx *c, + const struct iov_tail *ia_base, + const struct iov_tail *client_id_base, + int len, uint32_t xid) { const struct in6_addr *src = &c->ip6.our_tap_ll; + struct opt_hdr *ia = (struct opt_hdr *)resp_not_on_link.var; size_t n;
info("DHCPv6: received CONFIRM with inappropriate IA," " sending NotOnLink status in REPLY");
- ia->l = htons(OPT_VSIZE(ia_na) + sizeof(sc_not_on_link)); - n = sizeof(struct opt_ia_na); - memcpy(resp_not_on_link.var, ia, n); + iov_to_buf(&ia_base->iov[0], ia_base->cnt, ia_base->off, + resp_not_on_link.var, n); + ia->l = htons(OPT_VSIZE(ia_na) + sizeof(sc_not_on_link)); memcpy(resp_not_on_link.var + n, &sc_not_on_link, sizeof(sc_not_on_link));
n += sizeof(sc_not_on_link); - memcpy(resp_not_on_link.var + n, client_id, - sizeof(struct opt_hdr) + ntohs(client_id->l)); + iov_to_buf(&client_id_base->iov[0], client_id_base->cnt, + client_id_base->off, resp_not_on_link.var + n, + sizeof(struct opt_hdr) + len);
- n += sizeof(struct opt_hdr) + ntohs(client_id->l); + n += sizeof(struct opt_hdr) + len;
n = offsetof(struct resp_not_on_link_t, var) + n;
@@ -474,17 +484,19 @@ search:
/** * dhcpv6_client_fqdn_fill() - Fill in client FQDN option + * @data: Data to look at * @c: Execution context * @buf: Response message buffer where options will be appended * @offset: Offset in message buffer for new options * * Return: updated length of response message buffer. */ -static size_t dhcpv6_client_fqdn_fill(const struct pool *p, const struct ctx *c, +static size_t dhcpv6_client_fqdn_fill(const struct iov_tail *data, + const struct ctx *c, char *buf, int offset)
{ - struct opt_client_fqdn const *req_opt; + struct iov_tail current = *data; struct opt_client_fqdn *o; size_t opt_len;
@@ -502,14 +514,16 @@ static size_t dhcpv6_client_fqdn_fill(const struct pool *p, const struct ctx *c, }
o = (struct opt_client_fqdn *)(buf + offset); + o->flags = 0x00; encode_domain_name(o->domain_name, c->fqdn); - req_opt = (struct opt_client_fqdn *)dhcpv6_opt(p, - &(size_t){ UDP_MSG_HDR_SIZE }, - OPT_CLIENT_FQDN); - if (req_opt && req_opt->flags & 0x01 /* S flag */) - o->flags = 0x02 /* O flag */; - else - o->flags = 0x00; + if (dhcpv6_opt(¤t, OPT_CLIENT_FQDN)) { + struct opt_client_fqdn req_opt_storage; + struct opt_client_fqdn const *req_opt; + + req_opt = IOV_PEEK_HEADER(¤t, req_opt_storage); + if (req_opt && req_opt->flags & 0x01 /* S flag */) + o->flags = 0x02 /* O flag */; + }
opt_len++;
@@ -531,14 +545,18 @@ static size_t dhcpv6_client_fqdn_fill(const struct pool *p, const struct ctx *c, int dhcpv6(struct ctx *c, const struct pool *p, const struct in6_addr *saddr, const struct in6_addr *daddr) { - const struct opt_hdr *client_id, *server_id, *ia; + const struct opt_server_id *server_id = NULL; + struct iov_tail data, opt, client_id_base; + const struct opt_hdr *client_id = NULL; + struct opt_server_id server_id_storage; + const struct opt_ia_na *ia = NULL; + struct opt_hdr client_id_storage; + struct opt_ia_na ia_storage; const struct in6_addr *src; struct msg_hdr mh_storage; const struct msg_hdr *mh; struct udphdr uh_storage; const struct udphdr *uh; - struct opt_hdr *bad_ia; - struct iov_tail data; size_t mlen, n;
if (!packet_data(p, 0, &data)) @@ -565,20 +583,26 @@ int dhcpv6(struct ctx *c, const struct pool *p,
src = &c->ip6.our_tap_ll;
- mh = IOV_PEEK_HEADER(&data, mh_storage); + mh = IOV_REMOVE_HEADER(&data, mh_storage); if (!mh) return -1;
- client_id = dhcpv6_opt(p, &(size_t){ UDP_MSG_HDR_SIZE }, OPT_CLIENTID); + client_id_base = data; + if (dhcpv6_opt(&client_id_base, OPT_CLIENTID)) + client_id = IOV_PEEK_HEADER(&client_id_base, client_id_storage); if (!client_id || ntohs(client_id->l) > OPT_VSIZE(client_id)) return -1;
- server_id = dhcpv6_opt(p, &(size_t){ UDP_MSG_HDR_SIZE }, OPT_SERVERID); - if (server_id && ntohs(server_id->l) != OPT_VSIZE(server_id)) + opt = data; + if (dhcpv6_opt(&opt, OPT_SERVERID)) + server_id = IOV_PEEK_HEADER(&opt, server_id_storage); + if (server_id && ntohs(server_id->hdr.l) != OPT_VSIZE(server_id)) return -1;
- ia = dhcpv6_opt(p, &(size_t){ UDP_MSG_HDR_SIZE }, OPT_IA_NA); - if (ia && ntohs(ia->l) < MIN(OPT_VSIZE(ia_na), OPT_VSIZE(ia_ta))) + opt = data; + if (dhcpv6_opt(&opt, OPT_IA_NA)) + ia = IOV_PEEK_HEADER(&opt, ia_storage); + if (ia && ntohs(ia->hdr.l) < MIN(OPT_VSIZE(ia_na), OPT_VSIZE(ia_ta))) return -1;
resp.hdr.type = TYPE_REPLY; @@ -593,9 +617,10 @@ int dhcpv6(struct ctx *c, const struct pool *p, if (mh->type == TYPE_CONFIRM && server_id) return -1;
- if ((bad_ia = dhcpv6_ia_notonlink(p, &c->ip6.addr))) { + if (dhcpv6_ia_notonlink(&data, &c->ip6.addr)) {
- dhcpv6_send_ia_notonlink(c, bad_ia, client_id, mh->xid); + dhcpv6_send_ia_notonlink(c, &data, &client_id_base, + ntohs(client_id->l), mh->xid);
return 1; } @@ -607,7 +632,7 @@ int dhcpv6(struct ctx *c, const struct pool *p, memcmp(&resp.server_id, server_id, sizeof(resp.server_id))) return -1;
- if (ia || dhcpv6_opt(p, &(size_t){ UDP_MSG_HDR_SIZE }, OPT_IA_TA)) + if (ia || dhcpv6_opt(&data, OPT_IA_TA)) return -1;
info("DHCPv6: received INFORMATION_REQUEST, sending REPLY"); @@ -633,13 +658,14 @@ int dhcpv6(struct ctx *c, const struct pool *p, if (ia) resp.ia_na.iaid = ((struct opt_ia_na *)ia)->iaid;
- memcpy(&resp.client_id, client_id, - ntohs(client_id->l) + sizeof(struct opt_hdr)); + iov_to_buf(&client_id_base.iov[0], client_id_base.cnt, + client_id_base.off, &resp.client_id, + ntohs(client_id->l) + sizeof(struct opt_hdr));
n = offsetof(struct resp_t, client_id) + sizeof(struct opt_hdr) + ntohs(client_id->l); n = dhcpv6_dns_fill(c, (char *)&resp, n); - n = dhcpv6_client_fqdn_fill(p, c, (char *)&resp, n); + n = dhcpv6_client_fqdn_fill(&data, c, (char *)&resp, n);
resp.hdr.xid = mh->xid;
diff --git a/iov.c b/iov.c index ca6a35574f1b..76d261f804ca 100644 --- a/iov.c +++ b/iov.c @@ -109,7 +109,6 @@ size_t iov_from_buf(const struct iovec *iov, size_t iov_cnt, * * Returns: The number of bytes successfully copied. */ -/* cppcheck-suppress [staticFunction] */ size_t iov_to_buf(const struct iovec *iov, size_t iov_cnt, size_t offset, void *buf, size_t bytes) {
-- Stefano
Use packet_data() and extract headers using IOV_REMOVE_HEADER()
and IOV_PEEK_HEADER() rather than packet_get().
Signed-off-by: Laurent Vivier
On Wed, 4 Jun 2025 15:08:21 +0200
Laurent Vivier
Use packet_data() and extract headers using IOV_REMOVE_HEADER() and IOV_PEEK_HEADER() rather than packet_get().
Signed-off-by: Laurent Vivier
--- dhcp.c | 45 +++++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/dhcp.c b/dhcp.c index b0de04be6f27..7c92f30d0b39 100644 --- a/dhcp.c +++ b/dhcp.c @@ -302,27 +302,33 @@ static void opt_set_dns_search(const struct ctx *c, size_t max_len) */ int dhcp(const struct ctx *c, const struct pool *p) { - size_t mlen, dlen, offset = 0, opt_len, opt_off = 0; char macstr[ETH_ADDRSTRLEN]; + size_t mlen, dlen, opt_len; struct in_addr mask, dst; + struct ethhdr eh_storage; + struct iphdr iph_storage; + struct udphdr uh_storage; const struct ethhdr *eh; const struct iphdr *iph; const struct udphdr *uh; + struct iov_tail data; struct msg const *m; struct msg reply; unsigned int i; + struct msg mc;
Perhaps this could become msg_storage now? Or m_storage? Indentation shouldn't get much worse than it already is.
- eh = packet_get(p, 0, offset, sizeof(*eh), NULL); - offset += sizeof(*eh); + if (!packet_data(p, 0, &data)) + return -1;
- iph = packet_get(p, 0, offset, sizeof(*iph), NULL); + eh = IOV_REMOVE_HEADER(&data, eh_storage); + iph = IOV_PEEK_HEADER(&data, iph_storage); if (!eh || !iph) return -1;
- offset += iph->ihl * 4UL; - uh = packet_get(p, 0, offset, sizeof(*uh), &mlen); - offset += sizeof(*uh); + if (!iov_tail_drop(&data, iph->ihl * 4UL)) + return -1;
+ uh = IOV_REMOVE_HEADER(&data, uh_storage); if (!uh) return -1;
@@ -332,7 +338,9 @@ int dhcp(const struct ctx *c, const struct pool *p) if (c->no_dhcp) return 1;
- m = packet_get(p, 0, offset, offsetof(struct msg, o), &opt_len); + mlen = iov_tail_size(&data); + m = (struct msg const *)iov_remove_header_(&data, &mc, offsetof(struct msg, o), + __alignof__(struct msg)); if (!m || mlen != ntohs(uh->len) - sizeof(*uh) || mlen < offsetof(struct msg, o) || @@ -355,27 +363,28 @@ int dhcp(const struct ctx *c, const struct pool *p) memset(&reply.file, 0, sizeof(reply.file)); reply.magic = m->magic;
- offset += offsetof(struct msg, o); - for (i = 0; i < ARRAY_SIZE(opts); i++) opts[i].clen = -1;
- while (opt_off + 2 < opt_len) { - const uint8_t *olen, *val; + opt_len = iov_tail_size(&data); + while (opt_len >= 2) { + uint8_t olen_storage, type_storage; + const uint8_t *olen; uint8_t *type;
- type = packet_get(p, 0, offset + opt_off, 1, NULL); - olen = packet_get(p, 0, offset + opt_off + 1, 1, NULL); + type = IOV_REMOVE_HEADER(&data, type_storage); + olen = IOV_REMOVE_HEADER(&data, olen_storage); if (!type || !olen) return -1;
- val = packet_get(p, 0, offset + opt_off + 2, *olen, NULL); - if (!val) + opt_len = iov_tail_size(&data); + if (opt_len < *olen) return -1;
- memcpy(&opts[*type].c, val, *olen); + iov_to_buf(&data.iov[0], data.cnt, data.off, &opts[*type].c, *olen); opts[*type].clen = *olen; - opt_off += *olen + 2; + iov_tail_drop(&data, *olen); + opt_len -= *olen; }
opts[80].slen = -1;
-- Stefano
Use packet_data() and extract headers using IOV_REMOVE_HEADER()
and IOV_PEEK_HEADER() rather than packet_get().
Signed-off-by: Laurent Vivier
Use packet_data() and extract headers using IOV_PEEK_HEADER()
rather than packet_get().
Signed-off-by: Laurent Vivier
Use packet_data() and extract headers using IOV_REMOVE_HEADER()
and IOV_PEEK_HEADER() rather than packet_get().
Remove packet_get() as it is not used anymore.
Signed-off-by: Laurent Vivier
As we have removed packet_get(), we can rename packet_data() to packet_get()
as the name is clearer.
Signed-off-by: Laurent Vivier
The arp() function signature is changed to accept `struct iov_tail *data`
directly, replacing the previous `const struct pool *p` parameter.
Consequently, arp() no longer fetches packet data internally using
packet_data(), streamlining its logic.
This simplifies callers like tap4_handler(), which now pass the iov_tail
for the L2 ARP frame directly, removing intermediate pool handling.
Signed-off-by: Laurent Vivier
This patch refactors the dhcp() function to accept `struct iov_tail *data`
directly as its packet input, replacing the previous `const struct pool *p`
parameter. Consequently, dhcp() no longer fetches packet data internally
using packet_data().
This change simplifies callers, such as tap4_handler(), which now pass
the iov_tail representing the L2 frame directly to dhcp(). This removes
the need for intermediate packet pool handling for DHCP processing.
Signed-off-by: Laurent Vivier
This patch refactors the dhcpv6() function to accept `struct iov_tail *data`
directly as its packet input, replacing the `const struct pool *p` parameter.
Consequently, dhcpv6() no longer fetches packet data internally using
packet_data().
This change simplifies callers, such as tap6_handler(), which now pass
the iov_tail representing the L4 UDP segment (DHCPv6 message) directly.
This removes the need for intermediate packet pool handling.
Signed-off-by: Laurent Vivier
As the iov_tail has a non zero offset (because of the presence of
packet headers in the iov array), we must copy it to a new
iov array (using iov_tail_splice()) to pass it to sendmsg().
We cannot use anymore iov_tail_msghdr(), so remove it.
Signed-off-by: Laurent Vivier
The ndp() function signature is changed to accept `struct iov_tail *data`
directly, replacing the previous `const struct pool *p` and
`const struct icmp6hdr *ih` parameters.
This change simplifies callers, like tap6_handler(), which now provide
the iov_tail representing the L4 ICMPv6 segment directly to ndp().
Signed-off-by: Laurent Vivier
These macros are no longer used following the refactoring of packet
handlers to directly use iov_tail. Callers no longer require PACKET_POOL_P
for temporary pools, and PACKET_POOL can be replaced by PACKET_POOL_DECL
and separate initialization if needed.
Signed-off-by: Laurent Vivier
_buf is not used in the macro. Remove it.
Remove it also from PACKET_POOL_NOINIT() as it was needed
for PACKET_POOL_DECL().
Signed-off-by: Laurent Vivier
This patch introduces a dedicated `struct vdev_memory *memory` field
to the `pool` structure. This field explicitly stores memory region
information, primarily for vhost-user mode, replacing the previous
hack of using `pool->buf` and `pool->buf_size == 0` for this purpose.
Pool initialization macros and `vu_packet_check_range` are updated
to use this new `memory` field, leading to cleaner code.
Signed-off-by: Laurent Vivier
In vhost-user mode, where `pool->buf` doesn't store packet data directly,
this patch repurposes it to hold the `struct iovec` array describing
a scattered packet. This enables pools to manage true scatter-gather
descriptors, with iovecs pointing to guest memory.
`p->pkt[idx].iov_base` is now an index into this `pool->buf` iovec array,
and `p->pkt[idx].iov_len` is the count of iovecs. `packet_add_do` uses
`iov_tail_slice` to store these iovec descriptors from an input iov_tail
into `pool->buf`. `packet_data_do` reconstructs the iov_tail for a
packet by pointing to this stored array of iovecs.
Signed-off-by: Laurent Vivier
participants (2)
-
Laurent Vivier
-
Stefano Brivio