?
/**
@file net_compat.h
@brief Network based events compat functions
@details Copyright (c) 2025 Acronis International GmbH
@author Denis Kopyrin (denis.kopyrin@acronis.com)
@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;
}