1/* 2 * Copyright (c) 2011, 2012 Patrick McHardy <kaber@trash.net> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 */ 8 9#include <linux/module.h> 10#include <linux/skbuff.h> 11#include <linux/ipv6.h> 12#include <net/ipv6.h> 13#include <linux/netfilter.h> 14#include <linux/netfilter_ipv6.h> 15#include <linux/netfilter_ipv6/ip6t_NPT.h> 16#include <linux/netfilter/x_tables.h> 17 18static int ip6t_npt_checkentry(const struct xt_tgchk_param *par) 19{ 20 struct ip6t_npt_tginfo *npt = par->targinfo; 21 struct in6_addr pfx; 22 __wsum src_sum, dst_sum; 23 24 if (npt->src_pfx_len > 64 || npt->dst_pfx_len > 64) 25 return -EINVAL; 26 27 /* Ensure that LSB of prefix is zero */ 28 ipv6_addr_prefix(&pfx, &npt->src_pfx.in6, npt->src_pfx_len); 29 if (!ipv6_addr_equal(&pfx, &npt->src_pfx.in6)) 30 return -EINVAL; 31 ipv6_addr_prefix(&pfx, &npt->dst_pfx.in6, npt->dst_pfx_len); 32 if (!ipv6_addr_equal(&pfx, &npt->dst_pfx.in6)) 33 return -EINVAL; 34 35 src_sum = csum_partial(&npt->src_pfx.in6, sizeof(npt->src_pfx.in6), 0); 36 dst_sum = csum_partial(&npt->dst_pfx.in6, sizeof(npt->dst_pfx.in6), 0); 37 38 npt->adjustment = ~csum_fold(csum_sub(src_sum, dst_sum)); 39 return 0; 40} 41 42static bool ip6t_npt_map_pfx(const struct ip6t_npt_tginfo *npt, 43 struct in6_addr *addr) 44{ 45 unsigned int pfx_len; 46 unsigned int i, idx; 47 __be32 mask; 48 __sum16 sum; 49 50 pfx_len = max(npt->src_pfx_len, npt->dst_pfx_len); 51 for (i = 0; i < pfx_len; i += 32) { 52 if (pfx_len - i >= 32) 53 mask = 0; 54 else 55 mask = htonl((1 << (i - pfx_len + 32)) - 1); 56 57 idx = i / 32; 58 addr->s6_addr32[idx] &= mask; 59 addr->s6_addr32[idx] |= ~mask & npt->dst_pfx.in6.s6_addr32[idx]; 60 } 61 62 if (pfx_len <= 48) 63 idx = 3; 64 else { 65 for (idx = 4; idx < ARRAY_SIZE(addr->s6_addr16); idx++) { 66 if ((__force __sum16)addr->s6_addr16[idx] != 67 CSUM_MANGLED_0) 68 break; 69 } 70 if (idx == ARRAY_SIZE(addr->s6_addr16)) 71 return false; 72 } 73 74 sum = ~csum_fold(csum_add(csum_unfold((__force __sum16)addr->s6_addr16[idx]), 75 csum_unfold(npt->adjustment))); 76 if (sum == CSUM_MANGLED_0) 77 sum = 0; 78 *(__force __sum16 *)&addr->s6_addr16[idx] = sum; 79 80 return true; 81} 82 83static unsigned int 84ip6t_snpt_tg(struct sk_buff *skb, const struct xt_action_param *par) 85{ 86 const struct ip6t_npt_tginfo *npt = par->targinfo; 87 88 if (!ip6t_npt_map_pfx(npt, &ipv6_hdr(skb)->saddr)) { 89 icmpv6_send(skb, ICMPV6_PARAMPROB, ICMPV6_HDR_FIELD, 90 offsetof(struct ipv6hdr, saddr)); 91 return NF_DROP; 92 } 93 return XT_CONTINUE; 94} 95 96static unsigned int 97ip6t_dnpt_tg(struct sk_buff *skb, const struct xt_action_param *par) 98{ 99 const struct ip6t_npt_tginfo *npt = par->targinfo; 100 101 if (!ip6t_npt_map_pfx(npt, &ipv6_hdr(skb)->daddr)) { 102 icmpv6_send(skb, ICMPV6_PARAMPROB, ICMPV6_HDR_FIELD, 103 offsetof(struct ipv6hdr, daddr)); 104 return NF_DROP; 105 } 106 return XT_CONTINUE; 107} 108 109static struct xt_target ip6t_npt_target_reg[] __read_mostly = { 110 { 111 .name = "SNPT", 112 .table = "mangle", 113 .target = ip6t_snpt_tg, 114 .targetsize = sizeof(struct ip6t_npt_tginfo), 115 .checkentry = ip6t_npt_checkentry, 116 .family = NFPROTO_IPV6, 117 .hooks = (1 << NF_INET_LOCAL_IN) | 118 (1 << NF_INET_POST_ROUTING), 119 .me = THIS_MODULE, 120 }, 121 { 122 .name = "DNPT", 123 .table = "mangle", 124 .target = ip6t_dnpt_tg, 125 .targetsize = sizeof(struct ip6t_npt_tginfo), 126 .checkentry = ip6t_npt_checkentry, 127 .family = NFPROTO_IPV6, 128 .hooks = (1 << NF_INET_PRE_ROUTING) | 129 (1 << NF_INET_LOCAL_OUT), 130 .me = THIS_MODULE, 131 }, 132}; 133 134static int __init ip6t_npt_init(void) 135{ 136 return xt_register_targets(ip6t_npt_target_reg, 137 ARRAY_SIZE(ip6t_npt_target_reg)); 138} 139 140static void __exit ip6t_npt_exit(void) 141{ 142 xt_unregister_targets(ip6t_npt_target_reg, 143 ARRAY_SIZE(ip6t_npt_target_reg)); 144} 145 146module_init(ip6t_npt_init); 147module_exit(ip6t_npt_exit); 148 149MODULE_LICENSE("GPL"); 150MODULE_DESCRIPTION("IPv6-to-IPv6 Network Prefix Translation (RFC 6296)"); 151MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); 152MODULE_ALIAS("ip6t_SNPT"); 153MODULE_ALIAS("ip6t_DNPT"); 154