1. Introduction
Netlink 提供 userspace & kernel 之間的溝通, 它定義在 RFC 3549 "Linux Netlink as an IP Services Protocol", 相較於其它溝通方式, 它有這些優點:- 不用 polling
- 可以從 kernel 當作訊息發起端
- 提供 multicast
- Netlink core files
- net/netlink/af_netlink.c
- include/linux/netlink.h
- net/netlink/genetlink.c
- Route netlink files
- net/core/rtnetlink.c
- include/linux/rtnetlink.h
- Generic netlink files
- net/netlink/genetlink.c
- include/linux/genetlink.h
2. Create netlink socket
userspace & kernel 在create socket 時, 最後都會 call __netlink_create(), 唯一的差別是, kernel會加上屬性 NETLINK_KERNEL_SOCKET2.1 create netlink socket in userspace
底下是sample code
int sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); struct sockaddr_nl addr; bzero (&addr, sizeof(addr)); addr.nl_family = AF_NETLINK; addr.nl_groups = RTMGRP_IPV4_ROUTE; bind(sockfd,(struct sockaddr *)&addr,sizeof(addr);
其中 sockaddr_nl 的定義是
struct sockaddr_nl { __kernel_sa_family_t nl_family; /* AF_NETLINK */ unsigned short nl_pad; /* zero */ __u32 nl_pid; /* port ID */ __u32 nl_groups; /* multicast groups mask */ };( include/linux/netlink.h)
其中
nl_family 填 AF_NETLINK
nl_pad 通常得填 0
nl_pid 代表 netlink 的 unicast address, 如果是 kernel space 就填 0, 如果是 userspace, 在bind的時候就會被填上 user process 的 pid, 但如果 user process 跳過 bind, 那麼就得自行管理這個欄位的值
nl_groups 填這個 socket 所屬的 multicast group
2.2 create netlink socket in kernel
kernel 的 netlink 是在 init 的時候就會呼叫, 不同的group它的init的點也不一樣, 底下是ROUTE的init的點
static int __net_init rtnetlink_net_init(struct net *net) { struct sock *sk; sk = netlink_kernel_create(net, NETLINK_ROUTE, RTNLGRP_MAX, rtnetlink_rcv, &rtnl_mutex, THIS_MODULE); if (!sk) return -ENOMEM; net->rtnl = sk; return 0; }( net/core/rtnetlink.c )
其中 rtnetlink_rcv 是callback function, 負責處理從userspace來的data
NETLINK_ROUTE 是 netlink protocol, 定義在 include/linux/netlink.h
RTNLGRP_MAX 是 route multicast group, 定義在 include/linux/rtnetlink.h
netlink_kernel_create 實作在這裡
netlink_kernel_create(struct net *net, int unit, unsigned int groups, void (*input)(struct sk_buff *skb), struct mutex *cb_mutex, struct module *module)( net/netlink/af_netlink.c )
netlink protocol 除了 route 之外還有很多, 比如說 kobject 也有自己 init 的點
static int uevent_net_init(struct net *net) { struct uevent_sock *ue_sk; ue_sk = kzalloc(sizeof(*ue_sk), GFP_KERNEL); if (!ue_sk) return -ENOMEM; ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT, 1, NULL, NULL, THIS_MODULE); if (!ue_sk->sk) { printk(KERN_ERR "kobject_uevent: unable to create netlink socket!\n"); kfree(ue_sk); return -ENODEV; } mutex_lock(&uevent_sock_mutex); list_add_tail(&ue_sk->list, &uevent_sock_list); mutex_unlock(&uevent_sock_mutex); return 0; }( lib/kobject_uevent.c )
可以看到它的 netlink protocol 是 NETLINK_KOBJECT_UEVENT, 因為 kobject 沒有 multicast group, 所以 group 的欄位填 1, callback填null因為它不需要處理從 userspace 來的 data, mutex 填null 表示使用 default 的 mutex
2.3 maintain netlink socket
許多不同 netlink protocol 的 kernel socket 用 netlink_table 來maintain, 它是個link list
struct netlink_table { struct nl_pid_hash hash; struct hlist_head mc_list; struct listeners __rcu *listeners; unsigned int nl_nonroot; unsigned int groups; struct mutex *cb_mutex; struct module *module; int registered; };
要操作這張 table, 有底下幾個 operation
- nl_table_lock()
- netlink_lookup()
- netlink_insert()
2.4 register maintain netlink socket
在開完 socket 之後, 就要註冊在不同的message要什麼callback
rtnetlink_init() { // ...... rtnl_register(PF_UNSPEC, RTM_GETLINK, rtnl_getlink, rtnl_dump_ifinfo, rtnl_calcit); rtnl_register(PF_UNSPEC, RTM_SETLINK, rtnl_setlink, NULL, NULL); // ......
rtnl_register 定義如下
rtnl_register(int protocol, int msgtype, // protocol = protocol family, ref include/linux/socket.h rtnl_doit_func doit, // for addition/deletion/modification rtnl_dumpit_func dumpit, // for retrieving information rtnl_calcit_func calcit) // for calculation of buffer size
從上面例子可以看到 RTM_GETLINK 有填對應的 callback, RTM_SETLINK 有些則填 null
而實際上, rtnl_register 用 array 存對應的callback
static struct rtnl_link *rtnl_msg_handlers[RTNL_FAMILY_MAX + 1]; struct rtnl_link { rtnl_doit_func doit; rtnl_dumpit_func dumpit; rtnl_calcit_func calcit; };
3. sending rtnetlink message
舉個例子, 當netwrok dev oepn之後, 要通知userspace3.1 netlink message format
netlink message 前面有 nlmsghdr 可供判斷這段message用的 netlink protocol 是什麼, 接著它的payload 則會依照不同的 netlink protocl 而有不同的format, 但通常都使用TLV的格式通常一段 netlink message, 可以包含多份 message, 每份 message 前面都有 nlmsghdr, 裡面有一些資訊像是 len 表示這段 message 的長度, type 表示netlink protocol, 也可以利用nlmsghdr和macro幫忙指到對的位置
裡面的 payload, 如果是 route netlink protocol, 它的 format 長這樣
可以看到它的 payload 前面有個 rtmsg 的標頭, 進一步提供 route 相關的 message, 其後接著 TLV 格式的資訊
底下有個例子, 假如收到一包 route 相關 message, 以 buffer 表示其中每段資料的內容
4. Generic Netlink protocol
因為 netlink protocol 只用1 byte來儲存, 所以只有32種 netlink protocol family 可以用, 如果加新的 netlink protocol family, 很快就不夠用, 再加上每次加上新的, header檔就得重新定義, 所以提出 generic netlink protocol它的作法就是定義 generic netlink protocol 在 netlink protocol family, 再利用mux來區分
5. libnl.so
在實作上, 拆開每包 netlink message 還是很麻煩, 可以使用 libnl.so它提供了
- Connecting/disconnectng of sockets
- Sending/receiving of data
- Construction/parsing of messages
- provides a customizeable receiving state machine
- provides a abstract data type framework
- generic netlink family (libnl-genl)
- routing family (libnl-route)
- netfilter family (libnl-nf)
底下是它的實作範例
太赞了!网上找了好多资料,还是老兄这篇讲得明白;特别是那几张format的图,解释得很清晰!
回覆刪除