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                .usersize       = offsetof(struct ip6t_npt_tginfo, adjustment),
 116                .checkentry     = ip6t_npt_checkentry,
 117                .family         = NFPROTO_IPV6,
 118                .hooks          = (1 << NF_INET_LOCAL_IN) |
 119                                  (1 << NF_INET_POST_ROUTING),
 120                .me             = THIS_MODULE,
 121        },
 122        {
 123                .name           = "DNPT",
 124                .table          = "mangle",
 125                .target         = ip6t_dnpt_tg,
 126                .targetsize     = sizeof(struct ip6t_npt_tginfo),
 127                .usersize       = offsetof(struct ip6t_npt_tginfo, adjustment),
 128                .checkentry     = ip6t_npt_checkentry,
 129                .family         = NFPROTO_IPV6,
 130                .hooks          = (1 << NF_INET_PRE_ROUTING) |
 131                                  (1 << NF_INET_LOCAL_OUT),
 132                .me             = THIS_MODULE,
 133        },
 134};
 135
 136static int __init ip6t_npt_init(void)
 137{
 138        return xt_register_targets(ip6t_npt_target_reg,
 139                                   ARRAY_SIZE(ip6t_npt_target_reg));
 140}
 141
 142static void __exit ip6t_npt_exit(void)
 143{
 144        xt_unregister_targets(ip6t_npt_target_reg,
 145                              ARRAY_SIZE(ip6t_npt_target_reg));
 146}
 147
 148module_init(ip6t_npt_init);
 149module_exit(ip6t_npt_exit);
 150
 151MODULE_LICENSE("GPL");
 152MODULE_DESCRIPTION("IPv6-to-IPv6 Network Prefix Translation (RFC 6296)");
 153MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
 154MODULE_ALIAS("ip6t_SNPT");
 155MODULE_ALIAS("ip6t_DNPT");
 156