文章目录
- 一、功能介绍
- 二、内核netlink初始化注册协议族AF_NETLINK
- 三、内核中注册AF_NETLINK协议族的特殊协议
- 四、应用层netlink通信API
一、功能介绍
Netlink 是一种IPC(Inter Process Commumicate)机制,它是一种用于内核与用户空间通信的机制,同时它也可以用于进程间通信,
(Netlink 更多用于内核通信,进程之间通信更多使用Unix域套接字,除非需要用到Netlink的广播特性时)。在一般情况下,
用户态和内核态通信会使用三种传统的通信方式: 1 ioctl; 2 sysfs属性文件; 3 proc fs属性文件
但这3种通信方式都是同步通信方式,由用户态主动发起向内核态的通信,内核无法主动发起通信。
而Netlink是一种异步全双工的通信方式,它支持由内核态主动发起通信,内核为Netlink通信提供了一组特殊的API接口,
用户态则基于socket API,内核发送的数据会保存在接收进程socket 的接收缓存中,由接收进程处理。Netlink 有以下优点:
1 双向全双工异步传输,支持由内核主动发起传输通信,而不需要用户空间出发(例如使用ioctl这类的单工方式)。
如此用户空间在等待内核某种触发条件满足时就无需不断轮询,而异步接收内核消息即可。
2 支持组播传输,即内核态可以将消息发送给多个接收进程,这样就不用每个进程单独来查询了。
目前已经有许多内核模块使用netlink 机制,其中驱动模型中使用的uevent 就是基于netlink 实现,
netlink的架构框图如下:
#define NETLINK_ROUTE 0 /* 用于设置和查询路由表等网络核心模块*/
#define NETLINK_UNUSED 1 /* Unused number */
#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */
#define NETLINK_FIREWALL 3 /* Unused number, formerly ip_queue */
#define NETLINK_SOCK_DIAG 4 /* socket monitoring */
#define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */
#define NETLINK_XFRM 6 /* ipsec */
#define NETLINK_SELINUX 7 /* SELinux event notifications */
#define NETLINK_ISCSI 8 /* Open-iSCSI */
#define NETLINK_AUDIT 9 /* auditing */
#define NETLINK_FIB_LOOKUP 10
#define NETLINK_CONNECTOR 11
#define NETLINK_NETFILTER 12 /* netfilter subsystem */
#define NETLINK_IP6_FW 13
#define NETLINK_DNRTMSG 14 /* DECnet routing messages */
#define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace 用于uevent消息通信*/
#define NETLINK_GENERIC 16 //generic netlink
/* leave room for NETLINK_DM (DM Events) */
#define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */
#define NETLINK_ECRYPTFS 19
#define NETLINK_RDMA 20
#define NETLINK_CRYPTO 21 /* Crypto layer */
对于在实际应用中,可能会有一些定制化的需求,以上这几种专用的协议类型无法满足,这时可以在不超过最大32种类型的基础之上自行添加。但是一般情况下这样做有些不妥,于是内核开发者就设计了一种通用netlink 协议类型(Generic Netlink)NETLINK_GENERIC,它就是一个Netlink复用器,便于用户自行扩展子协议类型。
下面来分析Netlink的具体创建和通信流程。
二、内核netlink初始化注册协议族AF_NETLINK
4.19.190内核
net/netlink/af_netlink.c
628 static struct proto netlink_proto = {
629 .name = "NETLINK",
630 .owner = THIS_MODULE,
631 .obj_size = sizeof(struct netlink_sock),
632 };
89 struct netlink_table *nl_table __read_mostly;
90 EXPORT_SYMBOL_GPL(nl_table);// 全局
2745 static int __init netlink_proto_init(void)
2746 {
2747 int i;
2748 int err = proto_register(&netlink_proto, 0);
//向内核注册netlink协议,第二个参数表示是否申请slab 将协议加入proto_list
2749
2750 if (err != 0)
2751 goto out;
2752
2753 BUILD_BUG_ON(sizeof(struct netlink_skb_parms) > FIELD_SIZEOF(struct sk_buff, cb));
2754
2755 nl_table = kcalloc(MAX_LINKS, sizeof(*nl_table), GFP_KERNEL);
//创建32组netlink_table 这个表是整个netlink实现的最关键的一步,每种协议类型占数组中的一项,后续内核中创建的不同种协议类型的netlink都将保存在这个表中,由该表统一维护 其定义见下面详解1
2756 if (!nl_table)
2757 goto panic;
2758
2759 for (i = 0; i < MAX_LINKS; i++) {
2760 if (rhashtable_init(&nl_table[i].hash,
2761 &netlink_rhashtable_params) < 0) {
//初始化哈希表,有时间再分析
2762 while (--i > 0)
2763 rhashtable_destroy(&nl_table[i].hash);
2764 kfree(nl_table);
2765 goto panic;
2766 }
2767 }
2768
2769 netlink_add_usersock_entry();
//初始化应用层使用的NETLINK_USERSOCK协议类型的netlink(用于应用层进程间通信)
2770
2771 sock_register(&netlink_family_ops);//将netlink socket 创建 函数注册到内核中,
//即创建netlink socket 时 回调函数为 netlink_family_ops->create 回调 见详解2
2772 register_pernet_subsys(&netlink_net_ops);//见详解3
2773 register_pernet_subsys(&netlink_tap_net_ops);
2774 /* The netlink device handler may be needed early. */
2775 rtnetlink_init();//调用rtnetlink_init()创建NETLINK_ROUTE协议类型的netlink,
//该种类型的netlink才是当初内核设计netlink的初衷,它用来传递网络路由子系统、邻居子系统、
//接口设置、防火墙等消息。至此整个netlink子系统初始化完成。
2776 out:
2777 return err;
2778 panic:
2779 panic("netlink_init: Cannot allocate nl_table\n");
2780 }
详解1:
56 struct netlink_table {
57 struct rhashtable hash;//hash(哈希表)用来索引同种协议类型的不同netlink套接字实例 //通过portid
58 struct hlist_head mc_list;//为多播使用的sock散列表
59 struct listeners __rcu *listeners;//监听者掩码
60 unsigned int flags;
61 unsigned int groups;//协议支持的最大多播组数量
62 struct mutex *cb_mutex;
63 struct module *module;
/*定义了一些函数指针,它们会在内核首次创建netlink时被赋值,后续应用层创建和绑定socket时调用到*/
64 int (*bind)(struct net *net, int group);
65 void (*unbind)(struct net *net, int group);
66 bool (*compare)(struct net *net, struct sock *sock);
67 int registered;//代表已经经过注册
68 };
详解2:
SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)-》__sys_socket-》sock_create-》__sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0);
{
...
pf = rcu_dereference(net_families[family]);//读者调用它来获得一个被RCU保护的指针。 这里会得到netlink_family_ops,原理如下《而net_families怎么初始化的呢》
pf->create(net, sock, protocol, kern);
...
}
2680 static const struct net_proto_family netlink_family_ops = {
2681 .family = PF_NETLINK, //#define PF_NETLINK AF_NETLINK
2682 .create = netlink_create,
2683 .owner = THIS_MODULE, /* for consistency 8) */
2684 };
net_families怎么初始化的
sock_register->rcu_assign_pointer(net_families[ops->family], ops);
详解3:
调用register_pernet_subsys向内核所有的网络命名空间注册”子系统“的初始化和去初始化函数,这里的"子系统”并非指的是netlink子系统,而是一种通用的处理方式,在网络命名空间创建和注销时(后面新创建如用socket时)会调用这里注册的初始化和去初始化函数,当然对于已经存在的网络命名空间,在注册的过程中也会调用其初始化函数。netlink_net_ops定义如下:
2723 static struct pernet_operations __net_initdata netlink_net_ops = {
2724 .init = netlink_net_init, //大概就是在/proc下每个进程的下建立netlimk如下
/*
/proc/1/task/1/net/netlink
/proc/1/net/netlink
/proc/2/task/2/net/netlink
/proc/2/net/netlink
。。。
*/
2725 .exit = netlink_net_exit,
2726 };
三、内核中注册AF_NETLINK协议族的特殊协议
Rtnetlink 允许对内核路由表进行读和更改,它用于内核与各个子系统之间(路由子系统、IP地址、链接参数等)的通信,用户空间可以通过NET_LINK_ROUTER socket 与内核进行通信,该过程基于标准的netlink消息进行。
./net/core/rtnetlink.c
4872 void __init rtnetlink_init(void)
4873 {
4874 if (register_pernet_subsys(&rtnetlink_net_ops))//详解3.1
4875 panic("rtnetlink_init: cannot initialize rtnetlink\n");
4876
4877 register_netdevice_notifier(&rtnetlink_dev_notifier);
4878
4879 rtnl_register(PF_UNSPEC, RTM_GETLINK, rtnl_getlink, //《== here
4880 rtnl_dump_ifinfo, 0);
4881 rtnl_register(PF_UNSPEC, RTM_SETLINK, rtnl_setlink, NULL, 0);
4882 rtnl_register(PF_UNSPEC, RTM_NEWLINK, rtnl_newlink, NULL, 0);
4883 rtnl_register(PF_UNSPEC, RTM_DELLINK, rtnl_dellink, NULL, 0);
4884
4885 rtnl_register(PF_UNSPEC, RTM_GETADDR, NULL, rtnl_dump_all, 0);
4886 rtnl_register(PF_UNSPEC, RTM_GETROUTE, NULL, rtnl_dump_all, 0);
4887 rtnl_register(PF_UNSPEC, RTM_GETNETCONF, NULL, rtnl_dump_all, 0);
4888
4889 rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, rtnl_fdb_add, NULL, 0);
4890 rtnl_register(PF_BRIDGE, RTM_DELNEIGH, rtnl_fdb_del, NULL, 0);
4891 rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, rtnl_fdb_dump, 0);
4892
4893 rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, rtnl_bridge_getlink, 0);//《== here
4894 rtnl_register(PF_BRIDGE, RTM_DELLINK, rtnl_bridge_dellink, NULL, 0);
4895 rtnl_register(PF_BRIDGE, RTM_SETLINK, rtnl_bridge_setlink, NULL, 0);
4896
4897 rtnl_register(PF_UNSPEC, RTM_GETSTATS, rtnl_stats_get, rtnl_stats_dump,
4898 0);
4899 }
详解3.1
将rtnetlink的init函数和exit函数注册到内核的每个网络命名空间中,对于已经存在的网络命名空间会调用其中个的init函数,即函数rtnetlink_net_init()。rtnetlink_net_ops定义如下;
4867 static struct pernet_operations rtnetlink_net_ops = {
4868 .init = rtnetlink_net_init,
4869 .exit = rtnetlink_net_exit,
4870 };
分析函数rtnetlink_net_init
4843 static int __net_init rtnetlink_net_init(struct net *net)
4844 {
4845 struct sock *sk;
4846 struct netlink_kernel_cfg cfg = {
//首先这里定义了一个 netlink_kernel_cfg结构体实例,该结构体定义位于include\linux\netlink.h
//关于这个结构的详细解释见详解3.1.1
4847 .groups = RTNLGRP_MAX,
4848 .input = rtnetlink_rcv,
4849 .cb_mutex = &rtnl_mutex,
4850 .flags = NL_CFG_F_NONROOT_RECV,
4851 .bind = rtnetlink_bind,
4852 };
4853 //设置groups为RTNLGRP_MAX后指定消息接收处理函数为rtnetlink_rcv,并设置flag为NL_CFG_F_NONROOT_RECV,这表明非超级用户可以绑定到多播组,但是没有设置NL_CFG_F_NONROOT_SEND,这表明非超级用户将不能发送组播消息。
4854 sk = netlink_kernel_create(net, NETLINK_ROUTE, &cfg);
//创建了内核的netlinksock用来接收用户态消息
//调用netlink_kernel_create()向当前的网络命名空间创建NETLINK_ROUTE类型的套接字,并指定定义的那个配置结构cfg。 见详解3.1.2
4855 if (!sk)
4856 return -ENOMEM;
4857 net->rtnl = sk;
4858 return 0;
4859 }
详解3.1.1:
43 /* optional Netlink kernel configuration parameters */
44 struct netlink_kernel_cfg {
45 unsigned int groups;//用于指定最大的多播组
46 unsigned int flags;
//可以为NL_CFG_F_NONROOT_RECV或NL_CFG_F_NONROOT_SEND,这两个符号前者用来限定非超级用户是否可以绑定到多播组,后者用来限定非超级用户是否可以发送组播;
47 void (*input)(struct sk_buff *skb);
//用于指定回调函数,该回调函数用于接收和处理来自用户空间的消息(若无需接收来自用户空间的消息可不指定);
48 struct mutex *cb_mutex;
//最后的三个函数指针实现sock的绑定和解绑定等操作,会添加到nl_table对应的项中去。
/*
以bind为例分析,产生作用的地方:
netlink_kernel_create -》 nl_table[unit].bind = cfg->bind;
应用层socket ...(省略部分见上面)->create //netlink_create
netlink_create{
bind = nl_table[protocol].bind;
nlk->netlink_bind = bind;
}
用户态bind-》 netlink_ops->bind //netlink_bind
netlink_bind
{
if (nlk->netlink_bind && groups) {//看起来和多播有关
。。。
nlk->netlink_bind(net, group + 1);
。。。
}
}
以rtnetlink_bind为例其仅是检查group为多播组时的权限问题
*/
49 int (*bind)(struct net *net, int group);
50 void (*unbind)(struct net *net, int group);
51 bool (*compare)(struct net *net, struct sock *sk);
52 };
详解3.1.2
58 netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)
59 {
60 return __netlink_kernel_create(net, unit, THIS_MODULE, cfg);
61 }
2010 /*
2011 * We export these functions to other modules. They provide a
2012 * complete set of kernel non-blocking support for message
2013 * queueing.
2014 */
2015
2016 struct sock *
2017 __netlink_kernel_create(struct net *net, int unit, struct module *module,
2018 struct netlink_kernel_cfg *cfg)
2019 {
2020 struct socket *sock;
2021 struct sock *sk;
2022 struct netlink_sock *nlk;
2023 struct listeners *listeners = NULL;
2024 struct mutex *cb_mutex = cfg ? cfg->cb_mutex : NULL;
2025 unsigned int groups;
2026
2027 BUG_ON(!nl_table);
2028
2029 if (unit < 0 || unit >= MAX_LINKS)
2030 return NULL;
2031 //创建了一个以PF_NETLINK为地址族的 SOCK_DGRAM类型的 socket 套接字,
//其协议类型就是作为参数传入的NETLINK_ROUTE (PF_NETLINK, SOCK_DGRAM, unit)
2032 if (sock_create_lite(PF_NETLINK, SOCK_DGRAM, unit, &sock))
2033 return NULL;
2034 //初始化netlink套接字 --------- 详解3.1.2.1
2035 if (__netlink_create(net, sock, cb_mutex, unit, 1) < 0)
2036 goto out_sock_release_nosk;
2037
//上面两步说明 family type proto 对应唯一socket 唯一sock
2038 sk = sock->sk;
2039
2040 if (!cfg || cfg->groups < 32)
//默认最小支持32个组播地址(因为后文会看到用户层在绑定地址时最多绑定32个组播地址),但内核也有可能支持大于32个组播地址的情况(Genetlink就属于这种情况)
2041 groups = 32;
2042 else
2043 groups = cfg->groups;
2044
2045 listeners = kzalloc(sizeof(*listeners) + NLGRPSZ(groups), GFP_KERNEL);
2046 if (!listeners)
2047 goto out_sock_release;
2048
2049 sk->sk_data_ready = netlink_data_ready;
2050 if (cfg && cfg->input)
2051 nlk_sk(sk)->netlink_rcv = cfg->input;
2052 //这里将前文中定义的rtnetlink_rcv注册到了nlk_sk(sk)->netlink_rcv中,这样就设置完了内核态的消息处理函数
2053 if (netlink_insert(sk, 0))//将本次创建的这个套接字添加到nl_table中去(其核心是调用__netlink_insert()),注册的套接字是通过nl_table中的哈希表来管理的。
2054 goto out_sock_release;
2055
2056 nlk = nlk_sk(sk);
2057 nlk->flags |= NETLINK_F_KERNEL_SOCKET;
2058 //设置标识NETLINK_KERNEL_SOCKET表明这个netlink套接字是一个内核套接字
2059 netlink_table_grab();//将进程放到nl_table_wait等待链表上,并调度其它进程。
//接下来继续初始化nl_table表中对应传入 NETLINK_ROUTE协议类型的数组项,首先会判断是否已经先有同样协议类型的已经注册过了,如果有就不再初始化该表项了,直接释放刚才申请的listeners内存空间然后递增注册个数并返回。这里假定是首次注册NETLINK_ROUTE协议类型的套接字,这里依次初始化了nl_table表项中的groups、listeners、cb_mutex、module、bind、unbind、flags和compare字段。通过前文中cfg的实例分析,
2060 if (!nl_table[unit].registered) {
//没有进过初始化
2061 nl_table[unit].groups = groups;
2062 rcu_assign_pointer(nl_table[unit].listeners, listeners);
2063 nl_table[unit].cb_mutex = cb_mutex;
2064 nl_table[unit].module = module;
2065 if (cfg) {
2066 nl_table[unit].bind = cfg->bind;
2067 nl_table[unit].unbind = cfg->unbind;
2068 nl_table[unit].flags = cfg->flags;
2069 if (cfg->compare)
2070 nl_table[unit].compare = cfg->compare;
2071 }
2072 nl_table[unit].registered = 1;//已经初始化
2073 } else {
//已经注册过
2074 kfree(listeners);
2075 nl_table[unit].registered++;//增加注册个数
2076 }
2077 netlink_table_ungrab();
2078 return sk;在函数的最后返回成功创建的netlink套接字中的sock指针,它会在最先前的rtnetlink_net_init()函数中被保存到net->rtnl中去,注意只有NETLINK_ROUTE协议类型的套接字才会执行这个步骤,因为网络命名空间中专门为其预留了一个sock指针。
2079
2080 out_sock_release:
2081 kfree(listeners);
2082 netlink_kernel_release(sk);
2083 return NULL;
2084
2085 out_sock_release_nosk:
2086 sock_release(sock);
2087 return NULL;
2088 }
详解3.1.2.1:
调用 __netlink_create()函数向内核初始化netlink socket 申请 sock ,sock作为协议相关的通用数据这里将被申请初始化,其实在用户态创建netlink套接字也是间接调用到该函数。//上面有调用流程netlink_create-》__netlink_create
634 static int __netlink_create(struct net *net, struct socket *sock,
635 struct mutex *cb_mutex, int protocol,
636 int kern)//是否由内核创建 1代表内核
637 {
638 struct sock *sk;
639 struct netlink_sock *nlk;
640
641 sock->ops = &netlink_ops;//将sock的操作函数集指针设置为netlink_ops
/*
1491 SYSCALL_DEFINE3(bind, int, fd, struct sockaddr __user *, umyaddr, int, addrlen)
1492 {
1493 return __sys_bind(fd, umyaddr, addrlen);
1494 }
__sys_bind
err = sock->ops->bind(sock
*/
642
643 sk = sk_alloc(net, PF_NETLINK, GFP_KERNEL, &netlink_proto, kern);//分配sock结构并进行初始化
644 if (!sk)
645 return -ENOMEM;
646
647 sock_init_data(sock, sk);//初始化发送接收消息队列
/*
2501 #define _SK_MEM_PACKETS 256
2502 #define _SK_MEM_OVERHEAD SKB_TRUESIZE(256)
2503 #define SK_WMEM_MAX (_SK_MEM_OVERHEAD * _SK_MEM_PACKETS)
2504 #define SK_RMEM_MAX (_SK_MEM_OVERHEAD * _SK_MEM_PACKETS)
*/
648
649 nlk = nlk_sk(sk);//转换得到netlink_sock结构体地址
650 if (cb_mutex) {
651 nlk->cb_mutex = cb_mutex;
652 } else {
653 nlk->cb_mutex = &nlk->cb_def_mutex;
654 mutex_init(nlk->cb_mutex);//初始化互斥锁
655 lockdep_set_class_and_name(nlk->cb_mutex,
656 nlk_cb_mutex_keys + protocol,
657 nlk_cb_mutex_key_strings[protocol]);
658 }
659 init_waitqueue_head(&nlk->wait);//初始化等待队列
660
661 sk->sk_destruct = netlink_sock_destruct;//设置sk_destruct回调函数
662 sk->sk_protocol = protocol;//设置和协议类型 //后面根据设置的protocol将创建的sock插入到对应的nl_table
663 return 0;
664 }
netlink套接字结构:netlink_sock
22 struct netlink_sock {
23 /* struct sock has to be the first member of netlink_sock */
24 struct sock sk;
25 u32 portid;//表示本套接字自己绑定的id号,对于内核来说它就是0
26 u32 dst_portid;//表示目的id号
27 u32 dst_group;
28 u32 flags;//NETLINK_F_KERNEL_SOCKET表明由内核创建
29 u32 subscriptions;
30 u32 ngroups;//表示协议支持多播组数量
31 unsigned long *groups;//保存组首地址
32 unsigned long state;
33 size_t max_recvmsg_len;
34 wait_queue_head_t wait;
35 bool bound;//指示是否已经绑定通信地址 1代表绑定
36 bool cb_running;
37 int dump_done_errno;
38 struct netlink_callback cb;
39 struct mutex *cb_mutex;
40 struct mutex cb_def_mutex;
41 void (*netlink_rcv)(struct sk_buff *skb);//保存接收到用户态数据后的处理函数
42 int (*netlink_bind)(struct net *net, int group);//用于协议子协议自身特有的绑定
43 void (*netlink_unbind)(struct net *net, int group);//用于协议子协议自身特有的解绑
44 struct module *module;
45
46 struct rhash_head node;
47 struct rcu_head rcu;
48 struct work_struct work;
49 };
50
3.2 rtnl_register 注册rtnetlink的消息类型
250 /**
251 * rtnl_register - Register a rtnetlink message type
252 * @protocol: Protocol family or PF_UNSPEC 协议族
253 * @msgtype: rtnetlink message type
254 * @doit: Function pointer called for each request message
255 * @dumpit: Function pointer called for each dump request (NLM_F_DUMP) message
256 * @flags: rtnl_link_flags to modifiy behaviour of doit/dumpit functions
257 *
258 * Registers the specified function pointers (at least one of them has
259 * to be non-NULL) to be called whenever a request message for the
260 * specified protocol family and message type is received.
261 * 注册指定的函数指针(至少其中一个必须是非null),以便在接收到指定协议族和消息类型的请求消息时调用。
262 * The special protocol family PF_UNSPEC may be used to define fallback
263 * function pointers for the case when no entry for the specific protocol
264 * family exists.特殊的协议族PF_UNSPEC可以用来定义回调函数指针,当特定的协议族没有入口时。
265 */
266 void rtnl_register(int protocol, int msgtype,
267 rtnl_doit_func doit, rtnl_dumpit_func dumpit,
268 unsigned int flags)
269 {
270 int err;
271
272 err = rtnl_register_internal(NULL, protocol, msgtype, doit, dumpit,
273 flags);
274 if (err)
275 pr_err("Unable to register rtnetlink message handler, "
276 "protocol = %d, message type = %d\n", protocol, msgtype);
277 }
//第一个参数:
//第二个参数:协议族
//第三个参数:消息类型msgtype需要大于等于RTM_BASE(16),小于等于__RTM_MAX(103)
//第四个参数:request message的回调函数 指定设备信息
//第五个参数: dump request (NLM_F_DUMP) message的回调函数 dump名字空间里所有接口信息
//第六个参数:flags: rtnl_link_flags to modifiy behaviour of doit/dumpit functions
171 static int rtnl_register_internal(struct module *owner,
172 int protocol, int msgtype,
173 rtnl_doit_func doit, rtnl_dumpit_func dumpit,
174 unsigned int flags)
175 {
176 struct rtnl_link *link, *old;
177 struct rtnl_link __rcu **tab;
178 int msgindex;
179 int ret = -ENOBUFS;
180
181 BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX);//129种协议
182 msgindex = rtm_msgindex(msgtype);//msgindex = msgtype - RTM_BASE;
183
184 rtnl_lock();
185 tab = rtnl_msg_handlers[protocol];
186 if (tab == NULL) {
//每种协议最多103个消息类型
187 tab = kcalloc(RTM_NR_MSGTYPES, sizeof(void *), GFP_KERNEL);
188 if (!tab)
189 goto unlock;
190
191 /* ensures we see the 0 stores */
192 rcu_assign_pointer(rtnl_msg_handlers[protocol], tab);
193 }
194
195 old = rtnl_dereference(tab[msgindex]);
196 if (old) {
//代表已经注册
197 link = kmemdup(old, sizeof(*old), GFP_KERNEL);//是根据给定的一段地址空间(这里由void * src和size_t len决定),再分配一个内存空间(分配模式是gfp),并将原地址空间中的内容拷贝到新分配的内存空间中。返回值为新分配的内存空间的起始地址,如果分配不成功则返回NULL
198 if (!link)
199 goto unlock;
200 } else {
201 link = kzalloc(sizeof(*link), GFP_KERNEL);
202 if (!link)
203 goto unlock;
204 }
205
206 WARN_ON(link->owner && link->owner != owner);
207 link->owner = owner;
208
209 WARN_ON(doit && link->doit && link->doit != doit);//代表重复注册
210 if (doit)
211 link->doit = doit;//代表已经注册过了 指定设备的信息
212 WARN_ON(dumpit && link->dumpit && link->dumpit != dumpit);
213 if (dumpit)
214 link->dumpit = dumpit;//代表已经注册过了 指定名字空间里所有接口的信息
215
216 link->flags |= flags;
217
218 /* publish protocol:msgtype */
219 rcu_assign_pointer(tab[msgindex], link);
220 ret = 0;
221 if (old)
222 kfree_rcu(old, rcu);
223 unlock:
224 rtnl_unlock();
225 return ret;
226 }
3.3 doit函数 获取指定设备信息
为了加深理解以RTM_GETLINK rtnl_getlink为例分析,//暂时没时间分析,以后再说
3.3.1rtnl_getlink
3193 static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr *nlh,
3194 struct netlink_ext_ack *extack)
3195 {
3196 struct net *net = sock_net(skb->sk);
3197 struct net *tgt_net = net;
3198 struct ifinfomsg *ifm;
3199 char ifname[IFNAMSIZ];
3200 struct nlattr *tb[IFLA_MAX+1];
3201 struct net_device *dev = NULL;
3202 struct sk_buff *nskb;
3203 int netnsid = -1;
3204 int err;
3205 u32 ext_filter_mask = 0;
3206 //见详解3.3.1.1 nlmsg_parse 将属性流解析到tb缓冲区中,每种类型的指针可以通过tb[type]获得
3207 err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy, extack);
3208 if (err < 0)
3209 return err;
3210 //验证rtnetlink请求没有传递可能引用不同网络名称空间的附加属性
3211 err = rtnl_ensure_unique_netns(tb, extack, true);
3212 if (err < 0)
3213 return err;
3214
3215 if (tb[IFLA_IF_NETNSID]) {
3216 netnsid = nla_get_s32(tb[IFLA_IF_NETNSID]);
3217 tgt_net = get_target_net(NETLINK_CB(skb).sk, netnsid);
3218 if (IS_ERR(tgt_net))
3219 return PTR_ERR(tgt_net);
3220 }
3221
3222 if (tb[IFLA_IFNAME])//将此类型attr payload拷贝到ifname里
3223 nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ);
3224
3225 if (tb[IFLA_EXT_MASK])
3226 ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]);
3227
3228 err = -EINVAL;
3229 ifm = nlmsg_data(nlh);//得到ifm的首地址
3230 if (ifm->ifi_index > 0)//通过ifi_index指定了设备
3231 dev = __dev_get_by_index(tgt_net, ifm->ifi_index);//从适用的网络名字空间中通过index得到网络设备
3232 else if (tb[IFLA_IFNAME])//通过name获取设备
3233 dev = __dev_get_by_name(tgt_net, ifname);
3234 else
3235 goto out;
3236
3237 err = -ENODEV;
3238 if (dev == NULL)
3239 goto out;
3240
3241 err = -ENOBUFS;
3242 nskb = nlmsg_new(if_nlmsg_size(dev, ext_filter_mask), GFP_KERNEL);
//nlmsg_new分配一个新的netlink消息,第一个参数payload,为nlmsg消息中payload的大小,会调用alloc_skb申请nlmsghdr+payload
3243 if (nskb == NULL)
3244 goto out;
3245 //见详解3.3.1.2rtnl_fill_ifinfo 将属性信息填充到skb线性数据区,成功返回0
3246 err = rtnl_fill_ifinfo(nskb, dev, net,
3247 RTM_NEWLINK, NETLINK_CB(s