Blame view
net/netfilter/nf_conntrack_proto_udp.c
11.4 KB
9fb9cbb10
|
1 2 |
/* (C) 1999-2001 Paul `Rusty' Russell * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> |
f229f6ce4
|
3 |
* (C) 2006-2012 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 |
#include <linux/timer.h> #include <linux/module.h> |
9fb9cbb10
|
13 14 15 16 17 18 |
#include <linux/udp.h> #include <linux/seq_file.h> #include <linux/skbuff.h> #include <linux/ipv6.h> #include <net/ip6_checksum.h> #include <net/checksum.h> |
f61801218
|
19 |
|
9fb9cbb10
|
20 21 22 |
#include <linux/netfilter.h> #include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv6.h> |
605dcad6c
|
23 |
#include <net/netfilter/nf_conntrack_l4proto.h> |
f61801218
|
24 |
#include <net/netfilter/nf_conntrack_ecache.h> |
c779e8496
|
25 |
#include <net/netfilter/nf_conntrack_timeout.h> |
f01ffbd6e
|
26 |
#include <net/netfilter/nf_log.h> |
9d2493f88
|
27 28 |
#include <net/netfilter/ipv4/nf_conntrack_ipv4.h> #include <net/netfilter/ipv6/nf_conntrack_ipv6.h> |
9fb9cbb10
|
29 |
|
2c9e8637e
|
30 |
static const unsigned int udp_timeouts[UDP_CT_MAX] = { |
5a41db94c
|
31 32 33 |
[UDP_CT_UNREPLIED] = 30*HZ, [UDP_CT_REPLIED] = 180*HZ, }; |
9fb9cbb10
|
34 |
|
0ce490ad4
|
35 36 37 38 |
static inline struct nf_udp_net *udp_pernet(struct net *net) { return &net->ct.nf_ct_proto.udp; } |
2c8503f55
|
39 40 |
static unsigned int *udp_get_timeouts(struct net *net) { |
0ce490ad4
|
41 |
return udp_pernet(net)->timeouts; |
2c8503f55
|
42 |
} |
83d213fd9
|
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
static void udp_error_log(const struct sk_buff *skb, const struct nf_hook_state *state, const char *msg) { nf_l4proto_log_invalid(skb, state->net, state->pf, IPPROTO_UDP, "%s", msg); } static bool udp_error(struct sk_buff *skb, unsigned int dataoff, const struct nf_hook_state *state) { unsigned int udplen = skb->len - dataoff; const struct udphdr *hdr; struct udphdr _hdr; /* Header is too small? */ hdr = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr); if (!hdr) { udp_error_log(skb, state, "short packet"); return true; } /* Truncated/malformed packets */ if (ntohs(hdr->len) > udplen || ntohs(hdr->len) < sizeof(*hdr)) { udp_error_log(skb, state, "truncated/malformed packet"); return true; } /* Packet with no checksum */ if (!hdr->check) return false; /* Checksum invalid? Ignore. * We skip checking packets on the outgoing path * because the checksum is assumed to be correct. * FIXME: Source route IP option packets --RR */ if (state->hook == NF_INET_PRE_ROUTING && state->net->ct.sysctl_checksum && nf_checksum(skb, state->hook, dataoff, IPPROTO_UDP, state->pf)) { udp_error_log(skb, state, "bad checksum"); return true; } return false; } |
9fb9cbb10
|
89 |
/* Returns verdict for packet, and may modify conntracktype */ |
c88130bcd
|
90 |
static int udp_packet(struct nf_conn *ct, |
83d213fd9
|
91 |
struct sk_buff *skb, |
9fb9cbb10
|
92 |
unsigned int dataoff, |
93e66024b
|
93 94 |
enum ip_conntrack_info ctinfo, const struct nf_hook_state *state) |
9fb9cbb10
|
95 |
{ |
c779e8496
|
96 |
unsigned int *timeouts; |
83d213fd9
|
97 98 |
if (udp_error(skb, dataoff, state)) return -NF_ACCEPT; |
c779e8496
|
99 100 101 |
timeouts = nf_ct_timeout_lookup(ct); if (!timeouts) timeouts = udp_get_timeouts(nf_ct_net(ct)); |
9fb9cbb10
|
102 103 |
/* If we've seen traffic both ways, this is some kind of UDP stream. Extend timeout. */ |
c88130bcd
|
104 |
if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { |
5a41db94c
|
105 |
nf_ct_refresh_acct(ct, ctinfo, skb, |
2c8503f55
|
106 |
timeouts[UDP_CT_REPLIED]); |
9fb9cbb10
|
107 |
/* Also, more likely to be important, and not a probe */ |
c88130bcd
|
108 |
if (!test_and_set_bit(IPS_ASSURED_BIT, &ct->status)) |
858b31330
|
109 |
nf_conntrack_event_cache(IPCT_ASSURED, ct); |
5a41db94c
|
110 111 |
} else { nf_ct_refresh_acct(ct, ctinfo, skb, |
2c8503f55
|
112 |
timeouts[UDP_CT_UNREPLIED]); |
5a41db94c
|
113 |
} |
9fb9cbb10
|
114 115 |
return NF_ACCEPT; } |
e4781421e
|
116 |
#ifdef CONFIG_NF_CT_PROTO_UDPLITE |
93e66024b
|
117 118 119 |
static void udplite_error_log(const struct sk_buff *skb, const struct nf_hook_state *state, const char *msg) |
c4f3db159
|
120 |
{ |
93e66024b
|
121 122 |
nf_l4proto_log_invalid(skb, state->net, state->pf, IPPROTO_UDPLITE, "%s", msg); |
c4f3db159
|
123 |
} |
83d213fd9
|
124 125 126 |
static bool udplite_error(struct sk_buff *skb, unsigned int dataoff, const struct nf_hook_state *state) |
e4781421e
|
127 128 129 130 131 132 133 134 135 |
{ unsigned int udplen = skb->len - dataoff; const struct udphdr *hdr; struct udphdr _hdr; unsigned int cscov; /* Header is too small? */ hdr = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr); if (!hdr) { |
93e66024b
|
136 |
udplite_error_log(skb, state, "short packet"); |
83d213fd9
|
137 |
return true; |
e4781421e
|
138 139 140 141 142 143 |
} cscov = ntohs(hdr->len); if (cscov == 0) { cscov = udplen; } else if (cscov < sizeof(*hdr) || cscov > udplen) { |
93e66024b
|
144 |
udplite_error_log(skb, state, "invalid checksum coverage"); |
83d213fd9
|
145 |
return true; |
e4781421e
|
146 147 148 149 |
} /* UDPLITE mandates checksums */ if (!hdr->check) { |
93e66024b
|
150 |
udplite_error_log(skb, state, "checksum missing"); |
83d213fd9
|
151 |
return true; |
e4781421e
|
152 153 154 |
} /* Checksum invalid? Ignore. */ |
93e66024b
|
155 156 157 158 159 |
if (state->hook == NF_INET_PRE_ROUTING && state->net->ct.sysctl_checksum && nf_checksum_partial(skb, state->hook, dataoff, cscov, IPPROTO_UDP, state->pf)) { udplite_error_log(skb, state, "bad checksum"); |
83d213fd9
|
160 |
return true; |
e4781421e
|
161 |
} |
83d213fd9
|
162 |
return false; |
e4781421e
|
163 |
} |
e4781421e
|
164 |
|
83d213fd9
|
165 166 167 168 169 170 |
/* Returns verdict for packet, and may modify conntracktype */ static int udplite_packet(struct nf_conn *ct, struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, const struct nf_hook_state *state) |
9fb9cbb10
|
171 |
{ |
83d213fd9
|
172 |
unsigned int *timeouts; |
9fb9cbb10
|
173 |
|
83d213fd9
|
174 |
if (udplite_error(skb, dataoff, state)) |
9fb9cbb10
|
175 |
return -NF_ACCEPT; |
9fb9cbb10
|
176 |
|
83d213fd9
|
177 178 179 |
timeouts = nf_ct_timeout_lookup(ct); if (!timeouts) timeouts = udp_get_timeouts(nf_ct_net(ct)); |
9fb9cbb10
|
180 |
|
83d213fd9
|
181 182 183 184 185 186 187 188 189 190 191 |
/* If we've seen traffic both ways, this is some kind of UDP stream. Extend timeout. */ if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { nf_ct_refresh_acct(ct, ctinfo, skb, timeouts[UDP_CT_REPLIED]); /* Also, more likely to be important, and not a probe */ if (!test_and_set_bit(IPS_ASSURED_BIT, &ct->status)) nf_conntrack_event_cache(IPCT_ASSURED, ct); } else { nf_ct_refresh_acct(ct, ctinfo, skb, timeouts[UDP_CT_UNREPLIED]); |
9fb9cbb10
|
192 |
} |
9fb9cbb10
|
193 194 |
return NF_ACCEPT; } |
83d213fd9
|
195 |
#endif |
9fb9cbb10
|
196 |
|
a874752a1
|
197 |
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT |
509784623
|
198 199 200 |
#include <linux/netfilter/nfnetlink.h> #include <linux/netfilter/nfnetlink_cttimeout.h> |
8264deb81
|
201 202 |
static int udp_timeout_nlattr_to_obj(struct nlattr *tb[], struct net *net, void *data) |
509784623
|
203 204 |
{ unsigned int *timeouts = data; |
8264deb81
|
205 |
struct nf_udp_net *un = udp_pernet(net); |
509784623
|
206 |
|
c779e8496
|
207 208 |
if (!timeouts) timeouts = un->timeouts; |
509784623
|
209 |
/* set default timeouts for UDP. */ |
8264deb81
|
210 211 |
timeouts[UDP_CT_UNREPLIED] = un->timeouts[UDP_CT_UNREPLIED]; timeouts[UDP_CT_REPLIED] = un->timeouts[UDP_CT_REPLIED]; |
509784623
|
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
if (tb[CTA_TIMEOUT_UDP_UNREPLIED]) { timeouts[UDP_CT_UNREPLIED] = ntohl(nla_get_be32(tb[CTA_TIMEOUT_UDP_UNREPLIED])) * HZ; } if (tb[CTA_TIMEOUT_UDP_REPLIED]) { timeouts[UDP_CT_REPLIED] = ntohl(nla_get_be32(tb[CTA_TIMEOUT_UDP_REPLIED])) * HZ; } return 0; } static int udp_timeout_obj_to_nlattr(struct sk_buff *skb, const void *data) { const unsigned int *timeouts = data; |
3c60a17b1
|
228 229 230 231 232 |
if (nla_put_be32(skb, CTA_TIMEOUT_UDP_UNREPLIED, htonl(timeouts[UDP_CT_UNREPLIED] / HZ)) || nla_put_be32(skb, CTA_TIMEOUT_UDP_REPLIED, htonl(timeouts[UDP_CT_REPLIED] / HZ))) goto nla_put_failure; |
509784623
|
233 234 235 236 237 238 239 240 241 242 243 |
return 0; nla_put_failure: return -ENOSPC; } static const struct nla_policy udp_timeout_nla_policy[CTA_TIMEOUT_UDP_MAX+1] = { [CTA_TIMEOUT_UDP_UNREPLIED] = { .type = NLA_U32 }, [CTA_TIMEOUT_UDP_REPLIED] = { .type = NLA_U32 }, }; |
a874752a1
|
244 |
#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ |
509784623
|
245 |
|
933a41e7e
|
246 |
#ifdef CONFIG_SYSCTL |
933a41e7e
|
247 248 |
static struct ctl_table udp_sysctl_table[] = { { |
933a41e7e
|
249 |
.procname = "nf_conntrack_udp_timeout", |
933a41e7e
|
250 251 |
.maxlen = sizeof(unsigned int), .mode = 0644, |
6d9f239a1
|
252 |
.proc_handler = proc_dointvec_jiffies, |
933a41e7e
|
253 254 |
}, { |
933a41e7e
|
255 |
.procname = "nf_conntrack_udp_timeout_stream", |
933a41e7e
|
256 257 |
.maxlen = sizeof(unsigned int), .mode = 0644, |
6d9f239a1
|
258 |
.proc_handler = proc_dointvec_jiffies, |
933a41e7e
|
259 |
}, |
f8572d8f2
|
260 |
{ } |
933a41e7e
|
261 262 |
}; #endif /* CONFIG_SYSCTL */ |
dee7364e0
|
263 264 |
static int udp_kmemdup_sysctl_table(struct nf_proto_net *pn, struct nf_udp_net *un) |
0ce490ad4
|
265 266 |
{ #ifdef CONFIG_SYSCTL |
0ce490ad4
|
267 268 269 270 271 272 273 274 275 276 277 278 |
if (pn->ctl_table) return 0; pn->ctl_table = kmemdup(udp_sysctl_table, sizeof(udp_sysctl_table), GFP_KERNEL); if (!pn->ctl_table) return -ENOMEM; pn->ctl_table[0].data = &un->timeouts[UDP_CT_UNREPLIED]; pn->ctl_table[1].data = &un->timeouts[UDP_CT_REPLIED]; #endif return 0; } |
dee7364e0
|
279 |
static int udp_init_net(struct net *net, u_int16_t proto) |
0ce490ad4
|
280 |
{ |
0ce490ad4
|
281 |
struct nf_udp_net *un = udp_pernet(net); |
dee7364e0
|
282 |
struct nf_proto_net *pn = &un->pn; |
0ce490ad4
|
283 |
|
dee7364e0
|
284 285 |
if (!pn->users) { int i; |
0ce490ad4
|
286 |
|
dee7364e0
|
287 288 |
for (i = 0; i < UDP_CT_MAX; i++) un->timeouts[i] = udp_timeouts[i]; |
0ce490ad4
|
289 |
} |
0ce490ad4
|
290 |
|
adf051684
|
291 |
return udp_kmemdup_sysctl_table(pn, un); |
0ce490ad4
|
292 |
} |
08911475d
|
293 294 295 296 |
static struct nf_proto_net *udp_get_net_proto(struct net *net) { return &net->ct.nf_ct_proto.udp.pn; } |
9dae47aba
|
297 |
const struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4 = |
9fb9cbb10
|
298 299 |
{ .l3proto = PF_INET, |
605dcad6c
|
300 |
.l4proto = IPPROTO_UDP, |
71d8c47fc
|
301 |
.allow_clash = true, |
9fb9cbb10
|
302 |
.packet = udp_packet, |
c0cd11566
|
303 |
#if IS_ENABLED(CONFIG_NF_CT_NETLINK) |
fdf708322
|
304 305 |
.tuple_to_nlattr = nf_ct_port_tuple_to_nlattr, .nlattr_to_tuple = nf_ct_port_nlattr_to_tuple, |
a400c30ed
|
306 |
.nlattr_tuple_size = nf_ct_port_nlattr_tuple_size, |
f73e924cd
|
307 |
.nla_policy = nf_ct_port_nla_policy, |
c1d10adb4
|
308 |
#endif |
a874752a1
|
309 |
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT |
509784623
|
310 311 312 313 314 315 316 |
.ctnl_timeout = { .nlattr_to_obj = udp_timeout_nlattr_to_obj, .obj_to_nlattr = udp_timeout_obj_to_nlattr, .nlattr_max = CTA_TIMEOUT_UDP_MAX, .obj_size = sizeof(unsigned int) * CTA_TIMEOUT_UDP_MAX, .nla_policy = udp_timeout_nla_policy, }, |
a874752a1
|
317 |
#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ |
dee7364e0
|
318 |
.init_net = udp_init_net, |
08911475d
|
319 |
.get_net_proto = udp_get_net_proto, |
9fb9cbb10
|
320 |
}; |
13b183391
|
321 |
EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_udp4); |
9fb9cbb10
|
322 |
|
e4781421e
|
323 |
#ifdef CONFIG_NF_CT_PROTO_UDPLITE |
9dae47aba
|
324 |
const struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite4 = |
e4781421e
|
325 326 327 |
{ .l3proto = PF_INET, .l4proto = IPPROTO_UDPLITE, |
e4781421e
|
328 |
.allow_clash = true, |
83d213fd9
|
329 |
.packet = udplite_packet, |
e4781421e
|
330 331 332 333 334 335 |
#if IS_ENABLED(CONFIG_NF_CT_NETLINK) .tuple_to_nlattr = nf_ct_port_tuple_to_nlattr, .nlattr_to_tuple = nf_ct_port_nlattr_to_tuple, .nlattr_tuple_size = nf_ct_port_nlattr_tuple_size, .nla_policy = nf_ct_port_nla_policy, #endif |
a874752a1
|
336 |
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT |
e4781421e
|
337 338 339 340 341 342 343 |
.ctnl_timeout = { .nlattr_to_obj = udp_timeout_nlattr_to_obj, .obj_to_nlattr = udp_timeout_obj_to_nlattr, .nlattr_max = CTA_TIMEOUT_UDP_MAX, .obj_size = sizeof(unsigned int) * CTA_TIMEOUT_UDP_MAX, .nla_policy = udp_timeout_nla_policy, }, |
a874752a1
|
344 |
#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ |
e4781421e
|
345 346 347 348 349 |
.init_net = udp_init_net, .get_net_proto = udp_get_net_proto, }; EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_udplite4); #endif |
9dae47aba
|
350 |
const struct nf_conntrack_l4proto nf_conntrack_l4proto_udp6 = |
9fb9cbb10
|
351 352 |
{ .l3proto = PF_INET6, |
605dcad6c
|
353 |
.l4proto = IPPROTO_UDP, |
71d8c47fc
|
354 |
.allow_clash = true, |
9fb9cbb10
|
355 |
.packet = udp_packet, |
c0cd11566
|
356 |
#if IS_ENABLED(CONFIG_NF_CT_NETLINK) |
fdf708322
|
357 358 |
.tuple_to_nlattr = nf_ct_port_tuple_to_nlattr, .nlattr_to_tuple = nf_ct_port_nlattr_to_tuple, |
a400c30ed
|
359 |
.nlattr_tuple_size = nf_ct_port_nlattr_tuple_size, |
f73e924cd
|
360 |
.nla_policy = nf_ct_port_nla_policy, |
c1d10adb4
|
361 |
#endif |
a874752a1
|
362 |
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT |
509784623
|
363 364 365 366 367 368 369 |
.ctnl_timeout = { .nlattr_to_obj = udp_timeout_nlattr_to_obj, .obj_to_nlattr = udp_timeout_obj_to_nlattr, .nlattr_max = CTA_TIMEOUT_UDP_MAX, .obj_size = sizeof(unsigned int) * CTA_TIMEOUT_UDP_MAX, .nla_policy = udp_timeout_nla_policy, }, |
a874752a1
|
370 |
#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ |
dee7364e0
|
371 |
.init_net = udp_init_net, |
08911475d
|
372 |
.get_net_proto = udp_get_net_proto, |
9fb9cbb10
|
373 |
}; |
13b183391
|
374 |
EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_udp6); |
e4781421e
|
375 376 |
#ifdef CONFIG_NF_CT_PROTO_UDPLITE |
9dae47aba
|
377 |
const struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite6 = |
e4781421e
|
378 379 380 |
{ .l3proto = PF_INET6, .l4proto = IPPROTO_UDPLITE, |
e4781421e
|
381 |
.allow_clash = true, |
83d213fd9
|
382 |
.packet = udplite_packet, |
e4781421e
|
383 384 385 386 387 388 |
#if IS_ENABLED(CONFIG_NF_CT_NETLINK) .tuple_to_nlattr = nf_ct_port_tuple_to_nlattr, .nlattr_to_tuple = nf_ct_port_nlattr_to_tuple, .nlattr_tuple_size = nf_ct_port_nlattr_tuple_size, .nla_policy = nf_ct_port_nla_policy, #endif |
a874752a1
|
389 |
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT |
e4781421e
|
390 391 392 393 394 395 396 |
.ctnl_timeout = { .nlattr_to_obj = udp_timeout_nlattr_to_obj, .obj_to_nlattr = udp_timeout_obj_to_nlattr, .nlattr_max = CTA_TIMEOUT_UDP_MAX, .obj_size = sizeof(unsigned int) * CTA_TIMEOUT_UDP_MAX, .nla_policy = udp_timeout_nla_policy, }, |
a874752a1
|
397 |
#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ |
e4781421e
|
398 399 400 401 402 |
.init_net = udp_init_net, .get_net_proto = udp_get_net_proto, }; EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_udplite6); #endif |