~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/netfilter/nf_nat_proto.c

Version: ~ [ linux-5.9-rc6 ] ~ [ linux-5.8.10 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.66 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.146 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.198 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.236 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.236 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.85 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /* (C) 1999-2001 Paul `Rusty' Russell
  3  * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
  4  */
  5 
  6 #include <linux/types.h>
  7 #include <linux/export.h>
  8 #include <linux/init.h>
  9 #include <linux/udp.h>
 10 #include <linux/tcp.h>
 11 #include <linux/icmp.h>
 12 #include <linux/icmpv6.h>
 13 
 14 #include <linux/dccp.h>
 15 #include <linux/sctp.h>
 16 #include <net/sctp/checksum.h>
 17 
 18 #include <linux/netfilter.h>
 19 #include <net/netfilter/nf_nat.h>
 20 
 21 #include <linux/ipv6.h>
 22 #include <linux/netfilter_ipv6.h>
 23 #include <net/checksum.h>
 24 #include <net/ip6_checksum.h>
 25 #include <net/ip6_route.h>
 26 #include <net/xfrm.h>
 27 #include <net/ipv6.h>
 28 
 29 #include <net/netfilter/nf_conntrack_core.h>
 30 #include <net/netfilter/nf_conntrack.h>
 31 #include <linux/netfilter/nfnetlink_conntrack.h>
 32 
 33 static void nf_csum_update(struct sk_buff *skb,
 34                            unsigned int iphdroff, __sum16 *check,
 35                            const struct nf_conntrack_tuple *t,
 36                            enum nf_nat_manip_type maniptype);
 37 
 38 static void
 39 __udp_manip_pkt(struct sk_buff *skb,
 40                 unsigned int iphdroff, struct udphdr *hdr,
 41                 const struct nf_conntrack_tuple *tuple,
 42                 enum nf_nat_manip_type maniptype, bool do_csum)
 43 {
 44         __be16 *portptr, newport;
 45 
 46         if (maniptype == NF_NAT_MANIP_SRC) {
 47                 /* Get rid of src port */
 48                 newport = tuple->src.u.udp.port;
 49                 portptr = &hdr->source;
 50         } else {
 51                 /* Get rid of dst port */
 52                 newport = tuple->dst.u.udp.port;
 53                 portptr = &hdr->dest;
 54         }
 55         if (do_csum) {
 56                 nf_csum_update(skb, iphdroff, &hdr->check, tuple, maniptype);
 57                 inet_proto_csum_replace2(&hdr->check, skb, *portptr, newport,
 58                                          false);
 59                 if (!hdr->check)
 60                         hdr->check = CSUM_MANGLED_0;
 61         }
 62         *portptr = newport;
 63 }
 64 
 65 static bool udp_manip_pkt(struct sk_buff *skb,
 66                           unsigned int iphdroff, unsigned int hdroff,
 67                           const struct nf_conntrack_tuple *tuple,
 68                           enum nf_nat_manip_type maniptype)
 69 {
 70         struct udphdr *hdr;
 71         bool do_csum;
 72 
 73         if (skb_ensure_writable(skb, hdroff + sizeof(*hdr)))
 74                 return false;
 75 
 76         hdr = (struct udphdr *)(skb->data + hdroff);
 77         do_csum = hdr->check || skb->ip_summed == CHECKSUM_PARTIAL;
 78 
 79         __udp_manip_pkt(skb, iphdroff, hdr, tuple, maniptype, do_csum);
 80         return true;
 81 }
 82 
 83 static bool udplite_manip_pkt(struct sk_buff *skb,
 84                               unsigned int iphdroff, unsigned int hdroff,
 85                               const struct nf_conntrack_tuple *tuple,
 86                               enum nf_nat_manip_type maniptype)
 87 {
 88 #ifdef CONFIG_NF_CT_PROTO_UDPLITE
 89         struct udphdr *hdr;
 90 
 91         if (skb_ensure_writable(skb, hdroff + sizeof(*hdr)))
 92                 return false;
 93 
 94         hdr = (struct udphdr *)(skb->data + hdroff);
 95         __udp_manip_pkt(skb, iphdroff, hdr, tuple, maniptype, true);
 96 #endif
 97         return true;
 98 }
 99 
100 static bool
101 sctp_manip_pkt(struct sk_buff *skb,
102                unsigned int iphdroff, unsigned int hdroff,
103                const struct nf_conntrack_tuple *tuple,
104                enum nf_nat_manip_type maniptype)
105 {
106 #ifdef CONFIG_NF_CT_PROTO_SCTP
107         struct sctphdr *hdr;
108         int hdrsize = 8;
109 
110         /* This could be an inner header returned in imcp packet; in such
111          * cases we cannot update the checksum field since it is outside
112          * of the 8 bytes of transport layer headers we are guaranteed.
113          */
114         if (skb->len >= hdroff + sizeof(*hdr))
115                 hdrsize = sizeof(*hdr);
116 
117         if (skb_ensure_writable(skb, hdroff + hdrsize))
118                 return false;
119 
120         hdr = (struct sctphdr *)(skb->data + hdroff);
121 
122         if (maniptype == NF_NAT_MANIP_SRC) {
123                 /* Get rid of src port */
124                 hdr->source = tuple->src.u.sctp.port;
125         } else {
126                 /* Get rid of dst port */
127                 hdr->dest = tuple->dst.u.sctp.port;
128         }
129 
130         if (hdrsize < sizeof(*hdr))
131                 return true;
132 
133         if (skb->ip_summed != CHECKSUM_PARTIAL) {
134                 hdr->checksum = sctp_compute_cksum(skb, hdroff);
135                 skb->ip_summed = CHECKSUM_NONE;
136         }
137 
138 #endif
139         return true;
140 }
141 
142 static bool
143 tcp_manip_pkt(struct sk_buff *skb,
144               unsigned int iphdroff, unsigned int hdroff,
145               const struct nf_conntrack_tuple *tuple,
146               enum nf_nat_manip_type maniptype)
147 {
148         struct tcphdr *hdr;
149         __be16 *portptr, newport, oldport;
150         int hdrsize = 8; /* TCP connection tracking guarantees this much */
151 
152         /* this could be a inner header returned in icmp packet; in such
153            cases we cannot update the checksum field since it is outside of
154            the 8 bytes of transport layer headers we are guaranteed */
155         if (skb->len >= hdroff + sizeof(struct tcphdr))
156                 hdrsize = sizeof(struct tcphdr);
157 
158         if (skb_ensure_writable(skb, hdroff + hdrsize))
159                 return false;
160 
161         hdr = (struct tcphdr *)(skb->data + hdroff);
162 
163         if (maniptype == NF_NAT_MANIP_SRC) {
164                 /* Get rid of src port */
165                 newport = tuple->src.u.tcp.port;
166                 portptr = &hdr->source;
167         } else {
168                 /* Get rid of dst port */
169                 newport = tuple->dst.u.tcp.port;
170                 portptr = &hdr->dest;
171         }
172 
173         oldport = *portptr;
174         *portptr = newport;
175 
176         if (hdrsize < sizeof(*hdr))
177                 return true;
178 
179         nf_csum_update(skb, iphdroff, &hdr->check, tuple, maniptype);
180         inet_proto_csum_replace2(&hdr->check, skb, oldport, newport, false);
181         return true;
182 }
183 
184 static bool
185 dccp_manip_pkt(struct sk_buff *skb,
186                unsigned int iphdroff, unsigned int hdroff,
187                const struct nf_conntrack_tuple *tuple,
188                enum nf_nat_manip_type maniptype)
189 {
190 #ifdef CONFIG_NF_CT_PROTO_DCCP
191         struct dccp_hdr *hdr;
192         __be16 *portptr, oldport, newport;
193         int hdrsize = 8; /* DCCP connection tracking guarantees this much */
194 
195         if (skb->len >= hdroff + sizeof(struct dccp_hdr))
196                 hdrsize = sizeof(struct dccp_hdr);
197 
198         if (skb_ensure_writable(skb, hdroff + hdrsize))
199                 return false;
200 
201         hdr = (struct dccp_hdr *)(skb->data + hdroff);
202 
203         if (maniptype == NF_NAT_MANIP_SRC) {
204                 newport = tuple->src.u.dccp.port;
205                 portptr = &hdr->dccph_sport;
206         } else {
207                 newport = tuple->dst.u.dccp.port;
208                 portptr = &hdr->dccph_dport;
209         }
210 
211         oldport = *portptr;
212         *portptr = newport;
213 
214         if (hdrsize < sizeof(*hdr))
215                 return true;
216 
217         nf_csum_update(skb, iphdroff, &hdr->dccph_checksum, tuple, maniptype);
218         inet_proto_csum_replace2(&hdr->dccph_checksum, skb, oldport, newport,
219                                  false);
220 #endif
221         return true;
222 }
223 
224 static bool
225 icmp_manip_pkt(struct sk_buff *skb,
226                unsigned int iphdroff, unsigned int hdroff,
227                const struct nf_conntrack_tuple *tuple,
228                enum nf_nat_manip_type maniptype)
229 {
230         struct icmphdr *hdr;
231 
232         if (skb_ensure_writable(skb, hdroff + sizeof(*hdr)))
233                 return false;
234 
235         hdr = (struct icmphdr *)(skb->data + hdroff);
236         inet_proto_csum_replace2(&hdr->checksum, skb,
237                                  hdr->un.echo.id, tuple->src.u.icmp.id, false);
238         hdr->un.echo.id = tuple->src.u.icmp.id;
239         return true;
240 }
241 
242 static bool
243 icmpv6_manip_pkt(struct sk_buff *skb,
244                  unsigned int iphdroff, unsigned int hdroff,
245                  const struct nf_conntrack_tuple *tuple,
246                  enum nf_nat_manip_type maniptype)
247 {
248         struct icmp6hdr *hdr;
249 
250         if (skb_ensure_writable(skb, hdroff + sizeof(*hdr)))
251                 return false;
252 
253         hdr = (struct icmp6hdr *)(skb->data + hdroff);
254         nf_csum_update(skb, iphdroff, &hdr->icmp6_cksum, 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               unsigned int iphdroff, unsigned int hdroff,
269               const struct nf_conntrack_tuple *tuple,
270               enum nf_nat_manip_type maniptype)
271 {
272 #if IS_ENABLED(CONFIG_NF_CT_PROTO_GRE)
273         const struct gre_base_hdr *greh;
274         struct pptp_gre_header *pgreh;
275 
276         /* pgreh includes two optional 32bit fields which are not required
277          * to be there.  That's where the magic '8' comes from */
278         if (skb_ensure_writable(skb, hdroff + sizeof(*pgreh) - 8))
279                 return false;
280 
281         greh = (void *)skb->data + hdroff;
282         pgreh = (struct pptp_gre_header *)greh;
283 
284         /* we only have destination manip of a packet, since 'source key'
285          * is not present in the packet itself */
286         if (maniptype != NF_NAT_MANIP_DST)
287                 return true;
288 
289         switch (greh->flags & GRE_VERSION) {
290         case GRE_VERSION_0:
291                 /* We do not currently NAT any GREv0 packets.
292                  * Try to behave like "nf_nat_proto_unknown" */
293                 break;
294         case GRE_VERSION_1:
295                 pr_debug("call_id -> 0x%04x\n", ntohs(tuple->dst.u.gre.key));
296                 pgreh->call_id = tuple->dst.u.gre.key;
297                 break;
298         default:
299                 pr_debug("can't nat unknown GRE version\n");
300                 return false;
301         }
302 #endif
303         return true;
304 }
305 
306 static bool l4proto_manip_pkt(struct sk_buff *skb,
307                               unsigned int iphdroff, unsigned int hdroff,
308                               const struct nf_conntrack_tuple *tuple,
309                               enum nf_nat_manip_type maniptype)
310 {
311         switch (tuple->dst.protonum) {
312         case IPPROTO_TCP:
313                 return tcp_manip_pkt(skb, iphdroff, hdroff,
314                                      tuple, maniptype);
315         case IPPROTO_UDP:
316                 return udp_manip_pkt(skb, iphdroff, hdroff,
317                                      tuple, maniptype);
318         case IPPROTO_UDPLITE:
319                 return udplite_manip_pkt(skb, iphdroff, hdroff,
320                                          tuple, maniptype);
321         case IPPROTO_SCTP:
322                 return sctp_manip_pkt(skb, iphdroff, hdroff,
323                                       tuple, maniptype);
324         case IPPROTO_ICMP:
325                 return icmp_manip_pkt(skb, iphdroff, hdroff,
326                                       tuple, maniptype);
327         case IPPROTO_ICMPV6:
328                 return icmpv6_manip_pkt(skb, iphdroff, hdroff,
329                                         tuple, maniptype);
330         case IPPROTO_DCCP:
331                 return dccp_manip_pkt(skb, iphdroff, hdroff,
332                                       tuple, maniptype);
333         case IPPROTO_GRE:
334                 return gre_manip_pkt(skb, iphdroff, hdroff,
335                                      tuple, maniptype);
336         }
337 
338         /* If we don't know protocol -- no error, pass it unmodified. */
339         return true;
340 }
341 
342 static bool nf_nat_ipv4_manip_pkt(struct sk_buff *skb,
343                                   unsigned int iphdroff,
344                                   const struct nf_conntrack_tuple *target,
345                                   enum nf_nat_manip_type maniptype)
346 {
347         struct iphdr *iph;
348         unsigned int hdroff;
349 
350         if (skb_ensure_writable(skb, iphdroff + sizeof(*iph)))
351                 return false;
352 
353         iph = (void *)skb->data + iphdroff;
354         hdroff = iphdroff + iph->ihl * 4;
355 
356         if (!l4proto_manip_pkt(skb, iphdroff, hdroff, target, maniptype))
357                 return false;
358         iph = (void *)skb->data + iphdroff;
359 
360         if (maniptype == NF_NAT_MANIP_SRC) {
361                 csum_replace4(&iph->check, iph->saddr, target->src.u3.ip);
362                 iph->saddr = target->src.u3.ip;
363         } else {
364                 csum_replace4(&iph->check, iph->daddr, target->dst.u3.ip);
365                 iph->daddr = target->dst.u3.ip;
366         }
367         return true;
368 }
369 
370 static bool nf_nat_ipv6_manip_pkt(struct sk_buff *skb,
371                                   unsigned int iphdroff,
372                                   const struct nf_conntrack_tuple *target,
373                                   enum nf_nat_manip_type maniptype)
374 {
375 #if IS_ENABLED(CONFIG_IPV6)
376         struct ipv6hdr *ipv6h;
377         __be16 frag_off;
378         int hdroff;
379         u8 nexthdr;
380 
381         if (skb_ensure_writable(skb, iphdroff + sizeof(*ipv6h)))
382                 return false;
383 
384         ipv6h = (void *)skb->data + iphdroff;
385         nexthdr = ipv6h->nexthdr;
386         hdroff = ipv6_skip_exthdr(skb, iphdroff + sizeof(*ipv6h),
387                                   &nexthdr, &frag_off);
388         if (hdroff < 0)
389                 goto manip_addr;
390 
391         if ((frag_off & htons(~0x7)) == 0 &&
392             !l4proto_manip_pkt(skb, iphdroff, hdroff, target, maniptype))
393                 return false;
394 
395         /* must reload, offset might have changed */
396         ipv6h = (void *)skb->data + iphdroff;
397 
398 manip_addr:
399         if (maniptype == NF_NAT_MANIP_SRC)
400                 ipv6h->saddr = target->src.u3.in6;
401         else
402                 ipv6h->daddr = target->dst.u3.in6;
403 
404 #endif
405         return true;
406 }
407 
408 unsigned int nf_nat_manip_pkt(struct sk_buff *skb, struct nf_conn *ct,
409                               enum nf_nat_manip_type mtype,
410                               enum ip_conntrack_dir dir)
411 {
412         struct nf_conntrack_tuple target;
413 
414         /* We are aiming to look like inverse of other direction. */
415         nf_ct_invert_tuple(&target, &ct->tuplehash[!dir].tuple);
416 
417         switch (target.src.l3num) {
418         case NFPROTO_IPV6:
419                 if (nf_nat_ipv6_manip_pkt(skb, 0, &target, mtype))
420                         return NF_ACCEPT;
421                 break;
422         case NFPROTO_IPV4:
423                 if (nf_nat_ipv4_manip_pkt(skb, 0, &target, mtype))
424                         return NF_ACCEPT;
425                 break;
426         default:
427                 WARN_ON_ONCE(1);
428                 break;
429         }
430 
431         return NF_DROP;
432 }
433 
434 static void nf_nat_ipv4_csum_update(struct sk_buff *skb,
435                                     unsigned int iphdroff, __sum16 *check,
436                                     const struct nf_conntrack_tuple *t,
437                                     enum nf_nat_manip_type maniptype)
438 {
439         struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff);
440         __be32 oldip, newip;
441 
442         if (maniptype == NF_NAT_MANIP_SRC) {
443                 oldip = iph->saddr;
444                 newip = t->src.u3.ip;
445         } else {
446                 oldip = iph->daddr;
447                 newip = t->dst.u3.ip;
448         }
449         inet_proto_csum_replace4(check, skb, oldip, newip, true);
450 }
451 
452 static void nf_nat_ipv6_csum_update(struct sk_buff *skb,
453                                     unsigned int iphdroff, __sum16 *check,
454                                     const struct nf_conntrack_tuple *t,
455                                     enum nf_nat_manip_type maniptype)
456 {
457 #if IS_ENABLED(CONFIG_IPV6)
458         const struct ipv6hdr *ipv6h = (struct ipv6hdr *)(skb->data + iphdroff);
459         const struct in6_addr *oldip, *newip;
460 
461         if (maniptype == NF_NAT_MANIP_SRC) {
462                 oldip = &ipv6h->saddr;
463                 newip = &t->src.u3.in6;
464         } else {
465                 oldip = &ipv6h->daddr;
466                 newip = &t->dst.u3.in6;
467         }
468         inet_proto_csum_replace16(check, skb, oldip->s6_addr32,
469                                   newip->s6_addr32, true);
470 #endif
471 }
472 
473 static void nf_csum_update(struct sk_buff *skb,
474                            unsigned int iphdroff, __sum16 *check,
475                            const struct nf_conntrack_tuple *t,
476                            enum nf_nat_manip_type maniptype)
477 {
478         switch (t->src.l3num) {
479         case NFPROTO_IPV4:
480                 nf_nat_ipv4_csum_update(skb, iphdroff, check, t, maniptype);
481                 return;
482         case NFPROTO_IPV6:
483                 nf_nat_ipv6_csum_update(skb, iphdroff, check, t, maniptype);
484                 return;
485         }
486 }
487 
488 static void nf_nat_ipv4_csum_recalc(struct sk_buff *skb,
489                                     u8 proto, void *data, __sum16 *check,
490                                     int datalen, int oldlen)
491 {
492         if (skb->ip_summed != CHECKSUM_PARTIAL) {
493                 const struct iphdr *iph = ip_hdr(skb);
494 
495                 skb->ip_summed = CHECKSUM_PARTIAL;
496                 skb->csum_start = skb_headroom(skb) + skb_network_offset(skb) +
497                         ip_hdrlen(skb);
498                 skb->csum_offset = (void *)check - data;
499                 *check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, datalen,
500                                             proto, 0);
501         } else {
502                 inet_proto_csum_replace2(check, skb,
503                                          htons(oldlen), htons(datalen), true);
504         }
505 }
506 
507 #if IS_ENABLED(CONFIG_IPV6)
508 static void nf_nat_ipv6_csum_recalc(struct sk_buff *skb,
509                                     u8 proto, void *data, __sum16 *check,
510                                     int datalen, int oldlen)
511 {
512         if (skb->ip_summed != CHECKSUM_PARTIAL) {
513                 const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
514 
515                 skb->ip_summed = CHECKSUM_PARTIAL;
516                 skb->csum_start = skb_headroom(skb) + skb_network_offset(skb) +
517                         (data - (void *)skb->data);
518                 skb->csum_offset = (void *)check - data;
519                 *check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
520                                           datalen, proto, 0);
521         } else {
522                 inet_proto_csum_replace2(check, skb,
523                                          htons(oldlen), htons(datalen), true);
524         }
525 }
526 #endif
527 
528 void nf_nat_csum_recalc(struct sk_buff *skb,
529                         u8 nfproto, u8 proto, void *data, __sum16 *check,
530                         int datalen, int oldlen)
531 {
532         switch (nfproto) {
533         case NFPROTO_IPV4:
534                 nf_nat_ipv4_csum_recalc(skb, proto, data, check,
535                                         datalen, oldlen);
536                 return;
537 #if IS_ENABLED(CONFIG_IPV6)
538         case NFPROTO_IPV6:
539                 nf_nat_ipv6_csum_recalc(skb, proto, data, check,
540                                         datalen, oldlen);
541                 return;
542 #endif
543         }
544 
545         WARN_ON_ONCE(1);
546 }
547 
548 int nf_nat_icmp_reply_translation(struct sk_buff *skb,
549                                   struct nf_conn *ct,
550                                   enum ip_conntrack_info ctinfo,
551                                   unsigned int hooknum)
552 {
553         struct {
554                 struct icmphdr  icmp;
555                 struct iphdr    ip;
556         } *inside;
557         enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
558         enum nf_nat_manip_type manip = HOOK2MANIP(hooknum);
559         unsigned int hdrlen = ip_hdrlen(skb);
560         struct nf_conntrack_tuple target;
561         unsigned long statusbit;
562 
563         WARN_ON(ctinfo != IP_CT_RELATED && ctinfo != IP_CT_RELATED_REPLY);
564 
565         if (skb_ensure_writable(skb, hdrlen + sizeof(*inside)))
566                 return 0;
567         if (nf_ip_checksum(skb, hooknum, hdrlen, IPPROTO_ICMP))
568                 return 0;
569 
570         inside = (void *)skb->data + hdrlen;
571         if (inside->icmp.type == ICMP_REDIRECT) {
572                 if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK)
573                         return 0;
574                 if (ct->status & IPS_NAT_MASK)
575                         return 0;
576         }
577 
578         if (manip == NF_NAT_MANIP_SRC)
579                 statusbit = IPS_SRC_NAT;
580         else
581                 statusbit = IPS_DST_NAT;
582 
583         /* Invert if this is reply direction */
584         if (dir == IP_CT_DIR_REPLY)
585                 statusbit ^= IPS_NAT_MASK;
586 
587         if (!(ct->status & statusbit))
588                 return 1;
589 
590         if (!nf_nat_ipv4_manip_pkt(skb, hdrlen + sizeof(inside->icmp),
591                                    &ct->tuplehash[!dir].tuple, !manip))
592                 return 0;
593 
594         if (skb->ip_summed != CHECKSUM_PARTIAL) {
595                 /* Reloading "inside" here since manip_pkt may reallocate */
596                 inside = (void *)skb->data + hdrlen;
597                 inside->icmp.checksum = 0;
598                 inside->icmp.checksum =
599                         csum_fold(skb_checksum(skb, hdrlen,
600                                                skb->len - hdrlen, 0));
601         }
602 
603         /* Change outer to look like the reply to an incoming packet */
604         nf_ct_invert_tuple(&target, &ct->tuplehash[!dir].tuple);
605         target.dst.protonum = IPPROTO_ICMP;
606         if (!nf_nat_ipv4_manip_pkt(skb, 0, &target, manip))
607                 return 0;
608 
609         return 1;
610 }
611 EXPORT_SYMBOL_GPL(nf_nat_icmp_reply_translation);
612 
613 static unsigned int
614 nf_nat_ipv4_fn(void *priv, struct sk_buff *skb,
615                const struct nf_hook_state *state)
616 {
617         struct nf_conn *ct;
618         enum ip_conntrack_info ctinfo;
619 
620         ct = nf_ct_get(skb, &ctinfo);
621         if (!ct)
622                 return NF_ACCEPT;
623 
624         if (ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED_REPLY) {
625                 if (ip_hdr(skb)->protocol == IPPROTO_ICMP) {
626                         if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo,
627                                                            state->hook))
628                                 return NF_DROP;
629                         else
630                                 return NF_ACCEPT;
631                 }
632         }
633 
634         return nf_nat_inet_fn(priv, skb, state);
635 }
636 
637 static unsigned int
638 nf_nat_ipv4_in(void *priv, struct sk_buff *skb,
639                const struct nf_hook_state *state)
640 {
641         unsigned int ret;
642         __be32 daddr = ip_hdr(skb)->daddr;
643 
644         ret = nf_nat_ipv4_fn(priv, skb, state);
645         if (ret == NF_ACCEPT && daddr != ip_hdr(skb)->daddr)
646                 skb_dst_drop(skb);
647 
648         return ret;
649 }
650 
651 static unsigned int
652 nf_nat_ipv4_out(void *priv, struct sk_buff *skb,
653                 const struct nf_hook_state *state)
654 {
655 #ifdef CONFIG_XFRM
656         const struct nf_conn *ct;
657         enum ip_conntrack_info ctinfo;
658         int err;
659 #endif
660         unsigned int ret;
661 
662         ret = nf_nat_ipv4_fn(priv, skb, state);
663 #ifdef CONFIG_XFRM
664         if (ret != NF_ACCEPT)
665                 return ret;
666 
667         if (IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED)
668                 return ret;
669 
670         ct = nf_ct_get(skb, &ctinfo);
671         if (ct) {
672                 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
673 
674                 if (ct->tuplehash[dir].tuple.src.u3.ip !=
675                      ct->tuplehash[!dir].tuple.dst.u3.ip ||
676                     (ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMP &&
677                      ct->tuplehash[dir].tuple.src.u.all !=
678                      ct->tuplehash[!dir].tuple.dst.u.all)) {
679                         err = nf_xfrm_me_harder(state->net, skb, AF_INET);
680                         if (err < 0)
681                                 ret = NF_DROP_ERR(err);
682                 }
683         }
684 #endif
685         return ret;
686 }
687 
688 static unsigned int
689 nf_nat_ipv4_local_fn(void *priv, struct sk_buff *skb,
690                      const struct nf_hook_state *state)
691 {
692         const struct nf_conn *ct;
693         enum ip_conntrack_info ctinfo;
694         unsigned int ret;
695         int err;
696 
697         ret = nf_nat_ipv4_fn(priv, skb, state);
698         if (ret != NF_ACCEPT)
699                 return ret;
700 
701         ct = nf_ct_get(skb, &ctinfo);
702         if (ct) {
703                 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
704 
705                 if (ct->tuplehash[dir].tuple.dst.u3.ip !=
706                     ct->tuplehash[!dir].tuple.src.u3.ip) {
707                         err = ip_route_me_harder(state->net, skb, RTN_UNSPEC);
708                         if (err < 0)
709                                 ret = NF_DROP_ERR(err);
710                 }
711 #ifdef CONFIG_XFRM
712                 else if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
713                          ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMP &&
714                          ct->tuplehash[dir].tuple.dst.u.all !=
715                          ct->tuplehash[!dir].tuple.src.u.all) {
716                         err = nf_xfrm_me_harder(state->net, skb, AF_INET);
717                         if (err < 0)
718                                 ret = NF_DROP_ERR(err);
719                 }
720 #endif
721         }
722         return ret;
723 }
724 
725 const struct nf_hook_ops nf_nat_ipv4_ops[] = {
726         /* Before packet filtering, change destination */
727         {
728                 .hook           = nf_nat_ipv4_in,
729                 .pf             = NFPROTO_IPV4,
730                 .hooknum        = NF_INET_PRE_ROUTING,
731                 .priority       = NF_IP_PRI_NAT_DST,
732         },
733         /* After packet filtering, change source */
734         {
735                 .hook           = nf_nat_ipv4_out,
736                 .pf             = NFPROTO_IPV4,
737                 .hooknum        = NF_INET_POST_ROUTING,
738                 .priority       = NF_IP_PRI_NAT_SRC,
739         },
740         /* Before packet filtering, change destination */
741         {
742                 .hook           = nf_nat_ipv4_local_fn,
743                 .pf             = NFPROTO_IPV4,
744                 .hooknum        = NF_INET_LOCAL_OUT,
745                 .priority       = NF_IP_PRI_NAT_DST,
746         },
747         /* After packet filtering, change source */
748         {
749                 .hook           = nf_nat_ipv4_fn,
750                 .pf             = NFPROTO_IPV4,
751                 .hooknum        = NF_INET_LOCAL_IN,
752                 .priority       = NF_IP_PRI_NAT_SRC,
753         },
754 };
755 
756 int nf_nat_ipv4_register_fn(struct net *net, const struct nf_hook_ops *ops)
757 {
758         return nf_nat_register_fn(net, ops->pf, ops, nf_nat_ipv4_ops,
759                                   ARRAY_SIZE(nf_nat_ipv4_ops));
760 }
761 EXPORT_SYMBOL_GPL(nf_nat_ipv4_register_fn);
762 
763 void nf_nat_ipv4_unregister_fn(struct net *net, const struct nf_hook_ops *ops)
764 {
765         nf_nat_unregister_fn(net, ops->pf, ops, ARRAY_SIZE(nf_nat_ipv4_ops));
766 }
767 EXPORT_SYMBOL_GPL(nf_nat_ipv4_unregister_fn);
768 
769 #if IS_ENABLED(CONFIG_IPV6)
770 int nf_nat_icmpv6_reply_translation(struct sk_buff *skb,
771                                     struct nf_conn *ct,
772                                     enum ip_conntrack_info ctinfo,
773                                     unsigned int hooknum,
774                                     unsigned int hdrlen)
775 {
776         struct {
777                 struct icmp6hdr icmp6;
778                 struct ipv6hdr  ip6;
779         } *inside;
780         enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
781         enum nf_nat_manip_type manip = HOOK2MANIP(hooknum);
782         struct nf_conntrack_tuple target;
783         unsigned long statusbit;
784 
785         WARN_ON(ctinfo != IP_CT_RELATED && ctinfo != IP_CT_RELATED_REPLY);
786 
787         if (skb_ensure_writable(skb, hdrlen + sizeof(*inside)))
788                 return 0;
789         if (nf_ip6_checksum(skb, hooknum, hdrlen, IPPROTO_ICMPV6))
790                 return 0;
791 
792         inside = (void *)skb->data + hdrlen;
793         if (inside->icmp6.icmp6_type == NDISC_REDIRECT) {
794                 if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK)
795                         return 0;
796                 if (ct->status & IPS_NAT_MASK)
797                         return 0;
798         }
799 
800         if (manip == NF_NAT_MANIP_SRC)
801                 statusbit = IPS_SRC_NAT;
802         else
803                 statusbit = IPS_DST_NAT;
804 
805         /* Invert if this is reply direction */
806         if (dir == IP_CT_DIR_REPLY)
807                 statusbit ^= IPS_NAT_MASK;
808 
809         if (!(ct->status & statusbit))
810                 return 1;
811 
812         if (!nf_nat_ipv6_manip_pkt(skb, hdrlen + sizeof(inside->icmp6),
813                                    &ct->tuplehash[!dir].tuple, !manip))
814                 return 0;
815 
816         if (skb->ip_summed != CHECKSUM_PARTIAL) {
817                 struct ipv6hdr *ipv6h = ipv6_hdr(skb);
818 
819                 inside = (void *)skb->data + hdrlen;
820                 inside->icmp6.icmp6_cksum = 0;
821                 inside->icmp6.icmp6_cksum =
822                         csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
823                                         skb->len - hdrlen, IPPROTO_ICMPV6,
824                                         skb_checksum(skb, hdrlen,
825                                                      skb->len - hdrlen, 0));
826         }
827 
828         nf_ct_invert_tuple(&target, &ct->tuplehash[!dir].tuple);
829         target.dst.protonum = IPPROTO_ICMPV6;
830         if (!nf_nat_ipv6_manip_pkt(skb, 0, &target, manip))
831                 return 0;
832 
833         return 1;
834 }
835 EXPORT_SYMBOL_GPL(nf_nat_icmpv6_reply_translation);
836 
837 static unsigned int
838 nf_nat_ipv6_fn(void *priv, struct sk_buff *skb,
839                const struct nf_hook_state *state)
840 {
841         struct nf_conn *ct;
842         enum ip_conntrack_info ctinfo;
843         __be16 frag_off;
844         int hdrlen;
845         u8 nexthdr;
846 
847         ct = nf_ct_get(skb, &ctinfo);
848         /* Can't track?  It's not due to stress, or conntrack would
849          * have dropped it.  Hence it's the user's responsibilty to
850          * packet filter it out, or implement conntrack/NAT for that
851          * protocol. 8) --RR
852          */
853         if (!ct)
854                 return NF_ACCEPT;
855 
856         if (ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED_REPLY) {
857                 nexthdr = ipv6_hdr(skb)->nexthdr;
858                 hdrlen = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr),
859                                           &nexthdr, &frag_off);
860 
861                 if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) {
862                         if (!nf_nat_icmpv6_reply_translation(skb, ct, ctinfo,
863                                                              state->hook,
864                                                              hdrlen))
865                                 return NF_DROP;
866                         else
867                                 return NF_ACCEPT;
868                 }
869         }
870 
871         return nf_nat_inet_fn(priv, skb, state);
872 }
873 
874 static unsigned int
875 nf_nat_ipv6_in(void *priv, struct sk_buff *skb,
876                const struct nf_hook_state *state)
877 {
878         unsigned int ret;
879         struct in6_addr daddr = ipv6_hdr(skb)->daddr;
880 
881         ret = nf_nat_ipv6_fn(priv, skb, state);
882         if (ret != NF_DROP && ret != NF_STOLEN &&
883             ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr))
884                 skb_dst_drop(skb);
885 
886         return ret;
887 }
888 
889 static unsigned int
890 nf_nat_ipv6_out(void *priv, struct sk_buff *skb,
891                 const struct nf_hook_state *state)
892 {
893 #ifdef CONFIG_XFRM
894         const struct nf_conn *ct;
895         enum ip_conntrack_info ctinfo;
896         int err;
897 #endif
898         unsigned int ret;
899 
900         ret = nf_nat_ipv6_fn(priv, skb, state);
901 #ifdef CONFIG_XFRM
902         if (ret != NF_ACCEPT)
903                 return ret;
904 
905         if (IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED)
906                 return ret;
907         ct = nf_ct_get(skb, &ctinfo);
908         if (ct) {
909                 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
910 
911                 if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3,
912                                       &ct->tuplehash[!dir].tuple.dst.u3) ||
913                     (ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
914                      ct->tuplehash[dir].tuple.src.u.all !=
915                      ct->tuplehash[!dir].tuple.dst.u.all)) {
916                         err = nf_xfrm_me_harder(state->net, skb, AF_INET6);
917                         if (err < 0)
918                                 ret = NF_DROP_ERR(err);
919                 }
920         }
921 #endif
922 
923         return ret;
924 }
925 
926 static unsigned int
927 nf_nat_ipv6_local_fn(void *priv, struct sk_buff *skb,
928                      const struct nf_hook_state *state)
929 {
930         const struct nf_conn *ct;
931         enum ip_conntrack_info ctinfo;
932         unsigned int ret;
933         int err;
934 
935         ret = nf_nat_ipv6_fn(priv, skb, state);
936         if (ret != NF_ACCEPT)
937                 return ret;
938 
939         ct = nf_ct_get(skb, &ctinfo);
940         if (ct) {
941                 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
942 
943                 if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3,
944                                       &ct->tuplehash[!dir].tuple.src.u3)) {
945                         err = nf_ip6_route_me_harder(state->net, skb);
946                         if (err < 0)
947                                 ret = NF_DROP_ERR(err);
948                 }
949 #ifdef CONFIG_XFRM
950                 else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
951                          ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
952                          ct->tuplehash[dir].tuple.dst.u.all !=
953                          ct->tuplehash[!dir].tuple.src.u.all) {
954                         err = nf_xfrm_me_harder(state->net, skb, AF_INET6);
955                         if (err < 0)
956                                 ret = NF_DROP_ERR(err);
957                 }
958 #endif
959         }
960 
961         return ret;
962 }
963 
964 const struct nf_hook_ops nf_nat_ipv6_ops[] = {
965         /* Before packet filtering, change destination */
966         {
967                 .hook           = nf_nat_ipv6_in,
968                 .pf             = NFPROTO_IPV6,
969                 .hooknum        = NF_INET_PRE_ROUTING,
970                 .priority       = NF_IP6_PRI_NAT_DST,
971         },
972         /* After packet filtering, change source */
973         {
974                 .hook           = nf_nat_ipv6_out,
975                 .pf             = NFPROTO_IPV6,
976                 .hooknum        = NF_INET_POST_ROUTING,
977                 .priority       = NF_IP6_PRI_NAT_SRC,
978         },
979         /* Before packet filtering, change destination */
980         {
981                 .hook           = nf_nat_ipv6_local_fn,
982                 .pf             = NFPROTO_IPV6,
983                 .hooknum        = NF_INET_LOCAL_OUT,
984                 .priority       = NF_IP6_PRI_NAT_DST,
985         },
986         /* After packet filtering, change source */
987         {
988                 .hook           = nf_nat_ipv6_fn,
989                 .pf             = NFPROTO_IPV6,
990                 .hooknum        = NF_INET_LOCAL_IN,
991                 .priority       = NF_IP6_PRI_NAT_SRC,
992         },
993 };
994 
995 int nf_nat_ipv6_register_fn(struct net *net, const struct nf_hook_ops *ops)
996 {
997         return nf_nat_register_fn(net, ops->pf, ops, nf_nat_ipv6_ops,
998                                   ARRAY_SIZE(nf_nat_ipv6_ops));
999 }
1000 EXPORT_SYMBOL_GPL(nf_nat_ipv6_register_fn);
1001 
1002 void nf_nat_ipv6_unregister_fn(struct net *net, const struct nf_hook_ops *ops)
1003 {
1004         nf_nat_unregister_fn(net, ops->pf, ops, ARRAY_SIZE(nf_nat_ipv6_ops));
1005 }
1006 EXPORT_SYMBOL_GPL(nf_nat_ipv6_unregister_fn);
1007 #endif /* CONFIG_IPV6 */
1008 
1009 #if defined(CONFIG_NF_TABLES_INET) && IS_ENABLED(CONFIG_NFT_NAT)
1010 int nf_nat_inet_register_fn(struct net *net, const struct nf_hook_ops *ops)
1011 {
1012         int ret;
1013 
1014         if (WARN_ON_ONCE(ops->pf != NFPROTO_INET))
1015                 return -EINVAL;
1016 
1017         ret = nf_nat_register_fn(net, NFPROTO_IPV6, ops, nf_nat_ipv6_ops,
1018                                  ARRAY_SIZE(nf_nat_ipv6_ops));
1019         if (ret)
1020                 return ret;
1021 
1022         ret = nf_nat_register_fn(net, NFPROTO_IPV4, ops, nf_nat_ipv4_ops,
1023                                  ARRAY_SIZE(nf_nat_ipv4_ops));
1024         if (ret)
1025                 nf_nat_ipv6_unregister_fn(net, ops);
1026 
1027         return ret;
1028 }
1029 EXPORT_SYMBOL_GPL(nf_nat_inet_register_fn);
1030 
1031 void nf_nat_inet_unregister_fn(struct net *net, const struct nf_hook_ops *ops)
1032 {
1033         nf_nat_unregister_fn(net, NFPROTO_IPV4, ops, ARRAY_SIZE(nf_nat_ipv4_ops));
1034         nf_nat_unregister_fn(net, NFPROTO_IPV6, ops, ARRAY_SIZE(nf_nat_ipv6_ops));
1035 }
1036 EXPORT_SYMBOL_GPL(nf_nat_inet_unregister_fn);
1037 #endif /* NFT INET NAT */
1038 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp