是IPC。用户进程之间,用户进程和内核之间。
是双向通信机制。比syscall、ioctl、procfs优越。
是socket,故具有socket API
是异步通信方式
像UDP,支持多播,sub/pub机制
内核模块化设计,可动态加载
总结:支持多播双向异步socket方式模块化通信机制。
NETLINK_ROUTE、USERSOCK、FIREWALL、NETFILTER、KOBJECT_UEVEN、GENERIC
总结:内核路由子系统、防火墙等都在使用,用户进程的工具iproute2等也在使用。
/* netlink简单示例之: 检测网络设备up/down状态 */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
void parse_rtattr(struct rtattr **tb, int max, struct rtattr *attr, int len)
{
for (; RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) {
if (attr->rta_type <= max) {
tb[attr->rta_type] = attr;
}
}
}
int main(int argc, char **argv)
{
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (fd < 0) {
perror("netlink socket fail");
exit(-1);
}
struct sockaddr_nl sa; // { nl_family, nl_pad, nl_pid, nl_groups }
bzero(&sa, sizeof(sa));
sa.nl_family = AF_NETLINK;
sa.nl_groups = RTMGRP_LINK;
if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
perror("bind error");
close(fd);
exit(-1);
}
int rlen;
socklen_t len = 0;
char buff[2048] = {0};
struct sockaddr_nl addr;
struct nlmsghdr *nlh;
struct rtattr *tb[IFLA_MAX + 1];
struct ifinfomsg *ifinfo;
int msglen = 0;
/* 仅供演示,实际使用还是select/epoll */
while(1) {
len = sizeof(addr);
rlen = recvfrom(fd, (void *)buff, sizeof(buff), 0, (struct sockaddr *)&addr, &len);
if (rlen>0) {
nlh = (struct nlmsghdr *)buff;
for (; NLMSG_OK(nlh, rlen); nlh = NLMSG_NEXT(nlh, rlen)) {
switch(nlh->nlmsg_type) {
case RTM_NEWLINK:
case RTM_DELLINK:
bzero(tb, sizeof(tb));
ifinfo = NLMSG_DATA(nlh);
msglen = nlh->nlmsg_len - NLMSG_SPACE(sizeof(*ifinfo));
parse_rtattr(tb, IFLA_MAX, IFLA_RTA (ifinfo), msglen);
if (tb[IFLA_IFNAME]) {
printf("%s:%s\n", RTA_DATA(tb[IFLA_IFNAME]), (ifinfo->ifi_flags & IFF_UP) ? "up" : "down");
}
break;
default:
break;
}
}
} else {
sleep(1);
}
}
/* 仅供演示,实际使用还是要处理退出 */
close(fd);
return 0;
}
[root@~]#./a.out &
[root@~]#
[root@~]#ifconfig eth0 down up
eth0:down
eth0:up
“支持多播双向异步socket方式模块化通信机制”,体现了哪些特点:
多播(订阅)---可运行多个实例,每个都会收到消息;
异步;socket方式;
代码量:相比PF_INET + ioctl的方式,为同一级别,比ioctl略少,这里只有设备up/down状态监测,如果是还需要其它功能,例如路由信息变化,那相比ioctl的优势会明显体现。
来看iproute2。这个项目的librtnetlink提供下面API:
rtnl_open_byproto : 创建AF_NETLINK socket
rtnl_talk : 最终调用sendmsg和recvmsg通过netlink socket和内核通信
rtnl_listen: 最终调用recvmsg通过netlink socket收内核发来的消息
最终编译成ip可执行程序的ip.c,定义cmd结构体数组用来处理选项,例如用户的命令:ip route add default dev eth0,就是通过do_iproute来处理的。
static const struct cmd {
const char *cmd;
int (*func)(int argc, char **argv);
} cmds[] = {
{ "address", do_ipaddr },
{ "addrlabel", do_ipaddrlabel },
{ "maddress", do_multiaddr },
{ "route", do_iproute },
……
{ 0 }
};
调用链: main - - -> do_cmd - - -> do_iproute - - -> iproute_modify - - -> rtnl_talk - - -> sendmsg & recvmsg
因为呈现给用户的最终还是一个socket,从用户的角度来看,内核要实现一个DGRAM socket,利用L4 socket组件填充socket、bind、sendmsg、recvmsg系统调用。
双向异步socket方式多播:内部是DGRAM类型socket,因此天然具备这些特性。
内核实现进一步分析另文。