netfilter對IPv6的處理和IPv4流程類似,只有鉤子函數註冊協議不同,優先級和註冊的鏈都是一樣的。
{
.hook=nf_input_hook_v4,
.pf=NFPROTO_IPV4, //IPv4協議
.hooknum=NF_INET_POST_ROUTING,
.priority=0,
},
{
.hook=nf_input_hook_v6,
.pf=NFPROTO_IPV6, //IPv6協議
.hooknum=NF_INET_PRE_ROUTING,
.priority=NF_IP_PRI_FIRST,
},
下面是一個netfilter ipv6小栗子,對本機出去的IPv6 UDP報文做了端口變換處理,介紹一下IPv6地址操作、IPv6報文udp校驗值計算等等。
內核版本: 3.4.39
/*
* Netfilter IPv6 Demo, NAT
* Autor : Mason
* Date : 20180731
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/socket.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/in.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <net/ip.h>
#include <net/dsfield.h>
#include <linux/skbuff.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <net/route.h>
#include "nfdemov6.h"
/* IPv6輸入報文處理函數 */
static unsigned int nfdemo_input_hook_v6(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
/* do something */
return NF_ACCEPT;
}
/* IPv6輸出報文處理函數,這裏對IPv6 UDP報文做了源端口變換 */
static unsigned int nfdemo_output_hook_v6(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct ipv6hdr *iph6;
struct udphdr *udph;
unsigned int udp_len;
/* 獲取IPv6首部指針 */
iph6 = ipv6_hdr(skb);
if (!iph6)
return NF_ACCEPT;
/* 過濾 ::/128 空類型地址 */
if (ipv6_addr_any(&iph6->saddr) || ipv6_addr_any(&iph6->daddr))
return NF_ACCEPT;
/* 過濾源地址和目的地址相等的報文 */
if (ipv6_addr_equal(&iph6->saddr, &iph6->daddr))
return NF_ACCEPT;
/* 過濾環回地址報文 ::1/128 */
if (ipv6_addr_loopback(&iph6->saddr) || ipv6_addr_loopback(&iph6->daddr))
return NF_ACCEPT;
/* 只處理UDP報文 */
if (iph6->nexthdr != NEXTHDR_UDP)
return NF_ACCEPT;
/* 設置傳輸層首部 */
skb_set_transport_header(skb, sizeof(struct ipv6hdr));
/* 獲取UDP首部 */
udph = udp_hdr(skb);
if (!udph )
return NF_ACCEPT;
/* 更改端口 */
udph->source = htons(6666);
/* 重新計算校驗和
* IPv6校驗和計算使用 csum_ipv6_magic() 接口
* IPv4校驗和計算使用 csum_tcpudp_magic() 接口
*/
udph->check = 0;
udp_len = ntohs(udph->len);
skb->csum = csum_partial(skb_transport_header(skb), udp_len, 0);
udph->check = csum_ipv6_magic(&iph6->saddr, &iph6->daddr, udp_len, IPPROTO_UDP, skb->csum);
skb->ip_summed = CHECKSUM_NONE;
/* 如果udp首部校驗和爲0,替換成CSUM_MANGLED_0 */
if (0 == udph->check)
udph->check = CSUM_MANGLED_0;
/* 替換完成,把報文還給系統協議棧繼續處理 */
return NF_ACCEPT;
}
struct nf_hook_ops nfdemo_hook_ops[] ={
{
.hook = nfdemo_input_hook_v6, /* 鉤子處理函數 */
.pf = NFPROTO_IPV6, /* 協議類型IPv6 */
.hooknum = NF_INET_PRE_ROUTING, /* Pre_Routing鏈 */
.priority = NF_IP_PRI_FIRST + 20, /* 優先級 */
},
{
.hook = nfdemo_output_hook_v6, /* 鉤子處理函數 */
.pf = NFPROTO_IPV6, /* 協議類型IPv6 */
.hooknum = NF_INET_PRE_ROUTING, /* Post_Routing鏈 */
.priority = NF_IP_PRI_FIRST + 20, /* 優先級 */
},
{}
};
/* 模塊入口 */
static int __init nfdemov6_init(void)
{
printk("nfv6demo init \r\n");
/* 註冊 Netfilter 鉤子函數 */
nf_register_hooks(nfdemo_hook_ops,ARRAY_SIZE(nfdemo_hook_ops));
return 0;
}
/* 模塊出口 */
static void __exit nfdemov6_exit(void)
{
printk("nfv6demo exit \r\n");
/* 註銷 Netfilter 鉤子函數 */
nf_unregister_hooks(nfdemo_hook_ops,ARRAY_SIZE(nfdemo_hook_ops));
return ;
}
module_init(nfdemov6_init)
module_exit(nfdemov6_exit)
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mason");
代碼在本機編譯、運行通過。
調試遇到一個bug,地址替換直接使用128,IPv6地址 長度爲40字節128位,導致直接覆蓋後面內容,悲催
當然啦,聰明的你應該不會出現這樣的錯誤
//正確的做法
memcpy(remote_ip.in6, &iph6->saddr, sizeof(struct in6_addr));
//錯誤的做法
memcpy(remote_ip.in6, &iph6->saddr, 128);
有問題歡迎提出來
またね