zoukankan      html  css  js  c++  java
  • netlink通信

    1:参考网址

    2:netlink相关socket API

          netlink用于程序与内核模块之间进行通信。用户态使用netlink与内核态使用netlink方式不同,Linux container在网络管理这一块处于用户空间。netlink 在用户态的API与tcp/ip通信使用的socket api 类似,主要api为socket()、bind()、sendmsg()、recvmsg().

    • socket()

          使用netlink需要包含的头文件为linux/netlink.h和sys/socket.h(socket api头文件)。创建socket函数。

    int socket(AF_NETLNK,SOCK_RAW,netlink_type)

        创建socket时地址族选择AF_NETLINK或者PF_NETLINK。第二个参数还可以是SOCK_DGRAM。第三个参数指定netlink协议类型。socket所有的地址族有以下所有类型:
    AF_INET               IPv4协议族
    AF_INET6              IPv6协议族
    AF_LOCAL              Unix域协议
    AF_ROUTE              路由套接字
    AF_KEY                密钥套接字

         有时AF也用PF代替,PF代表 Protocol Family(协议族),AF代表Address Familiy(地址族)。

         第二个参数指明通信字节流类型,其取值如SOCK_STREAM(tcp方式),SOCK_DGRAM(udp方式)、SOCK_RAW(原始套接口)、SOCK_PACKET(支持数据链路访问)。

         netlink_type类型如下:

    #define NETLINK_ROUTE           0       /* Routing/device hook                          */
    #define NETLINK_W1              1       /* 1-wire subsystem                             */
    #define NETLINK_USERSOCK        2       /* Reserved for user mode socket protocols      */
    #define NETLINK_FIREWALL        3       /* Firewalling hook                             */
    #define NETLINK_INET_DIAG       4       /* INET socket monitoring                       */
    #define NETLINK_NFLOG           5       /* netfilter/iptables ULOG */
    #define NETLINK_XFRM            6       /* ipsec */
    #define NETLINK_SELINUX         7       /* SELinux event notifications */
    #define NETLINK_ISCSI           8       /* Open-iSCSI */
    #define NETLINK_AUDIT           9       /* auditing */
    #define NETLINK_FIB_LOOKUP      10
    #define NETLINK_CONNECTOR       11
    #define NETLINK_NETFILTER       12      /* netfilter subsystem */
    #define NETLINK_IP6_FW          13
    #define NETLINK_DNRTMSG         14      /* DECnet routing messages */
    #define NETLINK_KOBJECT_UEVENT  15      /* Kernel messages to userspace */
    #define NETLINK_GENERIC         16

         在linux container中,关于netlink中,使用的netlink协议类型为NETLINK_ROUTE。

    • bind()

          bind函数是将本地协议地址赋给一个套接字。

    int bind(int sockfd,const struct sockaddr *myaddr,socklen_t addrlen)

         在TCP/IP模型中, sockfd为创建的套接字描述符,myaddr为要绑定到套接字的地址,addrlen为struct sockaddr长度。bind函数把一个特定的IP地址捆绑到它的套接字上,这个IP地址必须属于主机所在的网络接口之一。即myaddr为本地源IP地址和所用的端口。针对TCP客户端,这指定了在该套接字上发送的IP数据报指派了源IP地址。对于TCP服务器,这限定了该套接字只接收那些目的地为这个IP地址的客户连接。

          针对netlink,bind函数的myaddr为struct sockaddr_nl。struct sockaddr_nl结构体如下

    struct sockaddr_nl
    {
      sa_family_t    nl_family;  
      unsigned short nl_pad;
      __u32          nl_pid;
      __u32          nl_groups;
    };

         字段nl_family必须设置为AF_NETLINK或者PF_NETLINK。字段nl_pad当前没有用,设置为0。nl_pid为0时表示接收或者发送消息的进程pid。当nl_pid为0时表示处理消息的为内核。字段nl_groups用于指定多播组,bind 函数用于把调用进程加入到该字段指定的多播组,如果设置为 0,表示调用者不加入任何多播组。

         在进行bind函数调用时,myaddr为本地地址,其中,myaddr.nl_pid为本地进程pid,myaddr.nl_groups为0,表示本地进程不加入任何多播组,否则本地进程加入多播组。

    • 发送消息、接收消息
    int sendmsg(int s, const struct msghdr *msg, unsigned int flags);
    int recvmsg(int s, struct msghdr *msg, unsigned int flags);

          其中s为socket描述符,msg为消息,flags为标志,

          消息头struct nlmsghdr

    struct nlmsghdr
    {
      __u32 nlmsg_len;   /* Length of message */
      __u16 nlmsg_type;  /* Message type*/
      __u16 nlmsg_flags; /* Additional flags */
      __u32 nlmsg_seq;   /* Sequence number */
      __u32 nlmsg_pid;   /* Sending process PID */
    };

         nlmsg_len为消息长度,包括紧跟该结构的数据部分长度和该结构大小。nlmsg_type用于应用内部定义的消息类型,它对netlink内核实现是透明的,因此大部分情况下设置为0。nlmsg_flags为消息标志,可用标志为以下内容:

    /* Flags values */
    #define NLM_F_REQUEST           1       /* It is request message.       */
    #define NLM_F_MULTI             2       /* Multipart message, terminated by NLMSG_DONE */
    #define NLM_F_ACK               4       /* Reply with ack, with zero or error code */
    #define NLM_F_ECHO              8       /* Echo this request            */
    /* Modifiers to GET request */
    #define NLM_F_ROOT      0x100   /* specify tree root    */
    #define NLM_F_MATCH     0x200   /* return all matching  */
    #define NLM_F_ATOMIC    0x400   /* atomic GET           */
    #define NLM_F_DUMP      (NLM_F_ROOT|NLM_F_MATCH)
    /* Modifiers to NEW request */
    #define NLM_F_REPLACE   0x100   /* Override existing            */
    #define NLM_F_EXCL      0x200   /* Do not touch, if it exists   */
    #define NLM_F_CREATE    0x400   /* Create, if it does not exist */
    #define NLM_F_APPEND    0x800   /* Add to end of list           */

    标志NLM_F_REQUEST用于表示消息是一个请求,所有应用首先发起的消息都应设置该标志。

    标志NLM_F_MULTI 用于指示该消息是一个多部分消息的一部分,后续的消息可以通过宏NLMSG_NEXT来获得。

    宏NLM_F_ACK表示该消息是前一个请求消息的响应,顺序号与进程ID可以把请求与响应关联起来。

    标志NLM_F_ECHO表示该消息是相关的一个包的回传。

    标志NLM_F_ROOT 被许多 netlink 协议的各种数据获取操作使用,该标志指示被请求的数据表应当整体返回用户应用,而不是一个条目一个条目地返回。有该标志的请求通常导致响应消息设置 NLM_F_MULTI标志。注意,当设置了该标志时,请求是协议特定的,因此,需要在字段 nlmsg_type 中指定协议类型。

    标志 NLM_F_MATCH 表示该协议特定的请求只需要一个数据子集,数据子集由指定的协议特定的过滤器来匹配。

    标志 NLM_F_ATOMIC 指示请求返回的数据应当原子地收集,这预防数据在获取期间被修改。

    标志 NLM_F_DUMP 未实现。

    标志 NLM_F_REPLACE 用于取代在数据表中的现有条目。

    标志 NLM_F_EXCL_ 用于和 CREATE 和 APPEND 配合使用,如果条目已经存在,将失败。

    标志 NLM_F_CREATE 指示应当在指定的表中创建一个条目。

    标志 NLM_F_APPEND 指示在表末尾添加新的条目。

          字段nlmsg_seq和nlmsg_pid用于追踪消息,nlmsg_seq表示顺序号,nlmsg_pid表示消息来源进程id。

          消息结构体struct msghdr

    struct msghdr {
        void         *msg_name;
        socklen_t    msg_namelen;
        struct iovec *msg_iov;
        size_t       msg_iovlen;
        void         *msg_control;
        size_t       msg_controllen;
        int          msg_flags;
    };

           msg_name和msg_namelen表示套接口地址成员,msg_name表示发送消息的目标地址,接收消息的源地址,msg_namelen表示地址长度。msg_iov和msg_iovlen表示I/O向量引用。其中msg_iov一般指向消息头,这些成员指定了我们的I/O向量数组的位置以及他包含多少项。msg_iov成员指向一个struct iovec数组。I/O向量指向我们的缓冲区。成员msg_iov指明了在我们的I/O向量数组中有多少元素.msg_iov指向了一个具体的消息,包括消息头nlmsghdr和消息头后紧跟的消息数据部分。

          成员msg_control与msg_controllen,这些成员指向了我们附属数据缓冲区并且表明了缓冲区大小。msg_control指向附属数据缓冲区,而msg_controllen指明了缓冲区大小。成员msg_flags为接收消息标记位,这个成员用于接收特定的标记位(他并不用于sendmsg)。msg_flags值为

    MSG_EOR    当接收到记录结尾时会设置这一位。这通常对于SOCK_SEQPACKET套接口类型十分有用。
    MSG_TRUNC    这个标记位表明数据的结尾被截短,因为接收缓冲区太小不足以接收全部的数据。
    MSG_CTRUNC    这个标记位表明某些控制数据(附属数据)被截短,因为缓冲区太小。
    MSG_OOB        这个标记位表明接收了带外数据。
    MSG_ERRQUEUE    这个标记位表明没有接收到数据,但是返回一个扩展错误。

           其他结构体 struct iovec

    struct iovec {
    void * iov_base ;    /*pointer to data */
    size_t iov_len    ;    /*length of data */
    }

    3. 发送和接收消息的具体流程

         消息宏

    #define NLMSG_ALIGNTO   4
    #define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )

          宏NLMSG_ALIGN(len)用于得到不小于len且字节对齐的最小数值。若某个对象的长度为18,那么在为其分配空间时,通过NLMSG_ALIGN宏就可以计算出最接近其的4的倍数为((18+4-1) & ~(3)) = 20,这样便为其申请/分配20字节空间。这是32位微控制器/微处理器中为了防止非对齐操作产生Exception而添加的保护措施。

    #define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(sizeof(struct nlmsghdr)))

          宏NLMSG_LENGTH(len)用于计算数据部分长度为len时实际的消息长度。即数据部分长度和消息头长度

    #define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))

          宏NLMSG_SPACE(len)返回不小于NLMSG_LENGTH(len)且字节对齐的最小数值

    #define NLMSG_DATA(nlh)  ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))

        宏NLMSG_DATA(nlh)用于取得消息的数据部分的首地址,设置和读取消息数据部分时需要使用该宏。

    #define NLMSG_NEXT(nlh,len)      ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), 
                          (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))

          宏NLMSG_NEXT(nlh,len)用于得到下一个消息的首地址,同时len也减少为剩余消息的总长度,该宏一般在一个消息被分成几个部分发送或接收时使用

    #define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && 
                               (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && 
                               (nlh)->nlmsg_len <= (len))

         宏NLMSG_OK(nlh,len)用于判断消息是否有len这么长。

    #define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))

        宏NLMSG_PAYLOAD(nlh,len)用于返回payload的长度。

         发送和接收消息过程

    无标题

          发送消息

         发送消息,设置消息头,以及消息头后的数据部分

    #define MAX_MSGSIZE 1024
    char buffer[] = "An example message";
    struct nlmsghdr nlhdr;
    nlhdr = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_MSGSIZE));
    strcpy(NLMSG_DATA(nlhdr),buffer);
    nlhdr->nlmsg_len = NLMSG_LENGTH(strlen(buffer));
    nlhdr->nlmsg_pid = getpid(); /* self pid */
    nlhdr->nlmsg_flags = 0;

    2

          发送消息时,需要设置指向消息的结构体struct msghdr。

    /*设置目标地址*/
    struct sockaddr_nl dest_addr;
    memset(&dest_addr, 0, sizeof(dest_addr));
    dest_addr.nl_family = AF_NETLINK;
    dest_addr.nl_pid = 0;  
    dest_addr.nl_groups = 0; 
    /*设置消息*/
    struct iovec iov;
    iov.iov_base = (void *)nlhdr;             /*指向消息头*/
    iov.iov_len = nlh->nlmsg_len;             /*整个消息长度*/
    struct msghdr msg;
    msg.msg_iov = &iov;                       /*I/O向量数组开始*/
    msg.msg_iovlen = 1;                       /*数组长度为1*/
    msg.msg_name = (void *)&(dest_addr);      /*设置目标地址*/
    msg.msg_namelen = sizeof(dest_addr);      /*目标地址长度*/
         最后发送消息

         sendmsg(fd,&msg,0);

        接收消息

    #设置消息头
    #define MAX_NL_MSG_LEN 1024
    struct nlmsghdr * nlhdr;
    nlhdr = (struct nlmsghdr *)malloc(MAX_NL_MSG_LEN);
    #设置消息源地址
    struct sockaddr_nl src_addr;
    memset(&src_addr,0,sizeof(src_addr));
    src_addr.nl_famlily=AF_NETLINK;
    src_addr.nl_pid=src_pid;     /*对方进程pid*/
    src_addr.nl_groups=0;
    #设置消息
    struct msghdr msg;
    struct iovec iov;
    iov.iov_base = (void *)nlhdr;
    iov.iov_len = MAX_NL_MSG_LEN;
    msg.msg_name = (void *)&(src_addr);
    msg.msg_namelen = sizeof(src_addr);
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;
    recvmsg(fd, &msg, 0);
  • 相关阅读:
    POJ 3253 Fence Repair
    POJ 2431 Expedition
    NYOJ 269 VF
    NYOJ 456 邮票分你一半
    划分数问题 DP
    HDU 1253 胜利大逃亡
    NYOJ 294 Bot Trust
    NYOJ 36 最长公共子序列
    HDU 1555 How many days?
    01背包 (大数据)
  • 原文地址:https://www.cnblogs.com/flyingdirt/p/4194649.html
Copyright © 2011-2022 走看看