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