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.
6 * Dumb Network Address Translation.
8 * Version: $Id: ip_nat_dumb.c,v 1.9 1999/08/20 11:05:46 davem Exp $
10 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
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.
18 * Rani Assaf : A zero checksum is a special case
20 * Rani Assaf : Added ICMP messages rewriting
21 * Rani Assaf : Repaired wrong changes, made by ANK.
24 * NOTE: It is just working model of real NAT.
27 #include <linux/config.h>
28 #include <linux/types.h>
30 #include <linux/sched.h>
31 #include <linux/skbuff.h>
33 #include <linux/icmp.h>
34 #include <linux/netdevice.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>
47 ip_do_nat(struct sk_buff
*skb
)
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
;
55 IPCB(skb
)->flags
|= IPSKB_TRANSLATED
;
57 /* Rewrite IP header */
58 iph
->daddr
= rt
->rt_dst_map
;
59 iph
->saddr
= rt
->rt_src_map
;
61 iph
->check
= ip_fast_csum((unsigned char *)iph
, iph
->ihl
);
63 /* If it is the first fragment, rewrite protocol headers */
65 if (!(iph
->frag_off
& htons(IP_OFFSET
))) {
68 switch(iph
->protocol
) {
70 cksum
= (u16
*)&((struct tcphdr
*)(((char*)iph
) + (iph
->ihl
<<2)))->check
;
71 if ((u8
*)(cksum
+1) > skb
->tail
)
73 check
= csum_tcpudp_magic(iph
->saddr
, iph
->daddr
, 0, 0, ~(*cksum
));
74 *cksum
= csum_tcpudp_magic(~osaddr
, ~odaddr
, 0, 0, ~check
);
77 cksum
= (u16
*)&((struct udphdr
*)(((char*)iph
) + (iph
->ihl
<<2)))->check
;
78 if ((u8
*)(cksum
+1) > skb
->tail
)
80 if ((check
= *cksum
) != 0) {
81 check
= csum_tcpudp_magic(iph
->saddr
, iph
->daddr
, 0, 0, ~check
);
82 check
= csum_tcpudp_magic(~osaddr
, ~odaddr
, 0, 0, ~check
);
83 *cksum
= check
? : 0xFFFF;
88 struct icmphdr
*icmph
= (struct icmphdr
*)((char*)iph
+ (iph
->ihl
<<2));
93 if ((icmph
->type
!= ICMP_DEST_UNREACH
) &&
94 (icmph
->type
!= ICMP_TIME_EXCEEDED
) &&
95 (icmph
->type
!= ICMP_PARAMETERPROB
))
98 ciph
= (struct iphdr
*) (icmph
+ 1);
100 if ((u8
*)(ciph
+1) > skb
->tail
)
103 isaddr
= ciph
->saddr
;
104 idaddr
= ciph
->daddr
;
107 if (rt
->rt_flags
&RTCF_DNAT
&& ciph
->saddr
== odaddr
) {
108 ciph
->saddr
= iph
->daddr
;
111 if (rt
->rt_flags
&RTCF_SNAT
) {
112 if (ciph
->daddr
!= osaddr
) {
113 struct fib_result res
;
117 key
.src
= ciph
->daddr
;
118 key
.dst
= ciph
->saddr
;
119 key
.iif
= skb
->dev
->ifindex
;
121 #ifdef CONFIG_IP_ROUTE_TOS
122 key
.tos
= RT_TOS(ciph
->tos
);
124 #ifdef CONFIG_IP_ROUTE_FWMARK
127 /* Use fib_lookup() until we get our own
128 * hash table of NATed hosts -- Rani
130 if (fib_lookup(&key
, &res
) == 0) {
132 ciph
->daddr
= fib_rules_policy(ciph
->daddr
, &res
, &flags
);
133 if (ciph
->daddr
!= idaddr
)
139 ciph
->daddr
= iph
->saddr
;
144 cksum
= &icmph
->checksum
;
145 /* Using tcpudp primitive. Why not? */
146 check
= csum_tcpudp_magic(ciph
->saddr
, ciph
->daddr
, 0, 0, ~(*cksum
));
147 *cksum
= csum_tcpudp_magic(~isaddr
, ~idaddr
, 0, 0, ~check
);