Netlink是Linux内核提供的一种用于内核与用户空间进程之间通信的机制。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

Netlink是Linux内核提供的一种用于内核与用户空间进程之间通信的机制。它允许内核向用户空间发送消息同时也可以接收用户空间的请求并做出相应的响应。

Netlink的主要功能包括
内核通知
当网络事件发生时例如接口状态变化、路由变化等内核可以通过Netlink向用户空间发送通知。这样用户空间的应用程序可以及时了解网络状态的变化并做出相应的处理。

#include <linux/netlink.h>
#include <linux/rtnetlink.h>

/* 假设我们有一个网络接口的回调函数 */
void network_interface_change(struct net_device *dev) {
    struct sk_buff *skb;
    struct nlmsghdr *nlh;
    struct ifinfomsg *ifi;
    char *data;

    /* 创建Netlink消息 */
    skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
    if (!skb) {
        printk(KERN_ERR "Failed to allocate Netlink socket buffer\n");
        return;
    }

    data = skb_put(skb, NLMSG_SPACE(sizeof(*ifi)));
    nlh = nlmsg_put(skb, 0, 0, RTM_NEWLINK, sizeof(*ifi), 0);
    if (!nlh) {
        printk(KERN_ERR "Failed to create Netlink message\n");
        kfree_skb(skb);
        return;
    }

    ifi = nlmsg_data(nlh);
    memset(ifi, 0, sizeof(*ifi));
    ifi->ifi_family = AF_PACKET;
    ifi->ifi_change = IFF_UP | IFF_RUNNING; // 设置网络接口状态变化为启用状态
    memcpy(ifi->ifi_name, dev->name, IFNAMSIZ); // 复制网络接口名称到消息中

    /* 通过Netlink发送消息到用户空间 */
    netlink_broadcast(netlink_socket, skb, 0, RTNLGRP_LINK, GFP_KERNEL);
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>

#define NETLINK_USER 27

struct msg_data {
    struct nlmsghdr nlh;
    char ifname[IFNAMSIZ]; // 接口名称
    char status[10]; // 接口状态
};

int main(void) {
    int sockfd;
    struct sockaddr_nl addr;
    struct msg_data msg;
    int status;

    // 创建Netlink套接字
    sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_USER);
    if (sockfd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 设置接收数据包的Netlink地址结构体
    memset(&addr, 0, sizeof(addr));
    addr.nl_family = AF_NETLINK;
    addr.nl_pid = getpid(); // 设置接收消息的进程ID为当前进程ID
    addr.nl_groups = RTMGRP_LINK; // 设置接收的数据包类型为链接相关的数据包

    // 绑定套接字到Netlink地址结构体
    if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    // 接收来自内核的通知消息
    while (1) {
        status = recvfrom(sockfd, &msg, sizeof(msg), MSG_DONTWAIT, (struct sockaddr *)&addr, sizeof(addr)); // 接收消息
        if (status < 0) {
            perror("recvfrom");
            exit(EXIT_FAILURE);
        }
        if (msg.nlh.nlmsg_type == RTM_NEWLINK) { // 判断消息类型为接口状态变化通知
            printf("Interface status changed: %s %s\n", msg.ifname, msg.status); // 输出接口名称和状态变化信息
            // 在这里可以根据接口状态变化进行相应的处理逻辑
        } else {
            printf("Received other message type: %d\n", msg.nlh.nlmsg_type); // 处理其他类型的消息
        }
    }

    return 0;
}

用户空间请求
用户空间的应用程序可以通过Netlink向内核发送请求例如配置网络接口、修改路由表等。这些请求会被内核接收并处理然后内核通过Netlink将处理结果返回给用户空间的应用程序。

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

int main() {
    int sockfd;
    struct nl_msg *msg;
    struct nlattr *tb[IFLA_MAX + 1];
    struct rtmsg *rtm;
    char buf[4096];
    ssize_t n;
    int status;

    // 创建Netlink套接字
    sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
    if (sockfd < 0) {
        perror("Failed to create Netlink socket");
        exit(EXIT_FAILURE);
    }

    // 创建Netlink消息
    msg = nlmsg_alloc();
    if (!msg) {
        perror("Failed to allocate Netlink message");
        exit(EXIT_FAILURE);
    }

    // 设置消息类型和标志
    rtm = (struct rtmsg *)nlmsg_data(nlmsg_hdr(msg));
    rtm->rtm_family = AF_PACKET;
    rtm->rtm_ifindex = 0; // 设置要配置的网络接口的索引
    rtm->rtm_type = RTM_NEWLINK; // 设置请求类型为配置网络接口
    rtm->rtm_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; // 设置标志为请求、创建和排他性
    rtm->rtm_msglen = NLMSG_LENGTH(sizeof(struct rtmsg)); // 设置消息长度

    // 填充消息数据
    tb[IFLA_IFNAME] = nla_alloc(); // 接口名称属性
    nla_put_string(tb[IFLA_IFNAME], "eth0"); // 设置接口名称为"eth0"
    nlmsg_end(msg, tb); // 结束消息结构体

    // 发送消息到内核
    status = nl_sendto(sockfd, msg, nlmsg_len(nlmsg_hdr(msg)));
    if (status < 0) {
        perror("Failed to send Netlink message");
        exit(EXIT_FAILURE);
    }

    // 接收内核的处理结果
    while ((n = recv(sockfd, buf, sizeof(buf), 0)) > 0) {
        status = nlmsg_parse(buf, n, tb, IFLA_MAX, NULL); // 解析消息结构体
        if (status < 0) {
            perror("Failed to parse Netlink message");
            exit(EXIT_FAILURE);
        }
        if (tb[IFLA_IFNAME]) { // 检查接口名称属性是否存在
            printf("Interface %s has been configured.\n", nla_get_string(tb[IFLA_IFNAME])); // 打印配置结果
        } else {
            printf("Failed to configure the interface.\n"); // 打印配置失败信息
        }
    }
    close(sockfd); // 关闭Netlink套接字
    return 0;
}

内核与用户空间的交互
Netlink提供了一种机制使得内核和用户空间可以交互地传递数据。例如当一个网络包到达时内核可以通过Netlink向用户空间发送一个包含该包数据的消息。用户空间的应用程序可以接收这个消息并对其进行处理。
内核代码

#include <linux/netlink.h>
#include <linux/skbuff.h>

/* 内核中的代码 */
struct sk_buff *skb;
struct nlmsghdr *nlh;

/* 创建Netlink消息 */
skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
if (!skb) {
    printk(KERN_ERR "Failed to allocate Netlink socket buffer\n");
    return;
}

nlh = nlmsg_put(skb, 0, 0, NLMSG_DONE, 0, 0);
if (!nlh) {
    printk(KERN_ERR "Failed to create Netlink message\n");
    kfree_skb(skb);
    return;
}

/* 通过Netlink发送消息到用户空间 */
netlink_send(netlink_socket, skb);

用户空间代码
在用户空间中首先需要创建一个Netlink套接字然后使用recvfrom()函数接收来自内核的消息。以下是一个简单的用户空间代码示例

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include <string.h>

int main() {
    int sockfd;
    struct sockaddr_nl addr;
    struct nlmsghdr *nlh;
    char buffer[4096];
    int len;
    int ret;

    /* 创建Netlink套接字 */
    sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
    if (sockfd < 0) {
        perror("Failed to create Netlink socket");
        exit(EXIT_FAILURE);
    }

    /* 配置目标地址 */
    memset(&addr, 0, sizeof(addr));
    addr.nl_family = AF_NETLINK;
    addr.nl_pid = 0; // 设置目标进程ID为0表示接收来自任意进程的消息。根据需要可以设置特定的PID。
    addr.nl_groups = RTNLGRP_LINK; // 设置消息组别为链接组别接收与链接相关的消息。根据需要可以设置其他组别。

    /* 接收消息 */
    len = recvfrom(sockfd, buffer, sizeof(buffer), MSG_WAITALL, (struct sockaddr *)&addr, &addr.nl_len);
    if (len < 0) {
        perror("Failed to receive Netlink message");
        exit(EXIT_FAILURE);
    }

    /* 处理接收到的消息 */
    nlh = (struct nlmsghdr *)buffer;
    while (nlmsg_ok(nlh, len)) {
        // 处理消息...根据消息类型和内容进行相应的处理
        nlh = nlmsg_next(nlh, &len);
    }
}

Netlink的使用场景非常广泛包括但不限于以下几个方面
网络管理工具
如ifconfig、ip等命令行工具它们使用Netlink与内核进行通信以获取或修改网络接口的状态和配置。

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

int main() {
    int sockfd;
    struct nl_msg *msg;
    struct nlattr *tb[IFLA_MAX + 1];
    struct rtmsg *rtm;
    char buf[4096];
    ssize_t n;
    int status;

    // 创建Netlink套接字
    sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
    if (sockfd < 0) {
        perror("Failed to create Netlink socket");
        exit(EXIT_FAILURE);
    }

    // 创建Netlink消息
    msg = nlmsg_alloc();
    if (!msg) {
        perror("Failed to allocate Netlink message");
        exit(EXIT_FAILURE);
    }

    // 设置消息类型和标志
    rtm = (struct rtmsg *)nlmsg_data(nlmsg_hdr(msg));
    rtm->rtm_family = AF_PACKET;
    rtm->rtm_ifindex = 0; // 设置要配置的网络接口的索引
    rtm->rtm_type = RTM_NEWLINK; // 设置请求类型为配置网络接口
    rtm->rtm_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; // 设置标志为请求、创建和排他性
    rtm->rtm_msglen = NLMSG_LENGTH(sizeof(struct rtmsg)); // 设置消息长度

    // 填充消息数据
    tb[IFLA_IFNAME] = nla_alloc(); // 接口名称属性
    nla_put_string(tb[IFLA_IFNAME], "eth0"); // 设置接口名称为"eth0"
    nlmsg_end(msg, tb); // 结束消息结构体

    // 发送消息到内核
    status = nl_sendto(sockfd, msg, nlmsg_len(nlmsg_hdr(msg)));
    if (status < 0) {
        perror("Failed to send Netlink message");
        exit(EXIT_FAILURE);
    }

    // 接收内核的处理结果
    while ((n = recv(sockfd, buf, sizeof(buf), 0)) > 0) {
        status = nlmsg_parse(buf, n, tb, IFLA_MAX, NULL); // 解析消息结构体
        if (status < 0) {
            perror("Failed to parse Netlink message");
            exit(EXIT_FAILURE);
        }
        if (tb[IFLA_IFNAME]) { // 检查接口名称属性是否存在
            printf("Interface %s has been configured.\n", nla_get_string(tb[IFLA_IFNAME])); // 打印配置结果
        } else {
            printf("Failed to configure the interface.\n"); // 打印配置失败信息
        }
    }
    close(sockfd); // 关闭Netlink套接字
    return 0;
}

网络监控工具
如tcpdump、wireshark等它们使用Netlink接收内核发送的网络包数据以进行网络流量分析和监控。

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

#define BUFFER_SIZE 4096

int main() {
    int sockfd;
    char buffer[BUFFER_SIZE];
    struct sockaddr_nl addr;
    struct nlmsghdr *nlh;
    struct rtattr *tb[IFLA_MAX + 1];
    int status;

    // 创建Netlink套接字
    sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
    if (sockfd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 设置接收数据包的Netlink地址结构体
    memset(&addr, 0, sizeof(addr));
    addr.nl_family = AF_NETLINK;
    addr.nl_pid = getpid(); // 设置发送消息的进程ID
    addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR; // 设置接收的数据包类型这里接收链接和IPv4地址相关的数据包

    // 绑定套接字到Netlink地址结构体
    if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    // 接收网络包数据
    while (1) {
        status = recvfrom(sockfd, buffer, BUFFER_SIZE, MSG_DONTWAIT, (struct sockaddr *)&addr, sizeof(addr));
        if (status < 0) {
            perror("recvfrom");
            exit(EXIT_FAILURE);
        }

        // 解析网络包数据这里只简单地打印消息类型和消息长度实际应用中需要根据具体需求进行解析和处理
        nlh = (struct nlmsghdr *)buffer;
        while (NLMSG_OK(nlh, status)) {
            printf("Message type: %d\n", nlh->nlmsg_type);
            printf("Message length: %d\n", nlh->nlmsg_len);
            nlh = NLMSG_NEXT(nlh, status);
        }
    }

    // 关闭套接字
    close(sockfd);
    return 0;
}

网络服务守护进程
如路由守护进程routed和动态主机配置协议DHCP服务器等它们使用Netlink与内核进行通信以处理网络路由和配置相关的请求。

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

#define BUFFER_SIZE 4096

int main() {
    int sockfd;
    char buffer[BUFFER_SIZE];
    struct sockaddr_nl addr;
    struct nlmsghdr *nlh;
    struct rtmsg *rtm;
    int status;

    // 创建Netlink套接字
    sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
    if (sockfd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 设置接收数据包的Netlink地址结构体
    memset(&addr, 0, sizeof(addr));
    addr.nl_family = AF_NETLINK;
    addr.nl_pid = getpid(); // 设置发送消息的进程ID
    addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE; // 设置接收的数据包类型这里接收链接和IPv4路由相关的数据包

    // 绑定套接字到Netlink地址结构体
    if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    // 发送路由信息到内核
    nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(sizeof(*rtm)));
    memset(nlh, 0, NLMSG_SPACE(sizeof(*rtm)));
    nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*rtm));
    nlh->nlmsg_type = RTM_NEWROUTE; // 消息类型为添加路由
    nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE; // 设置消息标志为请求、创建和替换路由
    nlh->nlmsg_seq = 1; // 消息序列号
    rtm = NLMSG_DATA(nlh); // 路由信息结构体指针
    memset(rtm, 0, sizeof(*rtm));
    rtm->rtm_family = AF_INET; // 地址族为IPv4
    rtm->rtm_dst_len = 32; // 目标地址长度为32位IPv4地址长度
    rtm->rtm_src_len = 0; // 源地址长度为0不指定源地址
    rtm->rtm_tos = 0; // 服务类型为0默认值
    rtm->rtm_table = RT_TABLEID(RT_TABLEID_MAIN); // 路由表ID为主表默认值
    rtm->rtm_protocol = RTPROT_BOOT; // 路由协议为BOOTPROTO默认值
    rtm->rtm_scope = RT_SCOPE_UNIVERSE; // 作用域为UNIVERSE默认值
    rtm->rtm_type = RTN_UNICAST; // 路由类型为单播默认值
    strncpy(rtm->rtm_dst, "192.168.1.0", RTA_LEN-1); // 设置目标地址为192.168.1.0/24网段示例
    status = sendto(sockfd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&addr, sizeof(addr)); // 发送消息到内核
    if (status < 0) {
        perror("sendto");
        exit(EXIT_FAILURE);
    }

    // 接收来自内核的路由信息响应消息
    while (1) {
        status = recvfrom(sockfd, buffer, BUFFER_SIZE, MSG_DONTWAIT, (struct sockaddr *)&addr, sizeof(addr)); // 接收消息
        if (status < 0) {
            perror("recvfrom");
            exit(EXIT_FAILURE);
        }
        nlh = (struct nlmsghdr *)buffer; // 从接收到的消息中获取NLMSGHDR结构体指针
        while (NLMSG_OK(nlh, status)) {
    // 解析消息
    switch (nlh->nlmsg_type) {
        case RTM_NEWROUTE:
            // 处理路由信息
            printf("Received RTM_NEWROUTE message\n");
            break;
        case RTM_DELROUTE:
            // 处理删除路由信息
            printf("Received RTM_DELROUTE message\n");
            break;
        default:
            // 处理其他消息类型
            printf("Received other message type: %d\n", nlh->nlmsg_type);
            break;
    }

    // 移动到下一个消息
    nlh = NLMSG_NEXT(nlh, status);
}

自定义应用程序
开发者可以使用Netlink来编写自定义的网络应用程序以实现特定的网络功能或服务。

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

#define NETLINK_USER 27

struct msg_data {
    struct nlmsghdr nlh;
    char msg[10];
};

int main(void) {
    int sockfd;
    struct sockaddr_nl addr;
    struct msg_data msg;
    int status;

    // 创建Netlink套接字
    sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_USER);
    if (sockfd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 设置接收数据包的Netlink地址结构体
    memset(&addr, 0, sizeof(addr));
    addr.nl_family = AF_NETLINK;
    addr.nl_pid = 0; // 设置接收消息的进程ID为0表示接收来自内核的消息
    addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE; // 设置接收的数据包类型这里接收链接和IPv4路由相关的数据包

    // 绑定套接字到Netlink地址结构体
    if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    // 发送消息到内核
    memset(&msg, 0, sizeof(msg));
    msg.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(msg));
    msg.nlh.nlmsg_type = NLMSG_USER; // 消息类型为自定义用户消息类型
    msg.nlh.nlmsg_flags = NLM_F_REQUEST; // 设置消息标志为请求消息
    msg.nlh.nlmsg_seq = 1; // 消息序列号
    strncpy(msg.msg, "Hello Netlink", sizeof(msg.msg)); // 设置消息内容为"Hello Netlink"
    status = sendto(sockfd, &msg, msg.nlh.nlmsg_len, 0, (struct sockaddr *)&addr, sizeof(addr)); // 发送消息到内核
    if (status < 0) {
        perror("sendto");
        exit(EXIT_FAILURE);
    }

    // 接收来自内核的响应消息
    status = recvfrom(sockfd, &msg, sizeof(msg), MSG_DONTWAIT, (struct sockaddr *)&addr, sizeof(addr)); // 接收消息
    if (status < 0) {
        perror("recvfrom");
        exit(EXIT_FAILURE);
    }
    printf("Received message from kernel: %s\n", msg.msg); // 输出接收到的消息内容

    return 0;
}
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

“Netlink是Linux内核提供的一种用于内核与用户空间进程之间通信的机制。” 的相关文章

怎么使用PHP编写数据库查询筛选语句 - 编程语言

这篇“怎么使用PHP编写数据库查询筛选语句”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“怎么使用PHP编写数据库查询筛选语句”文章吧。...

Qt中拿到发送信号过来的对象

QCheckBox *checkBox = qobject_cast<QCheckBox*>(sender())...

静态成员的声明与实现

#include"iostream" using namespace std; class A{ private: static double d;//声明 static long l; public: static void show(){ cout &l...

UVa 10780 Again Prime? No Time. (数论&素因子分解)

10780 - Again Prime? No Time.Time limit: 3.000 secondshttp://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=467&p...

Codeforces VK Cup 2012 Qualification Round 1 /158C (字符串处理)

C. Cd and pwd commands http://codeforces.com/problemset/problem/158/C time limit per test memory limit per test i...

PHP怎么实现异步定时多任务消息推送 - 开发技术

这篇“PHP怎么实现异步定时多任务消息推送”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“PHP怎么实现异步定时多任务消息推送”文章吧。在 PHP 中实现...