linux-bk/net/ipv4/ip_nat_dumb.c
<<
>>
Prefs
   1/*
   2 * INET         An implementation of the TCP/IP protocol suite for the LINUX
   3 *              operating system.  INET is implemented using the  BSD Socket
   4 *              interface as the means of communication with the user level.
   5 *
   6 *              Dumb Network Address Translation.
   7 *
   8 * Version:     $Id: ip_nat_dumb.c,v 1.11 2000/12/13 18:31:48 davem Exp $
   9 *
  10 * Authors:     Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  11 *
  12 *              This program is free software; you can redistribute it and/or
  13 *              modify it under the terms of the GNU General Public License
  14 *              as published by the Free Software Foundation; either version
  15 *              2 of the License, or (at your option) any later version.
  16 *
  17 * Fixes:
  18 *              Rani Assaf      :       A zero checksum is a special case
  19 *                                      only in UDP
  20 *              Rani Assaf      :       Added ICMP messages rewriting
  21 *              Rani Assaf      :       Repaired wrong changes, made by ANK.
  22 *
  23 *
  24 * NOTE:        It is just working model of real NAT.
  25 */
  26
  27#include <linux/config.h>
  28#include <linux/types.h>
  29#include <linux/mm.h>
  30#include <linux/sched.h>
  31#include <linux/skbuff.h>
  32#include <linux/ip.h>
  33#include <linux/icmp.h>
  34#include <linux/netdevice.h>
  35#include <net/sock.h>
  36#include <net/ip.h>
  37#include <net/icmp.h>
  38#include <linux/tcp.h>
  39#include <linux/udp.h>
  40#include <net/checksum.h>
  41#include <linux/route.h>
  42#include <net/route.h>
  43#include <net/ip_fib.h>
  44
  45
  46int
  47ip_do_nat(struct sk_buff *skb)
  48{
  49        struct rtable *rt = (struct rtable*)skb->dst;
  50        struct iphdr *iph = skb->nh.iph;
  51        u32 odaddr = iph->daddr;
  52        u32 osaddr = iph->saddr;
  53        u16     check;
  54
  55        IPCB(skb)->flags |= IPSKB_TRANSLATED;
  56
  57        /* Rewrite IP header */
  58        iph->daddr = rt->rt_dst_map;
  59        iph->saddr = rt->rt_src_map;
  60        iph->check = 0;
  61        iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
  62
  63        /* If it is the first fragment, rewrite protocol headers */
  64
  65        if (!(iph->frag_off & htons(IP_OFFSET))) {
  66                u16     *cksum;
  67
  68                switch(iph->protocol) {
  69                case IPPROTO_TCP:
  70                        cksum  = (u16*)&((struct tcphdr*)(((char*)iph) + (iph->ihl<<2)))->check;
  71                        if ((u8*)(cksum+1) > skb->tail)
  72                                goto truncated;
  73                        check = *cksum;
  74                        if (skb->ip_summed != CHECKSUM_HW)
  75                                check = ~check;
  76                        check = csum_tcpudp_magic(iph->saddr, iph->daddr, 0, 0, check);
  77                        check = csum_tcpudp_magic(~osaddr, ~odaddr, 0, 0, ~check);
  78                        if (skb->ip_summed == CHECKSUM_HW)
  79                                check = ~check;
  80                        *cksum = check;
  81                        break;
  82                case IPPROTO_UDP:
  83                        cksum  = (u16*)&((struct udphdr*)(((char*)iph) + (iph->ihl<<2)))->check;
  84                        if ((u8*)(cksum+1) > skb->tail)
  85                                goto truncated;
  86                        if ((check = *cksum) != 0) {
  87                                check = csum_tcpudp_magic(iph->saddr, iph->daddr, 0, 0, ~check);
  88                                check = csum_tcpudp_magic(~osaddr, ~odaddr, 0, 0, ~check);
  89                                *cksum = check ? : 0xFFFF;
  90                        }
  91                        break;
  92                case IPPROTO_ICMP:
  93                {
  94                        struct icmphdr *icmph = (struct icmphdr*)((char*)iph + (iph->ihl<<2));
  95                        struct   iphdr *ciph;
  96                        u32 idaddr, isaddr;
  97                        int updated;
  98
  99                        if ((icmph->type != ICMP_DEST_UNREACH) &&
 100                            (icmph->type != ICMP_TIME_EXCEEDED) &&
 101                            (icmph->type != ICMP_PARAMETERPROB))
 102                                break;
 103
 104                        ciph = (struct iphdr *) (icmph + 1);
 105
 106                        if ((u8*)(ciph+1) > skb->tail)
 107                                goto truncated;
 108
 109                        isaddr = ciph->saddr;
 110                        idaddr = ciph->daddr;
 111                        updated = 0;
 112
 113                        if (rt->rt_flags&RTCF_DNAT && ciph->saddr == odaddr) {
 114                                ciph->saddr = iph->daddr;
 115                                updated = 1;
 116                        }
 117                        if (rt->rt_flags&RTCF_SNAT) {
 118                                if (ciph->daddr != osaddr) {
 119                                        struct   fib_result res;
 120                                        unsigned flags = 0;
 121                                        struct flowi fl = {
 122                                                .iif = skb->dev->ifindex,
 123                                                .nl_u =
 124                                                { .ip4_u =
 125                                                  { .daddr = ciph->saddr,
 126                                                    .saddr = ciph->daddr,
 127#ifdef CONFIG_IP_ROUTE_TOS
 128                                                    .tos = RT_TOS(ciph->tos)
 129#endif
 130                                                  } },
 131                                                .proto = ciph->protocol };
 132
 133                                        /* Use fib_lookup() until we get our own
 134                                         * hash table of NATed hosts -- Rani
 135                                         */
 136                                        if (fib_lookup(&fl, &res) == 0) {
 137                                                if (res.r) {
 138                                                        ciph->daddr = fib_rules_policy(ciph->daddr, &res, &flags);
 139                                                        if (ciph->daddr != idaddr)
 140                                                                updated = 1;
 141                                                }
 142                                                fib_res_put(&res);
 143                                        }
 144                                } else {
 145                                        ciph->daddr = iph->saddr;
 146                                        updated = 1;
 147                                }
 148                        }
 149                        if (updated) {
 150                                cksum  = &icmph->checksum;
 151                                /* Using tcpudp primitive. Why not? */
 152                                check  = csum_tcpudp_magic(ciph->saddr, ciph->daddr, 0, 0, ~(*cksum));
 153                                *cksum = csum_tcpudp_magic(~isaddr, ~idaddr, 0, 0, ~check);
 154                        }
 155                        break;
 156                }
 157                default:
 158                        break;
 159                }
 160        }
 161        return NET_RX_SUCCESS;
 162
 163truncated:
 164        /* should be return NET_RX_BAD; */
 165        return -EINVAL;
 166}
 167
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.