linux/net/netfilter/nf_nat_redirect.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * (C) 1999-2001 Paul `Rusty' Russell
   4 * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
   5 * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
   6 *
   7 * Based on Rusty Russell's IPv4 REDIRECT target. Development of IPv6
   8 * NAT funded by Astaro.
   9 */
  10
  11#include <linux/if.h>
  12#include <linux/inetdevice.h>
  13#include <linux/ip.h>
  14#include <linux/kernel.h>
  15#include <linux/netdevice.h>
  16#include <linux/netfilter.h>
  17#include <linux/types.h>
  18#include <linux/netfilter_ipv4.h>
  19#include <linux/netfilter_ipv6.h>
  20#include <linux/netfilter/x_tables.h>
  21#include <net/addrconf.h>
  22#include <net/checksum.h>
  23#include <net/protocol.h>
  24#include <net/netfilter/nf_nat.h>
  25#include <net/netfilter/nf_nat_redirect.h>
  26
  27unsigned int
  28nf_nat_redirect_ipv4(struct sk_buff *skb,
  29                     const struct nf_nat_ipv4_multi_range_compat *mr,
  30                     unsigned int hooknum)
  31{
  32        struct nf_conn *ct;
  33        enum ip_conntrack_info ctinfo;
  34        __be32 newdst;
  35        struct nf_nat_range2 newrange;
  36
  37        WARN_ON(hooknum != NF_INET_PRE_ROUTING &&
  38                hooknum != NF_INET_LOCAL_OUT);
  39
  40        ct = nf_ct_get(skb, &ctinfo);
  41        WARN_ON(!(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)));
  42
  43        /* Local packets: make them go to loopback */
  44        if (hooknum == NF_INET_LOCAL_OUT) {
  45                newdst = htonl(0x7F000001);
  46        } else {
  47                const struct in_device *indev;
  48
  49                newdst = 0;
  50
  51                indev = __in_dev_get_rcu(skb->dev);
  52                if (indev) {
  53                        const struct in_ifaddr *ifa;
  54
  55                        ifa = rcu_dereference(indev->ifa_list);
  56                        if (ifa)
  57                                newdst = ifa->ifa_local;
  58                }
  59
  60                if (!newdst)
  61                        return NF_DROP;
  62        }
  63
  64        /* Transfer from original range. */
  65        memset(&newrange.min_addr, 0, sizeof(newrange.min_addr));
  66        memset(&newrange.max_addr, 0, sizeof(newrange.max_addr));
  67        newrange.flags       = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS;
  68        newrange.min_addr.ip = newdst;
  69        newrange.max_addr.ip = newdst;
  70        newrange.min_proto   = mr->range[0].min;
  71        newrange.max_proto   = mr->range[0].max;
  72
  73        /* Hand modified range to generic setup. */
  74        return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
  75}
  76EXPORT_SYMBOL_GPL(nf_nat_redirect_ipv4);
  77
  78static const struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT;
  79
  80unsigned int
  81nf_nat_redirect_ipv6(struct sk_buff *skb, const struct nf_nat_range2 *range,
  82                     unsigned int hooknum)
  83{
  84        struct nf_nat_range2 newrange;
  85        struct in6_addr newdst;
  86        enum ip_conntrack_info ctinfo;
  87        struct nf_conn *ct;
  88
  89        ct = nf_ct_get(skb, &ctinfo);
  90        if (hooknum == NF_INET_LOCAL_OUT) {
  91                newdst = loopback_addr;
  92        } else {
  93                struct inet6_dev *idev;
  94                struct inet6_ifaddr *ifa;
  95                bool addr = false;
  96
  97                idev = __in6_dev_get(skb->dev);
  98                if (idev != NULL) {
  99                        read_lock_bh(&idev->lock);
 100                        list_for_each_entry(ifa, &idev->addr_list, if_list) {
 101                                newdst = ifa->addr;
 102                                addr = true;
 103                                break;
 104                        }
 105                        read_unlock_bh(&idev->lock);
 106                }
 107
 108                if (!addr)
 109                        return NF_DROP;
 110        }
 111
 112        newrange.flags          = range->flags | NF_NAT_RANGE_MAP_IPS;
 113        newrange.min_addr.in6   = newdst;
 114        newrange.max_addr.in6   = newdst;
 115        newrange.min_proto      = range->min_proto;
 116        newrange.max_proto      = range->max_proto;
 117
 118        return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
 119}
 120EXPORT_SYMBOL_GPL(nf_nat_redirect_ipv6);
 121