有了ioctl,和proc,我們是不是還有其它可以與kernel交換資料的界面呢?沒錯,就是netlink,那什麼時候會用到netlink呢?當你設routing table或防火牆時就會用到了,kernel就是用netlink界面與route指令溝通,scsi驅動也可以看到netlink的影子,netlink有什麼好處呢?
優點:
1.它沒有ioctl等ret回來的kernel round trip time
2.可發多個訊息給不同的user process(如果user processes都在同一group)
缺點:
1.因為只有UDP封包,所以你送資料後也不曉得該資料到底是不是去到對的地方
2.用了一堆socket API,程式碼寫起來並不簡潔
簡單描述我現在做的測試,以下面的kernel udp echo server為例,它會接收使用者的訊息並echo回來,所以整個程式分為兩部份,udp echo server在kernel space,使用者程式在user space
udp echo server重點列如下
1. netlink_kernel_create: create kernel socket並且註冊你的callback函式處理netlink封包,請特別注意第二個參數,我用了NETLINK_USERSOCK這個linux保留給使用者測試的netlink type.
2. sock_release:移除module時,一定要release kernel socket
3. udp_receive:這個函式就是我們處理netlink傳入封包的地方,在這裡你可以取得user process id和data
4. udp_reply:做一個新的封包並呼叫函式netlink_unicast echo回去
[kudp echo server]
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <net/sock.h>
#include <net/netlink.h>
#include <linux/skbuff.h>
static struct sock *netlink_sock;
static void udp_reply(int pid,int seq,void *payload)
{
struct sk_buff *skb;
struct nlmsghdr *nlh;
int size=strlen(payload)+1;
int len = NLMSG_SPACE(size);
void *data;
int ret;
skb = alloc_skb(len, GFP_ATOMIC);
if (!skb)
return;
nlh= NLMSG_PUT(skb, pid, seq, 0, size);
nlh->nlmsg_flags = 0;
data=NLMSG_DATA(nlh);
memcpy(data, payload, size);
NETLINK_CB(skb).pid = 0; /* from kernel */
NETLINK_CB(skb).dst_group = 0; /* unicast */
ret=netlink_unicast(netlink_sock, skb, pid, MSG_DONTWAIT);
if (ret <0)
{
printk("send failed\n");
return;
}
return;
nlmsg_failure: /* Used by NLMSG_PUT */
if (skb)
kfree_skb(skb);
}
/* Receive messages from netlink socket. */
static void udp_receive(struct sk_buff *skb)
{
u_int uid, pid, seq, sid;
void *data;
struct nlmsghdr *nlh;
nlh = (struct nlmsghdr *)skb->data;
pid = NETLINK_CREDS(skb)->pid;
uid = NETLINK_CREDS(skb)->uid;
sid = NETLINK_CB(skb).sid;
seq = nlh->nlmsg_seq;
data = NLMSG_DATA(nlh);
printk("recv skb from user space uid:%d pid:%d seq:%d,sid:%d\n",uid,pid,seq,sid);
printk("data is :%s\n",(char *)data);
udp_reply(pid,seq,data);
return ;
}
static int __init kudp_init(void)
{
netlink_sock = netlink_kernel_create(&init_net, NETLINK_USERSOCK, 0,udp_receive, NULL, THIS_MODULE);
return 0;
}
static void __exit kudp_exit(void)
{
sock_release(netlink_sock->sk_socket);
printk("netlink driver remove successfully\n");
}
module_init(kudp_init);
module_exit(kudp_exit);
MODULE_DESCRIPTION("kudp echo server module");
MODULE_LICENSE("GPL");
MODULE_ALIAS("QT2410:kudp echo server module");
user space client重點列如下
1.宣告socket fd請用sock_fd = socket(PF_NETLINK, SOCK_RAW,NETLINK_USERSOCK),第三個傳入參數為netlink type
2.注意pid與group的使用方式,如果只是unicast,要把group設為0
3.小心struct msghdr msg,要先把裡面的iov,nlmsghdr等欄位宣告並初始化
4.簡單的用sendmsg和recvmsg就大功告成啦
[user space client]
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#define MAX_PAYLOAD 1024
int main(int argc,char *argv[])
{
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
int sock_fd;
struct msghdr msg;
sock_fd = socket(PF_NETLINK, SOCK_RAW,NETLINK_USERSOCK);
memset(&src_addr, 0, sizeof(src_addr));
src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = getpid(); /* self pid */
src_addr.nl_groups = 0; /* not in mcast groups */
bind(sock_fd, (struct sockaddr*)&src_addr,sizeof(src_addr));
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.nl_family = AF_NETLINK;
dest_addr.nl_pid = 0; /* For Linux Kernel */
dest_addr.nl_groups = 0; /* unicast */
nlh=(struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
/* Fill the netlink message header */
nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
nlh->nlmsg_pid = getpid(); /* self pid */
nlh->nlmsg_flags = 0;
/* Fill in the netlink message payload */
strcpy(NLMSG_DATA(nlh), "Hello World!");
iov.iov_base = (void *)nlh;
iov.iov_len = nlh->nlmsg_len;
msg.msg_name = (void *)&dest_addr;
msg.msg_namelen = sizeof(dest_addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
sendmsg(sock_fd, &msg, 0);
printf("Waiting for message from kernel\n");
/* Read message from kernel */
recvmsg(sock_fd, &msg, 0);
printf("Received message payload: %s\n",NLMSG_DATA(msg.msg_iov->iov_base));
close(sock_fd);
return 0;
}
全部的程式請在這邊下載,如果要嘗試multicast server,那麼請參考kdupb.c這支程式,和它的user space client mc1.c和mc2.c,只要group設定正確mc1和mc2都可以收到kudpb的multicast訊息,kudpb利用netlink_broadcast廣播給mc1和mc2,執行畫面如下

留言