linux的netlink

在进行程序开发时,经常需要得知ip的路由设置等的变化,我们很容易想到使用命令进行反复查询,但这样不仅不及时而且耗费性能,要是当网络发送变化时系统能通知到应用程序的话那就完美解决问题。这个时候我们就需要使用Netlink 。

Netlink 是一种IPC(Inter Process Commumicate)机制,它是一种用于内核与用户空间通信的机制,同时它也以用于进程间通信(Netlink 更多用于内核通信,进程之间通信更多使用Unix域套接字)。

在一般情况下,用户态和内核态通信会使用传统的Ioctl、sysfs属性文件或者procfs属性文件,这3种通信方式都是同步通信方式,由用户态主动发起向内核态的通信,内核无法主动发起通信。

而Netlink是一种异步全双工的通信方式,它支持由内核态主动发起通信,内核为Netlink通信提供了一组特殊的API接口,用户态则基于socket API,内核发送的数据会保存在接收进程socket 的接收缓存中,由接收进程处理。

下面提供一个Netlink 使用示例:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>


int netlink_handler(void)
{
    int                 sockfd;
    int                 ret;
    struct sockaddr_nl  sa = {0};
    struct nlmsghdr*    nlh;
    char                buff[4096] = {0};

    sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
	if (sockfd == -1)
	{
		printf("netlink socket failed: %d, err: %d<%s>", ret, errno, strerror(errno));
		return -1;
	}

    sa.nl_family = AF_NETLINK;
    sa.nl_groups = RTMGRP_LINK | RTMGRP_NOTIFY | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE;
    ret = bind(sockfd, (struct sockaddr*)&sa, sizeof(sa));
    if (ret < 0)
    {
        printf("netlink bind failed: %d, err: %d<%s>", ret, errno, strerror(errno));
        close(sockfd);
        return -2;
    }

    while (1)
    {
        ret = recv(sockfd, buff, sizeof(buff), 0);
        if (ret < 0)
        {
            if (errno != EINTR)
            {
                printf("netlink recv failed: %d, err: %d<%s>", ret, errno, strerror(errno));
            }
            continue;
        }
        for (nlh = (struct nlmsghdr*)buff; NLMSG_OK(nlh, ret); nlh = NLMSG_NEXT(nlh, ret))
        {
            switch (nlh->nlmsg_type)
            {
            case RTM_NEWADDR:
            case RTM_DELADDR:
                break;
            case RTM_NEWROUTE:
            case RTM_DELROUTE:
            case RTM_GETROUTE:
                break;
            case RTM_NEWLINK:
            case RTM_DELLINK:
            case RTM_GETLINK:
            case RTM_SETLINK:
                break;
            case NLMSG_DONE:
            case NLMSG_ERROR:
                break;
            default:
                break;
            }
        }
    }

    close(sockfd);
    return 0;
}