Skip to content

Commit db99b2f

Browse files
author
Florian Westphal
committed
netfilter: nf_reject: don't reply to icmp error messages
tcp reject code won't reply to a tcp reset. But the icmp reject 'netdev' family versions will reply to icmp dst-unreach errors, unlike icmp_send() and icmp6_send() which are used by the inet family implementation (and internally by the REJECT target). Check for the icmp(6) type and do not respond if its an unreachable error. Without this, something like 'ip protocol icmp reject', when used in a netdev chain attached to 'lo', cause a packet loop. Same for two hosts that both use such a rule: each error packet will be replied to. Such situation persist until the (bogus) rule is amended to ratelimit or checks the icmp type before the reject statement. As the inet versions don't do this make the netdev ones follow along. Signed-off-by: Florian Westphal <fw@strlen.de>
1 parent 944b6b2 commit db99b2f

2 files changed

Lines changed: 55 additions & 0 deletions

File tree

net/ipv4/netfilter/nf_reject_ipv4.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,27 @@ struct sk_buff *nf_reject_skb_v4_tcp_reset(struct net *net,
8080
}
8181
EXPORT_SYMBOL_GPL(nf_reject_skb_v4_tcp_reset);
8282

83+
static bool nf_skb_is_icmp_unreach(const struct sk_buff *skb)
84+
{
85+
const struct iphdr *iph = ip_hdr(skb);
86+
u8 *tp, _type;
87+
int thoff;
88+
89+
if (iph->protocol != IPPROTO_ICMP)
90+
return false;
91+
92+
thoff = skb_network_offset(skb) + sizeof(*iph);
93+
94+
tp = skb_header_pointer(skb,
95+
thoff + offsetof(struct icmphdr, type),
96+
sizeof(_type), &_type);
97+
98+
if (!tp)
99+
return false;
100+
101+
return *tp == ICMP_DEST_UNREACH;
102+
}
103+
83104
struct sk_buff *nf_reject_skb_v4_unreach(struct net *net,
84105
struct sk_buff *oldskb,
85106
const struct net_device *dev,
@@ -100,6 +121,10 @@ struct sk_buff *nf_reject_skb_v4_unreach(struct net *net,
100121
if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET))
101122
return NULL;
102123

124+
/* don't reply to ICMP_DEST_UNREACH with ICMP_DEST_UNREACH. */
125+
if (nf_skb_is_icmp_unreach(oldskb))
126+
return NULL;
127+
103128
/* RFC says return as much as we can without exceeding 576 bytes. */
104129
len = min_t(unsigned int, 536, oldskb->len);
105130

net/ipv6/netfilter/nf_reject_ipv6.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,32 @@ struct sk_buff *nf_reject_skb_v6_tcp_reset(struct net *net,
104104
}
105105
EXPORT_SYMBOL_GPL(nf_reject_skb_v6_tcp_reset);
106106

107+
static bool nf_skb_is_icmp6_unreach(const struct sk_buff *skb)
108+
{
109+
const struct ipv6hdr *ip6h = ipv6_hdr(skb);
110+
u8 proto = ip6h->nexthdr;
111+
u8 _type, *tp;
112+
int thoff;
113+
__be16 fo;
114+
115+
thoff = ipv6_skip_exthdr(skb, ((u8 *)(ip6h + 1) - skb->data), &proto, &fo);
116+
117+
if (thoff < 0 || thoff >= skb->len || fo != 0)
118+
return false;
119+
120+
if (proto != IPPROTO_ICMPV6)
121+
return false;
122+
123+
tp = skb_header_pointer(skb,
124+
thoff + offsetof(struct icmp6hdr, icmp6_type),
125+
sizeof(_type), &_type);
126+
127+
if (!tp)
128+
return false;
129+
130+
return *tp == ICMPV6_DEST_UNREACH;
131+
}
132+
107133
struct sk_buff *nf_reject_skb_v6_unreach(struct net *net,
108134
struct sk_buff *oldskb,
109135
const struct net_device *dev,
@@ -117,6 +143,10 @@ struct sk_buff *nf_reject_skb_v6_unreach(struct net *net,
117143
if (!nf_reject_ip6hdr_validate(oldskb))
118144
return NULL;
119145

146+
/* Don't reply to ICMPV6_DEST_UNREACH with ICMPV6_DEST_UNREACH */
147+
if (nf_skb_is_icmp6_unreach(oldskb))
148+
return NULL;
149+
120150
/* Include "As much of invoking packet as possible without the ICMPv6
121151
* packet exceeding the minimum IPv6 MTU" in the ICMP payload.
122152
*/

0 commit comments

Comments
 (0)