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