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

TOMOYO Linux Cross Reference
Linux/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c

Version: ~ [ linux-5.5-rc7 ] ~ [ linux-5.4.13 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.97 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.166 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.210 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.210 ] ~ [ 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.81 ] ~ [ 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-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ 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 /*
  2  * (C) 1999-2001 Paul `Rusty' Russell
  3  * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
  4  * (C) 2011 Patrick McHardy <kaber@trash.net>
  5  *
  6  * This program is free software; you can redistribute it and/or modify
  7  * it under the terms of the GNU General Public License version 2 as
  8  * published by the Free Software Foundation.
  9  */
 10 
 11 #include <linux/types.h>
 12 #include <linux/module.h>
 13 #include <linux/skbuff.h>
 14 #include <linux/ip.h>
 15 #include <linux/icmp.h>
 16 #include <linux/netfilter.h>
 17 #include <linux/netfilter_ipv4.h>
 18 #include <net/secure_seq.h>
 19 #include <net/checksum.h>
 20 #include <net/route.h>
 21 #include <net/ip.h>
 22 
 23 #include <net/netfilter/nf_conntrack_core.h>
 24 #include <net/netfilter/nf_conntrack.h>
 25 #include <net/netfilter/nf_nat_core.h>
 26 #include <net/netfilter/nf_nat_l3proto.h>
 27 #include <net/netfilter/nf_nat_l4proto.h>
 28 
 29 static const struct nf_nat_l3proto nf_nat_l3proto_ipv4;
 30 
 31 #ifdef CONFIG_XFRM
 32 static void nf_nat_ipv4_decode_session(struct sk_buff *skb,
 33                                        const struct nf_conn *ct,
 34                                        enum ip_conntrack_dir dir,
 35                                        unsigned long statusbit,
 36                                        struct flowi *fl)
 37 {
 38         const struct nf_conntrack_tuple *t = &ct->tuplehash[dir].tuple;
 39         struct flowi4 *fl4 = &fl->u.ip4;
 40 
 41         if (ct->status & statusbit) {
 42                 fl4->daddr = t->dst.u3.ip;
 43                 if (t->dst.protonum == IPPROTO_TCP ||
 44                     t->dst.protonum == IPPROTO_UDP ||
 45                     t->dst.protonum == IPPROTO_UDPLITE ||
 46                     t->dst.protonum == IPPROTO_DCCP ||
 47                     t->dst.protonum == IPPROTO_SCTP)
 48                         fl4->fl4_dport = t->dst.u.all;
 49         }
 50 
 51         statusbit ^= IPS_NAT_MASK;
 52 
 53         if (ct->status & statusbit) {
 54                 fl4->saddr = t->src.u3.ip;
 55                 if (t->dst.protonum == IPPROTO_TCP ||
 56                     t->dst.protonum == IPPROTO_UDP ||
 57                     t->dst.protonum == IPPROTO_UDPLITE ||
 58                     t->dst.protonum == IPPROTO_DCCP ||
 59                     t->dst.protonum == IPPROTO_SCTP)
 60                         fl4->fl4_sport = t->src.u.all;
 61         }
 62 }
 63 #endif /* CONFIG_XFRM */
 64 
 65 static bool nf_nat_ipv4_manip_pkt(struct sk_buff *skb,
 66                                   unsigned int iphdroff,
 67                                   const struct nf_conntrack_tuple *target,
 68                                   enum nf_nat_manip_type maniptype)
 69 {
 70         struct iphdr *iph;
 71         unsigned int hdroff;
 72 
 73         if (!skb_make_writable(skb, iphdroff + sizeof(*iph)))
 74                 return false;
 75 
 76         iph = (void *)skb->data + iphdroff;
 77         hdroff = iphdroff + iph->ihl * 4;
 78 
 79         if (!nf_nat_l4proto_manip_pkt(skb, &nf_nat_l3proto_ipv4, iphdroff,
 80                                       hdroff, target, maniptype))
 81                 return false;
 82         iph = (void *)skb->data + iphdroff;
 83 
 84         if (maniptype == NF_NAT_MANIP_SRC) {
 85                 csum_replace4(&iph->check, iph->saddr, target->src.u3.ip);
 86                 iph->saddr = target->src.u3.ip;
 87         } else {
 88                 csum_replace4(&iph->check, iph->daddr, target->dst.u3.ip);
 89                 iph->daddr = target->dst.u3.ip;
 90         }
 91         return true;
 92 }
 93 
 94 static void nf_nat_ipv4_csum_update(struct sk_buff *skb,
 95                                     unsigned int iphdroff, __sum16 *check,
 96                                     const struct nf_conntrack_tuple *t,
 97                                     enum nf_nat_manip_type maniptype)
 98 {
 99         struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff);
100         __be32 oldip, newip;
101 
102         if (maniptype == NF_NAT_MANIP_SRC) {
103                 oldip = iph->saddr;
104                 newip = t->src.u3.ip;
105         } else {
106                 oldip = iph->daddr;
107                 newip = t->dst.u3.ip;
108         }
109         inet_proto_csum_replace4(check, skb, oldip, newip, true);
110 }
111 
112 static void nf_nat_ipv4_csum_recalc(struct sk_buff *skb,
113                                     u8 proto, void *data, __sum16 *check,
114                                     int datalen, int oldlen)
115 {
116         if (skb->ip_summed != CHECKSUM_PARTIAL) {
117                 const struct iphdr *iph = ip_hdr(skb);
118 
119                 skb->ip_summed = CHECKSUM_PARTIAL;
120                 skb->csum_start = skb_headroom(skb) + skb_network_offset(skb) +
121                         ip_hdrlen(skb);
122                 skb->csum_offset = (void *)check - data;
123                 *check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, datalen,
124                                             proto, 0);
125         } else
126                 inet_proto_csum_replace2(check, skb,
127                                          htons(oldlen), htons(datalen), true);
128 }
129 
130 #if IS_ENABLED(CONFIG_NF_CT_NETLINK)
131 static int nf_nat_ipv4_nlattr_to_range(struct nlattr *tb[],
132                                        struct nf_nat_range2 *range)
133 {
134         if (tb[CTA_NAT_V4_MINIP]) {
135                 range->min_addr.ip = nla_get_be32(tb[CTA_NAT_V4_MINIP]);
136                 range->flags |= NF_NAT_RANGE_MAP_IPS;
137         }
138 
139         if (tb[CTA_NAT_V4_MAXIP])
140                 range->max_addr.ip = nla_get_be32(tb[CTA_NAT_V4_MAXIP]);
141         else
142                 range->max_addr.ip = range->min_addr.ip;
143 
144         return 0;
145 }
146 #endif
147 
148 static const struct nf_nat_l3proto nf_nat_l3proto_ipv4 = {
149         .l3proto                = NFPROTO_IPV4,
150         .manip_pkt              = nf_nat_ipv4_manip_pkt,
151         .csum_update            = nf_nat_ipv4_csum_update,
152         .csum_recalc            = nf_nat_ipv4_csum_recalc,
153 #if IS_ENABLED(CONFIG_NF_CT_NETLINK)
154         .nlattr_to_range        = nf_nat_ipv4_nlattr_to_range,
155 #endif
156 #ifdef CONFIG_XFRM
157         .decode_session         = nf_nat_ipv4_decode_session,
158 #endif
159 };
160 
161 int nf_nat_icmp_reply_translation(struct sk_buff *skb,
162                                   struct nf_conn *ct,
163                                   enum ip_conntrack_info ctinfo,
164                                   unsigned int hooknum)
165 {
166         struct {
167                 struct icmphdr  icmp;
168                 struct iphdr    ip;
169         } *inside;
170         enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
171         enum nf_nat_manip_type manip = HOOK2MANIP(hooknum);
172         unsigned int hdrlen = ip_hdrlen(skb);
173         struct nf_conntrack_tuple target;
174         unsigned long statusbit;
175 
176         WARN_ON(ctinfo != IP_CT_RELATED && ctinfo != IP_CT_RELATED_REPLY);
177 
178         if (!skb_make_writable(skb, hdrlen + sizeof(*inside)))
179                 return 0;
180         if (nf_ip_checksum(skb, hooknum, hdrlen, 0))
181                 return 0;
182 
183         inside = (void *)skb->data + hdrlen;
184         if (inside->icmp.type == ICMP_REDIRECT) {
185                 if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK)
186                         return 0;
187                 if (ct->status & IPS_NAT_MASK)
188                         return 0;
189         }
190 
191         if (manip == NF_NAT_MANIP_SRC)
192                 statusbit = IPS_SRC_NAT;
193         else
194                 statusbit = IPS_DST_NAT;
195 
196         /* Invert if this is reply direction */
197         if (dir == IP_CT_DIR_REPLY)
198                 statusbit ^= IPS_NAT_MASK;
199 
200         if (!(ct->status & statusbit))
201                 return 1;
202 
203         if (!nf_nat_ipv4_manip_pkt(skb, hdrlen + sizeof(inside->icmp),
204                                    &ct->tuplehash[!dir].tuple, !manip))
205                 return 0;
206 
207         if (skb->ip_summed != CHECKSUM_PARTIAL) {
208                 /* Reloading "inside" here since manip_pkt may reallocate */
209                 inside = (void *)skb->data + hdrlen;
210                 inside->icmp.checksum = 0;
211                 inside->icmp.checksum =
212                         csum_fold(skb_checksum(skb, hdrlen,
213                                                skb->len - hdrlen, 0));
214         }
215 
216         /* Change outer to look like the reply to an incoming packet */
217         nf_ct_invert_tuplepr(&target, &ct->tuplehash[!dir].tuple);
218         target.dst.protonum = IPPROTO_ICMP;
219         if (!nf_nat_ipv4_manip_pkt(skb, 0, &target, manip))
220                 return 0;
221 
222         return 1;
223 }
224 EXPORT_SYMBOL_GPL(nf_nat_icmp_reply_translation);
225 
226 static unsigned int
227 nf_nat_ipv4_fn(void *priv, struct sk_buff *skb,
228                const struct nf_hook_state *state)
229 {
230         struct nf_conn *ct;
231         enum ip_conntrack_info ctinfo;
232 
233         ct = nf_ct_get(skb, &ctinfo);
234         if (!ct)
235                 return NF_ACCEPT;
236 
237         if (ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED_REPLY) {
238                 if (ip_hdr(skb)->protocol == IPPROTO_ICMP) {
239                         if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo,
240                                                            state->hook))
241                                 return NF_DROP;
242                         else
243                                 return NF_ACCEPT;
244                 }
245         }
246 
247         return nf_nat_inet_fn(priv, skb, state);
248 }
249 
250 static unsigned int
251 nf_nat_ipv4_in(void *priv, struct sk_buff *skb,
252                const struct nf_hook_state *state)
253 {
254         unsigned int ret;
255         __be32 daddr = ip_hdr(skb)->daddr;
256 
257         ret = nf_nat_ipv4_fn(priv, skb, state);
258         if (ret != NF_DROP && ret != NF_STOLEN &&
259             daddr != ip_hdr(skb)->daddr)
260                 skb_dst_drop(skb);
261 
262         return ret;
263 }
264 
265 static unsigned int
266 nf_nat_ipv4_out(void *priv, struct sk_buff *skb,
267                 const struct nf_hook_state *state)
268 {
269 #ifdef CONFIG_XFRM
270         const struct nf_conn *ct;
271         enum ip_conntrack_info ctinfo;
272         int err;
273 #endif
274         unsigned int ret;
275 
276         ret = nf_nat_ipv4_fn(priv, skb, state);
277 #ifdef CONFIG_XFRM
278         if (ret != NF_DROP && ret != NF_STOLEN &&
279             !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
280             (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
281                 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
282 
283                 if ((ct->tuplehash[dir].tuple.src.u3.ip !=
284                      ct->tuplehash[!dir].tuple.dst.u3.ip) ||
285                     (ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMP &&
286                      ct->tuplehash[dir].tuple.src.u.all !=
287                      ct->tuplehash[!dir].tuple.dst.u.all)) {
288                         err = nf_xfrm_me_harder(state->net, skb, AF_INET);
289                         if (err < 0)
290                                 ret = NF_DROP_ERR(err);
291                 }
292         }
293 #endif
294         return ret;
295 }
296 
297 static unsigned int
298 nf_nat_ipv4_local_fn(void *priv, struct sk_buff *skb,
299                      const struct nf_hook_state *state)
300 {
301         const struct nf_conn *ct;
302         enum ip_conntrack_info ctinfo;
303         unsigned int ret;
304         int err;
305 
306         ret = nf_nat_ipv4_fn(priv, skb, state);
307         if (ret != NF_DROP && ret != NF_STOLEN &&
308             (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
309                 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
310 
311                 if (ct->tuplehash[dir].tuple.dst.u3.ip !=
312                     ct->tuplehash[!dir].tuple.src.u3.ip) {
313                         err = ip_route_me_harder(state->net, skb, RTN_UNSPEC);
314                         if (err < 0)
315                                 ret = NF_DROP_ERR(err);
316                 }
317 #ifdef CONFIG_XFRM
318                 else if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
319                          ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMP &&
320                          ct->tuplehash[dir].tuple.dst.u.all !=
321                          ct->tuplehash[!dir].tuple.src.u.all) {
322                         err = nf_xfrm_me_harder(state->net, skb, AF_INET);
323                         if (err < 0)
324                                 ret = NF_DROP_ERR(err);
325                 }
326 #endif
327         }
328         return ret;
329 }
330 
331 static const struct nf_hook_ops nf_nat_ipv4_ops[] = {
332         /* Before packet filtering, change destination */
333         {
334                 .hook           = nf_nat_ipv4_in,
335                 .pf             = NFPROTO_IPV4,
336                 .hooknum        = NF_INET_PRE_ROUTING,
337                 .priority       = NF_IP_PRI_NAT_DST,
338         },
339         /* After packet filtering, change source */
340         {
341                 .hook           = nf_nat_ipv4_out,
342                 .pf             = NFPROTO_IPV4,
343                 .hooknum        = NF_INET_POST_ROUTING,
344                 .priority       = NF_IP_PRI_NAT_SRC,
345         },
346         /* Before packet filtering, change destination */
347         {
348                 .hook           = nf_nat_ipv4_local_fn,
349                 .pf             = NFPROTO_IPV4,
350                 .hooknum        = NF_INET_LOCAL_OUT,
351                 .priority       = NF_IP_PRI_NAT_DST,
352         },
353         /* After packet filtering, change source */
354         {
355                 .hook           = nf_nat_ipv4_fn,
356                 .pf             = NFPROTO_IPV4,
357                 .hooknum        = NF_INET_LOCAL_IN,
358                 .priority       = NF_IP_PRI_NAT_SRC,
359         },
360 };
361 
362 int nf_nat_l3proto_ipv4_register_fn(struct net *net, const struct nf_hook_ops *ops)
363 {
364         return nf_nat_register_fn(net, ops, nf_nat_ipv4_ops, ARRAY_SIZE(nf_nat_ipv4_ops));
365 }
366 EXPORT_SYMBOL_GPL(nf_nat_l3proto_ipv4_register_fn);
367 
368 void nf_nat_l3proto_ipv4_unregister_fn(struct net *net, const struct nf_hook_ops *ops)
369 {
370         nf_nat_unregister_fn(net, ops, ARRAY_SIZE(nf_nat_ipv4_ops));
371 }
372 EXPORT_SYMBOL_GPL(nf_nat_l3proto_ipv4_unregister_fn);
373 
374 static int __init nf_nat_l3proto_ipv4_init(void)
375 {
376         return nf_nat_l3proto_register(&nf_nat_l3proto_ipv4);
377 }
378 
379 static void __exit nf_nat_l3proto_ipv4_exit(void)
380 {
381         nf_nat_l3proto_unregister(&nf_nat_l3proto_ipv4);
382 }
383 
384 MODULE_LICENSE("GPL");
385 MODULE_ALIAS("nf-nat-" __stringify(AF_INET));
386 
387 module_init(nf_nat_l3proto_ipv4_init);
388 module_exit(nf_nat_l3proto_ipv4_exit);
389 

~ [ 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