netlink(二)-API

190阅读 0评论2023-08-04 zpf1218
分类:LINUX

1、用户态API

1、创建socket

Int skfd = socket(PF_NETLINK, SOCK_RAW, NL_IMP2)

l   {BANNED}中国第一个参数必须是 AF_NETLINK PF_NETLINK,在 Linux 中,它们俩实际为一个东西,它表示要使用netlink

l   第二个参数必须是SOCK_RAWSOCK_DGRAM, 因为netlink是一个面向数据报的服务;

l   第三个参数指定netlink协议类型,它可以是一个自定义的类型,也可以使用内核预定义的类型

2、绑定socket

struct sockaddr_nl local;

memset(&local, 0, sizeof(local));

local.nl_family = AF_NETLINK;

local.nl_pid = getpid(); /*设置pid为自己的pid值,也可以自定义值,不能和现有的重复*/

local.nl_groups = 0;

bind(skfd, (struct sockaddr*)&local, sizeof(local));

3、发送socket

用户空间调用send函数(如sendtosendmsg等)向内核发送数据,使用同样的socket地址来描述内核,不过需要注意,由于对端是内核,nl_pid必须设置为0

l   如果该消息是发送至内核的,那么nl_pidnl_groups都置为0.

l   如果消息是发送给另一个进程的单播消息,nl_pid是另外一个进程的pid值而nl_groups为零。

struct sockaddr_nl daddr;

 memset(&daddr, 0, sizeof(daddr));

 daddr.nl_family = AF_NETLINK;

 daddr.nl_pid = 0; // to kernel

 daddr.nl_groups = 0;

    //初始化消息头

   struct nlmsghdr  nlh;

    memset(&nlh, 0, sizeof(struct nlmsghdr));

    nlh.nlmsg_len = NLMSG_SPACE(MAX_PLOAD);

    nlh.nlmsg_flags = 0;

    nlh.nlmsg_type = 0;

    nlh.nlmsg_seq = 0;

    nlh.nlmsg_pid = saddr.nl_pid; //self port

    //设置消息内容

    memcpy(NLMSG_DATA(nlh),msg,strlen(msg));

  int ret = sendto(skfd,nlh,nlh->nlmsg_len,0,(struct sockaddr *)&daddr,sizeof(struct sockaddr_nl));

ret大于0 ,表示发送的实际长度

ret小于0 ,表示发送失败,错误通过errno获取

4、接收socket

typedef struct _user_msg_info

{

    struct nlmsghdr hdr;

    char msg[MSG_LEN];

} user_msg_info;

user_msg_info  u_info

 memset(&u_info, 0, sizeof(u_info));

 len = sizeof(struct sockaddr_nl);

  //接收消息

Int ret = recvfrom(skfd,&u_info,sizeof(user_msg_info),0,(struct sockaddr *)&daddr,&len);

ret大于0 ,表示接收的实际长度

ret小于0 ,表示接收失败,错误原因通过errno获取

5、关闭 socket

close函数关闭打开的netlink socket

close(skfd)

2、内核态API

1、 创建netlink套接字

通过netlink_kernel_create创建一个netlink套接字,同时,注册一个回调函数,用于接收处理用户空间的消息。

struct sock * netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)

参数说明:

l   net:是一个网络名字空间namespace,在不同的名字空间里面可以有自己的转发信息库,有自己的一套net_device等等。默认情况下都是使用 init_net这个全局变量。

l   unit:表示netlink协议类型,如NETLINK_TESTNETLINK_SELINUX

l   cfg:存放的是netlink内核配置参数,参数都是可选的,不需要的可以不赋值

struct netlink_kernel_cfg {

       unsigned int   groups; //用户指定关联的多播组

       unsigned int   flags; //指定socket标志

       void        (*input)(struct sk_buff *skb); //指定接收处理回调函数

       struct mutex   *cb_mutex; //互斥量保护回调函数

       int          (*bind)(struct net *net, int group); //用于绑定到socket

       void        (*unbind)(struct net *net, int group); //用于解绑socket

       bool       (*compare)(struct net *net, struct sock *sk); //用于比较socket

};

2、发送单播消息

int netlink_unicast(struct sock *ssk, struct sk_buff *skb, u32 pid, int nonblock)

参数说明:

l   ssk:为函数 netlink_kernel_create()返回的socket

l   skb:存放消息,它的data字段指向要发送的netlink消息结构,而skb的控制块保存了消息的地址信息,宏NETLINK_CB(skb)就用于方便设置该控制块。

l   pid:为接收此消息进程的pid,即目标地址,如果目标为组或内核,它设置为 0

l   nonblock:表示该函数是否为非阻塞,如果为1,该函数将在没有接收缓存可利用时立即返回,内核设置超时时间为0;而如果为0,该函数在没有接收缓存可利用定时睡眠,内核设置了大于0的固定超时时间。

返回值:

1、大于0表示发送成功,返回值是发送数据实际长度(包括消息头)。

        2、小于0 表示发送失败,根据errno可以获取错误原因,如果是异步的需要对EAGAIN进行重发处理。

3、发送广播消息

int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, u32 group, gfp_t allocation)

前面的三个参数与 netlink_unicast相同

l   参数group为接收消息的多播组,该参数的每一个位代表一个多播组,因此如果发送给多个多播组,就把该参数设置为多个多播组组ID的位或。

l   参数allocation为内核内存分配类型,一般地为GFP_ATOMICGFP_KERNELGFP_ATOMIC用于原子的上下文(即不可以睡眠),而GFP_KERNEL用于非原子上下文。

4、接收处理用户空间发送的数据

接收消息应该是由内核接收到消息后,回调netlink_kernel_create 创建时注册的接收函数

void        (*input)(struct sk_buff *skb); //指定接收处理回调函数


上一篇:关于kafka消费者处理消息异常实验
下一篇:netlink(三)-用户态发起会话