// SPDX-License-Identifier: GPL-2.0-or-later /* PASST - Plug A Simple Socket Transport * for qemu/UNIX domain socket mode * * PASTA - Pack A Subtle Tap Abstraction * for network namespace/tap device mode * * packet.c - Packet abstraction: add packets to pool, flush, get packet data * * Copyright (c) 2020-2021 Red Hat GmbH * Author: Stefano Brivio */ #include #include #include #include #include "packet.h" #include "util.h" #include "log.h" /** * packet_check_range() - Check if a memory range is valid for a pool * @p: Packet pool * @ptr: Start of desired data range * @len: Length of desired data range * @func: For tracing: name of calling function * @line: For tracing: caller line of function call * * Return: 0 if the range is valid, -1 otherwise */ static int packet_check_range(const struct pool *p, const char *ptr, size_t len, const char *func, int line) { if (len > PACKET_MAX_LEN) { debug("packet range length %zu (max %zu), %s:%i", len, PACKET_MAX_LEN, func, line); return -1; } if (p->memory) { int ret; ret = vu_packet_check_range(p->memory, ptr, len); if (ret == -1) debug("cannot find region, %s:%i", func, line); return ret; } if (ptr < p->buf) { debug("packet range start %p before buffer start %p, %s:%i", (void *)ptr, (void *)p->buf, func, line); return -1; } if (len > p->buf_size) { debug("packet range length %zu larger than buffer %zu, %s:%i", len, p->buf_size, func, line); return -1; } if ((size_t)(ptr - p->buf) > p->buf_size - len) { debug("packet range %p, len %zu after buffer end %p, %s:%i", (void *)ptr, len, (void *)(p->buf + p->buf_size), func, line); return -1; } return 0; } /** * pool_full() - Is a packet pool full? * @p: Pointer to packet pool * * Return: true if the pool is full, false if more packets can be added */ bool pool_full(const struct pool *p) { return p->count >= p->size; } /** * packet_iov_max_cnt() - Return the maximum number of iovec we can store * @p: Pointer to packet pool * * Return: the maximum number of iovec we can store in the memory of * the pool buffer */ static size_t packet_iov_max_cnt(const struct pool *p) { return p->buf_size / sizeof(struct iovec); } /** * packet_iov_idx() - For a given packet index, return the iovec index and * the number of iovec entry of the packet * @p: Pointer to packet pool * @idx: Index of packet descriptor in pool * @iov_cnt: Pointer to store the number of the iovec entry of the packet * @func: For tracing: name of calling function * @line: For tracing: caller line of function call * * Return: the iovec index for the given packet index, @iov_cnt is set * to the number of the iovec entry of the packet */ static int packet_iov_idx(const struct pool *p, size_t idx, size_t *iov_cnt, const char *func, int line) { size_t iov_idx, max = packet_iov_max_cnt(p); iov_idx = (size_t)p->pkt[idx].iov_base; *iov_cnt = p->pkt[idx].iov_len; ASSERT_WITH_MSG(iov_idx + *iov_cnt <= max, "Corrupt iov entry: (%zu, %zu), max: %zu, %s:%i", iov_idx, *iov_cnt, max, func, line); return iov_idx; } /** * packet_iov_next_idx() - Give the the next available iovec index * @p: Pointer to packet pool * @idx: Index of packet descriptor in pool * @func: For tracing: name of calling function * @line: For tracing: caller line of function call * * Return: the next available iovec index */ static size_t packet_iov_next_idx(const struct pool *p, size_t idx, const char *func, int line) { size_t iov_idx, iov_cnt; if (idx == 0) return 0; iov_idx = packet_iov_idx(p, idx - 1, &iov_cnt, func, line); return iov_idx + iov_cnt; } /** * packet_iov_data() - For a given packet index, provide the iovec array * @p: Pointer to packet pool * @idx: Index of packet descriptor in pool * @data: iov_tail to store iovec array and count * (offset is always set to 0) * @func: For tracing: name of calling function * @line: For tracing: caller line of function call */ static void packet_iov_data(const struct pool *p, size_t idx, struct iov_tail *data, const char *func, int line) { struct iovec *iov = (struct iovec *)p->buf; size_t iov_idx, iov_cnt; iov_idx = packet_iov_idx(p, idx, &iov_cnt, func, line); data->iov = &iov[iov_idx]; data->cnt = iov_cnt; data->off = 0; } /** * packet_iov_check_range() - Check if iovec array is valid for a pool * @p: Pointer to packet pool * @data: iov_tail that stores the iovec array to check * @func: For tracing: name of calling function * @line: For tracing: caller line of function call * * Return: 0 if the range is valid, -1 otherwise */ static int packet_iov_check_range(const struct pool *p, const struct iov_tail *data, const char *func, int line) { size_t offset, i; offset = data->off; for (i = 0; i < data->cnt; i++) { int ret; ret = packet_check_range(p, (char *)data->iov[i].iov_base + offset, data->iov[i].iov_len - offset, func, line); if (ret) return ret; offset = 0; } return 0; } /** * packet_add_do() - Add data as packet descriptor to given pool * @p: Existing pool * @data: Data to add * @func: For tracing: name of calling function * @line: For tracing: caller line of function call */ void packet_add_do(struct pool *p, struct iov_tail *data, const char *func, int line) { size_t idx = p->count; const char *start; size_t len; if (pool_full(p)) { debug("add packet index %zu to pool with size %zu, %s:%i", idx, p->size, func, line); return; } if (!iov_tail_prune(data)) return; if (packet_iov_check_range(p, data, func, line)) return; if (p->memory) { size_t iov_max_cnt = packet_iov_max_cnt(p); struct iovec *iov = (struct iovec *)p->buf; size_t iov_idx; int iov_cnt; iov_idx = packet_iov_next_idx(p, idx, func, line); iov_cnt = iov_tail_slice(&iov[iov_idx], iov_max_cnt - iov_idx, data, NULL); if (iov_cnt < 0) { debug("add iov (%zu,%zu) to buf with size %zu, %s:%i", iov_idx, data->cnt, iov_max_cnt, func, line); return; } len = iov_cnt; /* NOLINTNEXTLINE(performance-no-int-to-ptr) */ start = (char *)iov_idx; } else { len = data->iov[0].iov_len - data->off; start = (char *)data->iov[0].iov_base + data->off; } p->pkt[idx].iov_base = (void *)start; p->pkt[idx].iov_len = len; p->count++; } /** * packet_data_do() - Get data range from packet descriptor from given pool * @p: Packet pool * @idx: Index of packet descriptor in pool * @data: IOV tail to store the address of the data (output) * @func: For tracing: name of calling function, NULL means no trace() * @line: For tracing: caller line of function call * * Return: true if @data contains valid data, false otherwise */ bool packet_data_do(const struct pool *p, size_t idx, struct iov_tail *data, const char *func, int line) { ASSERT_WITH_MSG(p->count <= p->size, "Corrupt pool count: %zu, size: %zu, %s:%i", p->count, p->size, func, line); if (idx >= p->count) { debug("packet %zu from pool size: %zu, count: %zu, " "%s:%i", idx, p->size, p->count, func, line); return false; } if (p->memory) { packet_iov_data(p, idx, data, func, line); } else { data->cnt = 1; data->off = 0; data->iov = &p->pkt[idx]; } ASSERT_WITH_MSG(!packet_iov_check_range(p, data, func, line), "Corrupt packet pool, %s:%i", func, line); return true; } /** * pool_flush() - Flush a packet pool * @p: Pointer to packet pool */ void pool_flush(struct pool *p) { p->count = 0; }