Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: Fix ip_route_me_harder triggering ip_rt_bug

Avoid creating input routes with ip_route_me_harder.
It does not work for locally generated packets. Instead,
restrict sockets to provide valid saddr for output route (or
unicast saddr for transparent proxy). For other traffic
allow saddr to be unicast or local but if callers forget
to check saddr type use 0 for the output route.

The resulting handling should be:

- REJECT TCP:
- in INPUT we can provide addr_type = RTN_LOCAL but
better allow rejecting traffic delivered with
local route (no IP address => use RTN_UNSPEC to
allow also RTN_UNICAST).
- FORWARD: RTN_UNSPEC => allow RTN_LOCAL/RTN_UNICAST
saddr, add fix to ignore RTN_BROADCAST and RTN_MULTICAST
- OUTPUT: RTN_UNSPEC

- NAT, mangle, ip_queue, nf_ip_reroute: RTN_UNSPEC in LOCAL_OUT

- IPVS:
- use RTN_LOCAL in LOCAL_OUT and FORWARD after SNAT
to restrict saddr to be local

Signed-off-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Julian Anastasov and committed by
David S. Miller
ed6e4ef8 353e5c9a

+26 -48
+22 -38
net/ipv4/netfilter.c
··· 17 17 const struct iphdr *iph = ip_hdr(skb); 18 18 struct rtable *rt; 19 19 struct flowi4 fl4 = {}; 20 - unsigned long orefdst; 20 + __be32 saddr = iph->saddr; 21 + __u8 flags = 0; 21 22 unsigned int hh_len; 22 - unsigned int type; 23 23 24 - type = inet_addr_type(net, iph->saddr); 25 - if (skb->sk && inet_sk(skb->sk)->transparent) 26 - type = RTN_LOCAL; 27 - if (addr_type == RTN_UNSPEC) 28 - addr_type = type; 24 + if (!skb->sk && addr_type != RTN_LOCAL) { 25 + if (addr_type == RTN_UNSPEC) 26 + addr_type = inet_addr_type(net, saddr); 27 + if (addr_type == RTN_LOCAL || addr_type == RTN_UNICAST) 28 + flags |= FLOWI_FLAG_ANYSRC; 29 + else 30 + saddr = 0; 31 + } 29 32 30 33 /* some non-standard hacks like ipt_REJECT.c:send_reset() can cause 31 34 * packets with foreign saddr to appear on the NF_INET_LOCAL_OUT hook. 32 35 */ 33 - if (addr_type == RTN_LOCAL) { 34 - fl4.daddr = iph->daddr; 35 - if (type == RTN_LOCAL) 36 - fl4.saddr = iph->saddr; 37 - fl4.flowi4_tos = RT_TOS(iph->tos); 38 - fl4.flowi4_oif = skb->sk ? skb->sk->sk_bound_dev_if : 0; 39 - fl4.flowi4_mark = skb->mark; 40 - fl4.flowi4_flags = skb->sk ? inet_sk_flowi_flags(skb->sk) : 0; 41 - rt = ip_route_output_key(net, &fl4); 42 - if (IS_ERR(rt)) 43 - return -1; 36 + fl4.daddr = iph->daddr; 37 + fl4.saddr = saddr; 38 + fl4.flowi4_tos = RT_TOS(iph->tos); 39 + fl4.flowi4_oif = skb->sk ? skb->sk->sk_bound_dev_if : 0; 40 + fl4.flowi4_mark = skb->mark; 41 + fl4.flowi4_flags = skb->sk ? inet_sk_flowi_flags(skb->sk) : flags; 42 + rt = ip_route_output_key(net, &fl4); 43 + if (IS_ERR(rt)) 44 + return -1; 44 45 45 - /* Drop old route. */ 46 - skb_dst_drop(skb); 47 - skb_dst_set(skb, &rt->dst); 48 - } else { 49 - /* non-local src, find valid iif to satisfy 50 - * rp-filter when calling ip_route_input. */ 51 - fl4.daddr = iph->saddr; 52 - rt = ip_route_output_key(net, &fl4); 53 - if (IS_ERR(rt)) 54 - return -1; 55 - 56 - orefdst = skb->_skb_refdst; 57 - if (ip_route_input(skb, iph->daddr, iph->saddr, 58 - RT_TOS(iph->tos), rt->dst.dev) != 0) { 59 - dst_release(&rt->dst); 60 - return -1; 61 - } 62 - dst_release(&rt->dst); 63 - refdst_drop(orefdst); 64 - } 46 + /* Drop old route. */ 47 + skb_dst_drop(skb); 48 + skb_dst_set(skb, &rt->dst); 65 49 66 50 if (skb_dst(skb)->error) 67 51 return -1;
+4 -10
net/ipv4/netfilter/ipt_REJECT.c
··· 40 40 struct iphdr *niph; 41 41 const struct tcphdr *oth; 42 42 struct tcphdr _otcph, *tcph; 43 - unsigned int addr_type; 44 43 45 44 /* IP header checks: fragment. */ 46 45 if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET)) ··· 52 53 53 54 /* No RST for RST. */ 54 55 if (oth->rst) 56 + return; 57 + 58 + if (skb_rtable(oldskb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) 55 59 return; 56 60 57 61 /* Check checksum */ ··· 103 101 nskb->csum_start = (unsigned char *)tcph - nskb->head; 104 102 nskb->csum_offset = offsetof(struct tcphdr, check); 105 103 106 - addr_type = RTN_UNSPEC; 107 - if (hook != NF_INET_FORWARD 108 - #ifdef CONFIG_BRIDGE_NETFILTER 109 - || (nskb->nf_bridge && nskb->nf_bridge->mask & BRNF_BRIDGED) 110 - #endif 111 - ) 112 - addr_type = RTN_LOCAL; 113 - 114 104 /* ip_route_me_harder expects skb->dst to be set */ 115 105 skb_dst_set_noref(nskb, skb_dst(oldskb)); 116 106 117 107 nskb->protocol = htons(ETH_P_IP); 118 - if (ip_route_me_harder(nskb, addr_type)) 108 + if (ip_route_me_harder(nskb, RTN_UNSPEC)) 119 109 goto free_nskb; 120 110 121 111 niph->ttl = ip4_dst_hoplimit(skb_dst(nskb));