diff options
Diffstat (limited to 'net/ipv4/ip_forward.c')
| -rw-r--r-- | net/ipv4/ip_forward.c | 40 | 
1 files changed, 31 insertions, 9 deletions
diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c index 99461f09320..3a83ce5efa8 100644 --- a/net/ipv4/ip_forward.c +++ b/net/ipv4/ip_forward.c @@ -39,11 +39,30 @@  #include <net/route.h>  #include <net/xfrm.h> +static bool ip_may_fragment(const struct sk_buff *skb) +{ +	return unlikely((ip_hdr(skb)->frag_off & htons(IP_DF)) == 0) || +		skb->ignore_df; +} + +static bool ip_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu) +{ +	if (skb->len <= mtu) +		return false; + +	if (skb_is_gso(skb) && skb_gso_network_seglen(skb) <= mtu) +		return false; + +	return true; +} + +  static int ip_forward_finish(struct sk_buff *skb)  { -	struct ip_options * opt	= &(IPCB(skb)->opt); +	struct ip_options *opt	= &(IPCB(skb)->opt);  	IP_INC_STATS_BH(dev_net(skb_dst(skb)->dev), IPSTATS_MIB_OUTFORWDATAGRAMS); +	IP_ADD_STATS_BH(dev_net(skb_dst(skb)->dev), IPSTATS_MIB_OUTOCTETS, skb->len);  	if (unlikely(opt->optlen))  		ip_forward_options(skb); @@ -53,9 +72,14 @@ static int ip_forward_finish(struct sk_buff *skb)  int ip_forward(struct sk_buff *skb)  { +	u32 mtu;  	struct iphdr *iph;	/* Our header */  	struct rtable *rt;	/* Route we use */ -	struct ip_options * opt	= &(IPCB(skb)->opt); +	struct ip_options *opt	= &(IPCB(skb)->opt); + +	/* that should never happen */ +	if (skb->pkt_type != PACKET_HOST) +		goto drop;  	if (skb_warn_if_lro(skb))  		goto drop; @@ -66,9 +90,6 @@ int ip_forward(struct sk_buff *skb)  	if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb))  		return NET_RX_SUCCESS; -	if (skb->pkt_type != PACKET_HOST) -		goto drop; -  	skb_forward_csum(skb);  	/* @@ -84,14 +105,15 @@ int ip_forward(struct sk_buff *skb)  	rt = skb_rtable(skb); -	if (opt->is_strictroute && rt->rt_dst != rt->rt_gateway) +	if (opt->is_strictroute && rt->rt_uses_gateway)  		goto sr_failed; -	if (unlikely(skb->len > dst_mtu(&rt->dst) && !skb_is_gso(skb) && -		     (ip_hdr(skb)->frag_off & htons(IP_DF))) && !skb->local_df) { +	IPCB(skb)->flags |= IPSKB_FORWARDED; +	mtu = ip_dst_mtu_maybe_forward(&rt->dst, true); +	if (!ip_may_fragment(skb) && ip_exceeds_mtu(skb, mtu)) {  		IP_INC_STATS(dev_net(rt->dst.dev), IPSTATS_MIB_FRAGFAILS);  		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, -			  htonl(dst_mtu(&rt->dst))); +			  htonl(mtu));  		goto drop;  	}  | 
