netfiler的优势不仅包含防火墙的实现,还包括各种报文的处理工作(如报文的加密,统计等)。可以方便地利用netfilter提供的接口实现内核态的报文处理。
在netfilter中可以解析报文,同时根据阻断规则做匹配,将符合阻断的报文直接DROP,但是在tcp的会话中,这样并不优雅,tcp是可靠传输,如果是直接drop掉某一个报文,则发送端会一直重发,所以在tcp的协议中需要向发送端发送一个fin或者是rst的报文,用来断来链接。下面是我测试使用的代码。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/udp.h>
#include <linux/ip.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/etherdevice.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <linux/inet.h>
#include <linux/types.h>
#include <linux/string.h>
#include <net/checksum.h>
#include <net/tcp.h>
#include <net/netfilter/nf_conntrack.h>
//模块声明
//GPL, Dual BSD/GPL, Dual MPL/GPL, Proprietary
//内核可以识别上述四种许可方式,没有采用上面的则被假定为私有的,内核加载这种私有模块会被“污染”
MODULE_LICENSE("GPL");
#define NIPQUAD(addr) \
((uint8_t *)&addr)[0], \
((uint8_t *)&addr)[1], \
((uint8_t *)&addr)[2], \
((uint8_t *)&addr)[3], \
addr
#define MAC_FFF(mac) \
((uint8_t *)&mac)[0], \
((uint8_t *)&mac)[1], \
((uint8_t *)&mac)[2], \
((uint8_t *)&mac)[3], \
((uint8_t *)&mac)[4], \
((uint8_t *)&mac)[5]
#define BLOKEN_IP (1107077312) // 断链的IP (192.168.252.65 网络字节序)
const char* client_break_msg = "C_break"; //检查payload 前面的字符
static uint32_t ipv4_hook_in_func(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
{
//struct SKernelMsgIpV4Stats _ipv4Stats;
struct ethhdr *eth_header = (struct ethhdr *)skb_mac_header(skb); //eth_hdr
struct iphdr *ip_header = (struct iphdr *)skb_network_header(skb); //ip_hdr
struct tcphdr *tcp_header = NULL;
unsigned int nip_hdr_len;
unsigned int ntcp_hdr_len;
struct net_device * send_dev = NULL;
int i=0;
uint16_t nt_header=0;
unsigned char *payload=NULL;
uint16_t payload_len=0;
int is_bloken = 0;
struct sk_buff *fack_skb = NULL;
struct ethhdr *fack_eth_header = NULL; //eth_hdr
struct iphdr *fack_ip_header = NULL; //ip_hdr
struct tcphdr *fack_tcp_header = NULL;
if (NULL == skb || NULL == eth_header || NULL == ip_header)
{
return NF_ACCEPT;
}
//tcp协议
if(likely(ip_header->protocol==IPPROTO_TCP))
{
tcp_header = (struct tcphdr *)skb_transport_header(skb); //tcp_hdr
if (NULL == tcp_header)
{
return NF_ACCEPT;
}
//判断 包中ip是不是符合需要 断链的IP
if (BLOKEN_IP == ip_header->saddr || BLOKEN_IP == ip_header->daddr)
{
printk(KERN_DEBUG "ipv4_hook_func. %02x:%02x:%02x:%02x:%02x:%02x_%u.%u.%u.%u(%u):%u->%02x:%02x:%02x:%02x:%02x:%02x_%u.%u.%u.%u(%u):%u. seq:%u ack:%u \n"
, MAC_FFF(eth_header->h_source), NIPQUAD(ip_header->saddr), ntohs(tcp_header->source)
, MAC_FFF(eth_header->h_dest), NIPQUAD(ip_header->daddr), ntohs(tcp_header->dest), tcp_header->seq, tcp_header->ack_seq);
is_bloken
nip_hdr_len = ip_header->ihl << 2; //IP头长度
ntcp_hdr_len = tcp_header->doff << 2; //tcp头长度
nt_header = nip_hdr_len + ntcp_hdr_len;
payload = skb->data + nt_header;
payload_len = skb->len - nt_header;
if(payload_len > 0)
{
printk("tcp segment msg: ");
for(i=0;i<payload_len;i++)
{
printk("%c", payload[i]);
}
printk("\r\n");
}
//检查payload中 从第一个字符开始是否符合
if(payload_len >= strlen(client_break_msg))
{
is_bloken = 1;
for(i=0; i < strlen(client_break_msg); i++)
{
if(payload[i] != client_break_msg[i])
{
is_bloken = 0;
break;
}
}
}
//符号断链
if(is_bloken)
{
printk("block.......\r\n");
if(skb->data_len!=0)
{
if(skb_linearize(skb))
{
printk("error line skb\r\n");
printk("skb->data_len %d\r\n",skb->data_len);
return NF_DROP;
}
}
//获取包出去的网卡
send_dev = dev_get_by_name(&init_net, "eth0");
//send_dev = dev_get_by_name(&init_net, skb->dev->name);
if(send_dev == NULL)
{
printk("%s\n\n", "dev_get_by_name return NULL");
return NF_DROP;
}
//分配一个sk_buff 这个包发送给源端,包含了tcp的fin或事rst,用来断链,这个包不需要payload数据
fack_skb = alloc_skb(LL_RESERVED_SPACE (send_dev) + sizeof (struct iphdr) + sizeof (struct tcphdr), GFP_ATOMIC);
if(NULL == fack_skb)
{
printk("skb_copy error\n\n");
return NF_DROP;
}
fack_skb->pkt_type = PACKET_OTHERHOST;
fack_skb->protocol = skb->protocol;// __constant_htons(ETH_P_IP);
fack_skb->ip_summed = CHECKSUM_NONE;
fack_skb->priority = 0;
//skb_reset_mac_header, skb_reset_network_header, skb_reset_transport_header
//skb_mac_header, skb_network_header, skb_transport_header
//skb_put、skb_push、skb_pull、skb_reserve
//fack_skb是一个新的 sk_buff,可以将data和tail都置到末尾,然后自顶向下填充数据
//data tail 全部置到末尾
skb_reserve (fack_skb, LL_RESERVED_SPACE (send_dev) + sizeof (struct iphdr) + sizeof (struct tcphdr));
//tcp data前移,此时data开始位置为tcphdr开始位置
skb_push(fack_skb, sizeof (struct tcphdr));
skb_reset_transport_header(fack_skb);
{
fack_tcp_header = (struct tcphdr *) skb_transport_header(fack_skb);
memset (fack_tcp_header, 0, sizeof (struct tcphdr));
fack_tcp_header->source = tcp_header->dest; //用原包的目的port填充
fack_tcp_header->dest = tcp_header->source; //用原包的源port填充
fack_tcp_header->seq = tcp_header->ack_seq; //用原包的ack填充
fack_tcp_header->ack_seq = htonl(ntohl(tcp_header->seq) + payload_len); //这个需要确认收到的原包,注意字节序
fack_tcp_header->doff = sizeof(struct tcphdr)>>2;
fack_tcp_header->psh = 0;
fack_tcp_header->rst = 1; //这里将rst置为1,用于断链
fack_tcp_header->fin = 0;
fack_tcp_header->syn = 0;
fack_tcp_header->ack = 1;
fack_tcp_header->window = __constant_htons (5840);
fack_tcp_header->check = 0;
}
//ip data前移
skb_push(fack_skb, sizeof (struct iphdr));
skb_reset_network_header(fack_skb);
{
fack_ip_header = (struct iphdr*) skb_network_header(fack_skb);
memset (fack_ip_header, 0, sizeof (struct iphdr));
fack_ip_header->version = ip_header->version;
fack_ip_header->ihl = sizeof(struct iphdr)>>2;
fack_ip_header->frag_off = 0x40;
fack_ip_header->protocol = ip_header->protocol;
fack_ip_header->tos = 0;
fack_ip_header->daddr = ip_header->saddr;
fack_ip_header->saddr = ip_header->daddr;
fack_ip_header->ttl = 0x40;
fack_ip_header->tot_len = __constant_htons(fack_skb->len);
fack_ip_header->check = 0;
fack_ip_header->check=ip_fast_csum((unsigned char*)fack_ip_header, fack_ip_header->ihl); //ip层的校验和
}
//校验和
fack_skb->csum = 0;
//fack_skb->csum = csum_partial(fack_tcp_header, sizeof(struct tcphdr), 0);
fack_skb->csum = skb_checksum (fack_skb, sizeof(struct iphdr), sizeof(struct tcphdr), 0);
fack_tcp_header->check = csum_tcpudp_magic (fack_ip_header->saddr, fack_ip_header->daddr,
sizeof(struct tcphdr), fack_ip_header->protocol, fack_skb->csum);
//mac层
skb_push(fack_skb, ETH_HLEN);
skb_reset_mac_header(fack_skb);
if(skb_mac_header_was_set(skb))
{
fack_eth_header = (struct ethhdr *)skb_mac_header(fack_skb); //eth_hdr
memcpy(fack_eth_header->h_dest, eth_header->h_source, ETH_ALEN);
memcpy(fack_eth_header->h_source, eth_header->h_dest, ETH_ALEN);
}
fack_eth_header->h_proto = eth_header->h_proto;
fack_skb->dev = send_dev;
dev_hold(fack_skb->dev);
printk("%s\n", "dev_hold ok");
if (dev_queue_xmit(fack_skb) < 0)
{
printk("dev_queue_xmit() error\n");
dev_put(fack_skb->dev);
kfree_skb(fack_skb);
return NF_DROP;
}
printk(KERN_DEBUG "ipv4_hook_func_sour. %02x:%02x:%02x:%02x:%02x:%02x_%u.%u.%u.%u(%u):%u->%02x:%02x:%02x:%02x:%02x:%02x_%u.%u.%u.%u(%u):%u seq:%u ack:%u. \n\n"
, MAC_FFF(fack_eth_header->h_source), NIPQUAD(fack_ip_header->saddr), ntohs(fack_tcp_header->source)
, MAC_FFF(fack_eth_header->h_dest), NIPQUAD(fack_ip_header->daddr), ntohs(fack_tcp_header->dest), fack_tcp_header->seq, fack_tcp_header->ack_seq);
}
}
}
return NF_ACCEPT;
}
static struct nf_hook_ops ipv4_nfin = {
.hook = ipv4_hook_in_func,
.hooknum = NF_INET_PRE_ROUTING,
.pf = PF_INET,
.priority = NF_IP_PRI_CONNTRACK - 1, // NF_IP_PRI_CONNTRACK
};
//子模块初始化回调函数
static int __init init_nf(void)
{
int ret = 0;
if(0 != nf_register_hook(&ipv4_nfin))
{
printk(KERN_ERR "nf_register_hook(&ipv4_nfin) error");
ret = -1;
}
printk(KERN_INFO "Register netfilter module succ.\n");
return 0;
}
//子模块销毁回调函数
static void __exit exit_nf(void)
{
nf_unregister_hook(&ipv4_nfin);
printk(KERN_INFO "Unregister netfilter module succ.\n");
}
module_init(init_nf);
module_exit(exit_nf);
MODULE_AUTHOR("xuwaiwai"); //作者描述
MODULE_DESCRIPTION("TCP broken chain test demo");//模块用途的简短描述
MODULE_VERSION("0.0.1");//模块的版本号
MODULE_ALLAS("broken ipv4");//模块的别名
例子中直接新分配了一个 skb_buff, 然后在此结构中填充依次L4,L3,L2的数据并发送。
关于编译可以参考:netfilter模块编译和运行
凡是过往,即为序章