网上参考了许多文章,有些忘记网址,在此谢谢。
经过两周的摸索,发现网上资料非常多,现在总结如下:
此文章分享了netlink和netfilter的框架,涉及到ip、tcp、udp等协议,struct sk_buf、netlink和netfilter hook等内容,具体如下:
ip协议头部介绍:http://apps.hi.baidu.com/share/detail/30118020
tcp协议头部介绍:http://apps.hi.baidu.com/share/detail/50639873
sk_buf介绍:http://www.cnblogs.com/iceocean/articles/1594160.html
netlink经典入门文章: 1、 Linux 用户态与内核态的交互——netlink 篇 http://bbs.chinaunix.net/viewthread.php?tid=822500
2、netlink socket 编程之 why & how http://linux.chinaunix.net/bbs/viewthread.php?tid=1031932&extra=page%3D1%26amp%3Bfilter%3Ddigest
netlink由于版本问题注意事项:http://hi.baidu.com/fiction_junru/blog/item/bd7ce701a3f9f701738b656d.html
netfilter hook介绍:http://wenku.baidu.com/view/7823cc3887c24028915fc3ad.html
http://blog.csdn.net/jiatingqiang/article/details/6282003
如果遇到netlink在驱动层创建失败,多是因为linux内核版本的问题,可以参考上面的连接。同时,还有netfilter获取ip头部和tcp头部错误,也是因为版本的问题。
下面程序是我修改适合我的版本,linux 2.6.21,应用层是修改了网上的一个程序。
在此说明下,网上有很多程序是驱动层直接发送给应用层,但是发送失败。后来,经过多次改代码和测试发现,在linux 2.6.21版本中,netlink驱动层和应用层的交互需要应用层发一个信息给驱动层初始化发送应用层pid,然后连接才成功。
驱动层:net_link.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/netfilter_bridge.h>
#include <linux/netfilter_ipv4.h>
#include <linux/if_packet.h>
#include <linux/netdevice.h>
#include <linux/netlink.h>
#include <net/sock.h>
#include <linux/if_ether.h>
#include <net/tcp.h>
#include <linux/netfilter_arp.h>
#include <linux/if_arp.h>
#include <net/arp.h>
#include <linux/string.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/time.h>
#define NETLINK_TEST 19
u32 pid;
struct sock *nl_sk = NULL;
MODULE_LICENSE("GPL");
void nl_data_ready(struct sock *sk, int len)
{
struct sk_buff *skb = NULL;
struct nlmsghdr *nlh = NULL;
int err;
wake_up_interruptible(sk->sk_sleep);
skb = skb_recv_datagram(nl_sk, 0, 0, &err);
nlh = (struct nlmsghdr *)skb->data;
pid = nlh->nlmsg_pid; /*记录应用层程序pid*/
kfree_skb(skb);
}
static int send_to_user(const char *str)
{
int size;
unsigned char *old_tail;
struct sk_buff *skb;
struct nlmsghdr *nlh;
size = NLMSG_SPACE(strlen(str));
skb = alloc_skb(size, GFP_ATOMIC);
old_tail = skb->tail;
nlh = NLMSG_PUT(skb, 0, 0, 0, size-sizeof(*nlh));
memcpy(NLMSG_DATA(nlh), str, strlen(str));
// memcpy(buff, NLMSG_DATA(nlh), DataLength);
nlh->nlmsg_len = skb->tail - old_tail;
/*向应用层发送数据*/
NETLINK_CB(skb).pid = 0; /* from kernel */
NETLINK_CB(skb).dst_group = 0; /* unicast */
return netlink_unicast(nl_sk, skb, pid, MSG_DONTWAIT);
nlmsg_failure:
if(skb)
kfree_skb(skb);
return -1;
}
/************************************************************************/
unsigned int hook_func(unsigned int hooknum,
struct sk_buff **skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
printk("entry success.\n");
send_to_user("hello kernel.\n");
return NF_ACCEPT;
}
static struct nf_hook_ops nfho[] = {
{
.hook = hook_func,
.owner = THIS_MODULE,
.pf = PF_BRIDGE,
.hooknum = NF_BR_PRE_ROUTING,
.priority = NF_BR_PRI_FIRST,
},
};
int init_module()
{
printk("insmod hook test!\n");
nl_sk = netlink_kernel_create(NETLINK_TEST, 0, nl_data_ready, THIS_MODULE);
if(!nl_sk)
{
printk("can not create nl_sk socket\n");
return -1;
}
nf_register_hooks(nfho, ARRAY_SIZE(nfho));
return 0;
}
void cleanup_module()
{
printk("rmmod hook test!\n");
if (nl_sk != NULL)
{
sock_release(nl_sk->sk_socket);
}
nf_unregister_hooks(nfho, ARRAY_SIZE(nfho));
}
应用层:user.c
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/socket.h>
#include <errno.h>
#define MAX_PAYLOAD 1024
struct sockaddr_nl src_addr, dest_addr;
struct msghdr msg;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
int sock_fd;
#define NETLINK_TEST 19
int main()
{
sock_fd = socket(PF_NETLINK, SOCK_RAW,NETLINK_TEST);
memset(&src_addr, 0, sizeof(src_addr));
src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = getpid();
src_addr.nl_groups = 0;
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;
dest_addr.nl_groups = 0;
nlh=(struct nlmsghdr *)malloc(
NLMSG_SPACE(MAX_PAYLOAD));
nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
nlh->nlmsg_pid = getpid();
nlh->nlmsg_flags = 0;
strcpy(NLMSG_DATA(nlh), "Hello you!");
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);
while(1)
{
memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
recvmsg(sock_fd, &msg, 0);
printf(" Received message payload: %s ",
(char *)NLMSG_DATA(nlh));
}
close(sock_fd);
return 0;
}
Makfile:
obj-m += net_link.o
KDIR := /lib/modules/$(shell uname -r)/build
all:
make -C $(KDIR) M=$(shell pwd) modules
gcc -Wall -o user user.c
clean:
make -C $(KDIR) M=$(shell pwd) clean
rm -f *odule* user
rm -rf *bak
执行./user可以看到:
$ Received message payload: hello kernel.
查看驱动层命令:dmesg 可以看到printk的打印信息。