Commit | Line | Data |
---|---|---|
9fb9cbb1 YK |
1 | /* (C) 1999-2001 Paul `Rusty' Russell |
2 | * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> | |
f229f6ce | 3 | * (C) 2006-2012 Patrick McHardy <kaber@trash.net> |
9fb9cbb1 YK |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License version 2 as | |
7 | * published by the Free Software Foundation. | |
9fb9cbb1 YK |
8 | */ |
9 | ||
10 | #include <linux/types.h> | |
9fb9cbb1 YK |
11 | #include <linux/timer.h> |
12 | #include <linux/module.h> | |
9fb9cbb1 YK |
13 | #include <linux/udp.h> |
14 | #include <linux/seq_file.h> | |
15 | #include <linux/skbuff.h> | |
16 | #include <linux/ipv6.h> | |
17 | #include <net/ip6_checksum.h> | |
18 | #include <net/checksum.h> | |
f6180121 | 19 | |
9fb9cbb1 YK |
20 | #include <linux/netfilter.h> |
21 | #include <linux/netfilter_ipv4.h> | |
22 | #include <linux/netfilter_ipv6.h> | |
605dcad6 | 23 | #include <net/netfilter/nf_conntrack_l4proto.h> |
f6180121 | 24 | #include <net/netfilter/nf_conntrack_ecache.h> |
c779e849 | 25 | #include <net/netfilter/nf_conntrack_timeout.h> |
f01ffbd6 | 26 | #include <net/netfilter/nf_log.h> |
9d2493f8 CP |
27 | #include <net/netfilter/ipv4/nf_conntrack_ipv4.h> |
28 | #include <net/netfilter/ipv6/nf_conntrack_ipv6.h> | |
9fb9cbb1 | 29 | |
2c9e8637 | 30 | static const unsigned int udp_timeouts[UDP_CT_MAX] = { |
5a41db94 PNA |
31 | [UDP_CT_UNREPLIED] = 30*HZ, |
32 | [UDP_CT_REPLIED] = 180*HZ, | |
33 | }; | |
9fb9cbb1 | 34 | |
2c8503f5 PNA |
35 | static unsigned int *udp_get_timeouts(struct net *net) |
36 | { | |
a95a7774 | 37 | return nf_udp_pernet(net)->timeouts; |
2c8503f5 PNA |
38 | } |
39 | ||
83d213fd FW |
40 | static void udp_error_log(const struct sk_buff *skb, |
41 | const struct nf_hook_state *state, | |
42 | const char *msg) | |
43 | { | |
44 | nf_l4proto_log_invalid(skb, state->net, state->pf, | |
45 | IPPROTO_UDP, "%s", msg); | |
46 | } | |
47 | ||
48 | static bool udp_error(struct sk_buff *skb, | |
49 | unsigned int dataoff, | |
50 | const struct nf_hook_state *state) | |
51 | { | |
52 | unsigned int udplen = skb->len - dataoff; | |
53 | const struct udphdr *hdr; | |
54 | struct udphdr _hdr; | |
55 | ||
56 | /* Header is too small? */ | |
57 | hdr = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr); | |
58 | if (!hdr) { | |
59 | udp_error_log(skb, state, "short packet"); | |
60 | return true; | |
61 | } | |
62 | ||
63 | /* Truncated/malformed packets */ | |
64 | if (ntohs(hdr->len) > udplen || ntohs(hdr->len) < sizeof(*hdr)) { | |
65 | udp_error_log(skb, state, "truncated/malformed packet"); | |
66 | return true; | |
67 | } | |
68 | ||
69 | /* Packet with no checksum */ | |
70 | if (!hdr->check) | |
71 | return false; | |
72 | ||
73 | /* Checksum invalid? Ignore. | |
74 | * We skip checking packets on the outgoing path | |
75 | * because the checksum is assumed to be correct. | |
76 | * FIXME: Source route IP option packets --RR */ | |
77 | if (state->hook == NF_INET_PRE_ROUTING && | |
78 | state->net->ct.sysctl_checksum && | |
79 | nf_checksum(skb, state->hook, dataoff, IPPROTO_UDP, state->pf)) { | |
80 | udp_error_log(skb, state, "bad checksum"); | |
81 | return true; | |
82 | } | |
83 | ||
84 | return false; | |
85 | } | |
86 | ||
9fb9cbb1 | 87 | /* Returns verdict for packet, and may modify conntracktype */ |
c88130bc | 88 | static int udp_packet(struct nf_conn *ct, |
83d213fd | 89 | struct sk_buff *skb, |
9fb9cbb1 | 90 | unsigned int dataoff, |
93e66024 FW |
91 | enum ip_conntrack_info ctinfo, |
92 | const struct nf_hook_state *state) | |
9fb9cbb1 | 93 | { |
c779e849 FW |
94 | unsigned int *timeouts; |
95 | ||
83d213fd FW |
96 | if (udp_error(skb, dataoff, state)) |
97 | return -NF_ACCEPT; | |
98 | ||
c779e849 FW |
99 | timeouts = nf_ct_timeout_lookup(ct); |
100 | if (!timeouts) | |
101 | timeouts = udp_get_timeouts(nf_ct_net(ct)); | |
102 | ||
d535c8a6 FW |
103 | if (!nf_ct_is_confirmed(ct)) |
104 | ct->proto.udp.stream_ts = 2 * HZ + jiffies; | |
105 | ||
9fb9cbb1 | 106 | /* If we've seen traffic both ways, this is some kind of UDP |
d535c8a6 FW |
107 | * stream. Set Assured. |
108 | */ | |
c88130bc | 109 | if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { |
d535c8a6 FW |
110 | unsigned long extra = timeouts[UDP_CT_UNREPLIED]; |
111 | ||
112 | /* Still active after two seconds? Extend timeout. */ | |
113 | if (time_after(jiffies, ct->proto.udp.stream_ts)) | |
114 | extra = timeouts[UDP_CT_REPLIED]; | |
115 | ||
116 | nf_ct_refresh_acct(ct, ctinfo, skb, extra); | |
117 | ||
9fb9cbb1 | 118 | /* Also, more likely to be important, and not a probe */ |
c88130bc | 119 | if (!test_and_set_bit(IPS_ASSURED_BIT, &ct->status)) |
858b3133 | 120 | nf_conntrack_event_cache(IPCT_ASSURED, ct); |
5a41db94 PNA |
121 | } else { |
122 | nf_ct_refresh_acct(ct, ctinfo, skb, | |
2c8503f5 | 123 | timeouts[UDP_CT_UNREPLIED]); |
5a41db94 | 124 | } |
9fb9cbb1 YK |
125 | return NF_ACCEPT; |
126 | } | |
127 | ||
e4781421 | 128 | #ifdef CONFIG_NF_CT_PROTO_UDPLITE |
93e66024 FW |
129 | static void udplite_error_log(const struct sk_buff *skb, |
130 | const struct nf_hook_state *state, | |
131 | const char *msg) | |
c4f3db15 | 132 | { |
93e66024 FW |
133 | nf_l4proto_log_invalid(skb, state->net, state->pf, |
134 | IPPROTO_UDPLITE, "%s", msg); | |
c4f3db15 FW |
135 | } |
136 | ||
83d213fd FW |
137 | static bool udplite_error(struct sk_buff *skb, |
138 | unsigned int dataoff, | |
139 | const struct nf_hook_state *state) | |
e4781421 FW |
140 | { |
141 | unsigned int udplen = skb->len - dataoff; | |
142 | const struct udphdr *hdr; | |
143 | struct udphdr _hdr; | |
144 | unsigned int cscov; | |
145 | ||
146 | /* Header is too small? */ | |
147 | hdr = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr); | |
148 | if (!hdr) { | |
93e66024 | 149 | udplite_error_log(skb, state, "short packet"); |
83d213fd | 150 | return true; |
e4781421 FW |
151 | } |
152 | ||
153 | cscov = ntohs(hdr->len); | |
154 | if (cscov == 0) { | |
155 | cscov = udplen; | |
156 | } else if (cscov < sizeof(*hdr) || cscov > udplen) { | |
93e66024 | 157 | udplite_error_log(skb, state, "invalid checksum coverage"); |
83d213fd | 158 | return true; |
e4781421 FW |
159 | } |
160 | ||
161 | /* UDPLITE mandates checksums */ | |
162 | if (!hdr->check) { | |
93e66024 | 163 | udplite_error_log(skb, state, "checksum missing"); |
83d213fd | 164 | return true; |
e4781421 FW |
165 | } |
166 | ||
167 | /* Checksum invalid? Ignore. */ | |
93e66024 FW |
168 | if (state->hook == NF_INET_PRE_ROUTING && |
169 | state->net->ct.sysctl_checksum && | |
170 | nf_checksum_partial(skb, state->hook, dataoff, cscov, IPPROTO_UDP, | |
171 | state->pf)) { | |
172 | udplite_error_log(skb, state, "bad checksum"); | |
83d213fd | 173 | return true; |
e4781421 FW |
174 | } |
175 | ||
83d213fd | 176 | return false; |
e4781421 | 177 | } |
e4781421 | 178 | |
83d213fd FW |
179 | /* Returns verdict for packet, and may modify conntracktype */ |
180 | static int udplite_packet(struct nf_conn *ct, | |
181 | struct sk_buff *skb, | |
182 | unsigned int dataoff, | |
183 | enum ip_conntrack_info ctinfo, | |
184 | const struct nf_hook_state *state) | |
9fb9cbb1 | 185 | { |
83d213fd | 186 | unsigned int *timeouts; |
9fb9cbb1 | 187 | |
83d213fd | 188 | if (udplite_error(skb, dataoff, state)) |
9fb9cbb1 | 189 | return -NF_ACCEPT; |
9fb9cbb1 | 190 | |
83d213fd FW |
191 | timeouts = nf_ct_timeout_lookup(ct); |
192 | if (!timeouts) | |
193 | timeouts = udp_get_timeouts(nf_ct_net(ct)); | |
9fb9cbb1 | 194 | |
83d213fd FW |
195 | /* If we've seen traffic both ways, this is some kind of UDP |
196 | stream. Extend timeout. */ | |
197 | if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { | |
198 | nf_ct_refresh_acct(ct, ctinfo, skb, | |
199 | timeouts[UDP_CT_REPLIED]); | |
200 | /* Also, more likely to be important, and not a probe */ | |
201 | if (!test_and_set_bit(IPS_ASSURED_BIT, &ct->status)) | |
202 | nf_conntrack_event_cache(IPCT_ASSURED, ct); | |
203 | } else { | |
204 | nf_ct_refresh_acct(ct, ctinfo, skb, | |
205 | timeouts[UDP_CT_UNREPLIED]); | |
9fb9cbb1 | 206 | } |
9fb9cbb1 YK |
207 | return NF_ACCEPT; |
208 | } | |
83d213fd | 209 | #endif |
9fb9cbb1 | 210 | |
a874752a | 211 | #ifdef CONFIG_NF_CONNTRACK_TIMEOUT |
50978462 PNA |
212 | |
213 | #include <linux/netfilter/nfnetlink.h> | |
214 | #include <linux/netfilter/nfnetlink_cttimeout.h> | |
215 | ||
8264deb8 G |
216 | static int udp_timeout_nlattr_to_obj(struct nlattr *tb[], |
217 | struct net *net, void *data) | |
50978462 PNA |
218 | { |
219 | unsigned int *timeouts = data; | |
a95a7774 | 220 | struct nf_udp_net *un = nf_udp_pernet(net); |
50978462 | 221 | |
c779e849 FW |
222 | if (!timeouts) |
223 | timeouts = un->timeouts; | |
224 | ||
50978462 | 225 | /* set default timeouts for UDP. */ |
8264deb8 G |
226 | timeouts[UDP_CT_UNREPLIED] = un->timeouts[UDP_CT_UNREPLIED]; |
227 | timeouts[UDP_CT_REPLIED] = un->timeouts[UDP_CT_REPLIED]; | |
50978462 PNA |
228 | |
229 | if (tb[CTA_TIMEOUT_UDP_UNREPLIED]) { | |
230 | timeouts[UDP_CT_UNREPLIED] = | |
231 | ntohl(nla_get_be32(tb[CTA_TIMEOUT_UDP_UNREPLIED])) * HZ; | |
232 | } | |
233 | if (tb[CTA_TIMEOUT_UDP_REPLIED]) { | |
234 | timeouts[UDP_CT_REPLIED] = | |
235 | ntohl(nla_get_be32(tb[CTA_TIMEOUT_UDP_REPLIED])) * HZ; | |
236 | } | |
237 | return 0; | |
238 | } | |
239 | ||
240 | static int | |
241 | udp_timeout_obj_to_nlattr(struct sk_buff *skb, const void *data) | |
242 | { | |
243 | const unsigned int *timeouts = data; | |
244 | ||
3c60a17b DM |
245 | if (nla_put_be32(skb, CTA_TIMEOUT_UDP_UNREPLIED, |
246 | htonl(timeouts[UDP_CT_UNREPLIED] / HZ)) || | |
247 | nla_put_be32(skb, CTA_TIMEOUT_UDP_REPLIED, | |
248 | htonl(timeouts[UDP_CT_REPLIED] / HZ))) | |
249 | goto nla_put_failure; | |
50978462 PNA |
250 | return 0; |
251 | ||
252 | nla_put_failure: | |
253 | return -ENOSPC; | |
254 | } | |
255 | ||
256 | static const struct nla_policy | |
257 | udp_timeout_nla_policy[CTA_TIMEOUT_UDP_MAX+1] = { | |
258 | [CTA_TIMEOUT_UDP_UNREPLIED] = { .type = NLA_U32 }, | |
259 | [CTA_TIMEOUT_UDP_REPLIED] = { .type = NLA_U32 }, | |
260 | }; | |
a874752a | 261 | #endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ |
50978462 | 262 | |
933a41e7 | 263 | #ifdef CONFIG_SYSCTL |
933a41e7 PM |
264 | static struct ctl_table udp_sysctl_table[] = { |
265 | { | |
933a41e7 | 266 | .procname = "nf_conntrack_udp_timeout", |
933a41e7 PM |
267 | .maxlen = sizeof(unsigned int), |
268 | .mode = 0644, | |
6d9f239a | 269 | .proc_handler = proc_dointvec_jiffies, |
933a41e7 PM |
270 | }, |
271 | { | |
933a41e7 | 272 | .procname = "nf_conntrack_udp_timeout_stream", |
933a41e7 PM |
273 | .maxlen = sizeof(unsigned int), |
274 | .mode = 0644, | |
6d9f239a | 275 | .proc_handler = proc_dointvec_jiffies, |
933a41e7 | 276 | }, |
f8572d8f | 277 | { } |
933a41e7 PM |
278 | }; |
279 | #endif /* CONFIG_SYSCTL */ | |
280 | ||
dee7364e G |
281 | static int udp_kmemdup_sysctl_table(struct nf_proto_net *pn, |
282 | struct nf_udp_net *un) | |
0ce490ad G |
283 | { |
284 | #ifdef CONFIG_SYSCTL | |
0ce490ad G |
285 | if (pn->ctl_table) |
286 | return 0; | |
287 | pn->ctl_table = kmemdup(udp_sysctl_table, | |
288 | sizeof(udp_sysctl_table), | |
289 | GFP_KERNEL); | |
290 | if (!pn->ctl_table) | |
291 | return -ENOMEM; | |
292 | pn->ctl_table[0].data = &un->timeouts[UDP_CT_UNREPLIED]; | |
293 | pn->ctl_table[1].data = &un->timeouts[UDP_CT_REPLIED]; | |
294 | #endif | |
295 | return 0; | |
296 | } | |
297 | ||
ca2ca6e1 | 298 | static int udp_init_net(struct net *net) |
0ce490ad | 299 | { |
a95a7774 | 300 | struct nf_udp_net *un = nf_udp_pernet(net); |
dee7364e | 301 | struct nf_proto_net *pn = &un->pn; |
0ce490ad | 302 | |
dee7364e G |
303 | if (!pn->users) { |
304 | int i; | |
0ce490ad | 305 | |
dee7364e G |
306 | for (i = 0; i < UDP_CT_MAX; i++) |
307 | un->timeouts[i] = udp_timeouts[i]; | |
0ce490ad | 308 | } |
0ce490ad | 309 | |
adf05168 | 310 | return udp_kmemdup_sysctl_table(pn, un); |
0ce490ad G |
311 | } |
312 | ||
08911475 PNA |
313 | static struct nf_proto_net *udp_get_net_proto(struct net *net) |
314 | { | |
315 | return &net->ct.nf_ct_proto.udp.pn; | |
316 | } | |
317 | ||
dd2934a9 | 318 | const struct nf_conntrack_l4proto nf_conntrack_l4proto_udp = |
9fb9cbb1 | 319 | { |
605dcad6 | 320 | .l4proto = IPPROTO_UDP, |
71d8c47f | 321 | .allow_clash = true, |
9fb9cbb1 | 322 | .packet = udp_packet, |
c0cd1156 | 323 | #if IS_ENABLED(CONFIG_NF_CT_NETLINK) |
fdf70832 PM |
324 | .tuple_to_nlattr = nf_ct_port_tuple_to_nlattr, |
325 | .nlattr_to_tuple = nf_ct_port_nlattr_to_tuple, | |
a400c30e | 326 | .nlattr_tuple_size = nf_ct_port_nlattr_tuple_size, |
f73e924c | 327 | .nla_policy = nf_ct_port_nla_policy, |
c1d10adb | 328 | #endif |
a874752a | 329 | #ifdef CONFIG_NF_CONNTRACK_TIMEOUT |
50978462 PNA |
330 | .ctnl_timeout = { |
331 | .nlattr_to_obj = udp_timeout_nlattr_to_obj, | |
332 | .obj_to_nlattr = udp_timeout_obj_to_nlattr, | |
333 | .nlattr_max = CTA_TIMEOUT_UDP_MAX, | |
334 | .obj_size = sizeof(unsigned int) * CTA_TIMEOUT_UDP_MAX, | |
335 | .nla_policy = udp_timeout_nla_policy, | |
336 | }, | |
a874752a | 337 | #endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ |
dee7364e | 338 | .init_net = udp_init_net, |
08911475 | 339 | .get_net_proto = udp_get_net_proto, |
9fb9cbb1 YK |
340 | }; |
341 | ||
e4781421 | 342 | #ifdef CONFIG_NF_CT_PROTO_UDPLITE |
dd2934a9 | 343 | const struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite = |
e4781421 | 344 | { |
e4781421 | 345 | .l4proto = IPPROTO_UDPLITE, |
e4781421 | 346 | .allow_clash = true, |
83d213fd | 347 | .packet = udplite_packet, |
e4781421 FW |
348 | #if IS_ENABLED(CONFIG_NF_CT_NETLINK) |
349 | .tuple_to_nlattr = nf_ct_port_tuple_to_nlattr, | |
350 | .nlattr_to_tuple = nf_ct_port_nlattr_to_tuple, | |
351 | .nlattr_tuple_size = nf_ct_port_nlattr_tuple_size, | |
352 | .nla_policy = nf_ct_port_nla_policy, | |
353 | #endif | |
a874752a | 354 | #ifdef CONFIG_NF_CONNTRACK_TIMEOUT |
e4781421 FW |
355 | .ctnl_timeout = { |
356 | .nlattr_to_obj = udp_timeout_nlattr_to_obj, | |
357 | .obj_to_nlattr = udp_timeout_obj_to_nlattr, | |
358 | .nlattr_max = CTA_TIMEOUT_UDP_MAX, | |
359 | .obj_size = sizeof(unsigned int) * CTA_TIMEOUT_UDP_MAX, | |
360 | .nla_policy = udp_timeout_nla_policy, | |
361 | }, | |
a874752a | 362 | #endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ |
e4781421 FW |
363 | .init_net = udp_init_net, |
364 | .get_net_proto = udp_get_net_proto, | |
365 | }; | |
e4781421 | 366 | #endif |