ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

netlink 2

2022-09-16 14:02:44  阅读:200  来源: 互联网

标签:netlink struct 报文 NETLINK len define


 

  • Netlink 介绍(译)

     

    原文地址:http://people.redhat.com/nhorman/papers/netlink.pdf


     

    译文:

    1 介绍   

      在Linux和Unix的众多发行版中的网络配置功能, 都是编程者事后需求的功能, 导致像添加路由、邻居表条目和配置接口等功能有着很多杂乱的方法, 比如raw socket, ioctl调用以及专门的伪网络协议等方法。在Linux 2.4内核中, 开发者努力实现了一种更标准化的配置网络的方法。这种方法被命名为netlink sockets, 它旨在创建一个适合所有网络控制方面的通信框架,虽然建立的netlink子系统不是完善的, 但是这是一种新的网络配置方法, 也是可靠的基础。此文档旨在介绍如何使用netlink socket族和其实现的协议。

        本文假设读者有C和socket编程的基础。

    2 Netlink 地址族

    2.1 socket创建

      netlink地址族使用标准的BSD socket API作为用户空间程序和内核交互的使者。创建一个netlink套接字和创建其它套接字是类似的方式。

    socket fd=socket(AF_NETLINK, SOCK_RAW, protocol);

      地址族参数总是AF_NETLINK, 并且类型值总是SOCK_RAW, 唯一可变的参数 就是协议protocol域, 此域将会继续添加可选项, 增加了它的可配置性, 下表是protocol的可选项(来自linux-2.6.32 kernel)。

    #define NETLINK_ROUTE           0       /* Routing/device hook                          */
    #define NETLINK_UNUSED          1       /* Unused number                                */
    #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
    /* leave room for NETLINK_DM (DM Events) */
    #define NETLINK_SCSITRANSPORT   18      /* SCSI Transports */
    #define NETLINK_ECRYPTFS        19
    
    #define NETLINK_L2TP 20
    
    #if defined(CONFIG_RTL_819X)
    #define NETLINK_RTK_DEBUG       21
    #define NETLINK_RTK_FILTER      22
    #define NETLINK_MULTICAST_DELETE        23
    #define NETLINK_RTK_FB          24
    #define NETLINK_RTK_HW_QOS 25
    #endif

    2.2 发送和接收数据包

      netlink套接字是无连接的, 收发的数据报就表示类似UDP套接字, 发送数据报通过sendto或者sendmsg系统调用, 用recvfrom或者recvmsg接收数据报。 注意, netlink套接字不使用send和recv交互, 这是因为netlink套接字是无连接的。就像UDP套接字, netlink消息是数据报格式, 虽然netlink消息头部有一些机制设计用于编程者增加协议的可靠性, 但仍旧不能保证连接是可靠的。

    2.3 netlink套接字地址结构

      struct sockaddr_nl 是它的地址结构, 用于netlink套接字的接收和发送, 定义如下:

    struct sockaddr_nl
    {
            sa_family_t     nl_family;      /* AF_NETLINK   */
            unsigned short  nl_pad;         /* zero         */
            __u32           nl_pid;         /* port ID      */
            __u32           nl_groups;      /* multicast groups mask */
    };
    • nl_family:此域定义了消息的地址族, 应该总是AF_NETLINK
    • nl_pad   : 总是为0
    • nl_pid  : 一般设置为本进程的pid或者填0理, 如果是进程接收来自内核netlink消息, 此域应为本进程PID,  如果是向内核发送netlink消息, 此域应置0
    • nl_groups :用于指定多播组, 如对接收来自内核的netlink消息来说, 内核可将要发送的消息指定一个多播组, 那么此消息就会发向同一多播组的接收端。而对于进程发送消息来讲, 设置了多播组就只会发送到此多播组的内核接收端。此域是32位, 最多可支持32个多播组。

    3 Netlink 消息格式

       与每个IP消息头一样, netlink消息也有类似的头部, 然而和其他协议不同的是, 编程者需要为每个数据包构建这个头部(一般的TCP/UDP socket都是直接操作报文的payload部分), 这个头部用来保存每个消息和格式的元数据, 这个头部也是netlink协议的基础。

    struct nlmsghdr {
      u32 nlmsg_len;
      u16 nlmsg_type;
      u16 nlmsg_flags;
      u32 nlmsg_seq;
      u32 nlmsg_pid;
    }
    • nlmsg_len:每个netlink头后面跟随者0个或者多个字节的辅助数据, 此域记录了消息的整个长度, 包括了头部在内。
    • nlmsg_type:此域标识了头部后面数据的格式。此域的取值和2.1中的protocol有关。
    • nlmsg_flags:此域标识了消息由谁进行处理和解析, 有如下取值
      NLM_F_REQUEST - 这个标志暗示这是一个请求消息, 它应该被设置到大多数应用程序的初始化消息中。
      NLM_F_ACK - 这个标志暗示对前一个请求消息的回应, 序列号和pid值能够辨别请求的回应报文。
      NLM_F_ECHO - 这个标志表示发出的报文将回响给发送进程一份。
      NLM_F_MULTI - 这个标志表示此消息是多个消息的一部分, 可用宏NLMSG_NEXT获得下一个消息。
      NLM_F_ROOT - 用于请求多个netlink消息, 有此标志的请求消息表示请求回复整个条目表而不是一条, 回复的报文通常是
      NLM_F_MULTI标志的。注意:此标志只适合特定的nlmsg_type才有效。
      NLM_F_ATOMIC - 标志任何通过get-->回复报文的过程都是原子的, 防止在中间有资源改变引起歧义。
      NLM_F_REPLACE - 替换条目表中的一条, 可用于覆盖条目表。
      NLM_F_CREATE - 在条目表中设置一个新的条目(比如添加一条新路由)
      NLM_F_APPEND - 在条目表末尾添加一个条目
      NLM_F_EXCL - 结合了CREATE和APPEND, 如果要添加的条目已存在将返回错误(推荐使用)
    • nlmsg_seq:seq用来联系请求和回复报文, 顺序标志的作用

    • nlmsg_pid:和seq类似

    3.2 netlink 实用宏

    #define NLMSG_ALIGNTO   4

    /* 返回不小于len的以4字节对齐的最近数字 (ex: len=9 --> 返回值为12)*/
    #define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )

    /* 返回netlink消息头的字节数, 且务必是以4字节对齐的 */
    #define NLMSG_HDRLEN     ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))

    /* 返回'len + netlink消息头长度' 一般len指定为消息的payload长度, 此宏可用来填充nlmsg_len域 */
    #define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(NLMSG_HDRLEN))

    /* 返回值同 NLMSG_LENGTH 宏一样有效, 主要是确保整个netlink消息长度4字节对齐(nlmsg_len域是否最好用此域来填充) */
    #define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))

    /* 返回指向netlink消息payload处的指针(nlh 参数一般是指向struct nlmsghdr结构的指针) */
    #define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0))) ----------------------------- | struct nlmsghdr | payload | ----------------------------- | NLMSG_DATA(nlh)

    /* 返回指向下一个netlink消息的指针 */
    /* 很多netlink回应由多个netlink消息组成 */
    *参数:
     nlh --> 指向struct nlmsghdr结构的指针
     len --> 一般为recvmsg函数的返回值(整个消息的长度)
    #define NLMSG_NEXT(nlh,len)      ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), 
                                      (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
    ---------------------------------------------------------
    | struct nlmsghdr | payload | struct nlmsghdr | payload |
    ---------------------------------------------------------
    |                           |            
    nlh                         |
                                NLMSG_NEXT(nlh,len)
    
    |--------------------------len--------------------------|
    *此宏先用len(整个消息的长度)减掉第一个消息的长度, 如果为0, 表示没有下一个消息了, 返回空, 若为非0, 表示后边还有消息, 指针移动指向下一个消息处并返回.
    
    
    /* 确保nlh指向的消息大小不大于len */
    *参数:
     nlh --> 指向struct nlmsghdr结构的指针
     len --> 一般为recvmsg函数的返回值
    #define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && 
                               (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && 
                               (nlh)->nlmsg_len <= (len))

     3.3 总结

      下图是netlink消息的内存分布图:

    NETLINK_FIREWALL 协议

      由3可知, netlink消息头nlmsg_type成员的取值和2.1中的protocol有关, 现在就介绍protocol为NETLINK_FIREWALL时的情况。

      这个协议是非常有用的开发协议。首先它是有意被设计来在用户空间来调试iptables模块的架构,这个协议与很多iptables模块相关联。ip-queue模块就是其中一个(详见:http://blog.csdn.net/u010807313/article/details/9236581),在创建此协议的netlink套接字之前都需要先安装相关模块,发往相关模块的报文都会同样发给由NETLINK_FIREWALL协议创建的netlink套接字, 由此实现在用户空间监听流经netfilter的报文的目的。例如:

    iptables -I OUTPUT -j QUEUE -p tcp –destination-port 7551

      上面一条命令表示在OUTPUT链上, 发往端口7551的TCP报文都交给QUEUE链来处理, 而ip-queue模块正好和NETLINK_FIREWALL协议的套接字关联(内核实现的), 所以套接字同样也会收到这样的报文, 实现了监听的目的, 自行修改iptables命令可达到监听多种类型报文的目的。

    4.1 创建和使用

    socket fd=socket(AF_NETLINK, SOCK_RAW, NETLINK_FIREWALL);

      此协议没有使用多播组, 所以地址结构struct sockaddr_nl中的nl_groups应该总是设置为0,并且此协议的套接字不需要bind函数,因为报文只是在进程和内核中传输,所以从进程发向内核的报文struct sockaddr_nl中的nl_pid应该设置为0。

    4.2 消息类型

      NETLINK_FIREWALL协议的套接字有三种消息类型(如3中所述, 成员nlmsg_type的取值),每个消息类型都有它各自的数据结构来描述。

    • IPQM_MODE
    • IPQM_PACKET < 这个是内核向用户空间返回的报文类型 >
    • IPQM_VERDICT
    4.2.1 IPQM_MODE

      此类型是使用NETLINK_FIREWALL协议需要第一个发向内核的包, 内核收到之后才会将匹配的报文从内核发至用户空间的netlink套接字。此报文的数据结构如下, 它是紧随在struct nlmsghdr之后的:

    typedef struct ipq mode msg {
      unsigned char value;
      size t range;
    };

    value的取值有三种:

    • IPQ_COPY_NONE - 不常用, 设置此值发给内核将导致iptables将所有到QUEUE链的报文丢弃。
    • IPQ_COPY_META - 表示我希望内核返回报文的元数据(我理解是struct nlmsghdr + struct ipq_packet_msg 两个头部)部分。
    • IPQ_COPY_PACKET - 表示希望内核返回报文, 报文长度由range控制, 若range为0表示返回整个报文。如果你需要在用户空间分析流经QUEUE链的报文应该设置此项并将range设置为0。

    range:

      此域只在value = IPQ_COPY_PACKET时才有效。

    也就是说, 用户进程使用IPQM_MODE类型的报文告诉内核, 我需要你返回给我的报文是什么样的(不需要 or 要元数据 or 要range长的报文)

    4.2.2 IPQM_PACKET

      这个类型的报文是根据4.2.1之后内核根据需求返回的报文。只要之前设置的value不是IPQ_COPY_NONE, socket就会收到此类型的报文, 结构如下:

    typedef struct ipq packet msg {
      unsigned long packet_id;
      unsigned long mark;
      long timestamp sec;
      long timestamp usec;
      unsigned int hook;
      char indev name[IFNAMSIZ];
      char outdev name[IFNAMSIZ];
      unsigned short hw_protocol;
      unsigned short hw_type;
      unsigned char hw_addrlen;
      unsigned char hw_addr[8];
      size t data len;
      unsigned char payload[0];
    };
    • packet_id - 这个是内核产生的独一无二的标识, 在4.2.3中发送IPQM_VERDICT报文需要。
    • mark - //
    • timestap_sec - 报文抵达时间(秒)
    • timestap_usec - 报文抵达时间(微秒)
    • hook - 报文被重定向到QUEUE的hook number
    • indev_name - //
    • outdev_name - //
    • hw_protocol - 通常是ETH_P_IP
    • hw_type - 通常是ARPHDR_ETHER
    • hw_addrlen - 通常为6
    • hw_addr - 报文的源MAC地址
    • data_len - payload数据长度
    • payload - 柔性数组头部, 指向了payload数据的头部
    4.2.3 IPQM_VERDICT

      此类型的报文是在收到内核的回复报文之后, 用户经过自己的检测, 决定对此报文执行何种操作, IPQM_MODE --> IPQM_PACKET <----> IPQM_VERDICT, 是顺序的过程。也就是说, 只有你向内核发送IPQM_VERDICT说明了报文处理方式之后, 你才能recvmsg下一个到达QUEUE链的报文, 否则recvmsg会一直阻塞。结构如下:

    typedef struct ipq verdict msg {
      unsigned int value;
      unsigned long id;
      size t data_len;
      unsigned char payload;
    };

    value 指示了对报文的处理方式:

    • NF_DROP - 立即丢弃报文
    • NF_ACCEPT - 接收报文(不参与之后的iptables链了)
    • NF_STOLEN - //
    • NF_QUEUE - 不常使用
    • NF_REPEAT - 将报文移入下一个iptables链

    id - 指示了要对哪个报文进行处理, 对应4.2.2的packet_id成员, 这个成员唯一关联了一个进入QUEUE的报文

    data_len - 指verdict报文的payload数据长度, 因为verdict是用户发向内核的, 此域一般设置为0

    payload - //

    5 NETLINK_ROUTE 协议

    5.1 创建和使用

    socket fd=socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);

      NETLINK_ROUTE协议是netlink套接字最大且是最成熟的协议, 它有它自己消息的处理宏(类似3.2节), 这些NETLINK_ROUTE的宏是为了添加它的辅助数据段和定制化特别的消息类型而做的。

      每个族都有同样的命名空间和辅助数据结构, 辅助数据结构后又跟着一个或多个消息。

      每个族都包含三个方法, NEW, DEL, GET; 这些方法用于创建、删除和接收路由相关条目。

    5.2 NETLINK_ROUTE消息宏

      NETLINK_ROUTE消息实际有自己的数据结构, 如下所示。

    struct rtattr {
        unsigned short rta_len;
        unsigned short rta_type;
    }

      下面是NETLINK_ROUTE的消息内存布局:

      对于struct rtattr结构, 与netlink消息头struct nlmsghdr结构相似, 有一些宏进行辅助处理, 参考3.2节。

    • int RTA OK(struct rtattr *rta, int rtabuflen); - Verify the data
    integrity of the data which succedes this rtattr header.
    • void * RTA DATA(struct rtattr *rta); - Return a pointer to the
    ancilliary data associated with this rtattr header.
    • struct rtattr *RTA NEXT(struct rtattr *rta); - Return a pointer
    to the next rtattr header in the chain.
    • unsigned int RTA PAYLOAD(struct rtattr *rta); - Return the
    length of the ancilliary data associated with the passed rtattr header.
    • unsigned int RTA LENGTH(unsigned int length); - Return the
    aligned length for the passed payload length. This value is assigned to
    the rta len field of the rtattr header
    • unsigned int RTA SPACE(unsigned int length); - Return the
    length of the ancilliary data, when aligned.

    5.3 消息类型

      在使用NETLINK_ROUTE协议的情况下, netlink控制块struct nlmsghdr中的nlmsg_type域标识了多种消息类型,举例如下:

    5.3.1 LINK消息

      LINK消息族允许设置和获取关于系统接口的消息nlmsg_type有如下取值:

    • RTM_NEWLINK  - 创建一个新接口/有一个新接口被创建
    • RTM_DELLINK   - 删除一个接口
    • RTM_GETLINK  - 接收一个接口消息

    每个消息的辅助数据结构是struct ifinfomsg:

    struct ifinfomsg {
        unsigned char ifi_family;
        unsigned short ifi_type;
        int ifi index;
        unsigned int ifi_flags;
        unsigned int ifi_change;
    };
    5.3.2 LINK消息struct rtattr结构rta_type取值

    5.3.3 LINK消息内存布局

    5.4 其它消息类型

      比如ADDR消息, ROUTE消息等和LINK消息类似, 不同的是它们有各自的struct ifinfomsg消息和支持不同的rta_type。  

     

    以上来自::https://www.cnblogs.com/Flychown/p/8065649.html

    =====================================================================================================================

 

6。 NETLINK_ROUTE

 

 

 

 

 

 

参考来源:

 

标签:netlink,struct,报文,NETLINK,len,define
来源: https://www.cnblogs.com/rebrobot/p/16699622.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有