您的位置:首页 > 其它

四 libnl库详解

2017-04-24 22:49 1291 查看
translate from http://www.infradead.org/~tgr/libnl/doc/core.html#core_netlink_fundamentals

1 介绍
   libnl核心库提供了通过netlink socket连接所需要的基础函数,用于处理socket的链接和断开、数据收发、消息创建分析,提供用户可定制接收状态机,并提供一种抽象的数据类型框架,用于简化netlink
协议对象的实现。
   1.1 libnl层次分类:
    
        如上图所示,libnl 库集可以被分为多个库:
        libnl:负责socket处理、数据收发、消息构建分析
        libnl-route:负责地址、链接、局域网、路由等
        libnl-genl:负责controller API、family和命令注册
 
   1.2 使用链接:
       主要的头文件是<netlink/netlink.h>,其他头文件按需添加
       #include <netlink/netlink.h>
       #include <netlink/cache.h>
       #include <netlink/route/link.h>

   1.3 调试:
      设置环境变量NLDBG>0,可以将库编译成为debugging状态,以人类可读的形式打印信息到stderr。
      

$ NLCB=debug ./myprogram

-- Debug: Sent Message:
--------------------------   BEGIN NETLINK MESSAGE ---------------------------
  [HEADER] 16 octets
    .nlmsg_len = 20
    .nlmsg_type = 18 <route/link::get>
    .nlmsg_flags = 773 <REQUEST,ACK,ROOT,MATCH>
    .nlmsg_seq = 1301410712
    .nlmsg_pid = 20014
  [PAYLOAD] 16 octets
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00       ................
---------------------------  END NETLINK MESSAGE   ---------------------------
-- Debug: Received Message:
--------------------------   BEGIN NETLINK MESSAGE ---------------------------
  [HEADER] 16 octets
    .nlmsg_len = 996
    .nlmsg_type = 16 <route/link::new>
    .nlmsg_flags = 2 <MULTI>
    .nlmsg_seq = 1301410712
    .nlmsg_pid = 20014
  [PAYLOAD] 16 octets
    00 00 04 03 01 00 00 00 49 00 01 00 00 00 00 00       ........I.......
  [ATTR 03] 3 octets
    6c 6f 00                                              lo.
  [PADDING] 1 octets
    00                                                    .
  [ATTR 13] 4 octets
    00 00 00 00                                           ....
  [ATTR 16] 1 octets
    00                                                    .
  [PADDING] 3 octets
    00 00 00                                              ...
  [ATTR 17] 1 octets
    00                                                    .
  [...]
---------------------------  END NETLINK MESSAGE   ---------------------------

2 netlink基础协议
     netlink是一种基于socket的IPC通信机制,用于实现用户空间于内核或者用户空间进程之间的通信。
     netlink协议是基于使用AF_NETLINK协议族的BSD socket。每个netlink protocol拥有自己的协议号(如NETLINK_ROUTE, NETLINK_NETFILTER等等),其地址由32位唯一端口号表示(早期取值为PID);

    2.1 寻址
     netlink地址(或称为port)是一个32位整数,取0表示和内核通信,否则为用户空间进程间通信。 
      
      上图说明了netlink常规应用案例:
 用户空间 to 内核
 用户空间 to 用户空间
 监听内核多播通知

     2.1.1 用户空间 to 内核
         netlink最常用的形式即用户空间应用发送请求到内核,并接收处理返回值(错误消息或成功返回)

   2.1.2 用户空间进程间通信
      Netlink也可用作用户空间应用进程间的通信方式。它不仅仅限于两个进程,可以用于任意多个进程间多播。但为保证多个socket相互可见,这些socket必须定义在同一个netlink协议族中。

   2.1.3 监听内核通知
       当用户空间进程需要监听内核事件而后做出响应时可以选用netlink这种通信方式。用户空间进程需要维护一个订阅多播组的netlink socket,用于接收内核特定事件通知。

     Use of multicasting is preferred over direct addressing due to the flexibility in exchanging the user space component at any time without the kernel noticing.

 2.2 消息格式
     netlink协议是典型的消息传递,其消息由netlink消息头 (
struct nlmsghdr
)加实际载荷数据组成,载荷数据可以是任意格式的数据,通常包含固定大小的协议头以及属性流数据. 
    Netlink message header (struct nlmsghdr)

struct nlmsghdr{
        uint32_t    nlmsg_len;      // * total length,32bit包括消息头在内的消息总长度
        uint16_t    nlmsg_type;    //  message type,16bit,指定载荷消息数据格式,netlink协议定义了几种标准格式,每个协议族可以执行定义消息格式
        uint16_t    nlmsg_flags;   // Message flags ,16bit,用于修改消息类型行为
        uint32_t    nlmsg_seq;  // * Sequence number,32bit,可选,一般用于定位上一条消息,例如根据错误信息定位到其请求
        uint32_t    nlmsg_pid;   // * Netlink PID of the proccess sending the message.,32bit,指定消息目标,若未给出,则发送消息到内核中同一协议族中第一个匹配到的socket
};

  2.2.1 message types

        request、notification、replies间netlink各不相同。requests消息标识位被置为NLM_F_REQUEST,一般由用户空间发送到内核,建议附带额外的sequence number.

       根据request特性,接收者相应消息也应符合netlink message格式,且响应信息的sequence number应和请求信息的相匹配。

       notifications无需响应回复,因此sequence number应设为0;

消息类型由netlink 消息头中16位的message type预先指定,其中netlink定义的标准消息类型如下:

- NLMSG_NOOP - No operation, message must be discarded
- NLMSG_ERROR - Error message or ACK, see Error Message respectively ACKs
- NLMSG_DONE - End of multipart sequence, see Multipart Messages
- NLMSG_OVERRUN - Overrun notification (Error)

   2.2.1.1 multipart messages

尽管netlink消息理论上最大可达4GB,但是没有足够大的socket buffers对应处理。因此最常见的方法是通过mulipart机制将过大的消息分割为多个大小为PAGE_SIZE的消息,每个multipart消息标志位均被设为NLM_F_MULTI,接收端接收到multipart消息后会继续接收直到收到消息的标志位变为NLMSG_DONE。

multipart消息无需像IP数据包一样需要重新组合,每个multipart消息可以独立处理。

2.2.1.2  Error message

     netlink 错误消息必须使用标准消息类型NLMSG_ERROR,载荷数据由错误码和原请求消息头组成。

    错误消息的sequence number 应和对应请求sequence number相一致

2.2.1.3 ACKs

      消息发送者设置NLM_F_ACK标志位后,接收者在接收到消息后会反馈ACK,这通常用于发送者需要根据接收情况进一步处理的场景。

      ACK消息格式和NLMSG_ERROR相同,但是错误码被设为0。

2.2.2 message flags

   netlink定义的标准message flags 如下:

- NLM_F_REQUEST - Message is a request, see Message Types.
- NLM_F_MULTI - Multipart message, see Multipart Messages
- NLM_F_ACK - ACK message requested, see ACKs.
- NLM_F_ECHO - Request to echo the request, 通常和NLM_F_REQUEST联合使用,在无论发送者是否订阅相关多播组的情况下,均向发送者返回notification。

   另外还定义了仅用于GET请求的全局message flags:

      这些flag完全可选,许多netlink protocol仅使用NLM_F_DUMP使接收者以multipart形式回复请求

- NLM_F_ROOT - Return based on root of tree.
- NLM_F_MATCH - Return all matching entries.
- NLM_F_ATOMIC - Obsoleted, once used to request an atomic operation.
- NLM_F_DUMP - Return a list of all objects (NLM_F_ROOT|NLM_F_MATCH).

2.2.3  Sequence Numbers

      Netlink使用sequence number关联请求和响应,但并不像TCP一样强制要求。 

3 netlink sockets
    我们通过netlink socket来实现netlink
protocol协议,每个socket定义独立的消息。同一应用中可以使用多个socket。
     3.1  socket结构体
        netlink socket及其相关属性、文件描述符由结构体struct nl_sock表示,应用需要为每一个netlink socket分配一个nl_sock:

#include <netlink/socket.h>
struct nl_sock{
   struct sockaddr_nl    s_local;
   struct sockaddr_nl    s_peer;
   int                   s_fd;
   int                   s_proto;
   unsigned int          s_seq_next;
   unsigned int          s_seq_expect;
   int                   s_flags;
   struct nl_cb *        s_cb;
};

struct nl_sock *nl_socket_alloc(void); //分配
void nl_socket_free(struct nl_sock *sk) //释放

  
     3.2 sequence number
        libnl自动维护应用处理的sequence number,struct nl_sock中保存了一个sequence number计数器。

#include <netlink/socket.h>
unsigned int nl_socket_use_seq(struct nl_sock *sk); //过去当前sequence number值并加1
void nl_socket_disable_seq_check(struct nl_sock *sk); //disable sequence number,当应用无需使用request/reply模型时

    3.3 多播组订阅
       通过订阅链接到当前netlink protocol的多播组,netlink socket可以接收到发往每个多播组的消息的副本。

#include <netlink/socket.h>
内核2.6.14前的接口限制多播组数目为32,内核2.6.14后如下接口可支持订阅多播组数目几乎无限大:
int nl_socket_add_memberships(struct nl_sock *sk, int group, ...);
int nl_socket_drop_memberships(struct nl_sock *sk, int group, ...);

     3.4 修改回调函数
         为实现不同socket对应不同处理方法,我们为每个socket都设置了callback函数(由结构体nl_cb维护)

#include <netlink/socket.h>
struct nl_cb *nl_socket_get_cb(const struct nl_sock *sk); //获取socket对应callbacks
void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb); //设置nl_socket callbacks
int nl_socket_modify_cb(struct nl_sock *sk, enum nl_cb_type type, enum nl_cb_kind kind,  nl_recvmsg_msg_cb_t func, void *arg); //修改现有callbacks

   3.5 socket attributes
      3.5.1 local port
         local port number作为socket识别符,用于寻址socket, 当分配nl_sock时会自动创建唯一的local port number。
         local port number由32位整数表示(其中22位表示Process ID, 10位随机码),因此一个进程最多可以允许创建1024个socket。

#include <netlink/socket.h>
uint32_t nl_socket_get_local_port(const struct nl_sock *sk);   //获取当前socket local port number
void nl_socket_set_local_port(struct nl_sock *sk, uint32_t port); //设置local port number,必须保证值的唯一性

    3.5.2 peer port
         peer port 用于寻址接收消息的对等socket。如若未指定,则将消息发送到属同一netlink 协议族的内核端socket。

#include <netlink/socket.h>
uint32_t nl_socket_get_peer_port(const struct nl_sock *sk);
void nl_socket_set_peer_port(struct nl_sock *sk, uint32_t port);

    3.5.3 file descriptor
       介于netlink采用BSD socket,因此每个socket对应一个文件描述符      

#include <netlink/socket.h>
int nl_socket_get_fd(const struct nl_sock *sk);
int nl_socket_set_nonblocking(const struct nl_sock *sk); //若socket用于接收notification,则最好设为非阻塞并定期查询

   
    3.5.4 send/receive buffer size
         指定接收或发送netlink message数据的大小,默认32kb。

#include <netlink/socket.h>
int nl_socket_set_buffer_size(struct nl_sock *sk, int rx, int tx);

4 数据收发
   4.1 发送数据
      通过netlink socket发送netlink message的标准方式是调用nl_send_auto_complete(),其实现如下:
       (1)nl_send_auto_complete()将 自动填充netlink message header,并根据nl_sock里面的选项或地址设置完成寻址,而后调用nl_send()。
              ps:所有nl_send_auto()内部调用都会使用nl_send()传输信息。如果nl_send()不能满足要求,可以调用nl_cb_overwrite_send()重写自己的send函数。
       (2)nl_send()将netlink message插入到struct iovec结构体中,而后调用nl_send_iovec();
              ps: struct iovec结构体定义一个向量元素, 对于每一个传输的元素,指针成员iov_base指向一个缓冲区,这个缓冲区是存放的是readv所接收的数据或是writev将要发送的数据。成员iov_len在各种情况下分别确定了接收的最大长度以及实际写入的长度。

struct iovec{
     void *iov_base; /* Pointer to data. */
     size_t iov_len; /* Length of data. */
};
- int readv(int fd, const struct iovec *vector, int count);
- int writev(int fd, const struct iovec *vector, int count);

       (3)nl_send_iovec()将利用nl_msg填充msghdr结构体,并检查寻址状态,而后调用nl_sendmsg()继续发送数据
       (4)nl_sendmsg()调用sendmsg()下发数据
      
      通过netlink socket发送raw data:
        int nl_sendto(struct nl_sock *sock, void *buf, size_t size);

   4.2 接收数据
        libnl调用nl_recvmsgs_default()按照socket格式规定调用nl_recvmsgs() 接收数据。
        nl_recvmsgs()在netlink message到来前保持阻塞,可以通过nl_cb_overwrite_recvmsgs()重写。nl_recvmsgs()调用recvmsgs()接收数据。

5 解析数据
     netlink protocol要求边界强制对齐,对齐大小由NLMSG_ALIGNTO指定:    

    <----------- nlmsg_total_size(len) ------------>
     <----------- nlmsg_size(len) ------------>
    +-------------------+- - -+- - - - - - - - +- - -+-------------------+- - -
    |  struct nlmsghdr  | Pad |     Payload    | Pad |  struct nlsmghdr  |
    +-------------------+- - -+- - - - - - - - +- - -+-------------------+- - -
     <---- NLMSG_HDRLEN -----> <- NLMSG_ALIGN(len) -> <---- NLMSG_HDRLEN ---

 可以通过如下函数获取消息长度:   

#include <netlink/msg.h>
int nlmsg_size(int payloadlen);
int nlmsg_total_size(int payloadlen);

 
    5.1 解析数据
        5.1.1 拆分流buffer
          netlink socket接收到的数据都是字节流的格式,给出buffer及其大小,且每个buffer 中有可能包含多个netlink messages,所以需要将其分为多个netlink messages。
          第一个netlink message起于消息流buffer开始,之后的netlink message通过nlmsg_next()获取。
              struct nlmsghdr *nlmsg_next(struct nlmsghdr *hdr, int *remaining);
                     nlmsg_next()自动减去上一消息长度,返回接下来netlink message header指针。
          需要注意的是,调用nlmsg_next()时无法确定剩余buffer是否还包含nl_msg,因此需要通过nlmsg_ok()验证:
              int nlmsg_ok(const struct nlmsghdr *hdr, int remaining);  //若remaining buffer包含nl_msg,则返回ture
              int nlmsg_valid_hdr(const struct nlmsghdr *hdr, int payloadlen);  //同nlmsg_ok
              libnl提供宏nlmsg_for_each_msg实现上述两个函数的功能:
              
            
            5.1.2 message payload
                   为保证字节对齐到NLMSG_ALIGNTO的整数倍,需要在message payload前nlmsghdr后填充空洞。
                   通过如下函数解析payload:

#include <netlink/msg.h>
void *nlmsg_data(const struct nlmsghdr *nlh);  //计算偏移数目,返回message payload的起始指针
void *nlmsg_tail(const struct nlmsghdr *nlh);  
int nlmsg_datalen(const struct nlmsghdr *nlh);  //返回message payload长度

                  

            5.1.3 message attributes                   
                  nlmsg payload由netlink attributes组成,nl_attr具有规范灵活的特点,可以在不影响兼容性的情况下添加、修改attributes.
                 

#include <netlink/msg.h>
struct nlattr *nlmsg_attrdata(const struct nlmsghdr *hdr, int hdrlen); //获取nl attributes起始地址
int nlmsg_attrlen(const struct nlmsghdr *hdr, int hdrlen);  //获取attributes长度

libnl提供如下参数综合解析nl_msg:
int nlmsg_parse(struct nlmsghdr *hdr, int hdrlen, struct nlattr **attrs,   int maxtype, struct nla_policy *policy);
//解析nl_msg,将结果填充nlattr数组
int nlmsg_validate(struct nlmsghdr *hdr, int hdrlen, intmaxtype, struct nla_policy *policy);

      5.2 构建消息
          libnl使用结构体nl_msg表示netlink message。 struct nl_msg并非指向netlink message header,可以通过nlmsg_hdr获取netlink message header指针。
          5.2.1 分配
             构建netlink message消息的第一步是分配一个struct nl_msg 用以保存消息头和载荷。       

     #include <netlink/msg.h>
     /*各类分配函数*/
     struct nl_msg *nlmsg_alloc(void);   //常见默认分配函数,用于分配nl_msg结构体空间,默认最大值为PAGE_SIZE
     void nlmsg_set_default_size(size_t);  //修改默认最大空间
     struct nl_msg *nlmsg_alloc_size(size_t max); //分配给定最大空间大小的nl_msg
     struct nl_msg *nlmsg_inherit(struct nlmsghdr *hdr); //为消息头已知的nl_msg分配空间时调用。若传入参数为NULL,则等同nlmsg_alloc
     struct nl_msg *nlmsg_alloc_simple(int nlmsg_type, int flags);  //为指定消息type和flag的nl_msg分配空间
     
     /*释放*/
     void nlmsg_free(struct nl_msg *msg);

5.2.2 填充消息头

         struct nlmsghdr *nlmsg_put(struct nl_msg *msg, uint32_t port, uint32_t seqnr, int nlmsg_type, int payload, int nlmsg_flags);
                             nlmsg_put根据给定的nlmsg_type、nlmsg_flags、seqnr、port构建一个nlmsghdr,然后拷贝到给定的nl_msg中。
                             seqnr若设为 NL_AUTO_SEQ,则表示自动分配sequence number, 则必须调用nl_send_auto()发送消息。
                             nlmsg_inherit()和nlmsg_alloc_simple()分配的nl_msg不需要这一步骤;

         5.2.3 留空

       void *nlmsg_reserve(struct nl_msg *msg, size_t len, int pad); //len为留空字节大小,pad为对齐大小
                libnl中多数函数都会自动留空,但有时会需要应用自己留空,则调用nlmsg_reserve。

         5.2.4 追加            

int nlmsg_append(struct nl_msg *msg, void *data, size_t len, int pad);
          追加len字节大小的data,对齐pad

  
6 attributes
      如若允许netlink message中所有的payload都应该被编码成attributes的格式,以保证消息的灵活性、兼容性。比如现设备需要32位计数器,几年后变为64位,使用attribute机制可以传任意长度值,否则需要修改整个实现。
     netlink attributes允许追加任意长度的数据块到netlink message。

     attribute格式如上图所示,attribute length、attribute type 由struct nlattr表示:
     每个attribute必须保证对齐到NLA_ALIGNTO的整数倍。nla_padlen()函数返回需要添加的空洞字节数。
     6.1 解析
       6.1.1 获取attributes
          libnl提供nlmsg_parse()提取attributes到指定数组,同时也提供手动解析接口。解析方式和nlmsg类似。
          netlink message的attribute部分包含无限多个attributes。首个attribute地址可以通过nlmsg_attrdata()获取,其余通过nla_next()访问:
          struct nlattr *nla_next(const struct nlattr *attr, int *remaining);
          同样需要调用nla_ok()验证余下空间是否还包含attributes:
          int nla_ok(const struct nlattr *attr, int remaining);
     
     6.1.2 解析attributes
           单个attributes解析方法如下:   

int nla_len(const struct nlattr *hdr);  //返回payload长度
int nla_type(const struct nlattr *hdr); //返回类型
void *nla_data(const struct nlattr *hdr); //返回payload地址

          
      6.1.3 attributes验证
           netlink协议要求需对接收到的attribute消息进行验证,并提供相关接口

struct nla_policy {
        uint16_t        type;  //数据类型,如NLA_U32,NLA_STRING,NLA_FLAGS
        uint16_t        minlen; //最小有效长度,基础类型忽略
        uint16_t        maxlen;  //最大有效长度,结构体封装不建议指定,以防影响扩展
};
      int nla_validate(struct nlattr *head, int len, int maxtype, struct nla_policy *policy);//根据给定policy按照索引顺序验证attribute有效性,若attribute type为空、超过maxtype规定最大值或全部通过则返回0,不在minlen、maxlen规定范围内则返回-NLE_RANGE。

           
    

      libnl提供attributes解析校验一步到位函数nla_parse,遍历attributes流并校验每个attribute合法性,如若全部pass,则填充attrs[]数组:
          int nla_parse(struct nlattr **attrs, int maxtype, struct nlattr *head, int len, struct nla_policy *policy);// 
        可用nlmsg_parse替换。

        6.1.4 查找指定attributes
                如下接口不支持嵌套查找:

struct nlattr *nla_find(struct nlattr *head, int len, int attrtype);
struct nlattr *nlmsg_find_attr(struct nlmsghdr *hdr, int hdrlen, int attrtype);

             遍历attributes流:
          

    6.2  在nlmsg 中添加attribute

struct nlattr *nla_reserve(struct nl_msg *msg, int attrtype, int len); //添加attrtype类型数据,并返回指针
int nla_put(struct nl_msg *msg, int attrtype, int attrlen, const void *data); //nla_reserve扩展,将*data数据添加到nlmsg

    6.3 属性数据类型
          libnl库又再次封装了基础数据类型的函数;
         
         6.3.1 整数
             NLA_U8/NLA_U16/NLA_U32分别表示8位、16位、32位整数

读取:
uint8_t  nla_get_u8(struct nlattr *hdr);
uint16_t nla_get_u16(struct nlattr *hdr);
uint32_t nla_get_u32(struct nlattr *hdr);
uint64_t nla_get_u64(struct nlattr *hdr);
写入:
int nla_put_u8(struct nl_msg *msg, int attrtype, uint8_t value);
int nla_put_u16(struct nl_msg *msg, int attrtype, uint16_t value);
int nla_put_u32(struct nl_msg *msg, int attrtype, uint32_t value);
int nla_put_u64(struct nl_msg *msg, int attrtype, uint64_t value);
校验例子:
static struct nla_policy my_policy[ATTR_MAX+1] = {
        [ATTR_FOO] = { .type = NLA_U32 },
        [ATTR_BAR] = { .type = NLA_U8 },
};

        
        6.3.2 字符串

 获取:

char *nla_get_string(struct nlattr *hdr);
char *nla_strdup(struct nlattr *hdr);
写入:
int nla_put_string(struct nl_msg *msg, int attrtype, const char *data);
NLA_PUT_STRING(msg, attrtype, data)
校验:
static struct nla_policy my_policy[] = {
        [ATTR_FOO] = { .type = NLA_STRING,
                       .maxlen = IFNAMSIZ },
};

  7 回调函数
      linbl还可为每个nl_sock设置消息处理回调函数,当该socket上收到消息后,就会回调此函数进行处理。 回调函数及其重写函数都封装在结构体struct nl_cb中

struct nl_cb{
   nl_recvmsg_msg_cb_t      cb_set[NL_CB_TYPE_MAX+1];
   void * cb_args[NL_CB_TYPE_MAX+1];
   nl_recvmsg_err_cb_t      cb_err;
   void *cb_err_arg;
   /** May be used to replace nl_recvmsgs with your own implementation in all internal calls to nl_recvmsgs. */
  int  (*cb_recvmsgs_ow)(struct nl_sock *,     struct nl_cb *);
   /** Overwrite internal calls to nl_recv, must return the number of octets read and allocate a buffer for the received data. */
  int   (*cb_recv_ow)(struct nl_sock *, struct sockaddr_nl *,   unsigned char **, struct ucred **);
   /** Overwrites internal calls to nl_send, must send the netlink message. */
  int    (*cb_send_ow)(struct nl_sock *,  struct nl_msg *);
  int   cb_refcnt;
};

设置回调:void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb);

获取该nl_sock设置的回调函数信息:struct nl_cb *nl_socket_get_cb(const struct nl_sock *sk);

7.1 回调钩子函数

    libnl中回调钩子函数遍布,以为消息处理提供入口点或根据events执行下一步动作。

   7.1.1 返回值:

      回调函数通常返回值如下,

enum nl_cb_action {
  NL_OK,   //表示处理正常。
  NL_SKIP,  //表示停止当前netlink消息分析,转而去分析接收buffer中下一条netlink消息(消息分 片的情况)。
  NL_STOP, //表示停止此次接收buffer中的消息分析。
};

 7.1.2 消息回调函数的类型:

    libnl默认提供回调函数的三种实现方式,并允许用户自定义     

enum  nl_cb_kind {
  NL_CB_DEFAULT,  //默认回调处理函数
  NL_CB_VERBOSE,  //默认处理函数,在default的基础上会将错误信息、无效信息等打印到stderr,且nl_cb_set()或者nl_cl_err()中的arg参数提供FILE*替代stderr
  NL_CB_DEBUG,    //默认处理函数,在VERBOSE的基础上打印所有信息到终端
  NL_CB_CUSTOM,   //用户自定义处理函数
  __NL_CB_KIND_MAX
};

7.1.3  type类型

可用于处理底层不同netlink消息的情况。   例如,当收到的netlink消息无效时,将调用NL_CB_INVALIDE设置的回调函数进行处理。

enum  nl_cb_type {
  NL_CB_VALID,  //有效消息
  NL_CB_FINISH, //multipart消息结尾
  NL_CB_OVERRUN, //数据丢失报告
  NL_CB_SKIPPED, //跳过处理消息
  NL_CB_ACK,      //确认消息
  NL_CB_MSG_IN,   //所有接收到的消息
  NL_CB_MSG_OUT,   //所有发出的消息,除nl_sendto()外
  NL_CB_INVALID,   //无效消息
  NL_CB_SEQ_CHECK,  
  NL_CB_SEND_ACK,
  NL_CB_DUMP_INTR,
  __NL_CB_TYPE_MAX
};

7.2 回调函数

#include <netlink/handlers.h>  // 必须包含此头文件

 

 7.2.1 初始化

  struct nl_cb *nl_cb_alloc(enum nl_cb_kind kind);//分配新的nl_cb,返回新nl_cb地址或者NULL

  struct nl_cb *nl_cb_clone(struct nl_cb *orig) ;   //根据给定nl_cb复制新的nl_cb

 

7.2.2 设置

    设置callback: 
               typedef int (*nl_recvmsg_msg_cb_t)(struct nl_msg *msg, void *arg);

     int nl_cb_set(struct nl_cb *cb, enum nl_cb_type type, enum nl_cb_kind kind,   nl_recvmsg_msg_cb_t func, void *arg); //设置给定kind_type的callback

    设置error message callback hook:
               typedef int(* nl_recvmsg_err_cb_t)(struct sockaddr_nl *nla, struct nlmsgerr *nlerr, void *arg);

     int nl_cb_err(struct nl_cb *cb, enum nl_cb_kind kind,   nl_recvmsg_err_cb_t func, void * arg);  //设置错误callback
              
    修改给定nl_sock的callbacks:

      int nl_socket_modify_cb(struct nl_sock *sk,   enum nl_cb_type type, enum nl_cb_kind kind,   nl_recvmsg_msg_cb_t func, void *arg); //

          7.2.3 重写内部函数
               重写recvmsgs()函数:
                 void nl_cb_overwrite_recvmsgs(struct nl_cb *cb, int (*func)(struct nl_sock *sk, struct nl_cb *cb));

            //必须给定cb

    

     重写nl_recv()函数:
                 void nl_cb_overwrite_recv(struct nl_cb *cb,  int (*func)(struct nl_sock * sk,  struct sockaddr_nl *addr,  unsigned char **buf, struct ucred **cred) );

       //func成功则返回读取字节数或0(表示无数据可读),失败返回负值。需给定发送方地址addr

     重写nl_send()函数:      
                  void nl_cb_overwrite_send(struct nl_cb *cb, int (*func)(struct nl_sock *sk, struct nl_msg *msg) );
                    //func成功返回0,否则返回负值

8 cache system
    8.1 分配cache
          几乎所有的子系统都为提供分配新的cache的函数,形如
           struct nl_cahe *<object_name>_alloc_cache(struct nl_sock *sk);
         这些函数为所有的对象类型分配、初始化一个新的cache,并更新使和master当前状态保持一致。例如,link cache应该包含所有内核中当前配置的links。
         有的分配函数需要额外的参数用以指定cache内容。
         函数执行成功则返回nl_cache指针,否则返回NULL。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  netlink libnl kernel