Blame view
net/netfilter/nf_conntrack_proto_udp.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-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> |
f01ffbd6e
|
25 |
#include <net/netfilter/nf_log.h> |
9d2493f88
|
26 27 |
#include <net/netfilter/ipv4/nf_conntrack_ipv4.h> #include <net/netfilter/ipv6/nf_conntrack_ipv6.h> |
9fb9cbb10
|
28 |
|
5a41db94c
|
29 30 31 32 |
static unsigned int udp_timeouts[UDP_CT_MAX] = { [UDP_CT_UNREPLIED] = 30*HZ, [UDP_CT_REPLIED] = 180*HZ, }; |
9fb9cbb10
|
33 |
|
0ce490ad4
|
34 35 36 37 |
static inline struct nf_udp_net *udp_pernet(struct net *net) { return &net->ct.nf_ct_proto.udp; } |
09f263cd3
|
38 |
static bool udp_pkt_to_tuple(const struct sk_buff *skb, |
9fb9cbb10
|
39 |
unsigned int dataoff, |
a31f1adc0
|
40 |
struct net *net, |
9fb9cbb10
|
41 42 |
struct nf_conntrack_tuple *tuple) { |
da3f13c95
|
43 44 |
const struct udphdr *hp; struct udphdr _hdr; |
9fb9cbb10
|
45 46 47 48 |
/* Actually only need first 8 bytes. */ hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr); if (hp == NULL) |
09f263cd3
|
49 |
return false; |
9fb9cbb10
|
50 51 52 |
tuple->src.u.udp.port = hp->source; tuple->dst.u.udp.port = hp->dest; |
09f263cd3
|
53 |
return true; |
9fb9cbb10
|
54 |
} |
09f263cd3
|
55 56 |
static bool udp_invert_tuple(struct nf_conntrack_tuple *tuple, const struct nf_conntrack_tuple *orig) |
9fb9cbb10
|
57 58 59 |
{ tuple->src.u.udp.port = orig->dst.u.udp.port; tuple->dst.u.udp.port = orig->src.u.udp.port; |
09f263cd3
|
60 |
return true; |
9fb9cbb10
|
61 62 63 |
} /* Print out the per-protocol part of the tuple. */ |
824f1fbee
|
64 65 |
static void udp_print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple) |
9fb9cbb10
|
66 |
{ |
824f1fbee
|
67 68 69 |
seq_printf(s, "sport=%hu dport=%hu ", ntohs(tuple->src.u.udp.port), ntohs(tuple->dst.u.udp.port)); |
9fb9cbb10
|
70 |
} |
2c8503f55
|
71 72 |
static unsigned int *udp_get_timeouts(struct net *net) { |
0ce490ad4
|
73 |
return udp_pernet(net)->timeouts; |
2c8503f55
|
74 |
} |
9fb9cbb10
|
75 |
/* Returns verdict for packet, and may modify conntracktype */ |
c88130bcd
|
76 |
static int udp_packet(struct nf_conn *ct, |
9fb9cbb10
|
77 78 79 |
const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, |
76108cea0
|
80 |
u_int8_t pf, |
2c8503f55
|
81 82 |
unsigned int hooknum, unsigned int *timeouts) |
9fb9cbb10
|
83 84 85 |
{ /* If we've seen traffic both ways, this is some kind of UDP stream. Extend timeout. */ |
c88130bcd
|
86 |
if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { |
5a41db94c
|
87 |
nf_ct_refresh_acct(ct, ctinfo, skb, |
2c8503f55
|
88 |
timeouts[UDP_CT_REPLIED]); |
9fb9cbb10
|
89 |
/* Also, more likely to be important, and not a probe */ |
c88130bcd
|
90 |
if (!test_and_set_bit(IPS_ASSURED_BIT, &ct->status)) |
858b31330
|
91 |
nf_conntrack_event_cache(IPCT_ASSURED, ct); |
5a41db94c
|
92 93 |
} else { nf_ct_refresh_acct(ct, ctinfo, skb, |
2c8503f55
|
94 |
timeouts[UDP_CT_UNREPLIED]); |
5a41db94c
|
95 |
} |
9fb9cbb10
|
96 97 98 99 |
return NF_ACCEPT; } /* Called when a new connection for this protocol found. */ |
09f263cd3
|
100 |
static bool udp_new(struct nf_conn *ct, const struct sk_buff *skb, |
2c8503f55
|
101 |
unsigned int dataoff, unsigned int *timeouts) |
9fb9cbb10
|
102 |
{ |
09f263cd3
|
103 |
return true; |
9fb9cbb10
|
104 |
} |
8fea97ec1
|
105 106 |
static int udp_error(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info *ctinfo, |
76108cea0
|
107 |
u_int8_t pf, |
96f6bf82e
|
108 |
unsigned int hooknum) |
9fb9cbb10
|
109 110 |
{ unsigned int udplen = skb->len - dataoff; |
da3f13c95
|
111 112 |
const struct udphdr *hdr; struct udphdr _hdr; |
9fb9cbb10
|
113 114 115 116 |
/* Header is too small? */ hdr = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr); if (hdr == NULL) { |
c2a2c7e0c
|
117 |
if (LOG_INVALID(net, IPPROTO_UDP)) |
30e0c6a6b
|
118 |
nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, |
9fb9cbb10
|
119 120 121 122 123 124 |
"nf_ct_udp: short packet "); return -NF_ACCEPT; } /* Truncated/malformed packets */ if (ntohs(hdr->len) > udplen || ntohs(hdr->len) < sizeof(*hdr)) { |
c2a2c7e0c
|
125 |
if (LOG_INVALID(net, IPPROTO_UDP)) |
30e0c6a6b
|
126 |
nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, |
9fb9cbb10
|
127 128 129 130 131 132 133 134 135 136 |
"nf_ct_udp: truncated/malformed packet "); return -NF_ACCEPT; } /* Packet with no checksum */ if (!hdr->check) return NF_ACCEPT; /* Checksum invalid? Ignore. * We skip checking packets on the outgoing path |
84fa7933a
|
137 |
* because the checksum is assumed to be correct. |
9fb9cbb10
|
138 |
* FIXME: Source route IP option packets --RR */ |
c04d05529
|
139 |
if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING && |
96f6bf82e
|
140 |
nf_checksum(skb, hooknum, dataoff, IPPROTO_UDP, pf)) { |
c2a2c7e0c
|
141 |
if (LOG_INVALID(net, IPPROTO_UDP)) |
30e0c6a6b
|
142 |
nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, |
9fb9cbb10
|
143 144 145 146 147 148 |
"nf_ct_udp: bad UDP checksum "); return -NF_ACCEPT; } return NF_ACCEPT; } |
509784623
|
149 150 151 152 |
#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT) #include <linux/netfilter/nfnetlink.h> #include <linux/netfilter/nfnetlink_cttimeout.h> |
8264deb81
|
153 154 |
static int udp_timeout_nlattr_to_obj(struct nlattr *tb[], struct net *net, void *data) |
509784623
|
155 156 |
{ unsigned int *timeouts = data; |
8264deb81
|
157 |
struct nf_udp_net *un = udp_pernet(net); |
509784623
|
158 159 |
/* set default timeouts for UDP. */ |
8264deb81
|
160 161 |
timeouts[UDP_CT_UNREPLIED] = un->timeouts[UDP_CT_UNREPLIED]; timeouts[UDP_CT_REPLIED] = un->timeouts[UDP_CT_REPLIED]; |
509784623
|
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
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
|
178 179 180 181 182 |
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
|
183 184 185 186 187 188 189 190 191 192 193 194 |
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 }, }; #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ |
933a41e7e
|
195 |
#ifdef CONFIG_SYSCTL |
933a41e7e
|
196 197 |
static struct ctl_table udp_sysctl_table[] = { { |
933a41e7e
|
198 |
.procname = "nf_conntrack_udp_timeout", |
933a41e7e
|
199 200 |
.maxlen = sizeof(unsigned int), .mode = 0644, |
6d9f239a1
|
201 |
.proc_handler = proc_dointvec_jiffies, |
933a41e7e
|
202 203 |
}, { |
933a41e7e
|
204 |
.procname = "nf_conntrack_udp_timeout_stream", |
933a41e7e
|
205 206 |
.maxlen = sizeof(unsigned int), .mode = 0644, |
6d9f239a1
|
207 |
.proc_handler = proc_dointvec_jiffies, |
933a41e7e
|
208 |
}, |
f8572d8f2
|
209 |
{ } |
933a41e7e
|
210 |
}; |
a999e6837
|
211 212 213 |
#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT static struct ctl_table udp_compat_sysctl_table[] = { { |
a999e6837
|
214 |
.procname = "ip_conntrack_udp_timeout", |
a999e6837
|
215 216 |
.maxlen = sizeof(unsigned int), .mode = 0644, |
6d9f239a1
|
217 |
.proc_handler = proc_dointvec_jiffies, |
a999e6837
|
218 219 |
}, { |
a999e6837
|
220 |
.procname = "ip_conntrack_udp_timeout_stream", |
a999e6837
|
221 222 |
.maxlen = sizeof(unsigned int), .mode = 0644, |
6d9f239a1
|
223 |
.proc_handler = proc_dointvec_jiffies, |
a999e6837
|
224 |
}, |
f8572d8f2
|
225 |
{ } |
a999e6837
|
226 227 |
}; #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */ |
933a41e7e
|
228 |
#endif /* CONFIG_SYSCTL */ |
dee7364e0
|
229 230 |
static int udp_kmemdup_sysctl_table(struct nf_proto_net *pn, struct nf_udp_net *un) |
0ce490ad4
|
231 232 |
{ #ifdef CONFIG_SYSCTL |
0ce490ad4
|
233 234 235 236 237 238 239 240 241 242 243 244 |
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
|
245 246 |
static int udp_kmemdup_compat_sysctl_table(struct nf_proto_net *pn, struct nf_udp_net *un) |
0ce490ad4
|
247 248 249 |
{ #ifdef CONFIG_SYSCTL #ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT |
0ce490ad4
|
250 251 252 253 254 255 256 257 258 259 260 261 |
pn->ctl_compat_table = kmemdup(udp_compat_sysctl_table, sizeof(udp_compat_sysctl_table), GFP_KERNEL); if (!pn->ctl_compat_table) return -ENOMEM; pn->ctl_compat_table[0].data = &un->timeouts[UDP_CT_UNREPLIED]; pn->ctl_compat_table[1].data = &un->timeouts[UDP_CT_REPLIED]; #endif #endif return 0; } |
dee7364e0
|
262 |
static int udp_init_net(struct net *net, u_int16_t proto) |
0ce490ad4
|
263 264 265 |
{ int ret; struct nf_udp_net *un = udp_pernet(net); |
dee7364e0
|
266 |
struct nf_proto_net *pn = &un->pn; |
0ce490ad4
|
267 |
|
dee7364e0
|
268 269 |
if (!pn->users) { int i; |
0ce490ad4
|
270 |
|
dee7364e0
|
271 272 |
for (i = 0; i < UDP_CT_MAX; i++) un->timeouts[i] = udp_timeouts[i]; |
0ce490ad4
|
273 |
} |
0ce490ad4
|
274 |
|
dee7364e0
|
275 276 277 278 |
if (proto == AF_INET) { ret = udp_kmemdup_compat_sysctl_table(pn, un); if (ret < 0) return ret; |
0ce490ad4
|
279 |
|
dee7364e0
|
280 281 282 283 284 285 286 |
ret = udp_kmemdup_sysctl_table(pn, un); if (ret < 0) nf_ct_kfree_compat_sysctl_table(pn); } else ret = udp_kmemdup_sysctl_table(pn, un); return ret; |
0ce490ad4
|
287 |
} |
08911475d
|
288 289 290 291 |
static struct nf_proto_net *udp_get_net_proto(struct net *net) { return &net->ct.nf_ct_proto.udp.pn; } |
61075af51
|
292 |
struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4 __read_mostly = |
9fb9cbb10
|
293 294 |
{ .l3proto = PF_INET, |
605dcad6c
|
295 |
.l4proto = IPPROTO_UDP, |
9fb9cbb10
|
296 297 298 299 |
.name = "udp", .pkt_to_tuple = udp_pkt_to_tuple, .invert_tuple = udp_invert_tuple, .print_tuple = udp_print_tuple, |
9fb9cbb10
|
300 |
.packet = udp_packet, |
2c8503f55
|
301 |
.get_timeouts = udp_get_timeouts, |
9fb9cbb10
|
302 |
.new = udp_new, |
96f6bf82e
|
303 |
.error = udp_error, |
c0cd11566
|
304 |
#if IS_ENABLED(CONFIG_NF_CT_NETLINK) |
fdf708322
|
305 306 |
.tuple_to_nlattr = nf_ct_port_tuple_to_nlattr, .nlattr_to_tuple = nf_ct_port_nlattr_to_tuple, |
a400c30ed
|
307 |
.nlattr_tuple_size = nf_ct_port_nlattr_tuple_size, |
f73e924cd
|
308 |
.nla_policy = nf_ct_port_nla_policy, |
c1d10adb4
|
309 |
#endif |
509784623
|
310 311 312 313 314 315 316 317 318 |
#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT) .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, }, #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ |
dee7364e0
|
319 |
.init_net = udp_init_net, |
08911475d
|
320 |
.get_net_proto = udp_get_net_proto, |
9fb9cbb10
|
321 |
}; |
13b183391
|
322 |
EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_udp4); |
9fb9cbb10
|
323 |
|
61075af51
|
324 |
struct nf_conntrack_l4proto nf_conntrack_l4proto_udp6 __read_mostly = |
9fb9cbb10
|
325 326 |
{ .l3proto = PF_INET6, |
605dcad6c
|
327 |
.l4proto = IPPROTO_UDP, |
9fb9cbb10
|
328 329 330 331 |
.name = "udp", .pkt_to_tuple = udp_pkt_to_tuple, .invert_tuple = udp_invert_tuple, .print_tuple = udp_print_tuple, |
9fb9cbb10
|
332 |
.packet = udp_packet, |
2c8503f55
|
333 |
.get_timeouts = udp_get_timeouts, |
9fb9cbb10
|
334 |
.new = udp_new, |
96f6bf82e
|
335 |
.error = udp_error, |
c0cd11566
|
336 |
#if IS_ENABLED(CONFIG_NF_CT_NETLINK) |
fdf708322
|
337 338 |
.tuple_to_nlattr = nf_ct_port_tuple_to_nlattr, .nlattr_to_tuple = nf_ct_port_nlattr_to_tuple, |
a400c30ed
|
339 |
.nlattr_tuple_size = nf_ct_port_nlattr_tuple_size, |
f73e924cd
|
340 |
.nla_policy = nf_ct_port_nla_policy, |
c1d10adb4
|
341 |
#endif |
509784623
|
342 343 344 345 346 347 348 349 350 |
#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT) .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, }, #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ |
dee7364e0
|
351 |
.init_net = udp_init_net, |
08911475d
|
352 |
.get_net_proto = udp_get_net_proto, |
9fb9cbb10
|
353 |
}; |
13b183391
|
354 |
EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_udp6); |