1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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
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
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
134
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
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
165 return -EINVAL;
166}
167