前言
netlink的通信方式可以类比TCP/IP协议族,类比如下:
- netlink——TCP/IP协议族
- NETLINK_ROUTE——UDP协议
- 内核态socket(端口为0)——UDP服务器
- 用户态socket(端口一般为进程的pid)——UDP客户端
netlink协议初始化
netlink_proto_init,分配MAX_LINKS个nl_table并初始化,在3.14内核中,netlink支持32个协议,通过 cat /proc/net/netlink 可以查看已经创建的netlink协议及端口号(包含用户态和内核态)
内核态创建AF_NETLINK类型的socket
内核态调用netlink_kernel_create创建netlink的socket
- sock_create_lite:创建"struct socket"
- __netlink_create:将netlink_ops存入"struct socket"中,创建"struct sock",并设置sk的协议号
- netlink_insert:将socket插入nl_table的对应协议号中,因为portid写死为0,所以每个协议只能在内核态创建一次。
用户态创建AF_NETLINK类型的socket
调用顺序为socket——>sock_create——>netlink_create
- __sock_create:创建"struct socket"
- __netlink_create:将netlink_ops存入"struct socket"中,创建"struct sock",并设置sk的协议号
到这里,用户态已经可以给内核态发送消息了,但是需要绑定之后才能收来自内核的消息。。。
用户态绑定端口号
调用顺序为bind——>netlink_bind
- netlink_insert:如果指定了端口号,将socket插入nl_table的对应协议号中,否则调用netlink_autobind自动生成端口号
用户态向内核态发送消息(同步调用)
调用顺序为sendto——>sock_sendmsg——>__sock_sendmsg——>__sock_sendmsg_nosec——>netlink_sendmsg——>netlink_unicast——>netlink_unicast_kernel——>nlk->netlink_rcv
- sendto:nl_family为AF_NETLINK,nl_pid为0(因为内核创建的端口是0),nl_groups为0(表示单播)
- netlink_getsockbyportid:根据源sock的协议号和目的端口来找到内核态的sock,所以用户态创建socket时需要与内核态使用相同的协议号。
内核态向用户态发送消息(异步调用)
调用顺序为netlink_unicast——>netlink_sendskb——>__netlink_sendskb_——>skb_queue_tail
- netlink_getsockbyportid:根据源sock的协议号和目的端口来找到用户态的sock
- skb_queue_tail:将skb加入用户态sock的收包队列中。