Commit | Line | Data |
---|---|---|
faec18db FW |
1 | /* (C) 1999-2001 Paul `Rusty' Russell |
2 | * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 as | |
6 | * published by the Free Software Foundation. | |
7 | */ | |
8 | ||
9 | #include <linux/types.h> | |
10 | #include <linux/export.h> | |
11 | #include <linux/init.h> | |
12 | #include <linux/udp.h> | |
13 | #include <linux/tcp.h> | |
14 | #include <linux/icmp.h> | |
15 | #include <linux/icmpv6.h> | |
16 | ||
17 | #include <linux/dccp.h> | |
18 | #include <linux/sctp.h> | |
19 | #include <net/sctp/checksum.h> | |
20 | ||
21 | #include <linux/netfilter.h> | |
22 | #include <net/netfilter/nf_nat.h> | |
23 | #include <net/netfilter/nf_nat_core.h> | |
24 | #include <net/netfilter/nf_nat_l3proto.h> | |
25 | #include <net/netfilter/nf_nat_l4proto.h> | |
26 | ||
27 | static void | |
28 | __udp_manip_pkt(struct sk_buff *skb, | |
29 | const struct nf_nat_l3proto *l3proto, | |
30 | unsigned int iphdroff, struct udphdr *hdr, | |
31 | const struct nf_conntrack_tuple *tuple, | |
32 | enum nf_nat_manip_type maniptype, bool do_csum) | |
33 | { | |
34 | __be16 *portptr, newport; | |
35 | ||
36 | if (maniptype == NF_NAT_MANIP_SRC) { | |
37 | /* Get rid of src port */ | |
38 | newport = tuple->src.u.udp.port; | |
39 | portptr = &hdr->source; | |
40 | } else { | |
41 | /* Get rid of dst port */ | |
42 | newport = tuple->dst.u.udp.port; | |
43 | portptr = &hdr->dest; | |
44 | } | |
45 | if (do_csum) { | |
46 | l3proto->csum_update(skb, iphdroff, &hdr->check, | |
47 | tuple, maniptype); | |
48 | inet_proto_csum_replace2(&hdr->check, skb, *portptr, newport, | |
49 | false); | |
50 | if (!hdr->check) | |
51 | hdr->check = CSUM_MANGLED_0; | |
52 | } | |
53 | *portptr = newport; | |
54 | } | |
55 | ||
56 | static bool udp_manip_pkt(struct sk_buff *skb, | |
57 | const struct nf_nat_l3proto *l3proto, | |
58 | unsigned int iphdroff, unsigned int hdroff, | |
59 | const struct nf_conntrack_tuple *tuple, | |
60 | enum nf_nat_manip_type maniptype) | |
61 | { | |
62 | struct udphdr *hdr; | |
63 | bool do_csum; | |
64 | ||
65 | if (!skb_make_writable(skb, hdroff + sizeof(*hdr))) | |
66 | return false; | |
67 | ||
68 | hdr = (struct udphdr *)(skb->data + hdroff); | |
69 | do_csum = hdr->check || skb->ip_summed == CHECKSUM_PARTIAL; | |
70 | ||
71 | __udp_manip_pkt(skb, l3proto, iphdroff, hdr, tuple, maniptype, do_csum); | |
72 | return true; | |
73 | } | |
74 | ||
75 | static bool udplite_manip_pkt(struct sk_buff *skb, | |
76 | const struct nf_nat_l3proto *l3proto, | |
77 | unsigned int iphdroff, unsigned int hdroff, | |
78 | const struct nf_conntrack_tuple *tuple, | |
79 | enum nf_nat_manip_type maniptype) | |
80 | { | |
81 | #ifdef CONFIG_NF_CT_PROTO_UDPLITE | |
82 | struct udphdr *hdr; | |
83 | ||
84 | if (!skb_make_writable(skb, hdroff + sizeof(*hdr))) | |
85 | return false; | |
86 | ||
87 | hdr = (struct udphdr *)(skb->data + hdroff); | |
88 | __udp_manip_pkt(skb, l3proto, iphdroff, hdr, tuple, maniptype, true); | |
89 | #endif | |
90 | return true; | |
91 | } | |
92 | ||
93 | static bool | |
94 | sctp_manip_pkt(struct sk_buff *skb, | |
95 | const struct nf_nat_l3proto *l3proto, | |
96 | unsigned int iphdroff, unsigned int hdroff, | |
97 | const struct nf_conntrack_tuple *tuple, | |
98 | enum nf_nat_manip_type maniptype) | |
99 | { | |
100 | #ifdef CONFIG_NF_CT_PROTO_SCTP | |
101 | struct sctphdr *hdr; | |
102 | int hdrsize = 8; | |
103 | ||
104 | /* This could be an inner header returned in imcp packet; in such | |
105 | * cases we cannot update the checksum field since it is outside | |
106 | * of the 8 bytes of transport layer headers we are guaranteed. | |
107 | */ | |
108 | if (skb->len >= hdroff + sizeof(*hdr)) | |
109 | hdrsize = sizeof(*hdr); | |
110 | ||
111 | if (!skb_make_writable(skb, hdroff + hdrsize)) | |
112 | return false; | |
113 | ||
114 | hdr = (struct sctphdr *)(skb->data + hdroff); | |
115 | ||
116 | if (maniptype == NF_NAT_MANIP_SRC) { | |
117 | /* Get rid of src port */ | |
118 | hdr->source = tuple->src.u.sctp.port; | |
119 | } else { | |
120 | /* Get rid of dst port */ | |
121 | hdr->dest = tuple->dst.u.sctp.port; | |
122 | } | |
123 | ||
124 | if (hdrsize < sizeof(*hdr)) | |
125 | return true; | |
126 | ||
127 | if (skb->ip_summed != CHECKSUM_PARTIAL) { | |
128 | hdr->checksum = sctp_compute_cksum(skb, hdroff); | |
129 | skb->ip_summed = CHECKSUM_NONE; | |
130 | } | |
131 | ||
132 | #endif | |
133 | return true; | |
134 | } | |
135 | ||
136 | static bool | |
137 | tcp_manip_pkt(struct sk_buff *skb, | |
138 | const struct nf_nat_l3proto *l3proto, | |
139 | unsigned int iphdroff, unsigned int hdroff, | |
140 | const struct nf_conntrack_tuple *tuple, | |
141 | enum nf_nat_manip_type maniptype) | |
142 | { | |
143 | struct tcphdr *hdr; | |
144 | __be16 *portptr, newport, oldport; | |
145 | int hdrsize = 8; /* TCP connection tracking guarantees this much */ | |
146 | ||
147 | /* this could be a inner header returned in icmp packet; in such | |
148 | cases we cannot update the checksum field since it is outside of | |
149 | the 8 bytes of transport layer headers we are guaranteed */ | |
150 | if (skb->len >= hdroff + sizeof(struct tcphdr)) | |
151 | hdrsize = sizeof(struct tcphdr); | |
152 | ||
153 | if (!skb_make_writable(skb, hdroff + hdrsize)) | |
154 | return false; | |
155 | ||
156 | hdr = (struct tcphdr *)(skb->data + hdroff); | |
157 | ||
158 | if (maniptype == NF_NAT_MANIP_SRC) { | |
159 | /* Get rid of src port */ | |
160 | newport = tuple->src.u.tcp.port; | |
161 | portptr = &hdr->source; | |
162 | } else { | |
163 | /* Get rid of dst port */ | |
164 | newport = tuple->dst.u.tcp.port; | |
165 | portptr = &hdr->dest; | |
166 | } | |
167 | ||
168 | oldport = *portptr; | |
169 | *portptr = newport; | |
170 | ||
171 | if (hdrsize < sizeof(*hdr)) | |
172 | return true; | |
173 | ||
174 | l3proto->csum_update(skb, iphdroff, &hdr->check, tuple, maniptype); | |
175 | inet_proto_csum_replace2(&hdr->check, skb, oldport, newport, false); | |
176 | return true; | |
177 | } | |
178 | ||
179 | static bool | |
180 | dccp_manip_pkt(struct sk_buff *skb, | |
181 | const struct nf_nat_l3proto *l3proto, | |
182 | unsigned int iphdroff, unsigned int hdroff, | |
183 | const struct nf_conntrack_tuple *tuple, | |
184 | enum nf_nat_manip_type maniptype) | |
185 | { | |
186 | #ifdef CONFIG_NF_CT_PROTO_DCCP | |
187 | struct dccp_hdr *hdr; | |
188 | __be16 *portptr, oldport, newport; | |
189 | int hdrsize = 8; /* DCCP connection tracking guarantees this much */ | |
190 | ||
191 | if (skb->len >= hdroff + sizeof(struct dccp_hdr)) | |
192 | hdrsize = sizeof(struct dccp_hdr); | |
193 | ||
194 | if (!skb_make_writable(skb, hdroff + hdrsize)) | |
195 | return false; | |
196 | ||
197 | hdr = (struct dccp_hdr *)(skb->data + hdroff); | |
198 | ||
199 | if (maniptype == NF_NAT_MANIP_SRC) { | |
200 | newport = tuple->src.u.dccp.port; | |
201 | portptr = &hdr->dccph_sport; | |
202 | } else { | |
203 | newport = tuple->dst.u.dccp.port; | |
204 | portptr = &hdr->dccph_dport; | |
205 | } | |
206 | ||
207 | oldport = *portptr; | |
208 | *portptr = newport; | |
209 | ||
210 | if (hdrsize < sizeof(*hdr)) | |
211 | return true; | |
212 | ||
213 | l3proto->csum_update(skb, iphdroff, &hdr->dccph_checksum, | |
214 | tuple, maniptype); | |
215 | inet_proto_csum_replace2(&hdr->dccph_checksum, skb, oldport, newport, | |
216 | false); | |
217 | #endif | |
218 | return true; | |
219 | } | |
220 | ||
221 | static bool | |
222 | icmp_manip_pkt(struct sk_buff *skb, | |
223 | const struct nf_nat_l3proto *l3proto, | |
224 | unsigned int iphdroff, unsigned int hdroff, | |
225 | const struct nf_conntrack_tuple *tuple, | |
226 | enum nf_nat_manip_type maniptype) | |
227 | { | |
228 | struct icmphdr *hdr; | |
229 | ||
230 | if (!skb_make_writable(skb, hdroff + sizeof(*hdr))) | |
231 | return false; | |
232 | ||
233 | hdr = (struct icmphdr *)(skb->data + hdroff); | |
234 | inet_proto_csum_replace2(&hdr->checksum, skb, | |
235 | hdr->un.echo.id, tuple->src.u.icmp.id, false); | |
236 | hdr->un.echo.id = tuple->src.u.icmp.id; | |
237 | return true; | |
238 | } | |
239 | ||
240 | static bool | |
241 | icmpv6_manip_pkt(struct sk_buff *skb, | |
242 | const struct nf_nat_l3proto *l3proto, | |
243 | unsigned int iphdroff, unsigned int hdroff, | |
244 | const struct nf_conntrack_tuple *tuple, | |
245 | enum nf_nat_manip_type maniptype) | |
246 | { | |
247 | struct icmp6hdr *hdr; | |
248 | ||
249 | if (!skb_make_writable(skb, hdroff + sizeof(*hdr))) | |
250 | return false; | |
251 | ||
252 | hdr = (struct icmp6hdr *)(skb->data + hdroff); | |
253 | l3proto->csum_update(skb, iphdroff, &hdr->icmp6_cksum, | |
254 | tuple, maniptype); | |
255 | if (hdr->icmp6_type == ICMPV6_ECHO_REQUEST || | |
256 | hdr->icmp6_type == ICMPV6_ECHO_REPLY) { | |
257 | inet_proto_csum_replace2(&hdr->icmp6_cksum, skb, | |
258 | hdr->icmp6_identifier, | |
259 | tuple->src.u.icmp.id, false); | |
260 | hdr->icmp6_identifier = tuple->src.u.icmp.id; | |
261 | } | |
262 | return true; | |
263 | } | |
264 | ||
265 | /* manipulate a GRE packet according to maniptype */ | |
266 | static bool | |
267 | gre_manip_pkt(struct sk_buff *skb, | |
268 | const struct nf_nat_l3proto *l3proto, | |
269 | unsigned int iphdroff, unsigned int hdroff, | |
270 | const struct nf_conntrack_tuple *tuple, | |
271 | enum nf_nat_manip_type maniptype) | |
272 | { | |
273 | #if IS_ENABLED(CONFIG_NF_CT_PROTO_GRE) | |
274 | const struct gre_base_hdr *greh; | |
275 | struct pptp_gre_header *pgreh; | |
276 | ||
277 | /* pgreh includes two optional 32bit fields which are not required | |
278 | * to be there. That's where the magic '8' comes from */ | |
279 | if (!skb_make_writable(skb, hdroff + sizeof(*pgreh) - 8)) | |
280 | return false; | |
281 | ||
282 | greh = (void *)skb->data + hdroff; | |
283 | pgreh = (struct pptp_gre_header *)greh; | |
284 | ||
285 | /* we only have destination manip of a packet, since 'source key' | |
286 | * is not present in the packet itself */ | |
287 | if (maniptype != NF_NAT_MANIP_DST) | |
288 | return true; | |
289 | ||
290 | switch (greh->flags & GRE_VERSION) { | |
291 | case GRE_VERSION_0: | |
292 | /* We do not currently NAT any GREv0 packets. | |
293 | * Try to behave like "nf_nat_proto_unknown" */ | |
294 | break; | |
295 | case GRE_VERSION_1: | |
296 | pr_debug("call_id -> 0x%04x\n", ntohs(tuple->dst.u.gre.key)); | |
297 | pgreh->call_id = tuple->dst.u.gre.key; | |
298 | break; | |
299 | default: | |
300 | pr_debug("can't nat unknown GRE version\n"); | |
301 | return false; | |
302 | } | |
303 | #endif | |
304 | return true; | |
305 | } | |
306 | ||
307 | bool nf_nat_l4proto_manip_pkt(struct sk_buff *skb, | |
308 | const struct nf_nat_l3proto *l3proto, | |
309 | unsigned int iphdroff, unsigned int hdroff, | |
310 | const struct nf_conntrack_tuple *tuple, | |
311 | enum nf_nat_manip_type maniptype) | |
312 | { | |
313 | switch (tuple->dst.protonum) { | |
314 | case IPPROTO_TCP: | |
315 | return tcp_manip_pkt(skb, l3proto, iphdroff, hdroff, | |
316 | tuple, maniptype); | |
317 | case IPPROTO_UDP: | |
318 | return udp_manip_pkt(skb, l3proto, iphdroff, hdroff, | |
319 | tuple, maniptype); | |
320 | case IPPROTO_UDPLITE: | |
321 | return udplite_manip_pkt(skb, l3proto, iphdroff, hdroff, | |
322 | tuple, maniptype); | |
323 | case IPPROTO_SCTP: | |
324 | return sctp_manip_pkt(skb, l3proto, iphdroff, hdroff, | |
325 | tuple, maniptype); | |
326 | case IPPROTO_ICMP: | |
327 | return icmp_manip_pkt(skb, l3proto, iphdroff, hdroff, | |
328 | tuple, maniptype); | |
329 | case IPPROTO_ICMPV6: | |
330 | return icmpv6_manip_pkt(skb, l3proto, iphdroff, hdroff, | |
331 | tuple, maniptype); | |
332 | case IPPROTO_DCCP: | |
333 | return dccp_manip_pkt(skb, l3proto, iphdroff, hdroff, | |
334 | tuple, maniptype); | |
335 | case IPPROTO_GRE: | |
336 | return gre_manip_pkt(skb, l3proto, iphdroff, hdroff, | |
337 | tuple, maniptype); | |
338 | } | |
339 | ||
340 | /* If we don't know protocol -- no error, pass it unmodified. */ | |
341 | return true; | |
342 | } | |
343 | EXPORT_SYMBOL_GPL(nf_nat_l4proto_manip_pkt); |