linux/net/netfilter/nf_nat_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#include <net/netfilter/nf_nat_redirect.h>
  30
  31unsigned int
  32nf_nat_redirect_ipv4(struct sk_buff *skb,
  33                     const struct nf_nat_ipv4_multi_range_compat *mr,
  34                     unsigned int hooknum)
  35{
  36        struct nf_conn *ct;
  37        enum ip_conntrack_info ctinfo;
  38        __be32 newdst;
  39        struct nf_nat_range newrange;
  40
  41        NF_CT_ASSERT(hooknum == NF_INET_PRE_ROUTING ||
  42                     hooknum == NF_INET_LOCAL_OUT);
  43
  44        ct = nf_ct_get(skb, &ctinfo);
  45        NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED));
  46
  47        /* Local packets: make them go to loopback */
  48        if (hooknum == NF_INET_LOCAL_OUT) {
  49                newdst = htonl(0x7F000001);
  50        } else {
  51                struct in_device *indev;
  52                struct in_ifaddr *ifa;
  53
  54                newdst = 0;
  55
  56                rcu_read_lock();
  57                indev = __in_dev_get_rcu(skb->dev);
  58                if (indev != NULL) {
  59                        ifa = indev->ifa_list;
  60                        newdst = ifa->ifa_local;
  61                }
  62                rcu_read_unlock();
  63
  64                if (!newdst)
  65                        return NF_DROP;
  66        }
  67
  68        /* Transfer from original range. */
  69        memset(&newrange.min_addr, 0, sizeof(newrange.min_addr));
  70        memset(&newrange.max_addr, 0, sizeof(newrange.max_addr));
  71        newrange.flags       = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS;
  72        newrange.min_addr.ip = newdst;
  73        newrange.max_addr.ip = newdst;
  74        newrange.min_proto   = mr->range[0].min;
  75        newrange.max_proto   = mr->range[0].max;
  76
  77        /* Hand modified range to generic setup. */
  78        return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
  79}
  80EXPORT_SYMBOL_GPL(nf_nat_redirect_ipv4);
  81
  82static const struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT;
  83
  84unsigned int
  85nf_nat_redirect_ipv6(struct sk_buff *skb, const struct nf_nat_range *range,
  86                     unsigned int hooknum)
  87{
  88        struct nf_nat_range newrange;
  89        struct in6_addr newdst;
  90        enum ip_conntrack_info ctinfo;
  91        struct nf_conn *ct;
  92
  93        ct = nf_ct_get(skb, &ctinfo);
  94        if (hooknum == NF_INET_LOCAL_OUT) {
  95                newdst = loopback_addr;
  96        } else {
  97                struct inet6_dev *idev;
  98                struct inet6_ifaddr *ifa;
  99                bool addr = false;
 100
 101                rcu_read_lock();
 102                idev = __in6_dev_get(skb->dev);
 103                if (idev != NULL) {
 104                        list_for_each_entry(ifa, &idev->addr_list, if_list) {
 105                                newdst = ifa->addr;
 106                                addr = true;
 107                                break;
 108                        }
 109                }
 110                rcu_read_unlock();
 111
 112                if (!addr)
 113                        return NF_DROP;
 114        }
 115
 116        newrange.flags          = range->flags | NF_NAT_RANGE_MAP_IPS;
 117        newrange.min_addr.in6   = newdst;
 118        newrange.max_addr.in6   = newdst;
 119        newrange.min_proto      = range->min_proto;
 120        newrange.max_proto      = range->max_proto;
 121
 122        return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
 123}
 124EXPORT_SYMBOL_GPL(nf_nat_redirect_ipv6);
 125
 126MODULE_LICENSE("GPL");
 127MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
 128