LINUX.ORG.RU

куда пойдём мы с recvfrom?

 


0

2

Ну вот, recvfrom это системный вызов.

  ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                struct sockaddr *src_addr, socklen_t *addrlen);
находится он в net/socket.c.
Если (struct sockaddr *) есть в переданных параметрах,
то она сохраняется в структуру (struct msghdr). Я пытаюсь отследить где эта структура получает значения.
msghdr передаётся в качестве аргумента в sock_recvmsg.
sock_recvmsg есть в этом же файле. Эта функция вызывает другую функцию security_socket_recvmsg, которая находится в security/security.c, которая в свою очередь возвращает определение call_int_hook.
int security_socket_recvmsg(struct socket *sock, struct msghdr *msg,
                            int size, int flags)
{
        return call_int_hook(socket_recvmsg, 0, sock, msg, size, flags);
}
Как понять это определение?
#define call_int_hook(FUNC, IRC, ...) ({                        \
        int RC = IRC;                                           \
        do {                                                    \
                struct security_hook_list *P;                   \
                                                                \
                list_for_each_entry(P, &security_hook_heads.FUNC, list) { \
                        RC = P->hook.FUNC(__VA_ARGS__);         \
                        if (RC != 0)                            \
                                break;                          \
                }                                               \
        } while (0);                                            \
        RC;                                                     \
})
struct security_hook_list находится в include/linux/lsm_hooks.h.
struct security_hook_list {
        struct list_head                list;
        struct list_head                *head;
        union security_list_options     hook;
};
Также struct security_hook_heads.
struct security_hook_heads {
  ...
#ifdef CONFIG_SECURITY_NETWORK
  ...
        struct list_head socket_recvmsg;
  ...
};
Я так и не понел, заполняется здесь структура msghdr?
Дальше в net/socket.c.
int sock_recvmsg(struct socket *sock, struct msghdr *msg, int flags)
{
        int err = security_socket_recvmsg(sock, msg, msg_data_left(msg), flags);

        return err ?: sock_recvmsg_nosec(sock, msg, flags);
}
Так как security_socket_recvmsg уже вызывался, следует функция sock_recvmsg_nosec, если нет ошибки(я правильно понимаю?).
Но функция sock_recvmsg_nosec
static inline int sock_recvmsg_nosec(struct socket *sock, struct msghdr *msg,
                                     int flags)
{
        return sock->ops->recvmsg(sock, msg, msg_data_left(msg), flags);
}
вызывает sock->ops->recvmsg
Вот struct socket из include/linux/net.h.
struct socket {
        socket_state            state;

        kmemcheck_bitfield_begin(type);
        short                   type;
        kmemcheck_bitfield_end(type);

        unsigned long           flags;

        struct socket_wq __rcu  *wq;

        struct file             *file;
        struct sock             *sk;
        const struct proto_ops  *ops;
};
Вот ops из const struct proto_ops.
struct proto_ops {
  ...
         * ===============================
         * msg->msg_namelen should get updated by the recvmsg handlers
         * iff msg_name != NULL. It is by default 0 to prevent
         * returning uninitialized memory to user space.  The recvfrom
         * handlers can assume that msg.msg_name is either NULL or has
         * a minimum size of sizeof(struct sockaddr_storage).
         */
        int             (*recvmsg)   (struct socket *sock, struct msghdr *m,
                                      size_t total_len, int flags);
  ...
Вот на всякий случай функция recvfrom.
SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, ubuf, size_t, size,
                unsigned int, flags, struct sockaddr __user *, addr,
                int __user *, addr_len)
{
        struct socket *sock;
        struct iovec iov;
        struct msghdr msg;
        struct sockaddr_storage address;
        int err, err2;
        int fput_needed;

        err = import_single_range(READ, ubuf, size, &iov, &msg.msg_iter);
        if (unlikely(err))
                return err;
        sock = sockfd_lookup_light(fd, &err, &fput_needed);
        if (!sock)
                goto out;

        msg.msg_control = NULL;
        msg.msg_controllen = 0;
        /* Save some cycles and don't copy the address if not needed */
        msg.msg_name = addr ? (struct sockaddr *)&address : NULL;
        /* We assume all kernel code knows the size of sockaddr_storage */
        msg.msg_namelen = 0;
        msg.msg_iocb = NULL;
        if (sock->file->f_flags & O_NONBLOCK)
                flags |= MSG_DONTWAIT;
        err = sock_recvmsg(sock, &msg, flags);

        if (err >= 0 && addr != NULL) {
                err2 = move_addr_to_user(&address,
                                         msg.msg_namelen, addr, addr_len);
                if (err2 < 0)
                        err = err2;
        }

        fput_light(sock->file, fput_needed);
out:
        return err;
}
Как мне найти код, где заполняются struct sockaddr * данные?

Я нихрена не понял. Ты хочешь пример заполнения struct sockaddr_{in,in6,un,etc} и вызова recvfrom или что?

hateyoufeel ★★★★★ ()
Ответ на: комментарий от hateyoufeel

функция recvfrom после выполнения содержит заполненую структуру (struct sockaddr *), я хочу узнать где эта функция заполняет эту структуру.
Вот же написано.

Если (struct sockaddr *) есть в переданных параметрах, то она сохраняется в структуру (struct msghdr). Я пытаюсь отследить где эта структура получает значения.

u0atgKIRznY5 ()

-> err2 = move_addr_to_user(&address, msg.msg_namelen, addr, addr_len);

anonymous ()
Ответ на: комментарий от anonymous

addr для пользовательского простанства. А

        struct sockaddr_storage address;
хранит то что нужно, но, это копируется уже в простанство пользователя, а нужно узнать где копируется в address. Да и к тому же что если я захочу отправить в recvfrom struct sockaddr_ll, там ведь вернётся не только адрес, но и другие нужные данные.
static int move_addr_to_user(struct sockaddr_storage *kaddr, int klen,
                             void __user *uaddr, int __user *ulen)
{
        int err;
        int len;

        BUG_ON(klen > sizeof(struct sockaddr_storage));
        err = get_user(len, ulen);
        if (err)
                return err;
        if (len > klen)
                len = klen;
        if (len < 0)
                return -EINVAL;
        if (len) {
                if (audit_sockaddr(klen, kaddr))
                        return -ENOMEM;
                if (copy_to_user(uaddr, kaddr, len))
                        return -EFAULT;
        }
        /*
         *      "fromlen shall refer to the value before truncation.."
         *                      1003.1g
         */
        return __put_user(klen, ulen);
}
Тут ясно видно что копируется уже struct sockaddr_storage
Вот после этого
        msg.msg_name = addr ? (struct sockaddr *)&address : NULL;
если в какой нибудь функции запишется msg.msg_name, то address будет заполнен.
Единственный маршрут через который можно узнать, это пойти в функцию
        err = sock_recvmsg(sock, &msg, flags);
Например в include/uapi/asm-generic/unistd.h есть определения
#define __NR_recvfrom 207
__SC_COMP(__NR_recvfrom, sys_recvfrom, compat_sys_recvfrom)
Вот например
#define __NR_recvmsg 212
__SC_COMP(__NR_recvmsg, sys_recvmsg, compat_sys_recvmsg)
Функция recv использует recvfrom
/*
 *      Receive a datagram from a socket.
 */

SYSCALL_DEFINE4(recv, int, fd, void __user *, ubuf, size_t, size,
                unsigned int, flags)
{
        return sys_recvfrom(fd, ubuf, size, flags, NULL, NULL);
}

Итак в net/socket.c есть функция

SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)
в ней есть
        switch (call) {
  ...
        case SYS_RECVFROM:
                err = sys_recvfrom(a0, (void __user *)a1, a[2], a[3],
                                   (struct sockaddr __user *)a[4],
                                   (int __user *)a[5]);
                break;
 ...
Но sys_recvfrom я не нашёл. А msghdr ведь надо как то заполнить.
И тут в дело вступает из include/uapi/asm-generic/unistd.h sys_recvmsg.
Но как? Она вызывается раньше чем recv_msg?
Если зайти в sys_recvmsg, то там начинается вот что
SYSCALL_DEFINE3(recvmsg, int, fd, struct user_msghdr __user *, msg,
                unsigned int, flags)
{
        if (flags & MSG_CMSG_COMPAT)
                return -EINVAL;
        return __sys_recvmsg(fd, msg, flags);
}
long __sys_recvmsg(int fd, struct user_msghdr __user *msg, unsigned flags)
{
        int fput_needed, err;
        struct msghdr msg_sys;
        struct socket *sock;

        sock = sockfd_lookup_light(fd, &err, &fput_needed);
        if (!sock)
                goto out;

        err = ___sys_recvmsg(sock, msg, &msg_sys, flags, 0);

        fput_light(sock->file, fput_needed);
out:
        return err;
}
Ну и почти долгожданная функция.
static int ___sys_recvmsg(struct socket *sock, struct user_msghdr __user *msg,
                         struct msghdr *msg_sys, unsigned int flags, int nosec)
{
        struct compat_msghdr __user *msg_compat =
            (struct compat_msghdr __user *)msg;
        struct iovec iovstack[UIO_FASTIOV];
        struct iovec *iov = iovstack;
        unsigned long cmsg_ptr;
        int len;
        ssize_t err;

        /* kernel mode address */
        struct sockaddr_storage addr;

        /* user mode address pointers */
        struct sockaddr __user *uaddr;
        int __user *uaddr_len = COMPAT_NAMELEN(msg);

        msg_sys->msg_name = &addr;
        if (MSG_CMSG_COMPAT & flags)
                err = get_compat_msghdr(msg_sys, msg_compat, &uaddr, &iov);
        else
                err = copy_msghdr_from_user(msg_sys, msg, &uaddr, &iov);
        if (err < 0)
                return err;
  ...
Там дальше проверки идут разные, но вот функция get_compat_msghdr из net/compat.c стоит внимания.
int get_compat_msghdr(struct msghdr *kmsg,
                      struct compat_msghdr __user *umsg,
                      struct sockaddr __user **save_addr,
                      struct iovec **iov)
{
        compat_uptr_t uaddr, uiov, tmp3;
        compat_size_t nr_segs;
        ssize_t err;

        if (!access_ok(VERIFY_READ, umsg, sizeof(*umsg)) ||
            __get_user(uaddr, &umsg->msg_name) ||
            __get_user(kmsg->msg_namelen, &umsg->msg_namelen) ||
            __get_user(uiov, &umsg->msg_iov) ||
            __get_user(nr_segs, &umsg->msg_iovlen) ||
            __get_user(tmp3, &umsg->msg_control) ||
            __get_user(kmsg->msg_controllen, &umsg->msg_controllen) ||
            __get_user(kmsg->msg_flags, &umsg->msg_flags))
                return -EFAULT;
        if (!uaddr)
                kmsg->msg_namelen = 0;

        if (kmsg->msg_namelen < 0)
                return -EINVAL;

        if (kmsg->msg_namelen > sizeof(struct sockaddr_storage))
                kmsg->msg_namelen = sizeof(struct sockaddr_storage);
        kmsg->msg_control = compat_ptr(tmp3);

        if (save_addr)
                *save_addr = compat_ptr(uaddr);

        if (uaddr && kmsg->msg_namelen) {
                if (!save_addr) {
                        err = move_addr_to_kernel(compat_ptr(uaddr),
                                                  kmsg->msg_namelen,
                                                  kmsg->msg_name);
                        if (err < 0)
                                return err;
                }
        } else {
                kmsg->msg_name = NULL;
                kmsg->msg_namelen = 0;
        }

        if (nr_segs > UIO_MAXIOV)
                return -EMSGSIZE;

        kmsg->msg_iocb = NULL;

        return compat_import_iovec(save_addr ? READ : WRITE,
                                   compat_ptr(uiov), nr_segs,
                                   UIO_FASTIOV, iov, &kmsg->msg_iter);
}
Если здесь заполняется структура, то где ?

u0atgKIRznY5 ()

Что-то я наверно не то нашёл. Ведь эти функции явно различаются по параметрам.

       ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);

       ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

Тогда я всё равно наверное незнаю где заполняется структура в пространстве ядра, не то, когда готовое копируется для пространства пользователя.

u0atgKIRznY5 ()

Посмотри, например, net/ipv4/udp.c:udp_recvmsg.

	/* Copy the address. */
	if (sin) {
		sin->sin_family = AF_INET;
		sin->sin_port = udp_hdr(skb)->source;
		sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
		memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
		*addr_len = sizeof(*sin);
	}
Аналогично для tcp в net/ipv4/tcp_ipv4.c.

Алсо, нафига тебе это?

hateyoufeel ★★★★★ ()
Последнее исправление: hateyoufeel (всего исправлений: 2)
Ответ на: комментарий от hateyoufeel

Чтобы знать, интересно. Вот например я вот так пишу

#include <linux/if_packet.h>
#include <net/if.h>

sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP))
struct sockaddr_ll sll; 

char buf[1500];
int size = sizeof(struct sockaddr_ll);

recvfrom(sock, &buf, 1500, 0, (struct sockaddr *)&sll, &size);

char interface[15];

if_indextoname(sll.sll_ifindex, interface);
У меня socket пакет, через recvfrom в sll сохраняется вся информация.
Я наверное нашёл это в net/packet/af_packet.c,
функция packet_rvc.
static int packet_rcv(struct sk_buff *skb, struct net_device *dev,
                      struct packet_type *pt, struct net_device *orig_dev)
{
Здесь заполняется структура sockaddr_ll,
Но как recvfrom получает данные структуры, наверное она узнаёт у socket какую функцию использовать для получения данных, значит, где-то это написано, но где?

u0atgKIRznY5 ()
Ответ на: комментарий от u0atgKIRznY5

Но как recvfrom получает данные структуры, наверное она узнаёт у socket какую функцию использовать для получения данных, значит, где-то это написано, но где?

С структуре с данными сокета, очевидно.

hateyoufeel ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.