linux-old/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.8 1999/03/21 05:22:40 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 <linux/firewall.h>
  41#include <linux/ip_fw.h>
  42#include <net/checksum.h>
  43#include <linux/route.h>
  44#include <net/route.h>
  45#include <net/ip_fib.h>
  46
  47
  48int
  49ip_do_nat(struct sk_buff *skb)
  50{
  51        struct rtable *rt = (struct rtable*)skb->dst;
  52        struct iphdr *iph = skb->nh.iph;
  53        u32 odaddr = iph->daddr;
  54        u32 osaddr = iph->saddr;
  55        u16     check;
  56
  57        IPCB(skb)->flags |= IPSKB_TRANSLATED;
  58
  59        /* Rewrite IP header */
  60        iph->daddr = rt->rt_dst_map;
  61        iph->saddr = rt->rt_src_map;
  62        iph->check = 0;
  63        iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
  64
  65        /* If it is the first fragment, rewrite protocol headers */
  66
  67        if (!(iph->frag_off & htons(IP_OFFSET))) {
  68                u16     *cksum;
  69
  70                switch(iph->protocol) {
  71                case IPPROTO_TCP:
  72                        cksum  = (u16*)&((struct tcphdr*)(((char*)iph) + (iph->ihl<<2)))->check;
  73                        if ((u8*)(cksum+1) > skb->tail)
  74                                goto truncated;
  75                        check  = csum_tcpudp_magic(iph->saddr, iph->daddr, 0, 0, ~(*cksum));
  76                        *cksum = csum_tcpudp_magic(~osaddr, ~odaddr, 0, 0, ~check);
  77                        break;
  78                case IPPROTO_UDP:
  79                        cksum  = (u16*)&((struct udphdr*)(((char*)iph) + (iph->ihl<<2)))->check;
  80                        if ((u8*)(cksum+1) > skb->tail)
  81                                goto truncated;
  82                        if ((check = *cksum) != 0) {
  83                                check = csum_tcpudp_magic(iph->saddr, iph->daddr, 0, 0, ~check);
  84                                check = csum_tcpudp_magic(~osaddr, ~odaddr, 0, 0, ~check);
  85                                *cksum = check ? : 0xFFFF;
  86                        }
  87                        break;
  88                case IPPROTO_ICMP:
  89                {
  90                        struct icmphdr *icmph = (struct icmphdr*)((char*)iph + (iph->ihl<<2));
  91                        struct   iphdr *ciph;
  92                        u32 idaddr, isaddr;
  93                        int updated;
  94
  95                        if ((icmph->type != ICMP_DEST_UNREACH) &&
  96                            (icmph->type != ICMP_TIME_EXCEEDED) &&
  97                            (icmph->type != ICMP_PARAMETERPROB))
  98                                break;
  99
 100                        ciph = (struct iphdr *) (icmph + 1);
 101
 102                        if ((u8*)(ciph+1) > skb->tail)
 103                                goto truncated;
 104
 105                        isaddr = ciph->saddr;
 106                        idaddr = ciph->daddr;
 107                        updated = 0;
 108
 109                        if (rt->rt_flags&RTCF_DNAT && ciph->saddr == odaddr) {
 110                                ciph->saddr = iph->daddr;
 111                                updated = 1;
 112                        }
 113                        if (rt->rt_flags&RTCF_SNAT) {
 114                                if (ciph->daddr != osaddr) {
 115                                        struct   fib_result res;
 116                                        struct   rt_key key;
 117                                        unsigned flags = 0;
 118
 119                                        key.src = ciph->daddr;
 120                                        key.dst = ciph->saddr;
 121                                        key.iif = skb->dev->ifindex;
 122                                        key.oif = 0;
 123#ifdef CONFIG_IP_ROUTE_TOS
 124                                        key.tos = RT_TOS(ciph->tos);
 125#endif
 126#ifdef CONFIG_IP_ROUTE_FWMARK
 127                                        key.fwmark = 0;
 128#endif
 129                                        /* Use fib_lookup() until we get our own
 130                                         * hash table of NATed hosts -- Rani
 131                                         */
 132                                        if (fib_lookup(&key, &res) == 0 && res.r) {
 133                                                ciph->daddr = fib_rules_policy(ciph->daddr, &res, &flags);
 134                                                if (ciph->daddr != idaddr)
 135                                                        updated = 1;
 136                                        }
 137                                } else {
 138                                        ciph->daddr = iph->saddr;
 139                                        updated = 1;
 140                                }
 141                        }
 142                        if (updated) {
 143                                cksum  = &icmph->checksum;
 144                                /* Using tcpudp primitive. Why not? */
 145                                check  = csum_tcpudp_magic(ciph->saddr, ciph->daddr, 0, 0, ~(*cksum));
 146                                *cksum = csum_tcpudp_magic(~isaddr, ~idaddr, 0, 0, ~check);
 147                        }
 148                        break;
 149                }
 150                default:
 151                        break;
 152                }
 153        }
 154        return 0;
 155
 156truncated:
 157        return -EINVAL;
 158}
 159
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.