diff options
author | Patrick McHardy <kaber@trash.net> | 2008-03-20 10:15:55 -0400 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2008-04-14 05:15:49 -0400 |
commit | 2bc780499aa33311ec0f3e42624dfaa7be0ade5e (patch) | |
tree | 5920fd09120573dc5a48ccdfa7d223ae9ae30fae /net | |
parent | d63a650736f566a1f9e9434725d2089597c0d2cc (diff) |
[NETFILTER]: nf_conntrack: add DCCP protocol support
Add DCCP conntrack helper. Thanks to Gerrit Renker <gerrit@erg.abdn.ac.uk>
for review and testing.
Signed-off-by: Patrick McHardy <kaber@trash.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/netfilter/Kconfig | 10 | ||||
-rw-r--r-- | net/netfilter/Makefile | 1 | ||||
-rw-r--r-- | net/netfilter/nf_conntrack_proto_dccp.c | 816 |
3 files changed, 827 insertions, 0 deletions
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index daf5b881064d..c1fc0f1a641c 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig | |||
@@ -86,6 +86,16 @@ config NF_CONNTRACK_EVENTS | |||
86 | 86 | ||
87 | If unsure, say `N'. | 87 | If unsure, say `N'. |
88 | 88 | ||
89 | config NF_CT_PROTO_DCCP | ||
90 | tristate 'DCCP protocol connection tracking support (EXPERIMENTAL)' | ||
91 | depends on EXPERIMENTAL && NF_CONNTRACK | ||
92 | depends on NETFILTER_ADVANCED | ||
93 | help | ||
94 | With this option enabled, the layer 3 independent connection | ||
95 | tracking code will be able to do state tracking on DCCP connections. | ||
96 | |||
97 | If unsure, say 'N'. | ||
98 | |||
89 | config NF_CT_PROTO_GRE | 99 | config NF_CT_PROTO_GRE |
90 | tristate | 100 | tristate |
91 | depends on NF_CONNTRACK | 101 | depends on NF_CONNTRACK |
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index ea7508387f95..5c4b183f6422 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile | |||
@@ -13,6 +13,7 @@ obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o | |||
13 | obj-$(CONFIG_NF_CONNTRACK) += nf_conntrack.o | 13 | obj-$(CONFIG_NF_CONNTRACK) += nf_conntrack.o |
14 | 14 | ||
15 | # SCTP protocol connection tracking | 15 | # SCTP protocol connection tracking |
16 | obj-$(CONFIG_NF_CT_PROTO_DCCP) += nf_conntrack_proto_dccp.o | ||
16 | obj-$(CONFIG_NF_CT_PROTO_GRE) += nf_conntrack_proto_gre.o | 17 | obj-$(CONFIG_NF_CT_PROTO_GRE) += nf_conntrack_proto_gre.o |
17 | obj-$(CONFIG_NF_CT_PROTO_SCTP) += nf_conntrack_proto_sctp.o | 18 | obj-$(CONFIG_NF_CT_PROTO_SCTP) += nf_conntrack_proto_sctp.o |
18 | obj-$(CONFIG_NF_CT_PROTO_UDPLITE) += nf_conntrack_proto_udplite.o | 19 | obj-$(CONFIG_NF_CT_PROTO_UDPLITE) += nf_conntrack_proto_udplite.o |
diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c new file mode 100644 index 000000000000..db88c5bcc5fd --- /dev/null +++ b/net/netfilter/nf_conntrack_proto_dccp.c | |||
@@ -0,0 +1,816 @@ | |||
1 | /* | ||
2 | * DCCP connection tracking protocol helper | ||
3 | * | ||
4 | * Copyright (c) 2005, 2006, 2008 Patrick McHardy <kaber@trash.net> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | */ | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/sysctl.h> | ||
15 | #include <linux/spinlock.h> | ||
16 | #include <linux/skbuff.h> | ||
17 | #include <linux/dccp.h> | ||
18 | |||
19 | #include <linux/netfilter/nfnetlink_conntrack.h> | ||
20 | #include <net/netfilter/nf_conntrack.h> | ||
21 | #include <net/netfilter/nf_conntrack_l4proto.h> | ||
22 | #include <net/netfilter/nf_log.h> | ||
23 | |||
24 | static DEFINE_RWLOCK(dccp_lock); | ||
25 | |||
26 | static int nf_ct_dccp_loose __read_mostly = 1; | ||
27 | |||
28 | /* Timeouts are based on values from RFC4340: | ||
29 | * | ||
30 | * - REQUEST: | ||
31 | * | ||
32 | * 8.1.2. Client Request | ||
33 | * | ||
34 | * A client MAY give up on its DCCP-Requests after some time | ||
35 | * (3 minutes, for example). | ||
36 | * | ||
37 | * - RESPOND: | ||
38 | * | ||
39 | * 8.1.3. Server Response | ||
40 | * | ||
41 | * It MAY also leave the RESPOND state for CLOSED after a timeout of | ||
42 | * not less than 4MSL (8 minutes); | ||
43 | * | ||
44 | * - PARTOPEN: | ||
45 | * | ||
46 | * 8.1.5. Handshake Completion | ||
47 | * | ||
48 | * If the client remains in PARTOPEN for more than 4MSL (8 minutes), | ||
49 | * it SHOULD reset the connection with Reset Code 2, "Aborted". | ||
50 | * | ||
51 | * - OPEN: | ||
52 | * | ||
53 | * The DCCP timestamp overflows after 11.9 hours. If the connection | ||
54 | * stays idle this long the sequence number won't be recognized | ||
55 | * as valid anymore. | ||
56 | * | ||
57 | * - CLOSEREQ/CLOSING: | ||
58 | * | ||
59 | * 8.3. Termination | ||
60 | * | ||
61 | * The retransmission timer should initially be set to go off in two | ||
62 | * round-trip times and should back off to not less than once every | ||
63 | * 64 seconds ... | ||
64 | * | ||
65 | * - TIMEWAIT: | ||
66 | * | ||
67 | * 4.3. States | ||
68 | * | ||
69 | * A server or client socket remains in this state for 2MSL (4 minutes) | ||
70 | * after the connection has been town down, ... | ||
71 | */ | ||
72 | |||
73 | #define DCCP_MSL (2 * 60 * HZ) | ||
74 | |||
75 | static unsigned int dccp_timeout[CT_DCCP_MAX + 1] __read_mostly = { | ||
76 | [CT_DCCP_REQUEST] = 2 * DCCP_MSL, | ||
77 | [CT_DCCP_RESPOND] = 4 * DCCP_MSL, | ||
78 | [CT_DCCP_PARTOPEN] = 4 * DCCP_MSL, | ||
79 | [CT_DCCP_OPEN] = 12 * 3600 * HZ, | ||
80 | [CT_DCCP_CLOSEREQ] = 64 * HZ, | ||
81 | [CT_DCCP_CLOSING] = 64 * HZ, | ||
82 | [CT_DCCP_TIMEWAIT] = 2 * DCCP_MSL, | ||
83 | }; | ||
84 | |||
85 | static const char * const dccp_state_names[] = { | ||
86 | [CT_DCCP_NONE] = "NONE", | ||
87 | [CT_DCCP_REQUEST] = "REQUEST", | ||
88 | [CT_DCCP_RESPOND] = "RESPOND", | ||
89 | [CT_DCCP_PARTOPEN] = "PARTOPEN", | ||
90 | [CT_DCCP_OPEN] = "OPEN", | ||
91 | [CT_DCCP_CLOSEREQ] = "CLOSEREQ", | ||
92 | [CT_DCCP_CLOSING] = "CLOSING", | ||
93 | [CT_DCCP_TIMEWAIT] = "TIMEWAIT", | ||
94 | [CT_DCCP_IGNORE] = "IGNORE", | ||
95 | [CT_DCCP_INVALID] = "INVALID", | ||
96 | }; | ||
97 | |||
98 | #define sNO CT_DCCP_NONE | ||
99 | #define sRQ CT_DCCP_REQUEST | ||
100 | #define sRS CT_DCCP_RESPOND | ||
101 | #define sPO CT_DCCP_PARTOPEN | ||
102 | #define sOP CT_DCCP_OPEN | ||
103 | #define sCR CT_DCCP_CLOSEREQ | ||
104 | #define sCG CT_DCCP_CLOSING | ||
105 | #define sTW CT_DCCP_TIMEWAIT | ||
106 | #define sIG CT_DCCP_IGNORE | ||
107 | #define sIV CT_DCCP_INVALID | ||
108 | |||
109 | /* | ||
110 | * DCCP state transistion table | ||
111 | * | ||
112 | * The assumption is the same as for TCP tracking: | ||
113 | * | ||
114 | * We are the man in the middle. All the packets go through us but might | ||
115 | * get lost in transit to the destination. It is assumed that the destination | ||
116 | * can't receive segments we haven't seen. | ||
117 | * | ||
118 | * The following states exist: | ||
119 | * | ||
120 | * NONE: Initial state, expecting Request | ||
121 | * REQUEST: Request seen, waiting for Response from server | ||
122 | * RESPOND: Response from server seen, waiting for Ack from client | ||
123 | * PARTOPEN: Ack after Response seen, waiting for packet other than Response, | ||
124 | * Reset or Sync from server | ||
125 | * OPEN: Packet other than Response, Reset or Sync seen | ||
126 | * CLOSEREQ: CloseReq from server seen, expecting Close from client | ||
127 | * CLOSING: Close seen, expecting Reset | ||
128 | * TIMEWAIT: Reset seen | ||
129 | * IGNORE: Not determinable whether packet is valid | ||
130 | * | ||
131 | * Some states exist only on one side of the connection: REQUEST, RESPOND, | ||
132 | * PARTOPEN, CLOSEREQ. For the other side these states are equivalent to | ||
133 | * the one it was in before. | ||
134 | * | ||
135 | * Packets are marked as ignored (sIG) if we don't know if they're valid | ||
136 | * (for example a reincarnation of a connection we didn't notice is dead | ||
137 | * already) and the server may send back a connection closing Reset or a | ||
138 | * Response. They're also used for Sync/SyncAck packets, which we don't | ||
139 | * care about. | ||
140 | */ | ||
141 | static const u_int8_t | ||
142 | dccp_state_table[CT_DCCP_ROLE_MAX + 1][DCCP_PKT_SYNCACK + 1][CT_DCCP_MAX + 1] = { | ||
143 | [CT_DCCP_ROLE_CLIENT] = { | ||
144 | [DCCP_PKT_REQUEST] = { | ||
145 | /* | ||
146 | * sNO -> sRQ Regular Request | ||
147 | * sRQ -> sRQ Retransmitted Request or reincarnation | ||
148 | * sRS -> sRS Retransmitted Request (apparently Response | ||
149 | * got lost after we saw it) or reincarnation | ||
150 | * sPO -> sIG Ignore, conntrack might be out of sync | ||
151 | * sOP -> sIG Ignore, conntrack might be out of sync | ||
152 | * sCR -> sIG Ignore, conntrack might be out of sync | ||
153 | * sCG -> sIG Ignore, conntrack might be out of sync | ||
154 | * sTW -> sRQ Reincarnation | ||
155 | * | ||
156 | * sNO, sRQ, sRS, sPO. sOP, sCR, sCG, sTW, */ | ||
157 | sRQ, sRQ, sRS, sIG, sIG, sIG, sIG, sRQ, | ||
158 | }, | ||
159 | [DCCP_PKT_RESPONSE] = { | ||
160 | /* | ||
161 | * sNO -> sIV Invalid | ||
162 | * sRQ -> sIG Ignore, might be response to ignored Request | ||
163 | * sRS -> sIG Ignore, might be response to ignored Request | ||
164 | * sPO -> sIG Ignore, might be response to ignored Request | ||
165 | * sOP -> sIG Ignore, might be response to ignored Request | ||
166 | * sCR -> sIG Ignore, might be response to ignored Request | ||
167 | * sCG -> sIG Ignore, might be response to ignored Request | ||
168 | * sTW -> sIV Invalid, reincarnation in reverse direction | ||
169 | * goes through sRQ | ||
170 | * | ||
171 | * sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */ | ||
172 | sIV, sIG, sIG, sIG, sIG, sIG, sIG, sIV, | ||
173 | }, | ||
174 | [DCCP_PKT_ACK] = { | ||
175 | /* | ||
176 | * sNO -> sIV No connection | ||
177 | * sRQ -> sIV No connection | ||
178 | * sRS -> sPO Ack for Response, move to PARTOPEN (8.1.5.) | ||
179 | * sPO -> sPO Retransmitted Ack for Response, remain in PARTOPEN | ||
180 | * sOP -> sOP Regular ACK, remain in OPEN | ||
181 | * sCR -> sCR Ack in CLOSEREQ MAY be processed (8.3.) | ||
182 | * sCG -> sCG Ack in CLOSING MAY be processed (8.3.) | ||
183 | * sTW -> sIV | ||
184 | * | ||
185 | * sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */ | ||
186 | sIV, sIV, sPO, sPO, sOP, sCR, sCG, sIV | ||
187 | }, | ||
188 | [DCCP_PKT_DATA] = { | ||
189 | /* | ||
190 | * sNO -> sIV No connection | ||
191 | * sRQ -> sIV No connection | ||
192 | * sRS -> sIV No connection | ||
193 | * sPO -> sIV MUST use DataAck in PARTOPEN state (8.1.5.) | ||
194 | * sOP -> sOP Regular Data packet | ||
195 | * sCR -> sCR Data in CLOSEREQ MAY be processed (8.3.) | ||
196 | * sCG -> sCG Data in CLOSING MAY be processed (8.3.) | ||
197 | * sTW -> sIV | ||
198 | * | ||
199 | * sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */ | ||
200 | sIV, sIV, sIV, sIV, sOP, sCR, sCG, sIV, | ||
201 | }, | ||
202 | [DCCP_PKT_DATAACK] = { | ||
203 | /* | ||
204 | * sNO -> sIV No connection | ||
205 | * sRQ -> sIV No connection | ||
206 | * sRS -> sPO Ack for Response, move to PARTOPEN (8.1.5.) | ||
207 | * sPO -> sPO Remain in PARTOPEN state | ||
208 | * sOP -> sOP Regular DataAck packet in OPEN state | ||
209 | * sCR -> sCR DataAck in CLOSEREQ MAY be processed (8.3.) | ||
210 | * sCG -> sCG DataAck in CLOSING MAY be processed (8.3.) | ||
211 | * sTW -> sIV | ||
212 | * | ||
213 | * sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */ | ||
214 | sIV, sIV, sPO, sPO, sOP, sCR, sCG, sIV | ||
215 | }, | ||
216 | [DCCP_PKT_CLOSEREQ] = { | ||
217 | /* | ||
218 | * CLOSEREQ may only be sent by the server. | ||
219 | * | ||
220 | * sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */ | ||
221 | sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV | ||
222 | }, | ||
223 | [DCCP_PKT_CLOSE] = { | ||
224 | /* | ||
225 | * sNO -> sIV No connection | ||
226 | * sRQ -> sIV No connection | ||
227 | * sRS -> sIV No connection | ||
228 | * sPO -> sCG Client-initiated close | ||
229 | * sOP -> sCG Client-initiated close | ||
230 | * sCR -> sCG Close in response to CloseReq (8.3.) | ||
231 | * sCG -> sCG Retransmit | ||
232 | * sTW -> sIV Late retransmit, already in TIME_WAIT | ||
233 | * | ||
234 | * sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */ | ||
235 | sIV, sIV, sIV, sCG, sCG, sCG, sIV, sIV | ||
236 | }, | ||
237 | [DCCP_PKT_RESET] = { | ||
238 | /* | ||
239 | * sNO -> sIV No connection | ||
240 | * sRQ -> sTW Sync received or timeout, SHOULD send Reset (8.1.1.) | ||
241 | * sRS -> sTW Response received without Request | ||
242 | * sPO -> sTW Timeout, SHOULD send Reset (8.1.5.) | ||
243 | * sOP -> sTW Connection reset | ||
244 | * sCR -> sTW Connection reset | ||
245 | * sCG -> sTW Connection reset | ||
246 | * sTW -> sIG Ignore (don't refresh timer) | ||
247 | * | ||
248 | * sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */ | ||
249 | sIV, sTW, sTW, sTW, sTW, sTW, sTW, sIG | ||
250 | }, | ||
251 | [DCCP_PKT_SYNC] = { | ||
252 | /* | ||
253 | * We currently ignore Sync packets | ||
254 | * | ||
255 | * sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */ | ||
256 | sIG, sIG, sIG, sIG, sIG, sIG, sIG, sIG, | ||
257 | }, | ||
258 | [DCCP_PKT_SYNCACK] = { | ||
259 | /* | ||
260 | * We currently ignore SyncAck packets | ||
261 | * | ||
262 | * sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */ | ||
263 | sIG, sIG, sIG, sIG, sIG, sIG, sIG, sIG, | ||
264 | }, | ||
265 | }, | ||
266 | [CT_DCCP_ROLE_SERVER] = { | ||
267 | [DCCP_PKT_REQUEST] = { | ||
268 | /* | ||
269 | * sNO -> sIV Invalid | ||
270 | * sRQ -> sIG Ignore, conntrack might be out of sync | ||
271 | * sRS -> sIG Ignore, conntrack might be out of sync | ||
272 | * sPO -> sIG Ignore, conntrack might be out of sync | ||
273 | * sOP -> sIG Ignore, conntrack might be out of sync | ||
274 | * sCR -> sIG Ignore, conntrack might be out of sync | ||
275 | * sCG -> sIG Ignore, conntrack might be out of sync | ||
276 | * sTW -> sRQ Reincarnation, must reverse roles | ||
277 | * | ||
278 | * sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */ | ||
279 | sIV, sIG, sIG, sIG, sIG, sIG, sIG, sRQ | ||
280 | }, | ||
281 | [DCCP_PKT_RESPONSE] = { | ||
282 | /* | ||
283 | * sNO -> sIV Response without Request | ||
284 | * sRQ -> sRS Response to clients Request | ||
285 | * sRS -> sRS Retransmitted Response (8.1.3. SHOULD NOT) | ||
286 | * sPO -> sIG Response to an ignored Request or late retransmit | ||
287 | * sOP -> sIG Ignore, might be response to ignored Request | ||
288 | * sCR -> sIG Ignore, might be response to ignored Request | ||
289 | * sCG -> sIG Ignore, might be response to ignored Request | ||
290 | * sTW -> sIV Invalid, Request from client in sTW moves to sRQ | ||
291 | * | ||
292 | * sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */ | ||
293 | sIV, sRS, sRS, sIG, sIG, sIG, sIG, sIV | ||
294 | }, | ||
295 | [DCCP_PKT_ACK] = { | ||
296 | /* | ||
297 | * sNO -> sIV No connection | ||
298 | * sRQ -> sIV No connection | ||
299 | * sRS -> sIV No connection | ||
300 | * sPO -> sOP Enter OPEN state (8.1.5.) | ||
301 | * sOP -> sOP Regular Ack in OPEN state | ||
302 | * sCR -> sIV Waiting for Close from client | ||
303 | * sCG -> sCG Ack in CLOSING MAY be processed (8.3.) | ||
304 | * sTW -> sIV | ||
305 | * | ||
306 | * sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */ | ||
307 | sIV, sIV, sIV, sOP, sOP, sIV, sCG, sIV | ||
308 | }, | ||
309 | [DCCP_PKT_DATA] = { | ||
310 | /* | ||
311 | * sNO -> sIV No connection | ||
312 | * sRQ -> sIV No connection | ||
313 | * sRS -> sIV No connection | ||
314 | * sPO -> sOP Enter OPEN state (8.1.5.) | ||
315 | * sOP -> sOP Regular Data packet in OPEN state | ||
316 | * sCR -> sIV Waiting for Close from client | ||
317 | * sCG -> sCG Data in CLOSING MAY be processed (8.3.) | ||
318 | * sTW -> sIV | ||
319 | * | ||
320 | * sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */ | ||
321 | sIV, sIV, sIV, sOP, sOP, sIV, sCG, sIV | ||
322 | }, | ||
323 | [DCCP_PKT_DATAACK] = { | ||
324 | /* | ||
325 | * sNO -> sIV No connection | ||
326 | * sRQ -> sIV No connection | ||
327 | * sRS -> sIV No connection | ||
328 | * sPO -> sOP Enter OPEN state (8.1.5.) | ||
329 | * sOP -> sOP Regular DataAck in OPEN state | ||
330 | * sCR -> sIV Waiting for Close from client | ||
331 | * sCG -> sCG Data in CLOSING MAY be processed (8.3.) | ||
332 | * sTW -> sIV | ||
333 | * | ||
334 | * sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */ | ||
335 | sIV, sIV, sIV, sOP, sOP, sIV, sCG, sIV | ||
336 | }, | ||
337 | [DCCP_PKT_CLOSEREQ] = { | ||
338 | /* | ||
339 | * sNO -> sIV No connection | ||
340 | * sRQ -> sIV No connection | ||
341 | * sRS -> sIV No connection | ||
342 | * sPO -> sOP -> sCR Move directly to CLOSEREQ (8.1.5.) | ||
343 | * sOP -> sCR CloseReq in OPEN state | ||
344 | * sCR -> sCR Retransmit | ||
345 | * sCG -> sCR Simultaneous close, client sends another Close | ||
346 | * sTW -> sIV Already closed | ||
347 | * | ||
348 | * sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */ | ||
349 | sIV, sIV, sIV, sCR, sCR, sCR, sCR, sIV | ||
350 | }, | ||
351 | [DCCP_PKT_CLOSE] = { | ||
352 | /* | ||
353 | * sNO -> sIV No connection | ||
354 | * sRQ -> sIV No connection | ||
355 | * sRS -> sIV No connection | ||
356 | * sPO -> sOP -> sCG Move direcly to CLOSING | ||
357 | * sOP -> sCG Move to CLOSING | ||
358 | * sCR -> sIV Close after CloseReq is invalid | ||
359 | * sCG -> sCG Retransmit | ||
360 | * sTW -> sIV Already closed | ||
361 | * | ||
362 | * sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */ | ||
363 | sIV, sIV, sIV, sCG, sCG, sIV, sCG, sIV | ||
364 | }, | ||
365 | [DCCP_PKT_RESET] = { | ||
366 | /* | ||
367 | * sNO -> sIV No connection | ||
368 | * sRQ -> sTW Reset in response to Request | ||
369 | * sRS -> sTW Timeout, SHOULD send Reset (8.1.3.) | ||
370 | * sPO -> sTW Timeout, SHOULD send Reset (8.1.3.) | ||
371 | * sOP -> sTW | ||
372 | * sCR -> sTW | ||
373 | * sCG -> sTW | ||
374 | * sTW -> sIG Ignore (don't refresh timer) | ||
375 | * | ||
376 | * sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW, sTW */ | ||
377 | sIV, sTW, sTW, sTW, sTW, sTW, sTW, sTW, sIG | ||
378 | }, | ||
379 | [DCCP_PKT_SYNC] = { | ||
380 | /* | ||
381 | * We currently ignore Sync packets | ||
382 | * | ||
383 | * sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */ | ||
384 | sIG, sIG, sIG, sIG, sIG, sIG, sIG, sIG, | ||
385 | }, | ||
386 | [DCCP_PKT_SYNCACK] = { | ||
387 | /* | ||
388 | * We currently ignore SyncAck packets | ||
389 | * | ||
390 | * sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */ | ||
391 | sIG, sIG, sIG, sIG, sIG, sIG, sIG, sIG, | ||
392 | }, | ||
393 | }, | ||
394 | }; | ||
395 | |||
396 | static int dccp_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff, | ||
397 | struct nf_conntrack_tuple *tuple) | ||
398 | { | ||
399 | struct dccp_hdr _hdr, *dh; | ||
400 | |||
401 | dh = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr); | ||
402 | if (dh == NULL) | ||
403 | return 0; | ||
404 | |||
405 | tuple->src.u.dccp.port = dh->dccph_sport; | ||
406 | tuple->dst.u.dccp.port = dh->dccph_dport; | ||
407 | return 1; | ||
408 | } | ||
409 | |||
410 | static int dccp_invert_tuple(struct nf_conntrack_tuple *inv, | ||
411 | const struct nf_conntrack_tuple *tuple) | ||
412 | { | ||
413 | inv->src.u.dccp.port = tuple->dst.u.dccp.port; | ||
414 | inv->dst.u.dccp.port = tuple->src.u.dccp.port; | ||
415 | return 1; | ||
416 | } | ||
417 | |||
418 | static int dccp_new(struct nf_conn *ct, const struct sk_buff *skb, | ||
419 | unsigned int dataoff) | ||
420 | { | ||
421 | int pf = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num; | ||
422 | struct dccp_hdr _dh, *dh; | ||
423 | const char *msg; | ||
424 | u_int8_t state; | ||
425 | |||
426 | dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &dh); | ||
427 | BUG_ON(dh == NULL); | ||
428 | |||
429 | state = dccp_state_table[CT_DCCP_ROLE_CLIENT][dh->dccph_type][CT_DCCP_NONE]; | ||
430 | switch (state) { | ||
431 | default: | ||
432 | if (nf_ct_dccp_loose == 0) { | ||
433 | msg = "nf_ct_dccp: not picking up existing connection "; | ||
434 | goto out_invalid; | ||
435 | } | ||
436 | case CT_DCCP_REQUEST: | ||
437 | break; | ||
438 | case CT_DCCP_INVALID: | ||
439 | msg = "nf_ct_dccp: invalid state transition "; | ||
440 | goto out_invalid; | ||
441 | } | ||
442 | |||
443 | ct->proto.dccp.role[IP_CT_DIR_ORIGINAL] = CT_DCCP_ROLE_CLIENT; | ||
444 | ct->proto.dccp.role[IP_CT_DIR_REPLY] = CT_DCCP_ROLE_SERVER; | ||
445 | ct->proto.dccp.state = CT_DCCP_NONE; | ||
446 | return 1; | ||
447 | |||
448 | out_invalid: | ||
449 | if (LOG_INVALID(IPPROTO_DCCP)) | ||
450 | nf_log_packet(pf, 0, skb, NULL, NULL, NULL, msg); | ||
451 | return 0; | ||
452 | } | ||
453 | |||
454 | static u64 dccp_ack_seq(const struct dccp_hdr *dh) | ||
455 | { | ||
456 | const struct dccp_hdr_ack_bits *dhack; | ||
457 | |||
458 | dhack = (void *)dh + __dccp_basic_hdr_len(dh); | ||
459 | return ((u64)ntohs(dhack->dccph_ack_nr_high) << 32) + | ||
460 | ntohl(dhack->dccph_ack_nr_low); | ||
461 | } | ||
462 | |||
463 | static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb, | ||
464 | unsigned int dataoff, enum ip_conntrack_info ctinfo, | ||
465 | int pf, unsigned int hooknum) | ||
466 | { | ||
467 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
468 | struct dccp_hdr _dh, *dh; | ||
469 | u_int8_t type, old_state, new_state; | ||
470 | enum ct_dccp_roles role; | ||
471 | |||
472 | dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &dh); | ||
473 | BUG_ON(dh == NULL); | ||
474 | type = dh->dccph_type; | ||
475 | |||
476 | if (type == DCCP_PKT_RESET && | ||
477 | !test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { | ||
478 | /* Tear down connection immediately if only reply is a RESET */ | ||
479 | if (del_timer(&ct->timeout)) | ||
480 | ct->timeout.function((unsigned long)ct); | ||
481 | return NF_ACCEPT; | ||
482 | } | ||
483 | |||
484 | write_lock_bh(&dccp_lock); | ||
485 | |||
486 | role = ct->proto.dccp.role[dir]; | ||
487 | old_state = ct->proto.dccp.state; | ||
488 | new_state = dccp_state_table[role][type][old_state]; | ||
489 | |||
490 | switch (new_state) { | ||
491 | case CT_DCCP_REQUEST: | ||
492 | if (old_state == CT_DCCP_TIMEWAIT && | ||
493 | role == CT_DCCP_ROLE_SERVER) { | ||
494 | /* Reincarnation in the reverse direction: reopen and | ||
495 | * reverse client/server roles. */ | ||
496 | ct->proto.dccp.role[dir] = CT_DCCP_ROLE_CLIENT; | ||
497 | ct->proto.dccp.role[!dir] = CT_DCCP_ROLE_SERVER; | ||
498 | } | ||
499 | break; | ||
500 | case CT_DCCP_RESPOND: | ||
501 | if (old_state == CT_DCCP_REQUEST) | ||
502 | ct->proto.dccp.handshake_seq = dccp_hdr_seq(dh); | ||
503 | break; | ||
504 | case CT_DCCP_PARTOPEN: | ||
505 | if (old_state == CT_DCCP_RESPOND && | ||
506 | type == DCCP_PKT_ACK && | ||
507 | dccp_ack_seq(dh) == ct->proto.dccp.handshake_seq) | ||
508 | set_bit(IPS_ASSURED_BIT, &ct->status); | ||
509 | break; | ||
510 | case CT_DCCP_IGNORE: | ||
511 | /* | ||
512 | * Connection tracking might be out of sync, so we ignore | ||
513 | * packets that might establish a new connection and resync | ||
514 | * if the server responds with a valid Response. | ||
515 | */ | ||
516 | if (ct->proto.dccp.last_dir == !dir && | ||
517 | ct->proto.dccp.last_pkt == DCCP_PKT_REQUEST && | ||
518 | type == DCCP_PKT_RESPONSE) { | ||
519 | ct->proto.dccp.role[!dir] = CT_DCCP_ROLE_CLIENT; | ||
520 | ct->proto.dccp.role[dir] = CT_DCCP_ROLE_SERVER; | ||
521 | ct->proto.dccp.handshake_seq = dccp_hdr_seq(dh); | ||
522 | new_state = CT_DCCP_RESPOND; | ||
523 | break; | ||
524 | } | ||
525 | ct->proto.dccp.last_dir = dir; | ||
526 | ct->proto.dccp.last_pkt = type; | ||
527 | |||
528 | write_unlock_bh(&dccp_lock); | ||
529 | if (LOG_INVALID(IPPROTO_DCCP)) | ||
530 | nf_log_packet(pf, 0, skb, NULL, NULL, NULL, | ||
531 | "nf_ct_dccp: invalid packet ignored "); | ||
532 | return NF_ACCEPT; | ||
533 | case CT_DCCP_INVALID: | ||
534 | write_unlock_bh(&dccp_lock); | ||
535 | if (LOG_INVALID(IPPROTO_DCCP)) | ||
536 | nf_log_packet(pf, 0, skb, NULL, NULL, NULL, | ||
537 | "nf_ct_dccp: invalid state transition "); | ||
538 | return -NF_ACCEPT; | ||
539 | } | ||
540 | |||
541 | ct->proto.dccp.last_dir = dir; | ||
542 | ct->proto.dccp.last_pkt = type; | ||
543 | ct->proto.dccp.state = new_state; | ||
544 | write_unlock_bh(&dccp_lock); | ||
545 | nf_ct_refresh_acct(ct, ctinfo, skb, dccp_timeout[new_state]); | ||
546 | |||
547 | return NF_ACCEPT; | ||
548 | } | ||
549 | |||
550 | static int dccp_error(struct sk_buff *skb, unsigned int dataoff, | ||
551 | enum ip_conntrack_info *ctinfo, int pf, | ||
552 | unsigned int hooknum) | ||
553 | { | ||
554 | struct dccp_hdr _dh, *dh; | ||
555 | unsigned int dccp_len = skb->len - dataoff; | ||
556 | unsigned int cscov; | ||
557 | const char *msg; | ||
558 | |||
559 | dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &dh); | ||
560 | if (dh == NULL) { | ||
561 | msg = "nf_ct_dccp: short packet "; | ||
562 | goto out_invalid; | ||
563 | } | ||
564 | |||
565 | if (dh->dccph_doff * 4 < sizeof(struct dccp_hdr) || | ||
566 | dh->dccph_doff * 4 > dccp_len) { | ||
567 | msg = "nf_ct_dccp: truncated/malformed packet "; | ||
568 | goto out_invalid; | ||
569 | } | ||
570 | |||
571 | cscov = dccp_len; | ||
572 | if (dh->dccph_cscov) { | ||
573 | cscov = (dh->dccph_cscov - 1) * 4; | ||
574 | if (cscov > dccp_len) { | ||
575 | msg = "nf_ct_dccp: bad checksum coverage "; | ||
576 | goto out_invalid; | ||
577 | } | ||
578 | } | ||
579 | |||
580 | if (nf_conntrack_checksum && hooknum == NF_INET_PRE_ROUTING && | ||
581 | nf_checksum_partial(skb, hooknum, dataoff, cscov, IPPROTO_DCCP, | ||
582 | pf)) { | ||
583 | msg = "nf_ct_dccp: bad checksum "; | ||
584 | goto out_invalid; | ||
585 | } | ||
586 | |||
587 | if (dh->dccph_type >= DCCP_PKT_INVALID) { | ||
588 | msg = "nf_ct_dccp: reserved packet type "; | ||
589 | goto out_invalid; | ||
590 | } | ||
591 | |||
592 | return NF_ACCEPT; | ||
593 | |||
594 | out_invalid: | ||
595 | if (LOG_INVALID(IPPROTO_DCCP)) | ||
596 | nf_log_packet(pf, 0, skb, NULL, NULL, NULL, msg); | ||
597 | return -NF_ACCEPT; | ||
598 | } | ||
599 | |||
600 | static int dccp_print_tuple(struct seq_file *s, | ||
601 | const struct nf_conntrack_tuple *tuple) | ||
602 | { | ||
603 | return seq_printf(s, "sport=%hu dport=%hu ", | ||
604 | ntohs(tuple->src.u.dccp.port), | ||
605 | ntohs(tuple->dst.u.dccp.port)); | ||
606 | } | ||
607 | |||
608 | static int dccp_print_conntrack(struct seq_file *s, const struct nf_conn *ct) | ||
609 | { | ||
610 | return seq_printf(s, "%s ", dccp_state_names[ct->proto.dccp.state]); | ||
611 | } | ||
612 | |||
613 | #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) | ||
614 | static int dccp_to_nlattr(struct sk_buff *skb, struct nlattr *nla, | ||
615 | const struct nf_conn *ct) | ||
616 | { | ||
617 | struct nlattr *nest_parms; | ||
618 | |||
619 | read_lock_bh(&dccp_lock); | ||
620 | nest_parms = nla_nest_start(skb, CTA_PROTOINFO_DCCP | NLA_F_NESTED); | ||
621 | if (!nest_parms) | ||
622 | goto nla_put_failure; | ||
623 | NLA_PUT_U8(skb, CTA_PROTOINFO_DCCP_STATE, ct->proto.dccp.state); | ||
624 | nla_nest_end(skb, nest_parms); | ||
625 | read_unlock_bh(&dccp_lock); | ||
626 | return 0; | ||
627 | |||
628 | nla_put_failure: | ||
629 | read_unlock_bh(&dccp_lock); | ||
630 | return -1; | ||
631 | } | ||
632 | |||
633 | static const struct nla_policy dccp_nla_policy[CTA_PROTOINFO_DCCP_MAX + 1] = { | ||
634 | [CTA_PROTOINFO_DCCP_STATE] = { .type = NLA_U8 }, | ||
635 | }; | ||
636 | |||
637 | static int nlattr_to_dccp(struct nlattr *cda[], struct nf_conn *ct) | ||
638 | { | ||
639 | struct nlattr *attr = cda[CTA_PROTOINFO_DCCP]; | ||
640 | struct nlattr *tb[CTA_PROTOINFO_DCCP_MAX + 1]; | ||
641 | int err; | ||
642 | |||
643 | if (!attr) | ||
644 | return 0; | ||
645 | |||
646 | err = nla_parse_nested(tb, CTA_PROTOINFO_DCCP_MAX, attr, | ||
647 | dccp_nla_policy); | ||
648 | if (err < 0) | ||
649 | return err; | ||
650 | |||
651 | if (!tb[CTA_PROTOINFO_DCCP_STATE] || | ||
652 | nla_get_u8(tb[CTA_PROTOINFO_DCCP_STATE]) >= CT_DCCP_IGNORE) | ||
653 | return -EINVAL; | ||
654 | |||
655 | write_lock_bh(&dccp_lock); | ||
656 | ct->proto.dccp.state = nla_get_u8(tb[CTA_PROTOINFO_DCCP_STATE]); | ||
657 | write_unlock_bh(&dccp_lock); | ||
658 | return 0; | ||
659 | } | ||
660 | #endif | ||
661 | |||
662 | #ifdef CONFIG_SYSCTL | ||
663 | static unsigned int dccp_sysctl_table_users; | ||
664 | static struct ctl_table_header *dccp_sysctl_header; | ||
665 | static ctl_table dccp_sysctl_table[] = { | ||
666 | { | ||
667 | .ctl_name = CTL_UNNUMBERED, | ||
668 | .procname = "nf_conntrack_dccp_timeout_request", | ||
669 | .data = &dccp_timeout[CT_DCCP_REQUEST], | ||
670 | .maxlen = sizeof(unsigned int), | ||
671 | .mode = 0644, | ||
672 | .proc_handler = proc_dointvec_jiffies, | ||
673 | }, | ||
674 | { | ||
675 | .ctl_name = CTL_UNNUMBERED, | ||
676 | .procname = "nf_conntrack_dccp_timeout_respond", | ||
677 | .data = &dccp_timeout[CT_DCCP_RESPOND], | ||
678 | .maxlen = sizeof(unsigned int), | ||
679 | .mode = 0644, | ||
680 | .proc_handler = proc_dointvec_jiffies, | ||
681 | }, | ||
682 | { | ||
683 | .ctl_name = CTL_UNNUMBERED, | ||
684 | .procname = "nf_conntrack_dccp_timeout_partopen", | ||
685 | .data = &dccp_timeout[CT_DCCP_PARTOPEN], | ||
686 | .maxlen = sizeof(unsigned int), | ||
687 | .mode = 0644, | ||
688 | .proc_handler = proc_dointvec_jiffies, | ||
689 | }, | ||
690 | { | ||
691 | .ctl_name = CTL_UNNUMBERED, | ||
692 | .procname = "nf_conntrack_dccp_timeout_open", | ||
693 | .data = &dccp_timeout[CT_DCCP_OPEN], | ||
694 | .maxlen = sizeof(unsigned int), | ||
695 | .mode = 0644, | ||
696 | .proc_handler = proc_dointvec_jiffies, | ||
697 | }, | ||
698 | { | ||
699 | .ctl_name = CTL_UNNUMBERED, | ||
700 | .procname = "nf_conntrack_dccp_timeout_closereq", | ||
701 | .data = &dccp_timeout[CT_DCCP_CLOSEREQ], | ||
702 | .maxlen = sizeof(unsigned int), | ||
703 | .mode = 0644, | ||
704 | .proc_handler = proc_dointvec_jiffies, | ||
705 | }, | ||
706 | { | ||
707 | .ctl_name = CTL_UNNUMBERED, | ||
708 | .procname = "nf_conntrack_dccp_timeout_closing", | ||
709 | .data = &dccp_timeout[CT_DCCP_CLOSING], | ||
710 | .maxlen = sizeof(unsigned int), | ||
711 | .mode = 0644, | ||
712 | .proc_handler = proc_dointvec_jiffies, | ||
713 | }, | ||
714 | { | ||
715 | .ctl_name = CTL_UNNUMBERED, | ||
716 | .procname = "nf_conntrack_dccp_timeout_timewait", | ||
717 | .data = &dccp_timeout[CT_DCCP_TIMEWAIT], | ||
718 | .maxlen = sizeof(unsigned int), | ||
719 | .mode = 0644, | ||
720 | .proc_handler = proc_dointvec_jiffies, | ||
721 | }, | ||
722 | { | ||
723 | .ctl_name = CTL_UNNUMBERED, | ||
724 | .procname = "nf_conntrack_dccp_loose", | ||
725 | .data = &nf_ct_dccp_loose, | ||
726 | .maxlen = sizeof(nf_ct_dccp_loose), | ||
727 | .mode = 0644, | ||
728 | .proc_handler = proc_dointvec, | ||
729 | }, | ||
730 | { | ||
731 | .ctl_name = 0, | ||
732 | } | ||
733 | }; | ||
734 | #endif /* CONFIG_SYSCTL */ | ||
735 | |||
736 | static struct nf_conntrack_l4proto dccp_proto4 __read_mostly = { | ||
737 | .l3proto = AF_INET, | ||
738 | .l4proto = IPPROTO_DCCP, | ||
739 | .name = "dccp", | ||
740 | .pkt_to_tuple = dccp_pkt_to_tuple, | ||
741 | .invert_tuple = dccp_invert_tuple, | ||
742 | .new = dccp_new, | ||
743 | .packet = dccp_packet, | ||
744 | .error = dccp_error, | ||
745 | .print_tuple = dccp_print_tuple, | ||
746 | .print_conntrack = dccp_print_conntrack, | ||
747 | #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) | ||
748 | .to_nlattr = dccp_to_nlattr, | ||
749 | .from_nlattr = nlattr_to_dccp, | ||
750 | .tuple_to_nlattr = nf_ct_port_tuple_to_nlattr, | ||
751 | .nlattr_to_tuple = nf_ct_port_nlattr_to_tuple, | ||
752 | .nla_policy = nf_ct_port_nla_policy, | ||
753 | #endif | ||
754 | #ifdef CONFIG_SYSCTL | ||
755 | .ctl_table_users = &dccp_sysctl_table_users, | ||
756 | .ctl_table_header = &dccp_sysctl_header, | ||
757 | .ctl_table = dccp_sysctl_table, | ||
758 | #endif | ||
759 | }; | ||
760 | |||
761 | static struct nf_conntrack_l4proto dccp_proto6 __read_mostly = { | ||
762 | .l3proto = AF_INET6, | ||
763 | .l4proto = IPPROTO_DCCP, | ||
764 | .name = "dccp", | ||
765 | .pkt_to_tuple = dccp_pkt_to_tuple, | ||
766 | .invert_tuple = dccp_invert_tuple, | ||
767 | .new = dccp_new, | ||
768 | .packet = dccp_packet, | ||
769 | .error = dccp_error, | ||
770 | .print_tuple = dccp_print_tuple, | ||
771 | .print_conntrack = dccp_print_conntrack, | ||
772 | #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) | ||
773 | .to_nlattr = dccp_to_nlattr, | ||
774 | .from_nlattr = nlattr_to_dccp, | ||
775 | .tuple_to_nlattr = nf_ct_port_tuple_to_nlattr, | ||
776 | .nlattr_to_tuple = nf_ct_port_nlattr_to_tuple, | ||
777 | .nla_policy = nf_ct_port_nla_policy, | ||
778 | #endif | ||
779 | #ifdef CONFIG_SYSCTL | ||
780 | .ctl_table_users = &dccp_sysctl_table_users, | ||
781 | .ctl_table_header = &dccp_sysctl_header, | ||
782 | .ctl_table = dccp_sysctl_table, | ||
783 | #endif | ||
784 | }; | ||
785 | |||
786 | static int __init nf_conntrack_proto_dccp_init(void) | ||
787 | { | ||
788 | int err; | ||
789 | |||
790 | err = nf_conntrack_l4proto_register(&dccp_proto4); | ||
791 | if (err < 0) | ||
792 | goto err1; | ||
793 | |||
794 | err = nf_conntrack_l4proto_register(&dccp_proto6); | ||
795 | if (err < 0) | ||
796 | goto err2; | ||
797 | return 0; | ||
798 | |||
799 | err2: | ||
800 | nf_conntrack_l4proto_unregister(&dccp_proto4); | ||
801 | err1: | ||
802 | return err; | ||
803 | } | ||
804 | |||
805 | static void __exit nf_conntrack_proto_dccp_fini(void) | ||
806 | { | ||
807 | nf_conntrack_l4proto_unregister(&dccp_proto6); | ||
808 | nf_conntrack_l4proto_unregister(&dccp_proto4); | ||
809 | } | ||
810 | |||
811 | module_init(nf_conntrack_proto_dccp_init); | ||
812 | module_exit(nf_conntrack_proto_dccp_fini); | ||
813 | |||
814 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); | ||
815 | MODULE_DESCRIPTION("DCCP connection tracking protocol helper"); | ||
816 | MODULE_LICENSE("GPL"); | ||