使用 netfilter 处理IPv6报文

        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);

有问题欢迎提出来

またね

猜你喜欢

转载自blog.csdn.net/fuyuande/article/details/81295433