Blame view
net/netfilter/nf_conntrack_proto_icmp.c
10 KB
9fb9cbb10
|
1 2 |
/* (C) 1999-2001 Paul `Rusty' Russell * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> |
f229f6ce4
|
3 |
* (C) 2006-2010 Patrick McHardy <kaber@trash.net> |
9fb9cbb10
|
4 5 6 7 |
* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. |
9fb9cbb10
|
8 9 10 |
*/ #include <linux/types.h> |
9fb9cbb10
|
11 12 13 14 15 16 17 18 19 |
#include <linux/timer.h> #include <linux/netfilter.h> #include <linux/in.h> #include <linux/icmp.h> #include <linux/seq_file.h> #include <net/ip.h> #include <net/checksum.h> #include <linux/netfilter_ipv4.h> #include <net/netfilter/nf_conntrack_tuple.h> |
605dcad6c
|
20 |
#include <net/netfilter/nf_conntrack_l4proto.h> |
9fb9cbb10
|
21 |
#include <net/netfilter/nf_conntrack_core.h> |
c779e8496
|
22 |
#include <net/netfilter/nf_conntrack_timeout.h> |
5d0aa2ccd
|
23 |
#include <net/netfilter/nf_conntrack_zones.h> |
f01ffbd6e
|
24 |
#include <net/netfilter/nf_log.h> |
9fb9cbb10
|
25 |
|
2c9e8637e
|
26 |
static const unsigned int nf_ct_icmp_timeout = 30*HZ; |
9fb9cbb10
|
27 |
|
09f263cd3
|
28 |
static bool icmp_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff, |
a31f1adc0
|
29 |
struct net *net, struct nf_conntrack_tuple *tuple) |
9fb9cbb10
|
30 |
{ |
7cc3864d3
|
31 32 |
const struct icmphdr *hp; struct icmphdr _hdr; |
9fb9cbb10
|
33 34 35 |
hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr); if (hp == NULL) |
09f263cd3
|
36 |
return false; |
9fb9cbb10
|
37 38 39 40 |
tuple->dst.u.icmp.type = hp->type; tuple->src.u.icmp.id = hp->un.echo.id; tuple->dst.u.icmp.code = hp->code; |
09f263cd3
|
41 |
return true; |
9fb9cbb10
|
42 |
} |
c1d10adb4
|
43 44 45 46 47 48 49 50 51 52 53 |
/* Add 1; spaces filled with 0. */ static const u_int8_t invmap[] = { [ICMP_ECHO] = ICMP_ECHOREPLY + 1, [ICMP_ECHOREPLY] = ICMP_ECHO + 1, [ICMP_TIMESTAMP] = ICMP_TIMESTAMPREPLY + 1, [ICMP_TIMESTAMPREPLY] = ICMP_TIMESTAMP + 1, [ICMP_INFO_REQUEST] = ICMP_INFO_REPLY + 1, [ICMP_INFO_REPLY] = ICMP_INFO_REQUEST + 1, [ICMP_ADDRESS] = ICMP_ADDRESSREPLY + 1, [ICMP_ADDRESSREPLY] = ICMP_ADDRESS + 1 }; |
09f263cd3
|
54 55 |
static bool icmp_invert_tuple(struct nf_conntrack_tuple *tuple, const struct nf_conntrack_tuple *orig) |
9fb9cbb10
|
56 |
{ |
3666ed1c4
|
57 58 |
if (orig->dst.u.icmp.type >= sizeof(invmap) || !invmap[orig->dst.u.icmp.type]) |
09f263cd3
|
59 |
return false; |
9fb9cbb10
|
60 61 62 63 |
tuple->src.u.icmp.id = orig->src.u.icmp.id; tuple->dst.u.icmp.type = invmap[orig->dst.u.icmp.type] - 1; tuple->dst.u.icmp.code = orig->dst.u.icmp.code; |
09f263cd3
|
64 |
return true; |
9fb9cbb10
|
65 |
} |
9fb9cbb10
|
66 |
/* Returns verdict for packet, or -1 for invalid. */ |
a47c54048
|
67 68 69 70 |
int nf_conntrack_icmp_packet(struct nf_conn *ct, struct sk_buff *skb, enum ip_conntrack_info ctinfo, const struct nf_hook_state *state) |
9fb9cbb10
|
71 |
{ |
f87fb666b
|
72 73 74 |
/* Do not immediately delete the connection after the first successful reply to avoid excessive conntrackd traffic and also to handle correctly ICMP echo reply duplicates. */ |
c779e8496
|
75 |
unsigned int *timeout = nf_ct_timeout_lookup(ct); |
c1d10adb4
|
76 77 78 79 80 81 |
static const u_int8_t valid_new[] = { [ICMP_ECHO] = 1, [ICMP_TIMESTAMP] = 1, [ICMP_INFO_REQUEST] = 1, [ICMP_ADDRESS] = 1 }; |
9fb9cbb10
|
82 |
|
dd2934a95
|
83 84 |
if (state->pf != NFPROTO_IPV4) return -NF_ACCEPT; |
3666ed1c4
|
85 86 |
if (ct->tuplehash[0].tuple.dst.u.icmp.type >= sizeof(valid_new) || !valid_new[ct->tuplehash[0].tuple.dst.u.icmp.type]) { |
9fb9cbb10
|
87 |
/* Can't create a new ICMP `conn' with this. */ |
0d53778e8
|
88 89 |
pr_debug("icmp: can't create new conn with type %u ", |
c88130bcd
|
90 |
ct->tuplehash[0].tuple.dst.u.icmp.type); |
3c9fba656
|
91 |
nf_ct_dump_tuple_ip(&ct->tuplehash[0].tuple); |
9976fc6e6
|
92 |
return -NF_ACCEPT; |
9fb9cbb10
|
93 |
} |
9976fc6e6
|
94 95 |
if (!timeout) |
a95a7774d
|
96 |
timeout = &nf_icmp_pernet(nf_ct_net(ct))->timeout; |
9976fc6e6
|
97 98 99 |
nf_ct_refresh_acct(ct, ctinfo, skb, *timeout); return NF_ACCEPT; |
9fb9cbb10
|
100 |
} |
9fb9cbb10
|
101 102 |
/* Returns conntrack if it dealt with ICMP, and filled in skb fields */ static int |
93e66024b
|
103 104 |
icmp_error_message(struct nf_conn *tmpl, struct sk_buff *skb, const struct nf_hook_state *state) |
9fb9cbb10
|
105 106 |
{ struct nf_conntrack_tuple innertuple, origtuple; |
7cc3864d3
|
107 108 |
const struct nf_conntrack_l4proto *innerproto; const struct nf_conntrack_tuple_hash *h; |
308ac9143
|
109 |
const struct nf_conntrack_zone *zone; |
11df4b760
|
110 |
enum ip_conntrack_info ctinfo; |
5e8018fc6
|
111 |
struct nf_conntrack_zone tmp; |
9fb9cbb10
|
112 |
|
44d6e2f27
|
113 |
WARN_ON(skb_nfct(skb)); |
5e8018fc6
|
114 |
zone = nf_ct_zone_tmpl(tmpl, skb, &tmp); |
9fb9cbb10
|
115 |
|
e2a3123fb
|
116 117 118 119 |
/* Are they talking about one of our connections? */ if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb) + ip_hdrlen(skb) + sizeof(struct icmphdr), |
93e66024b
|
120 |
PF_INET, state->net, &origtuple)) { |
e2a3123fb
|
121 122 |
pr_debug("icmp_error_message: failed to get tuple "); |
9fb9cbb10
|
123 124 |
return -NF_ACCEPT; } |
e2361cb90
|
125 |
/* rcu_read_lock()ed by nf_hook_thresh */ |
dd2934a95
|
126 |
innerproto = __nf_ct_l4proto_find(origtuple.dst.protonum); |
9fb9cbb10
|
127 |
|
e905a9eda
|
128 129 |
/* Ordinarily, we'd expect the inverted tupleproto, but it's been preserved inside the ICMP. */ |
d1b6fe949
|
130 |
if (!nf_ct_invert_tuple(&innertuple, &origtuple, innerproto)) { |
0d53778e8
|
131 132 |
pr_debug("icmp_error_message: no match "); |
9fb9cbb10
|
133 134 |
return -NF_ACCEPT; } |
11df4b760
|
135 |
ctinfo = IP_CT_RELATED; |
9fb9cbb10
|
136 |
|
93e66024b
|
137 |
h = nf_conntrack_find_get(state->net, zone, &innertuple); |
9fb9cbb10
|
138 |
if (!h) { |
130e7a83d
|
139 140 141 |
pr_debug("icmp_error_message: no match "); return -NF_ACCEPT; |
9fb9cbb10
|
142 |
} |
130e7a83d
|
143 |
if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) |
11df4b760
|
144 |
ctinfo += IP_CT_IS_REPLY; |
130e7a83d
|
145 |
|
e905a9eda
|
146 |
/* Update skb to refer to this connection */ |
c74454fad
|
147 |
nf_ct_set(skb, nf_ct_tuplehash_to_ctrack(h), ctinfo); |
88ed01d17
|
148 |
return NF_ACCEPT; |
9fb9cbb10
|
149 |
} |
93e66024b
|
150 151 152 |
static void icmp_error_log(const struct sk_buff *skb, const struct nf_hook_state *state, const char *msg) |
c4f3db159
|
153 |
{ |
93e66024b
|
154 155 |
nf_l4proto_log_invalid(skb, state->net, state->pf, IPPROTO_ICMP, "%s", msg); |
c4f3db159
|
156 |
} |
9fb9cbb10
|
157 |
/* Small and modified version of icmp_rcv */ |
6fe78fa48
|
158 159 160 |
int nf_conntrack_icmpv4_error(struct nf_conn *tmpl, struct sk_buff *skb, unsigned int dataoff, const struct nf_hook_state *state) |
9fb9cbb10
|
161 |
{ |
7cc3864d3
|
162 163 |
const struct icmphdr *icmph; struct icmphdr _ih; |
9fb9cbb10
|
164 165 |
/* Not enough header? */ |
c9bdd4b52
|
166 |
icmph = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_ih), &_ih); |
9fb9cbb10
|
167 |
if (icmph == NULL) { |
93e66024b
|
168 |
icmp_error_log(skb, state, "short packet"); |
9fb9cbb10
|
169 170 171 172 |
return -NF_ACCEPT; } /* See ip_conntrack_proto_tcp.c */ |
93e66024b
|
173 174 175 176 |
if (state->net->ct.sysctl_checksum && state->hook == NF_INET_PRE_ROUTING && nf_ip_checksum(skb, state->hook, dataoff, 0)) { icmp_error_log(skb, state, "bad hw icmp checksum"); |
9fb9cbb10
|
177 |
return -NF_ACCEPT; |
9fb9cbb10
|
178 |
} |
9fb9cbb10
|
179 180 181 182 183 184 185 |
/* * 18 is the highest 'known' ICMP type. Anything else is a mystery * * RFC 1122: 3.2.2 Unknown ICMP messages types MUST be silently * discarded. */ if (icmph->type > NR_ICMP_TYPES) { |
93e66024b
|
186 |
icmp_error_log(skb, state, "invalid icmp type"); |
9fb9cbb10
|
187 188 189 190 |
return -NF_ACCEPT; } /* Need to track icmp error message? */ |
3666ed1c4
|
191 192 193 194 195 |
if (icmph->type != ICMP_DEST_UNREACH && icmph->type != ICMP_SOURCE_QUENCH && icmph->type != ICMP_TIME_EXCEEDED && icmph->type != ICMP_PARAMETERPROB && icmph->type != ICMP_REDIRECT) |
9fb9cbb10
|
196 |
return NF_ACCEPT; |
93e66024b
|
197 |
return icmp_error_message(tmpl, skb, state); |
9fb9cbb10
|
198 |
} |
24de3d377
|
199 |
#if IS_ENABLED(CONFIG_NF_CT_NETLINK) |
c1d10adb4
|
200 201 202 |
#include <linux/netfilter/nfnetlink.h> #include <linux/netfilter/nfnetlink_conntrack.h> |
fdf708322
|
203 |
static int icmp_tuple_to_nlattr(struct sk_buff *skb, |
c1d10adb4
|
204 205 |
const struct nf_conntrack_tuple *t) { |
d317e4f68
|
206 207 208 209 |
if (nla_put_be16(skb, CTA_PROTO_ICMP_ID, t->src.u.icmp.id) || nla_put_u8(skb, CTA_PROTO_ICMP_TYPE, t->dst.u.icmp.type) || nla_put_u8(skb, CTA_PROTO_ICMP_CODE, t->dst.u.icmp.code)) goto nla_put_failure; |
c1d10adb4
|
210 |
return 0; |
df6fb868d
|
211 |
nla_put_failure: |
c1d10adb4
|
212 213 |
return -1; } |
f73e924cd
|
214 215 216 217 |
static const struct nla_policy icmp_nla_policy[CTA_PROTO_MAX+1] = { [CTA_PROTO_ICMP_TYPE] = { .type = NLA_U8 }, [CTA_PROTO_ICMP_CODE] = { .type = NLA_U8 }, [CTA_PROTO_ICMP_ID] = { .type = NLA_U16 }, |
c1d10adb4
|
218 |
}; |
fdf708322
|
219 |
static int icmp_nlattr_to_tuple(struct nlattr *tb[], |
c1d10adb4
|
220 221 |
struct nf_conntrack_tuple *tuple) { |
3666ed1c4
|
222 223 224 |
if (!tb[CTA_PROTO_ICMP_TYPE] || !tb[CTA_PROTO_ICMP_CODE] || !tb[CTA_PROTO_ICMP_ID]) |
c1d10adb4
|
225 |
return -EINVAL; |
77236b6e3
|
226 227 228 |
tuple->dst.u.icmp.type = nla_get_u8(tb[CTA_PROTO_ICMP_TYPE]); tuple->dst.u.icmp.code = nla_get_u8(tb[CTA_PROTO_ICMP_CODE]); tuple->src.u.icmp.id = nla_get_be16(tb[CTA_PROTO_ICMP_ID]); |
c1d10adb4
|
229 |
|
3666ed1c4
|
230 231 |
if (tuple->dst.u.icmp.type >= sizeof(invmap) || !invmap[tuple->dst.u.icmp.type]) |
c1d10adb4
|
232 233 234 235 |
return -EINVAL; return 0; } |
a400c30ed
|
236 |
|
5caaed151
|
237 |
static unsigned int icmp_nlattr_tuple_size(void) |
a400c30ed
|
238 |
{ |
5caaed151
|
239 240 241 242 243 244 |
static unsigned int size __read_mostly; if (!size) size = nla_policy_len(icmp_nla_policy, CTA_PROTO_MAX + 1); return size; |
a400c30ed
|
245 |
} |
c1d10adb4
|
246 |
#endif |
a874752a1
|
247 |
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT |
509784623
|
248 249 250 |
#include <linux/netfilter/nfnetlink.h> #include <linux/netfilter/nfnetlink_cttimeout.h> |
8264deb81
|
251 252 |
static int icmp_timeout_nlattr_to_obj(struct nlattr *tb[], struct net *net, void *data) |
509784623
|
253 254 |
{ unsigned int *timeout = data; |
a95a7774d
|
255 |
struct nf_icmp_net *in = nf_icmp_pernet(net); |
509784623
|
256 257 |
if (tb[CTA_TIMEOUT_ICMP_TIMEOUT]) { |
c779e8496
|
258 259 |
if (!timeout) timeout = &in->timeout; |
509784623
|
260 261 |
*timeout = ntohl(nla_get_be32(tb[CTA_TIMEOUT_ICMP_TIMEOUT])) * HZ; |
c779e8496
|
262 |
} else if (timeout) { |
509784623
|
263 |
/* Set default ICMP timeout. */ |
8264deb81
|
264 |
*timeout = in->timeout; |
509784623
|
265 266 267 268 269 270 271 272 |
} return 0; } static int icmp_timeout_obj_to_nlattr(struct sk_buff *skb, const void *data) { const unsigned int *timeout = data; |
d317e4f68
|
273 274 |
if (nla_put_be32(skb, CTA_TIMEOUT_ICMP_TIMEOUT, htonl(*timeout / HZ))) goto nla_put_failure; |
509784623
|
275 276 277 278 279 280 281 282 283 284 |
return 0; nla_put_failure: return -ENOSPC; } static const struct nla_policy icmp_timeout_nla_policy[CTA_TIMEOUT_ICMP_MAX+1] = { [CTA_TIMEOUT_ICMP_TIMEOUT] = { .type = NLA_U32 }, }; |
a874752a1
|
285 |
#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ |
509784623
|
286 |
|
933a41e7e
|
287 |
#ifdef CONFIG_SYSCTL |
933a41e7e
|
288 289 |
static struct ctl_table icmp_sysctl_table[] = { { |
933a41e7e
|
290 |
.procname = "nf_conntrack_icmp_timeout", |
933a41e7e
|
291 292 |
.maxlen = sizeof(unsigned int), .mode = 0644, |
6d9f239a1
|
293 |
.proc_handler = proc_dointvec_jiffies, |
933a41e7e
|
294 |
}, |
f8572d8f2
|
295 |
{ } |
933a41e7e
|
296 297 |
}; #endif /* CONFIG_SYSCTL */ |
a9082b45a
|
298 299 |
static int icmp_kmemdup_sysctl_table(struct nf_proto_net *pn, struct nf_icmp_net *in) |
4b626b9c5
|
300 |
{ |
4b626b9c5
|
301 302 303 304 305 306 |
#ifdef CONFIG_SYSCTL pn->ctl_table = kmemdup(icmp_sysctl_table, sizeof(icmp_sysctl_table), GFP_KERNEL); if (!pn->ctl_table) return -ENOMEM; |
a9082b45a
|
307 |
|
4b626b9c5
|
308 |
pn->ctl_table[0].data = &in->timeout; |
a9082b45a
|
309 310 311 |
#endif return 0; } |
ca2ca6e1c
|
312 |
static int icmp_init_net(struct net *net) |
a9082b45a
|
313 |
{ |
a95a7774d
|
314 |
struct nf_icmp_net *in = nf_icmp_pernet(net); |
a9082b45a
|
315 316 317 |
struct nf_proto_net *pn = &in->pn; in->timeout = nf_ct_icmp_timeout; |
adf051684
|
318 |
return icmp_kmemdup_sysctl_table(pn, in); |
a9082b45a
|
319 |
} |
08911475d
|
320 321 322 323 |
static struct nf_proto_net *icmp_get_net_proto(struct net *net) { return &net->ct.nf_ct_proto.icmp.pn; } |
9dae47aba
|
324 |
const struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp = |
9fb9cbb10
|
325 |
{ |
605dcad6c
|
326 |
.l4proto = IPPROTO_ICMP, |
9fb9cbb10
|
327 328 |
.pkt_to_tuple = icmp_pkt_to_tuple, .invert_tuple = icmp_invert_tuple, |
24de3d377
|
329 |
#if IS_ENABLED(CONFIG_NF_CT_NETLINK) |
fdf708322
|
330 |
.tuple_to_nlattr = icmp_tuple_to_nlattr, |
a400c30ed
|
331 |
.nlattr_tuple_size = icmp_nlattr_tuple_size, |
fdf708322
|
332 |
.nlattr_to_tuple = icmp_nlattr_to_tuple, |
f73e924cd
|
333 |
.nla_policy = icmp_nla_policy, |
c1d10adb4
|
334 |
#endif |
a874752a1
|
335 |
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT |
509784623
|
336 337 338 339 340 341 342 |
.ctnl_timeout = { .nlattr_to_obj = icmp_timeout_nlattr_to_obj, .obj_to_nlattr = icmp_timeout_obj_to_nlattr, .nlattr_max = CTA_TIMEOUT_ICMP_MAX, .obj_size = sizeof(unsigned int), .nla_policy = icmp_timeout_nla_policy, }, |
a874752a1
|
343 |
#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ |
4b626b9c5
|
344 |
.init_net = icmp_init_net, |
08911475d
|
345 |
.get_net_proto = icmp_get_net_proto, |
9fb9cbb10
|
346 |
}; |