linux/net/netfilter/xt_REDIRECT.c
<<
>>
Prefs
   1/*
   2 * (C) 1999-2001 Paul `Rusty' Russell
   3 * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
   4 * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 *
  10 * Based on Rusty Russell's IPv4 REDIRECT target. Development of IPv6
  11 * NAT funded by Astaro.
  12 */
  13
  14#include <linux/if.h>
  15#include <linux/inetdevice.h>
  16#include <linux/ip.h>
  17#include <linux/kernel.h>
  18#include <linux/module.h>
  19#include <linux/netdevice.h>
  20#include <linux/netfilter.h>
  21#include <linux/types.h>
  22#include <linux/netfilter_ipv4.h>
  23#include <linux/netfilter_ipv6.h>
  24#include <linux/netfilter/x_tables.h>
  25#include <net/addrconf.h>
  26#include <net/checksum.h>
  27#include <net/protocol.h>
  28#include <net/netfilter/nf_nat.h>
  29
  30static const struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT;
  31
  32static unsigned int
  33redirect_tg6(struct sk_buff *skb, const struct xt_action_param *par)
  34{
  35        const struct nf_nat_range *range = par->targinfo;
  36        struct nf_nat_range newrange;
  37        struct in6_addr newdst;
  38        enum ip_conntrack_info ctinfo;
  39        struct nf_conn *ct;
  40
  41        ct = nf_ct_get(skb, &ctinfo);
  42        if (par->hooknum == NF_INET_LOCAL_OUT)
  43                newdst = loopback_addr;
  44        else {
  45                struct inet6_dev *idev;
  46                struct inet6_ifaddr *ifa;
  47                bool addr = false;
  48
  49                rcu_read_lock();
  50                idev = __in6_dev_get(skb->dev);
  51                if (idev != NULL) {
  52                        list_for_each_entry(ifa, &idev->addr_list, if_list) {
  53                                newdst = ifa->addr;
  54                                addr = true;
  55                                break;
  56                        }
  57                }
  58                rcu_read_unlock();
  59
  60                if (!addr)
  61                        return NF_DROP;
  62        }
  63
  64        newrange.flags          = range->flags | NF_NAT_RANGE_MAP_IPS;
  65        newrange.min_addr.in6   = newdst;
  66        newrange.max_addr.in6   = newdst;
  67        newrange.min_proto      = range->min_proto;
  68        newrange.max_proto      = range->max_proto;
  69
  70        return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
  71}
  72
  73static int redirect_tg6_checkentry(const struct xt_tgchk_param *par)
  74{
  75        const struct nf_nat_range *range = par->targinfo;
  76
  77        if (range->flags & NF_NAT_RANGE_MAP_IPS)
  78                return -EINVAL;
  79        return 0;
  80}
  81
  82/* FIXME: Take multiple ranges --RR */
  83static int redirect_tg4_check(const struct xt_tgchk_param *par)
  84{
  85        const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo;
  86
  87        if (mr->range[0].flags & NF_NAT_RANGE_MAP_IPS) {
  88                pr_debug("bad MAP_IPS.\n");
  89                return -EINVAL;
  90        }
  91        if (mr->rangesize != 1) {
  92                pr_debug("bad rangesize %u.\n", mr->rangesize);
  93                return -EINVAL;
  94        }
  95        return 0;
  96}
  97
  98static unsigned int
  99redirect_tg4(struct sk_buff *skb, const struct xt_action_param *par)
 100{
 101        struct nf_conn *ct;
 102        enum ip_conntrack_info ctinfo;
 103        __be32 newdst;
 104        const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo;
 105        struct nf_nat_range newrange;
 106
 107        NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING ||
 108                     par->hooknum == NF_INET_LOCAL_OUT);
 109
 110        ct = nf_ct_get(skb, &ctinfo);
 111        NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED));
 112
 113        /* Local packets: make them go to loopback */
 114        if (par->hooknum == NF_INET_LOCAL_OUT)
 115                newdst = htonl(0x7F000001);
 116        else {
 117                struct in_device *indev;
 118                struct in_ifaddr *ifa;
 119
 120                newdst = 0;
 121
 122                rcu_read_lock();
 123                indev = __in_dev_get_rcu(skb->dev);
 124                if (indev && (ifa = indev->ifa_list))
 125                        newdst = ifa->ifa_local;
 126                rcu_read_unlock();
 127
 128                if (!newdst)
 129                        return NF_DROP;
 130        }
 131
 132        /* Transfer from original range. */
 133        memset(&newrange.min_addr, 0, sizeof(newrange.min_addr));
 134        memset(&newrange.max_addr, 0, sizeof(newrange.max_addr));
 135        newrange.flags       = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS;
 136        newrange.min_addr.ip = newdst;
 137        newrange.max_addr.ip = newdst;
 138        newrange.min_proto   = mr->range[0].min;
 139        newrange.max_proto   = mr->range[0].max;
 140
 141        /* Hand modified range to generic setup. */
 142        return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
 143}
 144
 145static struct xt_target redirect_tg_reg[] __read_mostly = {
 146        {
 147                .name       = "REDIRECT",
 148                .family     = NFPROTO_IPV6,
 149                .revision   = 0,
 150                .table      = "nat",
 151                .checkentry = redirect_tg6_checkentry,
 152                .target     = redirect_tg6,
 153                .targetsize = sizeof(struct nf_nat_range),
 154                .hooks      = (1 << NF_INET_PRE_ROUTING) |
 155                              (1 << NF_INET_LOCAL_OUT),
 156                .me         = THIS_MODULE,
 157        },
 158        {
 159                .name       = "REDIRECT",
 160                .family     = NFPROTO_IPV4,
 161                .revision   = 0,
 162                .table      = "nat",
 163                .target     = redirect_tg4,
 164                .checkentry = redirect_tg4_check,
 165                .targetsize = sizeof(struct nf_nat_ipv4_multi_range_compat),
 166                .hooks      = (1 << NF_INET_PRE_ROUTING) |
 167                              (1 << NF_INET_LOCAL_OUT),
 168                .me         = THIS_MODULE,
 169        },
 170};
 171
 172static int __init redirect_tg_init(void)
 173{
 174        return xt_register_targets(redirect_tg_reg,
 175                                   ARRAY_SIZE(redirect_tg_reg));
 176}
 177
 178static void __exit redirect_tg_exit(void)
 179{
 180        xt_unregister_targets(redirect_tg_reg, ARRAY_SIZE(redirect_tg_reg));
 181}
 182
 183module_init(redirect_tg_init);
 184module_exit(redirect_tg_exit);
 185
 186MODULE_LICENSE("GPL");
 187MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
 188MODULE_DESCRIPTION("Xtables: Connection redirection to localhost");
 189MODULE_ALIAS("ip6t_REDIRECT");
 190MODULE_ALIAS("ipt_REDIRECT");
 191