netlink的使用方法
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);
}
因为内核模块可能同时被多个进程同时调用,所以函数中使用了信号量和锁来进行互斥。
skb = skb_dequeue(&sk->receive_queue)用于取得socket sk 的接收队列上的消息,返回为一个struct sk_buff 的结构,skb->data 指向实际的netlink 消息。
程序中注册了一个Netfilter 钩子,钩子函数是get_icmp,它截获ICMP 数据包,然后调用send_to_user 函数将数据发送给应用空间进程。发送的数据是info 结构变量,它是struct packet_info结构,这个结构包含了来源/目的地址两个成员。Netfilter Hook 不是本文描述的重点,略过。
send_to_user 用于将数据发送给用户空间进程,发送调用的是API 函数netlink_unicast 完成的: int netlink_unicast(struct sock *sk, struct sk_buff *skb, u32 pid, int nonblock);
参数sk 为函数netlink_kernel_create()返回的套接字,参数skb 存放待发送的消息,它的data字段指向要发送的netlink 消息结构,而skb 的控制块保存了消息的地址信息, 参数pid 为接收消息进程的pid,参数nonblock 表示该函数是否为非阻塞,如果为1,该函数将在没有接收缓存可利用时立即返回,而如果为0,该函数在没有接收缓存可利用时睡眠。
向用户空间进程发送的消息包含三个部份:netlink 消息头部、数据部份和控制字段,控制字段包含了内核发送netlink 消息时,需要设置的目标地址与源地址,内核中消息是通过sk_buff 来管理的, linux/netlink.h 中定义了NETLINK_CB 宏来方便消息的地址设置:
#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;
字段pid 表示消息发送者进程ID,也即源地址,对于内核,它为 0, dst_pid 表示消息接收者进程 ID,也即目标地址,如果目标为组或内核,它设置为 0,否则 dst_group 表示目标组地址,如果它目标为某一进程或内核,dst_group 应当设置为 0。
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)); /*计算消息总长:消息首部加上数据加度*/