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

TOMOYO Linux Cross Reference
Linux/net/ipv4/ip_forward.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ 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.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0
  2 /*
  3  * INET         An implementation of the TCP/IP protocol suite for the LINUX
  4  *              operating system.  INET is implemented using the  BSD Socket
  5  *              interface as the means of communication with the user level.
  6  *
  7  *              The IP forwarding functionality.
  8  *
  9  * Authors:     see ip.c
 10  *
 11  * Fixes:
 12  *              Many            :       Split from ip.c , see ip_input.c for
 13  *                                      history.
 14  *              Dave Gregorich  :       NULL ip_rt_put fix for multicast
 15  *                                      routing.
 16  *              Jos Vos         :       Add call_out_firewall before sending,
 17  *                                      use output device for accounting.
 18  *              Jos Vos         :       Call forward firewall after routing
 19  *                                      (always use output device).
 20  *              Mike McLagan    :       Routing by source
 21  */
 22 
 23 #include <linux/types.h>
 24 #include <linux/mm.h>
 25 #include <linux/skbuff.h>
 26 #include <linux/ip.h>
 27 #include <linux/icmp.h>
 28 #include <linux/netdevice.h>
 29 #include <linux/slab.h>
 30 #include <net/sock.h>
 31 #include <net/ip.h>
 32 #include <net/tcp.h>
 33 #include <net/udp.h>
 34 #include <net/icmp.h>
 35 #include <linux/tcp.h>
 36 #include <linux/udp.h>
 37 #include <linux/netfilter_ipv4.h>
 38 #include <net/checksum.h>
 39 #include <linux/route.h>
 40 #include <net/route.h>
 41 #include <net/xfrm.h>
 42 
 43 static bool ip_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu)
 44 {
 45         if (skb->len <= mtu)
 46                 return false;
 47 
 48         if (unlikely((ip_hdr(skb)->frag_off & htons(IP_DF)) == 0))
 49                 return false;
 50 
 51         /* original fragment exceeds mtu and DF is set */
 52         if (unlikely(IPCB(skb)->frag_max_size > mtu))
 53                 return true;
 54 
 55         if (skb->ignore_df)
 56                 return false;
 57 
 58         if (skb_is_gso(skb) && skb_gso_validate_network_len(skb, mtu))
 59                 return false;
 60 
 61         return true;
 62 }
 63 
 64 
 65 static int ip_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
 66 {
 67         struct ip_options *opt  = &(IPCB(skb)->opt);
 68 
 69 #ifdef CONFIG_NET_SWITCHDEV
 70         if (skb->offload_l3_fwd_mark) {
 71                 consume_skb(skb);
 72                 return 0;
 73         }
 74 #endif
 75 
 76         if (unlikely(opt->optlen))
 77                 ip_forward_options(skb);
 78 
 79         skb_clear_tstamp(skb);
 80         return dst_output(net, sk, skb);
 81 }
 82 
 83 int ip_forward(struct sk_buff *skb)
 84 {
 85         u32 mtu;
 86         struct iphdr *iph;      /* Our header */
 87         struct rtable *rt;      /* Route we use */
 88         struct ip_options *opt  = &(IPCB(skb)->opt);
 89         struct net *net;
 90         SKB_DR(reason);
 91 
 92         /* that should never happen */
 93         if (skb->pkt_type != PACKET_HOST)
 94                 goto drop;
 95 
 96         if (unlikely(skb->sk))
 97                 goto drop;
 98 
 99         if (skb_warn_if_lro(skb))
100                 goto drop;
101 
102         if (!xfrm4_policy_check(NULL, XFRM_POLICY_FWD, skb)) {
103                 SKB_DR_SET(reason, XFRM_POLICY);
104                 goto drop;
105         }
106 
107         if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb))
108                 return NET_RX_SUCCESS;
109 
110         skb_forward_csum(skb);
111         net = dev_net(skb->dev);
112 
113         /*
114          *      According to the RFC, we must first decrease the TTL field. If
115          *      that reaches zero, we must reply an ICMP control message telling
116          *      that the packet's lifetime expired.
117          */
118         if (ip_hdr(skb)->ttl <= 1)
119                 goto too_many_hops;
120 
121         if (!xfrm4_route_forward(skb)) {
122                 SKB_DR_SET(reason, XFRM_POLICY);
123                 goto drop;
124         }
125 
126         rt = skb_rtable(skb);
127 
128         if (opt->is_strictroute && rt->rt_uses_gateway)
129                 goto sr_failed;
130 
131         __IP_INC_STATS(net, IPSTATS_MIB_OUTFORWDATAGRAMS);
132 
133         IPCB(skb)->flags |= IPSKB_FORWARDED;
134         mtu = ip_dst_mtu_maybe_forward(&rt->dst, true);
135         if (ip_exceeds_mtu(skb, mtu)) {
136                 IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
137                 icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
138                           htonl(mtu));
139                 SKB_DR_SET(reason, PKT_TOO_BIG);
140                 goto drop;
141         }
142 
143         /* We are about to mangle packet. Copy it! */
144         if (skb_cow(skb, LL_RESERVED_SPACE(rt->dst.dev)+rt->dst.header_len))
145                 goto drop;
146         iph = ip_hdr(skb);
147 
148         /* Decrease ttl after skb cow done */
149         ip_decrease_ttl(iph);
150 
151         /*
152          *      We now generate an ICMP HOST REDIRECT giving the route
153          *      we calculated.
154          */
155         if (IPCB(skb)->flags & IPSKB_DOREDIRECT && !opt->srr &&
156             !skb_sec_path(skb))
157                 ip_rt_send_redirect(skb);
158 
159         if (READ_ONCE(net->ipv4.sysctl_ip_fwd_update_priority))
160                 skb->priority = rt_tos2priority(iph->tos);
161 
162         return NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD,
163                        net, NULL, skb, skb->dev, rt->dst.dev,
164                        ip_forward_finish);
165 
166 sr_failed:
167         /*
168          *      Strict routing permits no gatewaying
169          */
170          icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0);
171          goto drop;
172 
173 too_many_hops:
174         /* Tell the sender its packet died... */
175         __IP_INC_STATS(net, IPSTATS_MIB_INHDRERRORS);
176         icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0);
177         SKB_DR_SET(reason, IP_INHDR);
178 drop:
179         kfree_skb_reason(skb, reason);
180         return NET_RX_DROP;
181 }
182 

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

kernel.org | git.kernel.org | LWN.net | Project Home | 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.

sflogo.php