原文链接:http://www.chinaunix.net/jh/4/822500.html
这是一篇学习笔记,主要是对《Linux 系统内核空间与用户空间通信的实现与分析》中的源码imp2的分析。其中的源码,可以到以下URL下载:
http://www-128.ibm.com/developerworks/cn/linux/l-netlink/imp2.tar.gzsocket(AF_NETLINK, SOCK_RAW, netlink_type)
#define NETLINK_ROUTE 0 /* Routing/device hook */ #define NETLINK_W1 1 /* 1-wire subsystem */ #define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */ #define NETLINK_FIREWALL 3 /* Firewalling hook */ #define NETLINK_INET_DIAG 4 /* INET 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 */#define NETLINK_GENERIC 16
static int skfd; skfd = socket(PF_NETLINK, SOCK_RAW, NL_IMP2); if(skfd < 0) { printf("can not create a netlink socket\n"); exit(0); }
struct sockaddr_nl { sa_family_t nl_family; unsigned short nl_pad; __u32 nl_pid; __u32 nl_groups; };
struct sockaddr_nl local; memset(&local, 0, sizeof(local)); local.nl_family = AF_NETLINK; local.nl_pid = getpid(); /*设置pid为自己的pid值*/ local.nl_groups = 0; /*绑定套接字*/ if(bind(skfd, (struct sockaddr*)&local, sizeof(local)) != 0) { printf("bind() error\n"); return -1; }
struct sockaddr_nl kpeer; memset(&kpeer, 0, sizeof(kpeer)); kpeer.nl_family = AF_NETLINK; kpeer.nl_pid = 0; kpeer.nl_groups = 0;
struct nlmsghdr { __u32 nlmsg_len; /* Length of message */ __u16 nlmsg_type; /* Message type*/ __u16 nlmsg_flags; /* Additional flags */ __u32 nlmsg_seq; /* Sequence number */ __u32 nlmsg_pid; /* Sending process PID */ };
/*计算包含报头的数据报长度*/ #define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(sizeof(struct nlmsghdr))) /*字节对齐*/ #define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
struct msg_to_kernel /*自定义消息首部,它仅包含了netlink的消息首部*/ { struct nlmsghdr hdr; }; struct msg_to_kernel message; memset(&message, 0, sizeof(message)); message.hdr.nlmsg_len = NLMSG_LENGTH(0); /*计算消息,因为这里只是发送一个请求消息,没有多余的数据,所以,数据长度为0*/ message.hdr.nlmsg_flags = 0; message.hdr.nlmsg_type = IMP2_U_PID; /*设置自定义消息类型*/ message.hdr.nlmsg_pid = local.nl_pid; /*设置发送者的PID*/ 这样,有了本地地址、对端地址和发送的数据,就可以调用发送函数将消息发送给内核了: /*发送一个请求*/ sendto(skfd, &message, message.hdr.nlmsg_len, 0, (struct sockaddr*)&kpeer, sizeof(kpeer));
/*接收的数据包含了netlink消息首部和自定义数据结构*/ struct u_packet_info { struct nlmsghdr hdr; struct packet_info icmp_info; }; struct u_packet_info info; while(1) { kpeerlen = sizeof(struct sockaddr_nl); /*接收内核空间返回的数据*/ rcvlen = recvfrom(skfd, &info, sizeof(struct u_packet_info), 0, (struct sockaddr*)&kpeer, &kpeerlen); /*处理接收到的数据*/ …… }
/*这个信号函数,处理一些程序退出时的动作*/ static void sig_int(int signo) { struct sockaddr_nl kpeer; struct msg_to_kernel message; memset(&kpeer, 0, sizeof(kpeer)); kpeer.nl_family = AF_NETLINK; kpeer.nl_pid = 0; kpeer.nl_groups = 0; memset(&message, 0, sizeof(message)); message.hdr.nlmsg_len = NLMSG_LENGTH(0); message.hdr.nlmsg_flags = 0; message.hdr.nlmsg_type = IMP2_CLOSE; message.hdr.nlmsg_pid = getpid(); /*向内核发送一个消息,由nlmsg_type表明,应用程序将关闭*/ sendto(skfd, &message, message.hdr.nlmsg_len, 0, (struct sockaddr *)(&kpeer), sizeof(kpeer)); close(skfd); exit(0); }
struct sock * netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len));
static int __init init(void) { rwlock_init(&user_proc.lock); /*初始化读写锁*/ /*创建一个netlink socket,协议类型是自定义的ML_IMP2,kernel_reveive为接受处理函数*/ nlfd = netlink_kernel_create(NL_IMP2, kernel_receive); if(!nlfd) /*创建失败*/ { printk("can not create a netlink socket\n"); return -1; } /*注册一个Netfilter 钩子*/ return nf_register_hook(&imp2_ops); } module_init(init);
DECLARE_MUTEX(receive_sem); /*初始化信号量*/ static void kernel_receive(struct sock *sk, int len) { do { struct sk_buff *skb; if(down_trylock(&receive_sem)) /*获取信号量*/ return; /*从接收队列中取得skb,然后进行一些基本的长度的合法性校验*/ while((skb = skb_dequeue(&sk->receive_queue)) != NULL) { { struct nlmsghdr *nlh = NULL; if(skb->len >= sizeof(struct nlmsghdr)) { /*获取数据中的nlmsghdr 结构的报头*/ nlh = (struct nlmsghdr *)skb->data; if((nlh->nlmsg_len >= sizeof(struct nlmsghdr)) && (skb->len >= nlh->nlmsg_len)) { /*长度的全法性校验完成后,处理应用程序自定义消息类型,主要是对用户PID的保存,即为内核保存“把消息发送给谁”*/ if(nlh->nlmsg_type == IMP2_U_PID) /*请求*/ { write_lock_bh(&user_proc.pid); user_proc.pid = nlh->nlmsg_pid; write_unlock_bh(&user_proc.pid); } else if(nlh->nlmsg_type == IMP2_CLOSE) /*应用程序关闭*/ { write_lock_bh(&user_proc.pid); if(nlh->nlmsg_pid == user_proc.pid) user_proc.pid = 0; write_unlock_bh(&user_proc.pid); } } } } kfree_skb(skb); } up(&receive_sem); /*返回信号量*/ }while(nlfd && nlfd->receive_queue.qlen); }
int netlink_unicast(struct sock *sk, struct sk_buff *skb, u32 pid, int nonblock);
#define NETLINK_CB(skb) (*(struct netlink_skb_parms*)&((skb)->cb))
NETLINK_CB(skb).pid = 0; NETLINK_CB(skb).dst_pid = 0; NETLINK_CB(skb).dst_group = 1;
static int send_to_user(struct packet_info *info) { int ret; int size; unsigned char *old_tail; struct sk_buff *skb; struct nlmsghdr *nlh; struct packet_info *packet; /*计算消息总长:消息首部加上数据加度*/ size = NLMSG_SPACE(sizeof(*info)); /*分配一个新的套接字缓存*/ skb = alloc_skb(size, GFP_ATOMIC); old_tail = skb->tail; /*初始化一个netlink消息首部*/ nlh = NLMSG_PUT(skb, 0, 0, IMP2_K_MSG, size-sizeof(*nlh)); /*跳过消息首部,指向数据区*/ packet = NLMSG_DATA(nlh); /*初始化数据区*/ memset(packet, 0, sizeof(struct packet_info)); /*填充待发送的数据*/ packet->src = info->src; packet->dest = info->dest; /*计算skb两次长度之差,即netlink的长度总和*/ nlh->nlmsg_len = skb->tail - old_tail; /*设置控制字段*/ NETLINK_CB(skb).dst_groups = 0; /*发送数据*/ read_lock_bh(&user_proc.lock); ret = netlink_unicast(nlfd, skb, user_proc.pid, MSG_DONTWAIT); read_unlock_bh(&user_proc.lock); }
#define NLMSG_PUT(skb, pid, seq, type, len) \ ({ if (skb_tailroom(skb) < (int)NLMSG_SPACE(len)) goto nlmsg_failure; \ __nlmsg_put(skb, pid, seq, type, len); }) static __inline__ struct nlmsghdr * __nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, int type, int len) { struct nlmsghdr *nlh; int size = NLMSG_LENGTH(len); nlh = (struct nlmsghdr*)skb_put(skb, NLMSG_ALIGN(size)); nlh->nlmsg_type = type; nlh->nlmsg_len = size; nlh->nlmsg_flags = 0; nlh->nlmsg_pid = pid; nlh->nlmsg_seq = seq; return nlh; }
void sock_release(struct socket * sock);
static void __exit fini(void) { if(nlfd) { sock_release(nlfd->socket); /*释放netlink socket*/ } nf_unregister_hook(&imp2_ops); /*撤锁netfilter 钩子*/ }