Blame view
net/netfilter/nf_conntrack_proto_udp.c
8.4 KB
d2912cb15
|
1 |
// SPDX-License-Identifier: GPL-2.0-only |
9fb9cbb10
|
2 3 |
/* (C) 1999-2001 Paul `Rusty' Russell * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> |
f229f6ce4
|
4 |
* (C) 2006-2012 Patrick McHardy <kaber@trash.net> |
9fb9cbb10
|
5 6 7 |
*/ #include <linux/types.h> |
9fb9cbb10
|
8 9 |
#include <linux/timer.h> #include <linux/module.h> |
9fb9cbb10
|
10 11 12 13 14 15 |
#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
|
16 |
|
9fb9cbb10
|
17 18 19 |
#include <linux/netfilter.h> #include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv6.h> |
605dcad6c
|
20 |
#include <net/netfilter/nf_conntrack_l4proto.h> |
f61801218
|
21 |
#include <net/netfilter/nf_conntrack_ecache.h> |
c779e8496
|
22 |
#include <net/netfilter/nf_conntrack_timeout.h> |
f01ffbd6e
|
23 |
#include <net/netfilter/nf_log.h> |
9d2493f88
|
24 25 |
#include <net/netfilter/ipv4/nf_conntrack_ipv4.h> #include <net/netfilter/ipv6/nf_conntrack_ipv6.h> |
9fb9cbb10
|
26 |
|
2c9e8637e
|
27 |
static const unsigned int udp_timeouts[UDP_CT_MAX] = { |
5a41db94c
|
28 |
[UDP_CT_UNREPLIED] = 30*HZ, |
294304e4c
|
29 |
[UDP_CT_REPLIED] = 120*HZ, |
5a41db94c
|
30 |
}; |
9fb9cbb10
|
31 |
|
2c8503f55
|
32 33 |
static unsigned int *udp_get_timeouts(struct net *net) { |
a95a7774d
|
34 |
return nf_udp_pernet(net)->timeouts; |
2c8503f55
|
35 |
} |
83d213fd9
|
36 37 38 39 40 41 42 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 |
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
|
82 |
/* Returns verdict for packet, and may modify conntracktype */ |
a47c54048
|
83 84 85 86 87 |
int nf_conntrack_udp_packet(struct nf_conn *ct, struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, const struct nf_hook_state *state) |
9fb9cbb10
|
88 |
{ |
c779e8496
|
89 |
unsigned int *timeouts; |
83d213fd9
|
90 91 |
if (udp_error(skb, dataoff, state)) return -NF_ACCEPT; |
c779e8496
|
92 93 94 |
timeouts = nf_ct_timeout_lookup(ct); if (!timeouts) timeouts = udp_get_timeouts(nf_ct_net(ct)); |
d535c8a69
|
95 96 |
if (!nf_ct_is_confirmed(ct)) ct->proto.udp.stream_ts = 2 * HZ + jiffies; |
9fb9cbb10
|
97 |
/* If we've seen traffic both ways, this is some kind of UDP |
d535c8a69
|
98 99 |
* stream. Set Assured. */ |
c88130bcd
|
100 |
if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { |
d535c8a69
|
101 102 103 104 105 106 107 |
unsigned long extra = timeouts[UDP_CT_UNREPLIED]; /* Still active after two seconds? Extend timeout. */ if (time_after(jiffies, ct->proto.udp.stream_ts)) extra = timeouts[UDP_CT_REPLIED]; nf_ct_refresh_acct(ct, ctinfo, skb, extra); |
9fb9cbb10
|
108 |
/* Also, more likely to be important, and not a probe */ |
c88130bcd
|
109 |
if (!test_and_set_bit(IPS_ASSURED_BIT, &ct->status)) |
858b31330
|
110 |
nf_conntrack_event_cache(IPCT_ASSURED, ct); |
5a41db94c
|
111 112 |
} else { nf_ct_refresh_acct(ct, ctinfo, skb, |
2c8503f55
|
113 |
timeouts[UDP_CT_UNREPLIED]); |
5a41db94c
|
114 |
} |
9fb9cbb10
|
115 116 |
return NF_ACCEPT; } |
e4781421e
|
117 |
#ifdef CONFIG_NF_CT_PROTO_UDPLITE |
93e66024b
|
118 119 120 |
static void udplite_error_log(const struct sk_buff *skb, const struct nf_hook_state *state, const char *msg) |
c4f3db159
|
121 |
{ |
93e66024b
|
122 123 |
nf_l4proto_log_invalid(skb, state->net, state->pf, IPPROTO_UDPLITE, "%s", msg); |
c4f3db159
|
124 |
} |
83d213fd9
|
125 126 127 |
static bool udplite_error(struct sk_buff *skb, unsigned int dataoff, const struct nf_hook_state *state) |
e4781421e
|
128 129 130 131 132 133 134 135 136 |
{ 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
|
137 |
udplite_error_log(skb, state, "short packet"); |
83d213fd9
|
138 |
return true; |
e4781421e
|
139 140 141 142 143 144 |
} cscov = ntohs(hdr->len); if (cscov == 0) { cscov = udplen; } else if (cscov < sizeof(*hdr) || cscov > udplen) { |
93e66024b
|
145 |
udplite_error_log(skb, state, "invalid checksum coverage"); |
83d213fd9
|
146 |
return true; |
e4781421e
|
147 148 149 150 |
} /* UDPLITE mandates checksums */ if (!hdr->check) { |
93e66024b
|
151 |
udplite_error_log(skb, state, "checksum missing"); |
83d213fd9
|
152 |
return true; |
e4781421e
|
153 154 155 |
} /* Checksum invalid? Ignore. */ |
93e66024b
|
156 157 158 159 160 |
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
|
161 |
return true; |
e4781421e
|
162 |
} |
83d213fd9
|
163 |
return false; |
e4781421e
|
164 |
} |
e4781421e
|
165 |
|
83d213fd9
|
166 |
/* Returns verdict for packet, and may modify conntracktype */ |
a47c54048
|
167 168 169 170 171 |
int nf_conntrack_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
|
172 |
{ |
83d213fd9
|
173 |
unsigned int *timeouts; |
9fb9cbb10
|
174 |
|
83d213fd9
|
175 |
if (udplite_error(skb, dataoff, state)) |
9fb9cbb10
|
176 |
return -NF_ACCEPT; |
9fb9cbb10
|
177 |
|
83d213fd9
|
178 179 180 |
timeouts = nf_ct_timeout_lookup(ct); if (!timeouts) timeouts = udp_get_timeouts(nf_ct_net(ct)); |
9fb9cbb10
|
181 |
|
83d213fd9
|
182 183 184 185 186 187 188 189 190 191 192 |
/* 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
|
193 |
} |
9fb9cbb10
|
194 195 |
return NF_ACCEPT; } |
83d213fd9
|
196 |
#endif |
9fb9cbb10
|
197 |
|
a874752a1
|
198 |
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT |
509784623
|
199 200 201 |
#include <linux/netfilter/nfnetlink.h> #include <linux/netfilter/nfnetlink_cttimeout.h> |
8264deb81
|
202 203 |
static int udp_timeout_nlattr_to_obj(struct nlattr *tb[], struct net *net, void *data) |
509784623
|
204 205 |
{ unsigned int *timeouts = data; |
a95a7774d
|
206 |
struct nf_udp_net *un = nf_udp_pernet(net); |
509784623
|
207 |
|
c779e8496
|
208 209 |
if (!timeouts) timeouts = un->timeouts; |
509784623
|
210 |
/* set default timeouts for UDP. */ |
8264deb81
|
211 212 |
timeouts[UDP_CT_UNREPLIED] = un->timeouts[UDP_CT_UNREPLIED]; timeouts[UDP_CT_REPLIED] = un->timeouts[UDP_CT_REPLIED]; |
509784623
|
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
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
|
229 230 231 232 233 |
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
|
234 235 236 237 238 239 240 241 242 243 244 |
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
|
245 |
#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ |
509784623
|
246 |
|
2a389de86
|
247 |
void nf_conntrack_udp_init_net(struct net *net) |
0ce490ad4
|
248 |
{ |
a95a7774d
|
249 |
struct nf_udp_net *un = nf_udp_pernet(net); |
2a389de86
|
250 |
int i; |
0ce490ad4
|
251 |
|
2a389de86
|
252 253 |
for (i = 0; i < UDP_CT_MAX; i++) un->timeouts[i] = udp_timeouts[i]; |
08911475d
|
254 |
} |
dd2934a95
|
255 |
const struct nf_conntrack_l4proto nf_conntrack_l4proto_udp = |
9fb9cbb10
|
256 |
{ |
605dcad6c
|
257 |
.l4proto = IPPROTO_UDP, |
71d8c47fc
|
258 |
.allow_clash = true, |
c0cd11566
|
259 |
#if IS_ENABLED(CONFIG_NF_CT_NETLINK) |
fdf708322
|
260 261 |
.tuple_to_nlattr = nf_ct_port_tuple_to_nlattr, .nlattr_to_tuple = nf_ct_port_nlattr_to_tuple, |
a400c30ed
|
262 |
.nlattr_tuple_size = nf_ct_port_nlattr_tuple_size, |
f73e924cd
|
263 |
.nla_policy = nf_ct_port_nla_policy, |
c1d10adb4
|
264 |
#endif |
a874752a1
|
265 |
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT |
509784623
|
266 267 268 269 270 271 272 |
.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
|
273 |
#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ |
9fb9cbb10
|
274 |
}; |
e4781421e
|
275 |
#ifdef CONFIG_NF_CT_PROTO_UDPLITE |
dd2934a95
|
276 |
const struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite = |
e4781421e
|
277 |
{ |
e4781421e
|
278 |
.l4proto = IPPROTO_UDPLITE, |
e4781421e
|
279 |
.allow_clash = true, |
e4781421e
|
280 281 282 283 284 285 |
#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
|
286 |
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT |
e4781421e
|
287 288 289 290 291 292 293 |
.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
|
294 |
#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ |
e4781421e
|
295 |
}; |
e4781421e
|
296 |
#endif |