/** @file net_compat.h @brief Network based events compat functions @details Copyright (c) 2025 Acronis International GmbH @author Denis Kopyrin ([email protected]) @since $Id: $ */ #pragma once #ifdef KERNEL_MOCK #include "mock/mock_net.h" #endif #include <linux/net.h> #include <linux/socket.h> #include <linux/types.h> #include <linux/uaccess.h> #include <linux/uio.h> // It is kind of a magic variable for inet and inet6. // If it is !0, it will refer to peer and if it is !1, it will always provide addr #define PEER_LOCAL 0 #define PEER_REMOTE_ALWAYS 2 static inline int sock_to_addr(struct socket *sock, struct sockaddr_storage* storage, int peer) { #ifdef HAVE_LEN_IN_SOCK_GETNAME int addr_len; const int error = sock->ops->getname(sock, (struct sockaddr*) storage, &addr_len, peer); if (error) return 0; else return addr_len; #else int addr_len = sock->ops->getname(sock, (struct sockaddr*) storage, peer); if (addr_len < 0) return 0; else return addr_len; #endif } #ifndef HAVE_IOV_ITER // Tries to copy at least 'len' iov from userspace. // Inspired by 'memcpy_fromiovecend' but improved to provide 'iov_len' check and return 'len' static inline size_t copy_from_user_iovec_safe(unsigned char *kdata, const struct iovec *iov, size_t iov_len, size_t len) { size_t orig_len = len; while (len > 0 && iov_len) { size_t copy = len; if (copy > iov->iov_len) copy = iov->iov_len; if (copy_from_user(kdata, iov->iov_base, copy)) return 0; len -= copy; kdata += copy; iov++; iov_len--; } // len will go to 0 and indicates of data amount left to copy, hence actually copied is difference below return orig_len - len; } #endif #if defined(HAVE_IOV_ITER_IN_MSGHDR) && !defined(HAVE_IOV_ITER_REVERT) // Copied from Linux 4.11 without support for ITER_PIPE static void iov_iter_revert(struct iov_iter *i, size_t unroll) { if (!unroll) return; i->count += unroll; if (unroll <= i->iov_offset) { i->iov_offset -= unroll; return; } unroll -= i->iov_offset; if (i->type & ITER_BVEC) { const struct bio_vec *bvec = i->bvec; while (1) { size_t n = (--bvec)->bv_len; i->nr_segs++; if (unroll <= n) { i->bvec = bvec; i->iov_offset = n - unroll; return; } unroll -= n; } } else { /* same logics for iovec and kvec */ const struct iovec *iov = i->iov; while (1) { size_t n = (--iov)->iov_len; i->nr_segs++; if (unroll <= n) { i->iov = iov; i->iov_offset = n - unroll; return; } unroll -= n; } } } #endif static inline size_t msg_data_peek(struct msghdr *msg, void* buf, size_t amount) { size_t copied_size = 0; #ifdef HAVE_IOV_ITER_IN_MSGHDR struct iov_iter* iter = &msg->msg_iter; // Do not try peek tricks with ITER_PIPE! It will discard the bytes from pipe so do not allow peeking at all. #ifdef HAVE_IOV_ITER_IS_PIPE if (iov_iter_is_pipe(iter)) return 0; #elif defined(HAVE_IOV_ITER_PIPE) if (iter->type & ITER_PIPE) return 0; #endif copied_size = copy_from_iter(buf, amount, iter); if (copied_size > 0) { iov_iter_revert(iter, copied_size); } #else if (msg->msg_iov) copied_size = copy_from_user_iovec_safe(buf, msg->msg_iov, msg->msg_iovlen, amount); else copied_size = 0; #endif return copied_size; }