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