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