• 内核中用于数据接收的结构体struct msghdr(转)


    内核中用于数据接收的结构体struct msghdr(转)  

      我们从一个实际的数据包发送的例子入手,来看看其发送的具体流程,以及过程中涉及到的相关数据结构。在我们的虚拟机上发送icmp回显请求包,ping另一台主机172.16.48.1。我们使用系统调用sendto发送这个icmp包。

         ssize_t sendto(int s, const void *buf, size_t len, int flags,

                            const struct sockaddr *to, socklen_t tolen);

        s就是socket的fd。buf是待发送的icmp数据包,len是buf的长度。flags可以是下列标志位的位或:

        MSG_DONTROUTE

        在发送分组时,不使用网关,只有直接连接在本网络中的主机才能接收到数据。这个标志通常仅用于诊断和路由程序。可路由的协议族才能使用这个标志;packet sockets不可以使用。

        MSG_DONTWAIT

        使用非阻塞操作。

        MSG_NOSIGNAL

        当流式套接字的另一端中断连接时不发送SIGPIPE信号,但仍然返回EPIPE错误。

        MSG_CONFIRM (仅用于Linux 2.3以上版本)

        通知链路层发生了转发过程:得到了另一端的成功应答。如果链路层没有收到通知,它将按照常规探测网络上的相邻主机(比如通过免费arp)。只能用于 SOCK_DGRAM和SOCK_RAW类型的套接字,且仅对IPv4和IPv6有效。

        除此之外,还有MSG_MORE,MSG_OOB,MSG_EOR。

        to是这个数据包发送的目地端地址,tolen是to的长度。

        系统调用sendto最终调用内核函数:

            asmlinkage long sys_sendto(int fd, void __user * buff, size_t len,

                            unsigned flags, struct sockaddr __user *addr, int addr_len)

        sys_sendto构建一个结构体struct msghdr,用于接收来自应用层的数据包,下面是结构体struct msghdr的定义:

            struct msghdr {

                void            *msg_name;

                int             msg_namelen;

                struct iovec    *msg_iov;

                __kernel_size_t msg_iovlen;

                void            *msg_control;

                __kernel_size_t msg_controllen;

                unsigned        msg_flags;

            };

        这个结构体的内容可以分为四组。

        第一组是msg_name和msg_namelen,记录这个消息的名字,其实就是数据包的目的地址。msg_name是指向一个结构体struct sockaddr的指针。长度为16:

            struct sockaddr{

                sa_family_t sa_family;

                char        sa_addr[14];

            }

    所以,msg_namelen的长度为16。需要注意的是,结构体struct sockaddr只在进行参数传递时使用,无论是在用户态还是在内核态,我们都把其强制转化为结构体struct sockaddr_in:

            strcut sockaddr_in{

                sa_family_t         sin_family;

                unsigned short int  sin_port;

                struct in_addr      sin_addr;

                unsigned char       __pad[__SOCK_SIZE__ - sizeof(short int) -

                                    sizeof(unsigned short int) - sizeof(struct in_addr)];

            };

            struct in_addr{

                __u32 s_addr;

            }

    __SOCK_SIZE__的值为16,所以,struct sockaddr中真正有用的数据只有8bytes。在我们的ping例子中,传入到内核的msghdr结构中:

        msg.msg_name = { sa_family_t = MY_AF_INET, sin_port = 0, sin_addr.s_addr = 172.16.48.1 }

        msg_msg_namelen = 16。

    请求回显icmp包没有目的端地址的端口号。

        第二组是msg_iov和msg_iovlen,记录这个消息的内容。msg_iov是一个指向结构体struct iovec的指针,实际上,确切地说,应该是一个结构体strcut iovec的数组。下面是该结构体的定义:

        struct iovec{

            void __user     *iov_base;

            __kernel_size_t iov_len;

        };

        iov_base指向数据包缓冲区,即参数buff,iov_len是buff的长度。msghdr中允许一次传递多个buff,以数组的形式组织在 msg_iov中,msg_iovlen就记录数组的长度(即有多少个buff)。在我们的ping程序的实例中:

        msg.msg_iov = { struct iovec = { iov_base = { icmp头+填充字符'E' }, iov_len = 40 } }

        msg.msg_len = 1

        第三组是msg_control和msg_controllen,它们可被用于发送任何的控制信息,在我们的例子中,没有控制信息要发送。暂时略过。

        第四组是msg_flags。其值即为传入的参数flags。raw协议不支持MSG_OOB标志,即带外数据。

  • 相关阅读:
    ubuntu18.04 常用命令
    docker常用命令
    git
    y7000 intel nvidia 双显卡安装Ubuntu16.04
    linux中fork() 函数详解
    理解GBN协议
    C++ sort
    最近点对-分治
    方便查看 linux/kernel/system_call.s
    方便查看 linux/kernel/asm.s
  • 原文地址:https://www.cnblogs.com/aspirant/p/4097333.html
Copyright © 2020-2023  润新知