个人环境:Linux 3.2.0-60-generic-pae #91-Ubuntu SMP Wed Feb 19 04:14:56 UTC 2014 i686 i686 i386 GNU/Linux
同时参考了网上一部分相关的资料, 简单的写了个 获得icmp src 和 icmp dst的demo。 Linux 内核版本不同的话,可能稍微改一下地方。 仅供参考。.
晚上有时间,再加注释。。。
其中 NETLINK_IMP 是要添加在Netlink.h文件中的。
kernel_icmp.h
#ifndef __KERNEL_ICMP_H__
#define __KERNEL_ICMP_H__
#define IMP_U_PID 0
#define IMP_K_MSG 1
#define IMP_CLOSE 2
struct packet_info {
unsigned int src;
unsigned int dest;
};
#endif
kernel_icmp.c
#ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef MODULE
#define MODULE
#endif
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/netfilter_ipv4.h>
#include <linux/inet.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/netlink.h>
#include <linux/spinlock.h>
#include <linux/semaphore.h>
#include <net/sock.h>
#include "kernel_icmp.h"
DEFINE_MUTEX(receive_mutex);
static struct sock *nlfd;
struct{
__u32 pid;
rwlock_t lock;
}user_proc;
static int imp_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
if((nlh->nlmsg_len >= sizeof(struct nlmsghdr)) && (skb->len >= nlh->nlmsg_len)) {
if(nlh->nlmsg_type == IMP_U_PID) {
write_lock_bh(&user_proc.lock);
user_proc.pid = nlh->nlmsg_pid;
write_unlock_bh(&user_proc.lock);
}
else if(nlh->nlmsg_type == IMP_CLOSE) {
write_lock_bh(&user_proc.lock);
if(nlh->nlmsg_pid == user_proc.pid) {
user_proc.pid = 0;
}
write_unlock_bh(&user_proc.lock);
}
}
return 0;
}
static void netlink_rcv_cb(struct sk_buff *skb)
{
struct nlmsghdr *nlh;
/*
* len MUST be signed for nlmsg_next to be able to dec it below 0
* if the nlmsg_len was not aligned
*/
int len;
int err;
nlh = nlmsg_hdr(skb);
len = skb->len;
while (nlmsg_ok(nlh, len)) {
err = imp_receive_msg(skb, nlh);
/* if err or if this message says it wants a response */
if (err || (nlh->nlmsg_flags & NLM_F_ACK))
netlink_ack(skb, nlh, err);
nlh = nlmsg_next(nlh, &len);
}
}
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;
nlh = NLMSG_PUT(skb, 0, 0, IMP_K_MSG, size-sizeof(*nlh));
packet = NLMSG_DATA(nlh);
memset(packet, 0, sizeof(struct packet_info));
packet->src = info->src;
packet->dest = info->dest;
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);
return ret;
nlmsg_failure:
if(skb) {
printk(KERN_INFO "nlmsg_failure \n");
kfree(skb);
}
return -1;
}
static void netlink_rcv(struct sk_buff *skb)
{
mutex_lock(&receive_mutex);
netlink_rcv_cb(skb);
mutex_unlock(&receive_mutex);
}
static unsigned int get_icmp(unsigned int hooknum, struct sk_buff *skb, \
const struct net_device *in,\
const struct net_device *out,\
int (*okfn)(struct sk_buff *))
{
struct iphdr *iph = ip_hdr(skb);
struct packet_info info;
if(iph->protocol == IPPROTO_ICMP) {
read_lock_bh(&user_proc.lock);
if(user_proc.pid != 0) {
read_unlock_bh(&user_proc.lock);
info.src = iph->saddr;
info.dest = iph->daddr;
send_to_user(&info);
}
else {
read_unlock_bh(&user_proc.lock);
}
}
return NF_ACCEPT;
}
static struct nf_hook_ops imp_ops ={
.hook = get_icmp,
.pf = PF_INET,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_FILTER -1,
};
static int __init init(void)
{
rwlock_init(&user_proc.lock);
nlfd = netlink_kernel_create(&init_net, NETLINK_IMP, 0, netlink_rcv, &receive_mutex, THIS_MODULE);
if(!nlfd) {
printk("can not create a netlink socket\n");
return -1;
}
return nf_register_hook(&imp_ops);
}
static void __exit fini(void)
{
if(nlfd) {
sock_release(nlfd->sk_socket);
}
nf_unregister_hook(&imp_ops);
}
module_init(init);
module_exit(fini);
user_icmp.c
#include <unistd.h>
#include <stdio.h>
#include <linux/types.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/netlink.h>
#include <signal.h>
#include <stdlib.h>
#include "kernel_icmp.h"
struct msg_to_kernel{
struct nlmsghdr hdr;
};
struct u_packet_info {
struct nlmsghdr hdr;
struct packet_info icmp_info;
};
static int skfd;
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 = IMP_CLOSE;
message.hdr.nlmsg_pid = getpid();
sendto(skfd, &message, message.hdr.nlmsg_len, 0, \
(struct sockaddr *)(&kpeer), sizeof(kpeer));
close(skfd);
exit(0);
}
int main(void)
{
struct sockaddr_nl local;
struct sockaddr_nl kpeer;
int kpeerlen;
struct msg_to_kernel message;
struct u_packet_info info;
int sendlen = 0;
int rcvlen = 0;
struct in_addr addr;
skfd = socket(PF_NETLINK, SOCK_RAW, NETLINK_IMP);
if(skfd < 0) {
printf("can not create a netlink socket\n");
exit(0);
}
memset(&local, 0, sizeof(local));
local.nl_family = AF_NETLINK;
local.nl_pid = getpid();
local.nl_groups = 0;
if(bind(skfd, (struct sockaddr*)&local, sizeof(local)) != 0) {
printf("bind() error\n");
return -1;
}
signal(SIGINT, sig_int);
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 = IMP_U_PID;
message.hdr.nlmsg_pid = local.nl_pid;
sendto(skfd, &message, message.hdr.nlmsg_len, 0,\
(struct sockaddr*)&kpeer, sizeof(kpeer));
while(1) {
kpeerlen = sizeof(struct sockaddr_nl);
rcvlen = recvfrom(skfd, &info, sizeof(struct u_packet_info),\
0, (struct sockaddr*)&kpeer, &kpeerlen);
addr.s_addr = info.icmp_info.src;
printf("src: %s, ", inet_ntoa(addr));
addr.s_addr = info.icmp_info.dest;
printf("dest: %s\n", inet_ntoa(addr));
}
return 0;
}
在这里,我们可以看见 利用netlink 和netfilter的hook 框架就能做很多的事情。 这样的话 就会根据自己具体的需求,制定符合自己的防火墙策略。当然 iptables 的filter, nat等等的, 也是利用Netfilter框架。 下次 我会拿个Iptables 的filter 模块 举个例子,再看一下 具体是如何操作的。
其实,内核并不难, 只是 Linux 现在支持的东西越来越多, 复杂性比以前高了很多。 而且 Iptables 很快就会 被nftables 取代。
对于 linux 而言, 只要你肯努力, 方法得当, 理解起来没什么难度的。。。。。