12bc78049SPatrick McHardy /*
22bc78049SPatrick McHardy  * DCCP connection tracking protocol helper
32bc78049SPatrick McHardy  *
42bc78049SPatrick McHardy  * Copyright (c) 2005, 2006, 2008 Patrick McHardy <kaber@trash.net>
52bc78049SPatrick McHardy  *
62bc78049SPatrick McHardy  * This program is free software; you can redistribute it and/or modify
72bc78049SPatrick McHardy  * it under the terms of the GNU General Public License version 2 as
82bc78049SPatrick McHardy  * published by the Free Software Foundation.
92bc78049SPatrick McHardy  *
102bc78049SPatrick McHardy  */
112bc78049SPatrick McHardy #include <linux/kernel.h>
122bc78049SPatrick McHardy #include <linux/module.h>
132bc78049SPatrick McHardy #include <linux/init.h>
142bc78049SPatrick McHardy #include <linux/sysctl.h>
152bc78049SPatrick McHardy #include <linux/spinlock.h>
162bc78049SPatrick McHardy #include <linux/skbuff.h>
172bc78049SPatrick McHardy #include <linux/dccp.h>
182bc78049SPatrick McHardy 
192bc78049SPatrick McHardy #include <linux/netfilter/nfnetlink_conntrack.h>
202bc78049SPatrick McHardy #include <net/netfilter/nf_conntrack.h>
212bc78049SPatrick McHardy #include <net/netfilter/nf_conntrack_l4proto.h>
222bc78049SPatrick McHardy #include <net/netfilter/nf_log.h>
232bc78049SPatrick McHardy 
242bc78049SPatrick McHardy static DEFINE_RWLOCK(dccp_lock);
252bc78049SPatrick McHardy 
262bc78049SPatrick McHardy static int nf_ct_dccp_loose __read_mostly = 1;
272bc78049SPatrick McHardy 
282bc78049SPatrick McHardy /* Timeouts are based on values from RFC4340:
292bc78049SPatrick McHardy  *
302bc78049SPatrick McHardy  * - REQUEST:
312bc78049SPatrick McHardy  *
322bc78049SPatrick McHardy  *   8.1.2. Client Request
332bc78049SPatrick McHardy  *
342bc78049SPatrick McHardy  *   A client MAY give up on its DCCP-Requests after some time
352bc78049SPatrick McHardy  *   (3 minutes, for example).
362bc78049SPatrick McHardy  *
372bc78049SPatrick McHardy  * - RESPOND:
382bc78049SPatrick McHardy  *
392bc78049SPatrick McHardy  *   8.1.3. Server Response
402bc78049SPatrick McHardy  *
412bc78049SPatrick McHardy  *   It MAY also leave the RESPOND state for CLOSED after a timeout of
422bc78049SPatrick McHardy  *   not less than 4MSL (8 minutes);
432bc78049SPatrick McHardy  *
442bc78049SPatrick McHardy  * - PARTOPEN:
452bc78049SPatrick McHardy  *
462bc78049SPatrick McHardy  *   8.1.5. Handshake Completion
472bc78049SPatrick McHardy  *
482bc78049SPatrick McHardy  *   If the client remains in PARTOPEN for more than 4MSL (8 minutes),
492bc78049SPatrick McHardy  *   it SHOULD reset the connection with Reset Code 2, "Aborted".
502bc78049SPatrick McHardy  *
512bc78049SPatrick McHardy  * - OPEN:
522bc78049SPatrick McHardy  *
532bc78049SPatrick McHardy  *   The DCCP timestamp overflows after 11.9 hours. If the connection
542bc78049SPatrick McHardy  *   stays idle this long the sequence number won't be recognized
552bc78049SPatrick McHardy  *   as valid anymore.
562bc78049SPatrick McHardy  *
572bc78049SPatrick McHardy  * - CLOSEREQ/CLOSING:
582bc78049SPatrick McHardy  *
592bc78049SPatrick McHardy  *   8.3. Termination
602bc78049SPatrick McHardy  *
612bc78049SPatrick McHardy  *   The retransmission timer should initially be set to go off in two
622bc78049SPatrick McHardy  *   round-trip times and should back off to not less than once every
632bc78049SPatrick McHardy  *   64 seconds ...
642bc78049SPatrick McHardy  *
652bc78049SPatrick McHardy  * - TIMEWAIT:
662bc78049SPatrick McHardy  *
672bc78049SPatrick McHardy  *   4.3. States
682bc78049SPatrick McHardy  *
692bc78049SPatrick McHardy  *   A server or client socket remains in this state for 2MSL (4 minutes)
702bc78049SPatrick McHardy  *   after the connection has been town down, ...
712bc78049SPatrick McHardy  */
722bc78049SPatrick McHardy 
732bc78049SPatrick McHardy #define DCCP_MSL (2 * 60 * HZ)
742bc78049SPatrick McHardy 
752bc78049SPatrick McHardy static unsigned int dccp_timeout[CT_DCCP_MAX + 1] __read_mostly = {
762bc78049SPatrick McHardy 	[CT_DCCP_REQUEST]	= 2 * DCCP_MSL,
772bc78049SPatrick McHardy 	[CT_DCCP_RESPOND]	= 4 * DCCP_MSL,
782bc78049SPatrick McHardy 	[CT_DCCP_PARTOPEN]	= 4 * DCCP_MSL,
792bc78049SPatrick McHardy 	[CT_DCCP_OPEN]		= 12 * 3600 * HZ,
802bc78049SPatrick McHardy 	[CT_DCCP_CLOSEREQ]	= 64 * HZ,
812bc78049SPatrick McHardy 	[CT_DCCP_CLOSING]	= 64 * HZ,
822bc78049SPatrick McHardy 	[CT_DCCP_TIMEWAIT]	= 2 * DCCP_MSL,
832bc78049SPatrick McHardy };
842bc78049SPatrick McHardy 
852bc78049SPatrick McHardy static const char * const dccp_state_names[] = {
862bc78049SPatrick McHardy 	[CT_DCCP_NONE]		= "NONE",
872bc78049SPatrick McHardy 	[CT_DCCP_REQUEST]	= "REQUEST",
882bc78049SPatrick McHardy 	[CT_DCCP_RESPOND]	= "RESPOND",
892bc78049SPatrick McHardy 	[CT_DCCP_PARTOPEN]	= "PARTOPEN",
902bc78049SPatrick McHardy 	[CT_DCCP_OPEN]		= "OPEN",
912bc78049SPatrick McHardy 	[CT_DCCP_CLOSEREQ]	= "CLOSEREQ",
922bc78049SPatrick McHardy 	[CT_DCCP_CLOSING]	= "CLOSING",
932bc78049SPatrick McHardy 	[CT_DCCP_TIMEWAIT]	= "TIMEWAIT",
942bc78049SPatrick McHardy 	[CT_DCCP_IGNORE]	= "IGNORE",
952bc78049SPatrick McHardy 	[CT_DCCP_INVALID]	= "INVALID",
962bc78049SPatrick McHardy };
972bc78049SPatrick McHardy 
982bc78049SPatrick McHardy #define sNO	CT_DCCP_NONE
992bc78049SPatrick McHardy #define sRQ	CT_DCCP_REQUEST
1002bc78049SPatrick McHardy #define sRS	CT_DCCP_RESPOND
1012bc78049SPatrick McHardy #define sPO	CT_DCCP_PARTOPEN
1022bc78049SPatrick McHardy #define sOP	CT_DCCP_OPEN
1032bc78049SPatrick McHardy #define sCR	CT_DCCP_CLOSEREQ
1042bc78049SPatrick McHardy #define sCG	CT_DCCP_CLOSING
1052bc78049SPatrick McHardy #define sTW	CT_DCCP_TIMEWAIT
1062bc78049SPatrick McHardy #define sIG	CT_DCCP_IGNORE
1072bc78049SPatrick McHardy #define sIV	CT_DCCP_INVALID
1082bc78049SPatrick McHardy 
1092bc78049SPatrick McHardy /*
1102bc78049SPatrick McHardy  * DCCP state transistion table
1112bc78049SPatrick McHardy  *
1122bc78049SPatrick McHardy  * The assumption is the same as for TCP tracking:
1132bc78049SPatrick McHardy  *
1142bc78049SPatrick McHardy  * We are the man in the middle. All the packets go through us but might
1152bc78049SPatrick McHardy  * get lost in transit to the destination. It is assumed that the destination
1162bc78049SPatrick McHardy  * can't receive segments we haven't seen.
1172bc78049SPatrick McHardy  *
1182bc78049SPatrick McHardy  * The following states exist:
1192bc78049SPatrick McHardy  *
1202bc78049SPatrick McHardy  * NONE:	Initial state, expecting Request
1212bc78049SPatrick McHardy  * REQUEST:	Request seen, waiting for Response from server
1222bc78049SPatrick McHardy  * RESPOND:	Response from server seen, waiting for Ack from client
1232bc78049SPatrick McHardy  * PARTOPEN:	Ack after Response seen, waiting for packet other than Response,
1242bc78049SPatrick McHardy  * 		Reset or Sync from server
1252bc78049SPatrick McHardy  * OPEN:	Packet other than Response, Reset or Sync seen
1262bc78049SPatrick McHardy  * CLOSEREQ:	CloseReq from server seen, expecting Close from client
1272bc78049SPatrick McHardy  * CLOSING:	Close seen, expecting Reset
1282bc78049SPatrick McHardy  * TIMEWAIT:	Reset seen
1292bc78049SPatrick McHardy  * IGNORE:	Not determinable whether packet is valid
1302bc78049SPatrick McHardy  *
1312bc78049SPatrick McHardy  * Some states exist only on one side of the connection: REQUEST, RESPOND,
1322bc78049SPatrick McHardy  * PARTOPEN, CLOSEREQ. For the other side these states are equivalent to
1332bc78049SPatrick McHardy  * the one it was in before.
1342bc78049SPatrick McHardy  *
1352bc78049SPatrick McHardy  * Packets are marked as ignored (sIG) if we don't know if they're valid
1362bc78049SPatrick McHardy  * (for example a reincarnation of a connection we didn't notice is dead
1372bc78049SPatrick McHardy  * already) and the server may send back a connection closing Reset or a
1382bc78049SPatrick McHardy  * Response. They're also used for Sync/SyncAck packets, which we don't
1392bc78049SPatrick McHardy  * care about.
1402bc78049SPatrick McHardy  */
1412bc78049SPatrick McHardy static const u_int8_t
1422bc78049SPatrick McHardy dccp_state_table[CT_DCCP_ROLE_MAX + 1][DCCP_PKT_SYNCACK + 1][CT_DCCP_MAX + 1] = {
1432bc78049SPatrick McHardy 	[CT_DCCP_ROLE_CLIENT] = {
1442bc78049SPatrick McHardy 		[DCCP_PKT_REQUEST] = {
1452bc78049SPatrick McHardy 		/*
1462bc78049SPatrick McHardy 		 * sNO -> sRQ		Regular Request
1472bc78049SPatrick McHardy 		 * sRQ -> sRQ		Retransmitted Request or reincarnation
1482bc78049SPatrick McHardy 		 * sRS -> sRS		Retransmitted Request (apparently Response
1492bc78049SPatrick McHardy 		 * 			got lost after we saw it) or reincarnation
1502bc78049SPatrick McHardy 		 * sPO -> sIG		Ignore, conntrack might be out of sync
1512bc78049SPatrick McHardy 		 * sOP -> sIG		Ignore, conntrack might be out of sync
1522bc78049SPatrick McHardy 		 * sCR -> sIG		Ignore, conntrack might be out of sync
1532bc78049SPatrick McHardy 		 * sCG -> sIG		Ignore, conntrack might be out of sync
1542bc78049SPatrick McHardy 		 * sTW -> sRQ		Reincarnation
1552bc78049SPatrick McHardy 		 *
1562bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO. sOP, sCR, sCG, sTW, */
1572bc78049SPatrick McHardy 			sRQ, sRQ, sRS, sIG, sIG, sIG, sIG, sRQ,
1582bc78049SPatrick McHardy 		},
1592bc78049SPatrick McHardy 		[DCCP_PKT_RESPONSE] = {
1602bc78049SPatrick McHardy 		/*
1612bc78049SPatrick McHardy 		 * sNO -> sIV		Invalid
1622bc78049SPatrick McHardy 		 * sRQ -> sIG		Ignore, might be response to ignored Request
1632bc78049SPatrick McHardy 		 * sRS -> sIG		Ignore, might be response to ignored Request
1642bc78049SPatrick McHardy 		 * sPO -> sIG		Ignore, might be response to ignored Request
1652bc78049SPatrick McHardy 		 * sOP -> sIG		Ignore, might be response to ignored Request
1662bc78049SPatrick McHardy 		 * sCR -> sIG		Ignore, might be response to ignored Request
1672bc78049SPatrick McHardy 		 * sCG -> sIG		Ignore, might be response to ignored Request
1682bc78049SPatrick McHardy 		 * sTW -> sIV		Invalid, reincarnation in reverse direction
1692bc78049SPatrick McHardy 		 *			goes through sRQ
1702bc78049SPatrick McHardy 		 *
1712bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
1722bc78049SPatrick McHardy 			sIV, sIG, sIG, sIG, sIG, sIG, sIG, sIV,
1732bc78049SPatrick McHardy 		},
1742bc78049SPatrick McHardy 		[DCCP_PKT_ACK] = {
1752bc78049SPatrick McHardy 		/*
1762bc78049SPatrick McHardy 		 * sNO -> sIV		No connection
1772bc78049SPatrick McHardy 		 * sRQ -> sIV		No connection
1782bc78049SPatrick McHardy 		 * sRS -> sPO		Ack for Response, move to PARTOPEN (8.1.5.)
1792bc78049SPatrick McHardy 		 * sPO -> sPO		Retransmitted Ack for Response, remain in PARTOPEN
1802bc78049SPatrick McHardy 		 * sOP -> sOP		Regular ACK, remain in OPEN
1812bc78049SPatrick McHardy 		 * sCR -> sCR		Ack in CLOSEREQ MAY be processed (8.3.)
1822bc78049SPatrick McHardy 		 * sCG -> sCG		Ack in CLOSING MAY be processed (8.3.)
1832bc78049SPatrick McHardy 		 * sTW -> sIV
1842bc78049SPatrick McHardy 		 *
1852bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
1862bc78049SPatrick McHardy 			sIV, sIV, sPO, sPO, sOP, sCR, sCG, sIV
1872bc78049SPatrick McHardy 		},
1882bc78049SPatrick McHardy 		[DCCP_PKT_DATA] = {
1892bc78049SPatrick McHardy 		/*
1902bc78049SPatrick McHardy 		 * sNO -> sIV		No connection
1912bc78049SPatrick McHardy 		 * sRQ -> sIV		No connection
1922bc78049SPatrick McHardy 		 * sRS -> sIV		No connection
1932bc78049SPatrick McHardy 		 * sPO -> sIV		MUST use DataAck in PARTOPEN state (8.1.5.)
1942bc78049SPatrick McHardy 		 * sOP -> sOP		Regular Data packet
1952bc78049SPatrick McHardy 		 * sCR -> sCR		Data in CLOSEREQ MAY be processed (8.3.)
1962bc78049SPatrick McHardy 		 * sCG -> sCG		Data in CLOSING MAY be processed (8.3.)
1972bc78049SPatrick McHardy 		 * sTW -> sIV
1982bc78049SPatrick McHardy 		 *
1992bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
2002bc78049SPatrick McHardy 			sIV, sIV, sIV, sIV, sOP, sCR, sCG, sIV,
2012bc78049SPatrick McHardy 		},
2022bc78049SPatrick McHardy 		[DCCP_PKT_DATAACK] = {
2032bc78049SPatrick McHardy 		/*
2042bc78049SPatrick McHardy 		 * sNO -> sIV		No connection
2052bc78049SPatrick McHardy 		 * sRQ -> sIV		No connection
2062bc78049SPatrick McHardy 		 * sRS -> sPO		Ack for Response, move to PARTOPEN (8.1.5.)
2072bc78049SPatrick McHardy 		 * sPO -> sPO		Remain in PARTOPEN state
2082bc78049SPatrick McHardy 		 * sOP -> sOP		Regular DataAck packet in OPEN state
2092bc78049SPatrick McHardy 		 * sCR -> sCR		DataAck in CLOSEREQ MAY be processed (8.3.)
2102bc78049SPatrick McHardy 		 * sCG -> sCG		DataAck in CLOSING MAY be processed (8.3.)
2112bc78049SPatrick McHardy 		 * sTW -> sIV
2122bc78049SPatrick McHardy 		 *
2132bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
2142bc78049SPatrick McHardy 			sIV, sIV, sPO, sPO, sOP, sCR, sCG, sIV
2152bc78049SPatrick McHardy 		},
2162bc78049SPatrick McHardy 		[DCCP_PKT_CLOSEREQ] = {
2172bc78049SPatrick McHardy 		/*
2182bc78049SPatrick McHardy 		 * CLOSEREQ may only be sent by the server.
2192bc78049SPatrick McHardy 		 *
2202bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
2212bc78049SPatrick McHardy 			sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV
2222bc78049SPatrick McHardy 		},
2232bc78049SPatrick McHardy 		[DCCP_PKT_CLOSE] = {
2242bc78049SPatrick McHardy 		/*
2252bc78049SPatrick McHardy 		 * sNO -> sIV		No connection
2262bc78049SPatrick McHardy 		 * sRQ -> sIV		No connection
2272bc78049SPatrick McHardy 		 * sRS -> sIV		No connection
2282bc78049SPatrick McHardy 		 * sPO -> sCG		Client-initiated close
2292bc78049SPatrick McHardy 		 * sOP -> sCG		Client-initiated close
2302bc78049SPatrick McHardy 		 * sCR -> sCG		Close in response to CloseReq (8.3.)
2312bc78049SPatrick McHardy 		 * sCG -> sCG		Retransmit
2322bc78049SPatrick McHardy 		 * sTW -> sIV		Late retransmit, already in TIME_WAIT
2332bc78049SPatrick McHardy 		 *
2342bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
2352bc78049SPatrick McHardy 			sIV, sIV, sIV, sCG, sCG, sCG, sIV, sIV
2362bc78049SPatrick McHardy 		},
2372bc78049SPatrick McHardy 		[DCCP_PKT_RESET] = {
2382bc78049SPatrick McHardy 		/*
2392bc78049SPatrick McHardy 		 * sNO -> sIV		No connection
2402bc78049SPatrick McHardy 		 * sRQ -> sTW		Sync received or timeout, SHOULD send Reset (8.1.1.)
2412bc78049SPatrick McHardy 		 * sRS -> sTW		Response received without Request
2422bc78049SPatrick McHardy 		 * sPO -> sTW		Timeout, SHOULD send Reset (8.1.5.)
2432bc78049SPatrick McHardy 		 * sOP -> sTW		Connection reset
2442bc78049SPatrick McHardy 		 * sCR -> sTW		Connection reset
2452bc78049SPatrick McHardy 		 * sCG -> sTW		Connection reset
2462bc78049SPatrick McHardy 		 * sTW -> sIG		Ignore (don't refresh timer)
2472bc78049SPatrick McHardy 		 *
2482bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
2492bc78049SPatrick McHardy 			sIV, sTW, sTW, sTW, sTW, sTW, sTW, sIG
2502bc78049SPatrick McHardy 		},
2512bc78049SPatrick McHardy 		[DCCP_PKT_SYNC] = {
2522bc78049SPatrick McHardy 		/*
2532bc78049SPatrick McHardy 		 * We currently ignore Sync packets
2542bc78049SPatrick McHardy 		 *
2552bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
2562bc78049SPatrick McHardy 			sIG, sIG, sIG, sIG, sIG, sIG, sIG, sIG,
2572bc78049SPatrick McHardy 		},
2582bc78049SPatrick McHardy 		[DCCP_PKT_SYNCACK] = {
2592bc78049SPatrick McHardy 		/*
2602bc78049SPatrick McHardy 		 * We currently ignore SyncAck packets
2612bc78049SPatrick McHardy 		 *
2622bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
2632bc78049SPatrick McHardy 			sIG, sIG, sIG, sIG, sIG, sIG, sIG, sIG,
2642bc78049SPatrick McHardy 		},
2652bc78049SPatrick McHardy 	},
2662bc78049SPatrick McHardy 	[CT_DCCP_ROLE_SERVER] = {
2672bc78049SPatrick McHardy 		[DCCP_PKT_REQUEST] = {
2682bc78049SPatrick McHardy 		/*
2692bc78049SPatrick McHardy 		 * sNO -> sIV		Invalid
2702bc78049SPatrick McHardy 		 * sRQ -> sIG		Ignore, conntrack might be out of sync
2712bc78049SPatrick McHardy 		 * sRS -> sIG		Ignore, conntrack might be out of sync
2722bc78049SPatrick McHardy 		 * sPO -> sIG		Ignore, conntrack might be out of sync
2732bc78049SPatrick McHardy 		 * sOP -> sIG		Ignore, conntrack might be out of sync
2742bc78049SPatrick McHardy 		 * sCR -> sIG		Ignore, conntrack might be out of sync
2752bc78049SPatrick McHardy 		 * sCG -> sIG		Ignore, conntrack might be out of sync
2762bc78049SPatrick McHardy 		 * sTW -> sRQ		Reincarnation, must reverse roles
2772bc78049SPatrick McHardy 		 *
2782bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
2792bc78049SPatrick McHardy 			sIV, sIG, sIG, sIG, sIG, sIG, sIG, sRQ
2802bc78049SPatrick McHardy 		},
2812bc78049SPatrick McHardy 		[DCCP_PKT_RESPONSE] = {
2822bc78049SPatrick McHardy 		/*
2832bc78049SPatrick McHardy 		 * sNO -> sIV		Response without Request
2842bc78049SPatrick McHardy 		 * sRQ -> sRS		Response to clients Request
2852bc78049SPatrick McHardy 		 * sRS -> sRS		Retransmitted Response (8.1.3. SHOULD NOT)
2862bc78049SPatrick McHardy 		 * sPO -> sIG		Response to an ignored Request or late retransmit
2872bc78049SPatrick McHardy 		 * sOP -> sIG		Ignore, might be response to ignored Request
2882bc78049SPatrick McHardy 		 * sCR -> sIG		Ignore, might be response to ignored Request
2892bc78049SPatrick McHardy 		 * sCG -> sIG		Ignore, might be response to ignored Request
2902bc78049SPatrick McHardy 		 * sTW -> sIV		Invalid, Request from client in sTW moves to sRQ
2912bc78049SPatrick McHardy 		 *
2922bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
2932bc78049SPatrick McHardy 			sIV, sRS, sRS, sIG, sIG, sIG, sIG, sIV
2942bc78049SPatrick McHardy 		},
2952bc78049SPatrick McHardy 		[DCCP_PKT_ACK] = {
2962bc78049SPatrick McHardy 		/*
2972bc78049SPatrick McHardy 		 * sNO -> sIV		No connection
2982bc78049SPatrick McHardy 		 * sRQ -> sIV		No connection
2992bc78049SPatrick McHardy 		 * sRS -> sIV		No connection
3002bc78049SPatrick McHardy 		 * sPO -> sOP		Enter OPEN state (8.1.5.)
3012bc78049SPatrick McHardy 		 * sOP -> sOP		Regular Ack in OPEN state
3022bc78049SPatrick McHardy 		 * sCR -> sIV		Waiting for Close from client
3032bc78049SPatrick McHardy 		 * sCG -> sCG		Ack in CLOSING MAY be processed (8.3.)
3042bc78049SPatrick McHardy 		 * sTW -> sIV
3052bc78049SPatrick McHardy 		 *
3062bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
3072bc78049SPatrick McHardy 			sIV, sIV, sIV, sOP, sOP, sIV, sCG, sIV
3082bc78049SPatrick McHardy 		},
3092bc78049SPatrick McHardy 		[DCCP_PKT_DATA] = {
3102bc78049SPatrick McHardy 		/*
3112bc78049SPatrick McHardy 		 * sNO -> sIV		No connection
3122bc78049SPatrick McHardy 		 * sRQ -> sIV		No connection
3132bc78049SPatrick McHardy 		 * sRS -> sIV		No connection
3142bc78049SPatrick McHardy 		 * sPO -> sOP		Enter OPEN state (8.1.5.)
3152bc78049SPatrick McHardy 		 * sOP -> sOP		Regular Data packet in OPEN state
3162bc78049SPatrick McHardy 		 * sCR -> sIV		Waiting for Close from client
3172bc78049SPatrick McHardy 		 * sCG -> sCG		Data in CLOSING MAY be processed (8.3.)
3182bc78049SPatrick McHardy 		 * sTW -> sIV
3192bc78049SPatrick McHardy 		 *
3202bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
3212bc78049SPatrick McHardy 			sIV, sIV, sIV, sOP, sOP, sIV, sCG, sIV
3222bc78049SPatrick McHardy 		},
3232bc78049SPatrick McHardy 		[DCCP_PKT_DATAACK] = {
3242bc78049SPatrick McHardy 		/*
3252bc78049SPatrick McHardy 		 * sNO -> sIV		No connection
3262bc78049SPatrick McHardy 		 * sRQ -> sIV		No connection
3272bc78049SPatrick McHardy 		 * sRS -> sIV		No connection
3282bc78049SPatrick McHardy 		 * sPO -> sOP		Enter OPEN state (8.1.5.)
3292bc78049SPatrick McHardy 		 * sOP -> sOP		Regular DataAck in OPEN state
3302bc78049SPatrick McHardy 		 * sCR -> sIV		Waiting for Close from client
3312bc78049SPatrick McHardy 		 * sCG -> sCG		Data in CLOSING MAY be processed (8.3.)
3322bc78049SPatrick McHardy 		 * sTW -> sIV
3332bc78049SPatrick McHardy 		 *
3342bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
3352bc78049SPatrick McHardy 			sIV, sIV, sIV, sOP, sOP, sIV, sCG, sIV
3362bc78049SPatrick McHardy 		},
3372bc78049SPatrick McHardy 		[DCCP_PKT_CLOSEREQ] = {
3382bc78049SPatrick McHardy 		/*
3392bc78049SPatrick McHardy 		 * sNO -> sIV		No connection
3402bc78049SPatrick McHardy 		 * sRQ -> sIV		No connection
3412bc78049SPatrick McHardy 		 * sRS -> sIV		No connection
3422bc78049SPatrick McHardy 		 * sPO -> sOP -> sCR	Move directly to CLOSEREQ (8.1.5.)
3432bc78049SPatrick McHardy 		 * sOP -> sCR		CloseReq in OPEN state
3442bc78049SPatrick McHardy 		 * sCR -> sCR		Retransmit
3452bc78049SPatrick McHardy 		 * sCG -> sCR		Simultaneous close, client sends another Close
3462bc78049SPatrick McHardy 		 * sTW -> sIV		Already closed
3472bc78049SPatrick McHardy 		 *
3482bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
3492bc78049SPatrick McHardy 			sIV, sIV, sIV, sCR, sCR, sCR, sCR, sIV
3502bc78049SPatrick McHardy 		},
3512bc78049SPatrick McHardy 		[DCCP_PKT_CLOSE] = {
3522bc78049SPatrick McHardy 		/*
3532bc78049SPatrick McHardy 		 * sNO -> sIV		No connection
3542bc78049SPatrick McHardy 		 * sRQ -> sIV		No connection
3552bc78049SPatrick McHardy 		 * sRS -> sIV		No connection
3562bc78049SPatrick McHardy 		 * sPO -> sOP -> sCG	Move direcly to CLOSING
3572bc78049SPatrick McHardy 		 * sOP -> sCG		Move to CLOSING
3582bc78049SPatrick McHardy 		 * sCR -> sIV		Close after CloseReq is invalid
3592bc78049SPatrick McHardy 		 * sCG -> sCG		Retransmit
3602bc78049SPatrick McHardy 		 * sTW -> sIV		Already closed
3612bc78049SPatrick McHardy 		 *
3622bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
3632bc78049SPatrick McHardy 			sIV, sIV, sIV, sCG, sCG, sIV, sCG, sIV
3642bc78049SPatrick McHardy 		},
3652bc78049SPatrick McHardy 		[DCCP_PKT_RESET] = {
3662bc78049SPatrick McHardy 		/*
3672bc78049SPatrick McHardy 		 * sNO -> sIV		No connection
3682bc78049SPatrick McHardy 		 * sRQ -> sTW		Reset in response to Request
3692bc78049SPatrick McHardy 		 * sRS -> sTW		Timeout, SHOULD send Reset (8.1.3.)
3702bc78049SPatrick McHardy 		 * sPO -> sTW		Timeout, SHOULD send Reset (8.1.3.)
3712bc78049SPatrick McHardy 		 * sOP -> sTW
3722bc78049SPatrick McHardy 		 * sCR -> sTW
3732bc78049SPatrick McHardy 		 * sCG -> sTW
3742bc78049SPatrick McHardy 		 * sTW -> sIG		Ignore (don't refresh timer)
3752bc78049SPatrick McHardy 		 *
3762bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW, sTW */
3772bc78049SPatrick McHardy 			sIV, sTW, sTW, sTW, sTW, sTW, sTW, sTW, sIG
3782bc78049SPatrick McHardy 		},
3792bc78049SPatrick McHardy 		[DCCP_PKT_SYNC] = {
3802bc78049SPatrick McHardy 		/*
3812bc78049SPatrick McHardy 		 * We currently ignore Sync packets
3822bc78049SPatrick McHardy 		 *
3832bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
3842bc78049SPatrick McHardy 			sIG, sIG, sIG, sIG, sIG, sIG, sIG, sIG,
3852bc78049SPatrick McHardy 		},
3862bc78049SPatrick McHardy 		[DCCP_PKT_SYNCACK] = {
3872bc78049SPatrick McHardy 		/*
3882bc78049SPatrick McHardy 		 * We currently ignore SyncAck packets
3892bc78049SPatrick McHardy 		 *
3902bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
3912bc78049SPatrick McHardy 			sIG, sIG, sIG, sIG, sIG, sIG, sIG, sIG,
3922bc78049SPatrick McHardy 		},
3932bc78049SPatrick McHardy 	},
3942bc78049SPatrick McHardy };
3952bc78049SPatrick McHardy 
3962bc78049SPatrick McHardy static int dccp_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff,
3972bc78049SPatrick McHardy 			     struct nf_conntrack_tuple *tuple)
3982bc78049SPatrick McHardy {
3992bc78049SPatrick McHardy 	struct dccp_hdr _hdr, *dh;
4002bc78049SPatrick McHardy 
4012bc78049SPatrick McHardy 	dh = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
4022bc78049SPatrick McHardy 	if (dh == NULL)
4032bc78049SPatrick McHardy 		return 0;
4042bc78049SPatrick McHardy 
4052bc78049SPatrick McHardy 	tuple->src.u.dccp.port = dh->dccph_sport;
4062bc78049SPatrick McHardy 	tuple->dst.u.dccp.port = dh->dccph_dport;
4072bc78049SPatrick McHardy 	return 1;
4082bc78049SPatrick McHardy }
4092bc78049SPatrick McHardy 
4102bc78049SPatrick McHardy static int dccp_invert_tuple(struct nf_conntrack_tuple *inv,
4112bc78049SPatrick McHardy 			     const struct nf_conntrack_tuple *tuple)
4122bc78049SPatrick McHardy {
4132bc78049SPatrick McHardy 	inv->src.u.dccp.port = tuple->dst.u.dccp.port;
4142bc78049SPatrick McHardy 	inv->dst.u.dccp.port = tuple->src.u.dccp.port;
4152bc78049SPatrick McHardy 	return 1;
4162bc78049SPatrick McHardy }
4172bc78049SPatrick McHardy 
4182bc78049SPatrick McHardy static int dccp_new(struct nf_conn *ct, const struct sk_buff *skb,
4192bc78049SPatrick McHardy 		    unsigned int dataoff)
4202bc78049SPatrick McHardy {
4212bc78049SPatrick McHardy 	struct dccp_hdr _dh, *dh;
4222bc78049SPatrick McHardy 	const char *msg;
4232bc78049SPatrick McHardy 	u_int8_t state;
4242bc78049SPatrick McHardy 
4252bc78049SPatrick McHardy 	dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &dh);
4262bc78049SPatrick McHardy 	BUG_ON(dh == NULL);
4272bc78049SPatrick McHardy 
4282bc78049SPatrick McHardy 	state = dccp_state_table[CT_DCCP_ROLE_CLIENT][dh->dccph_type][CT_DCCP_NONE];
4292bc78049SPatrick McHardy 	switch (state) {
4302bc78049SPatrick McHardy 	default:
4312bc78049SPatrick McHardy 		if (nf_ct_dccp_loose == 0) {
4322bc78049SPatrick McHardy 			msg = "nf_ct_dccp: not picking up existing connection ";
4332bc78049SPatrick McHardy 			goto out_invalid;
4342bc78049SPatrick McHardy 		}
4352bc78049SPatrick McHardy 	case CT_DCCP_REQUEST:
4362bc78049SPatrick McHardy 		break;
4372bc78049SPatrick McHardy 	case CT_DCCP_INVALID:
4382bc78049SPatrick McHardy 		msg = "nf_ct_dccp: invalid state transition ";
4392bc78049SPatrick McHardy 		goto out_invalid;
4402bc78049SPatrick McHardy 	}
4412bc78049SPatrick McHardy 
4422bc78049SPatrick McHardy 	ct->proto.dccp.role[IP_CT_DIR_ORIGINAL] = CT_DCCP_ROLE_CLIENT;
4432bc78049SPatrick McHardy 	ct->proto.dccp.role[IP_CT_DIR_REPLY] = CT_DCCP_ROLE_SERVER;
4442bc78049SPatrick McHardy 	ct->proto.dccp.state = CT_DCCP_NONE;
4452bc78049SPatrick McHardy 	return 1;
4462bc78049SPatrick McHardy 
4472bc78049SPatrick McHardy out_invalid:
4482bc78049SPatrick McHardy 	if (LOG_INVALID(IPPROTO_DCCP))
4495e8fbe2aSPatrick McHardy 		nf_log_packet(nf_ct_l3num(ct), 0, skb, NULL, NULL, NULL, msg);
4502bc78049SPatrick McHardy 	return 0;
4512bc78049SPatrick McHardy }
4522bc78049SPatrick McHardy 
4532bc78049SPatrick McHardy static u64 dccp_ack_seq(const struct dccp_hdr *dh)
4542bc78049SPatrick McHardy {
4552bc78049SPatrick McHardy 	const struct dccp_hdr_ack_bits *dhack;
4562bc78049SPatrick McHardy 
4572bc78049SPatrick McHardy 	dhack = (void *)dh + __dccp_basic_hdr_len(dh);
4582bc78049SPatrick McHardy 	return ((u64)ntohs(dhack->dccph_ack_nr_high) << 32) +
4592bc78049SPatrick McHardy 		     ntohl(dhack->dccph_ack_nr_low);
4602bc78049SPatrick McHardy }
4612bc78049SPatrick McHardy 
4622bc78049SPatrick McHardy static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb,
4632bc78049SPatrick McHardy 		       unsigned int dataoff, enum ip_conntrack_info ctinfo,
4642bc78049SPatrick McHardy 		       int pf, unsigned int hooknum)
4652bc78049SPatrick McHardy {
4662bc78049SPatrick McHardy 	enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
4672bc78049SPatrick McHardy 	struct dccp_hdr _dh, *dh;
4682bc78049SPatrick McHardy 	u_int8_t type, old_state, new_state;
4692bc78049SPatrick McHardy 	enum ct_dccp_roles role;
4702bc78049SPatrick McHardy 
4712bc78049SPatrick McHardy 	dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &dh);
4722bc78049SPatrick McHardy 	BUG_ON(dh == NULL);
4732bc78049SPatrick McHardy 	type = dh->dccph_type;
4742bc78049SPatrick McHardy 
4752bc78049SPatrick McHardy 	if (type == DCCP_PKT_RESET &&
4762bc78049SPatrick McHardy 	    !test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
4772bc78049SPatrick McHardy 		/* Tear down connection immediately if only reply is a RESET */
4782bc78049SPatrick McHardy 		if (del_timer(&ct->timeout))
4792bc78049SPatrick McHardy 			ct->timeout.function((unsigned long)ct);
4802bc78049SPatrick McHardy 		return NF_ACCEPT;
4812bc78049SPatrick McHardy 	}
4822bc78049SPatrick McHardy 
4832bc78049SPatrick McHardy 	write_lock_bh(&dccp_lock);
4842bc78049SPatrick McHardy 
4852bc78049SPatrick McHardy 	role = ct->proto.dccp.role[dir];
4862bc78049SPatrick McHardy 	old_state = ct->proto.dccp.state;
4872bc78049SPatrick McHardy 	new_state = dccp_state_table[role][type][old_state];
4882bc78049SPatrick McHardy 
4892bc78049SPatrick McHardy 	switch (new_state) {
4902bc78049SPatrick McHardy 	case CT_DCCP_REQUEST:
4912bc78049SPatrick McHardy 		if (old_state == CT_DCCP_TIMEWAIT &&
4922bc78049SPatrick McHardy 		    role == CT_DCCP_ROLE_SERVER) {
4932bc78049SPatrick McHardy 			/* Reincarnation in the reverse direction: reopen and
4942bc78049SPatrick McHardy 			 * reverse client/server roles. */
4952bc78049SPatrick McHardy 			ct->proto.dccp.role[dir] = CT_DCCP_ROLE_CLIENT;
4962bc78049SPatrick McHardy 			ct->proto.dccp.role[!dir] = CT_DCCP_ROLE_SERVER;
4972bc78049SPatrick McHardy 		}
4982bc78049SPatrick McHardy 		break;
4992bc78049SPatrick McHardy 	case CT_DCCP_RESPOND:
5002bc78049SPatrick McHardy 		if (old_state == CT_DCCP_REQUEST)
5012bc78049SPatrick McHardy 			ct->proto.dccp.handshake_seq = dccp_hdr_seq(dh);
5022bc78049SPatrick McHardy 		break;
5032bc78049SPatrick McHardy 	case CT_DCCP_PARTOPEN:
5042bc78049SPatrick McHardy 		if (old_state == CT_DCCP_RESPOND &&
5052bc78049SPatrick McHardy 		    type == DCCP_PKT_ACK &&
5062bc78049SPatrick McHardy 		    dccp_ack_seq(dh) == ct->proto.dccp.handshake_seq)
5072bc78049SPatrick McHardy 			set_bit(IPS_ASSURED_BIT, &ct->status);
5082bc78049SPatrick McHardy 		break;
5092bc78049SPatrick McHardy 	case CT_DCCP_IGNORE:
5102bc78049SPatrick McHardy 		/*
5112bc78049SPatrick McHardy 		 * Connection tracking might be out of sync, so we ignore
5122bc78049SPatrick McHardy 		 * packets that might establish a new connection and resync
5132bc78049SPatrick McHardy 		 * if the server responds with a valid Response.
5142bc78049SPatrick McHardy 		 */
5152bc78049SPatrick McHardy 		if (ct->proto.dccp.last_dir == !dir &&
5162bc78049SPatrick McHardy 		    ct->proto.dccp.last_pkt == DCCP_PKT_REQUEST &&
5172bc78049SPatrick McHardy 		    type == DCCP_PKT_RESPONSE) {
5182bc78049SPatrick McHardy 			ct->proto.dccp.role[!dir] = CT_DCCP_ROLE_CLIENT;
5192bc78049SPatrick McHardy 			ct->proto.dccp.role[dir] = CT_DCCP_ROLE_SERVER;
5202bc78049SPatrick McHardy 			ct->proto.dccp.handshake_seq = dccp_hdr_seq(dh);
5212bc78049SPatrick McHardy 			new_state = CT_DCCP_RESPOND;
5222bc78049SPatrick McHardy 			break;
5232bc78049SPatrick McHardy 		}
5242bc78049SPatrick McHardy 		ct->proto.dccp.last_dir = dir;
5252bc78049SPatrick McHardy 		ct->proto.dccp.last_pkt = type;
5262bc78049SPatrick McHardy 
5272bc78049SPatrick McHardy 		write_unlock_bh(&dccp_lock);
5282bc78049SPatrick McHardy 		if (LOG_INVALID(IPPROTO_DCCP))
5292bc78049SPatrick McHardy 			nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
5302bc78049SPatrick McHardy 				      "nf_ct_dccp: invalid packet ignored ");
5312bc78049SPatrick McHardy 		return NF_ACCEPT;
5322bc78049SPatrick McHardy 	case CT_DCCP_INVALID:
5332bc78049SPatrick McHardy 		write_unlock_bh(&dccp_lock);
5342bc78049SPatrick McHardy 		if (LOG_INVALID(IPPROTO_DCCP))
5352bc78049SPatrick McHardy 			nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
5362bc78049SPatrick McHardy 				      "nf_ct_dccp: invalid state transition ");
5372bc78049SPatrick McHardy 		return -NF_ACCEPT;
5382bc78049SPatrick McHardy 	}
5392bc78049SPatrick McHardy 
5402bc78049SPatrick McHardy 	ct->proto.dccp.last_dir = dir;
5412bc78049SPatrick McHardy 	ct->proto.dccp.last_pkt = type;
5422bc78049SPatrick McHardy 	ct->proto.dccp.state = new_state;
5432bc78049SPatrick McHardy 	write_unlock_bh(&dccp_lock);
5442bc78049SPatrick McHardy 	nf_ct_refresh_acct(ct, ctinfo, skb, dccp_timeout[new_state]);
5452bc78049SPatrick McHardy 
5462bc78049SPatrick McHardy 	return NF_ACCEPT;
5472bc78049SPatrick McHardy }
5482bc78049SPatrick McHardy 
5492bc78049SPatrick McHardy static int dccp_error(struct sk_buff *skb, unsigned int dataoff,
5502bc78049SPatrick McHardy 		      enum ip_conntrack_info *ctinfo, int pf,
5512bc78049SPatrick McHardy 		      unsigned int hooknum)
5522bc78049SPatrick McHardy {
5532bc78049SPatrick McHardy 	struct dccp_hdr _dh, *dh;
5542bc78049SPatrick McHardy 	unsigned int dccp_len = skb->len - dataoff;
5552bc78049SPatrick McHardy 	unsigned int cscov;
5562bc78049SPatrick McHardy 	const char *msg;
5572bc78049SPatrick McHardy 
5582bc78049SPatrick McHardy 	dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &dh);
5592bc78049SPatrick McHardy 	if (dh == NULL) {
5602bc78049SPatrick McHardy 		msg = "nf_ct_dccp: short packet ";
5612bc78049SPatrick McHardy 		goto out_invalid;
5622bc78049SPatrick McHardy 	}
5632bc78049SPatrick McHardy 
5642bc78049SPatrick McHardy 	if (dh->dccph_doff * 4 < sizeof(struct dccp_hdr) ||
5652bc78049SPatrick McHardy 	    dh->dccph_doff * 4 > dccp_len) {
5662bc78049SPatrick McHardy 		msg = "nf_ct_dccp: truncated/malformed packet ";
5672bc78049SPatrick McHardy 		goto out_invalid;
5682bc78049SPatrick McHardy 	}
5692bc78049SPatrick McHardy 
5702bc78049SPatrick McHardy 	cscov = dccp_len;
5712bc78049SPatrick McHardy 	if (dh->dccph_cscov) {
5722bc78049SPatrick McHardy 		cscov = (dh->dccph_cscov - 1) * 4;
5732bc78049SPatrick McHardy 		if (cscov > dccp_len) {
5742bc78049SPatrick McHardy 			msg = "nf_ct_dccp: bad checksum coverage ";
5752bc78049SPatrick McHardy 			goto out_invalid;
5762bc78049SPatrick McHardy 		}
5772bc78049SPatrick McHardy 	}
5782bc78049SPatrick McHardy 
5792bc78049SPatrick McHardy 	if (nf_conntrack_checksum && hooknum == NF_INET_PRE_ROUTING &&
5802bc78049SPatrick McHardy 	    nf_checksum_partial(skb, hooknum, dataoff, cscov, IPPROTO_DCCP,
5812bc78049SPatrick McHardy 				pf)) {
5822bc78049SPatrick McHardy 		msg = "nf_ct_dccp: bad checksum ";
5832bc78049SPatrick McHardy 		goto out_invalid;
5842bc78049SPatrick McHardy 	}
5852bc78049SPatrick McHardy 
5862bc78049SPatrick McHardy 	if (dh->dccph_type >= DCCP_PKT_INVALID) {
5872bc78049SPatrick McHardy 		msg = "nf_ct_dccp: reserved packet type ";
5882bc78049SPatrick McHardy 		goto out_invalid;
5892bc78049SPatrick McHardy 	}
5902bc78049SPatrick McHardy 
5912bc78049SPatrick McHardy 	return NF_ACCEPT;
5922bc78049SPatrick McHardy 
5932bc78049SPatrick McHardy out_invalid:
5942bc78049SPatrick McHardy 	if (LOG_INVALID(IPPROTO_DCCP))
5952bc78049SPatrick McHardy 		nf_log_packet(pf, 0, skb, NULL, NULL, NULL, msg);
5962bc78049SPatrick McHardy 	return -NF_ACCEPT;
5972bc78049SPatrick McHardy }
5982bc78049SPatrick McHardy 
5992bc78049SPatrick McHardy static int dccp_print_tuple(struct seq_file *s,
6002bc78049SPatrick McHardy 			    const struct nf_conntrack_tuple *tuple)
6012bc78049SPatrick McHardy {
6022bc78049SPatrick McHardy 	return seq_printf(s, "sport=%hu dport=%hu ",
6032bc78049SPatrick McHardy 			  ntohs(tuple->src.u.dccp.port),
6042bc78049SPatrick McHardy 			  ntohs(tuple->dst.u.dccp.port));
6052bc78049SPatrick McHardy }
6062bc78049SPatrick McHardy 
6072bc78049SPatrick McHardy static int dccp_print_conntrack(struct seq_file *s, const struct nf_conn *ct)
6082bc78049SPatrick McHardy {
6092bc78049SPatrick McHardy 	return seq_printf(s, "%s ", dccp_state_names[ct->proto.dccp.state]);
6102bc78049SPatrick McHardy }
6112bc78049SPatrick McHardy 
6122bc78049SPatrick McHardy #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
6132bc78049SPatrick McHardy static int dccp_to_nlattr(struct sk_buff *skb, struct nlattr *nla,
6142bc78049SPatrick McHardy 			  const struct nf_conn *ct)
6152bc78049SPatrick McHardy {
6162bc78049SPatrick McHardy 	struct nlattr *nest_parms;
6172bc78049SPatrick McHardy 
6182bc78049SPatrick McHardy 	read_lock_bh(&dccp_lock);
6192bc78049SPatrick McHardy 	nest_parms = nla_nest_start(skb, CTA_PROTOINFO_DCCP | NLA_F_NESTED);
6202bc78049SPatrick McHardy 	if (!nest_parms)
6212bc78049SPatrick McHardy 		goto nla_put_failure;
6222bc78049SPatrick McHardy 	NLA_PUT_U8(skb, CTA_PROTOINFO_DCCP_STATE, ct->proto.dccp.state);
6232bc78049SPatrick McHardy 	nla_nest_end(skb, nest_parms);
6242bc78049SPatrick McHardy 	read_unlock_bh(&dccp_lock);
6252bc78049SPatrick McHardy 	return 0;
6262bc78049SPatrick McHardy 
6272bc78049SPatrick McHardy nla_put_failure:
6282bc78049SPatrick McHardy 	read_unlock_bh(&dccp_lock);
6292bc78049SPatrick McHardy 	return -1;
6302bc78049SPatrick McHardy }
6312bc78049SPatrick McHardy 
6322bc78049SPatrick McHardy static const struct nla_policy dccp_nla_policy[CTA_PROTOINFO_DCCP_MAX + 1] = {
6332bc78049SPatrick McHardy 	[CTA_PROTOINFO_DCCP_STATE]	= { .type = NLA_U8 },
6342bc78049SPatrick McHardy };
6352bc78049SPatrick McHardy 
6362bc78049SPatrick McHardy static int nlattr_to_dccp(struct nlattr *cda[], struct nf_conn *ct)
6372bc78049SPatrick McHardy {
6382bc78049SPatrick McHardy 	struct nlattr *attr = cda[CTA_PROTOINFO_DCCP];
6392bc78049SPatrick McHardy 	struct nlattr *tb[CTA_PROTOINFO_DCCP_MAX + 1];
6402bc78049SPatrick McHardy 	int err;
6412bc78049SPatrick McHardy 
6422bc78049SPatrick McHardy 	if (!attr)
6432bc78049SPatrick McHardy 		return 0;
6442bc78049SPatrick McHardy 
6452bc78049SPatrick McHardy 	err = nla_parse_nested(tb, CTA_PROTOINFO_DCCP_MAX, attr,
6462bc78049SPatrick McHardy 			       dccp_nla_policy);
6472bc78049SPatrick McHardy 	if (err < 0)
6482bc78049SPatrick McHardy 		return err;
6492bc78049SPatrick McHardy 
6502bc78049SPatrick McHardy 	if (!tb[CTA_PROTOINFO_DCCP_STATE] ||
6512bc78049SPatrick McHardy 	    nla_get_u8(tb[CTA_PROTOINFO_DCCP_STATE]) >= CT_DCCP_IGNORE)
6522bc78049SPatrick McHardy 		return -EINVAL;
6532bc78049SPatrick McHardy 
6542bc78049SPatrick McHardy 	write_lock_bh(&dccp_lock);
6552bc78049SPatrick McHardy 	ct->proto.dccp.state = nla_get_u8(tb[CTA_PROTOINFO_DCCP_STATE]);
6562bc78049SPatrick McHardy 	write_unlock_bh(&dccp_lock);
6572bc78049SPatrick McHardy 	return 0;
6582bc78049SPatrick McHardy }
6592bc78049SPatrick McHardy #endif
6602bc78049SPatrick McHardy 
6612bc78049SPatrick McHardy #ifdef CONFIG_SYSCTL
6622bc78049SPatrick McHardy static unsigned int dccp_sysctl_table_users;
6632bc78049SPatrick McHardy static struct ctl_table_header *dccp_sysctl_header;
6642bc78049SPatrick McHardy static ctl_table dccp_sysctl_table[] = {
6652bc78049SPatrick McHardy 	{
6662bc78049SPatrick McHardy 		.ctl_name	= CTL_UNNUMBERED,
6672bc78049SPatrick McHardy 		.procname	= "nf_conntrack_dccp_timeout_request",
6682bc78049SPatrick McHardy 		.data		= &dccp_timeout[CT_DCCP_REQUEST],
6692bc78049SPatrick McHardy 		.maxlen		= sizeof(unsigned int),
6702bc78049SPatrick McHardy 		.mode		= 0644,
6712bc78049SPatrick McHardy 		.proc_handler	= proc_dointvec_jiffies,
6722bc78049SPatrick McHardy 	},
6732bc78049SPatrick McHardy 	{
6742bc78049SPatrick McHardy 		.ctl_name	= CTL_UNNUMBERED,
6752bc78049SPatrick McHardy 		.procname	= "nf_conntrack_dccp_timeout_respond",
6762bc78049SPatrick McHardy 		.data		= &dccp_timeout[CT_DCCP_RESPOND],
6772bc78049SPatrick McHardy 		.maxlen		= sizeof(unsigned int),
6782bc78049SPatrick McHardy 		.mode		= 0644,
6792bc78049SPatrick McHardy 		.proc_handler	= proc_dointvec_jiffies,
6802bc78049SPatrick McHardy 	},
6812bc78049SPatrick McHardy 	{
6822bc78049SPatrick McHardy 		.ctl_name	= CTL_UNNUMBERED,
6832bc78049SPatrick McHardy 		.procname	= "nf_conntrack_dccp_timeout_partopen",
6842bc78049SPatrick McHardy 		.data		= &dccp_timeout[CT_DCCP_PARTOPEN],
6852bc78049SPatrick McHardy 		.maxlen		= sizeof(unsigned int),
6862bc78049SPatrick McHardy 		.mode		= 0644,
6872bc78049SPatrick McHardy 		.proc_handler	= proc_dointvec_jiffies,
6882bc78049SPatrick McHardy 	},
6892bc78049SPatrick McHardy 	{
6902bc78049SPatrick McHardy 		.ctl_name	= CTL_UNNUMBERED,
6912bc78049SPatrick McHardy 		.procname	= "nf_conntrack_dccp_timeout_open",
6922bc78049SPatrick McHardy 		.data		= &dccp_timeout[CT_DCCP_OPEN],
6932bc78049SPatrick McHardy 		.maxlen		= sizeof(unsigned int),
6942bc78049SPatrick McHardy 		.mode		= 0644,
6952bc78049SPatrick McHardy 		.proc_handler	= proc_dointvec_jiffies,
6962bc78049SPatrick McHardy 	},
6972bc78049SPatrick McHardy 	{
6982bc78049SPatrick McHardy 		.ctl_name	= CTL_UNNUMBERED,
6992bc78049SPatrick McHardy 		.procname	= "nf_conntrack_dccp_timeout_closereq",
7002bc78049SPatrick McHardy 		.data		= &dccp_timeout[CT_DCCP_CLOSEREQ],
7012bc78049SPatrick McHardy 		.maxlen		= sizeof(unsigned int),
7022bc78049SPatrick McHardy 		.mode		= 0644,
7032bc78049SPatrick McHardy 		.proc_handler	= proc_dointvec_jiffies,
7042bc78049SPatrick McHardy 	},
7052bc78049SPatrick McHardy 	{
7062bc78049SPatrick McHardy 		.ctl_name	= CTL_UNNUMBERED,
7072bc78049SPatrick McHardy 		.procname	= "nf_conntrack_dccp_timeout_closing",
7082bc78049SPatrick McHardy 		.data		= &dccp_timeout[CT_DCCP_CLOSING],
7092bc78049SPatrick McHardy 		.maxlen		= sizeof(unsigned int),
7102bc78049SPatrick McHardy 		.mode		= 0644,
7112bc78049SPatrick McHardy 		.proc_handler	= proc_dointvec_jiffies,
7122bc78049SPatrick McHardy 	},
7132bc78049SPatrick McHardy 	{
7142bc78049SPatrick McHardy 		.ctl_name	= CTL_UNNUMBERED,
7152bc78049SPatrick McHardy 		.procname	= "nf_conntrack_dccp_timeout_timewait",
7162bc78049SPatrick McHardy 		.data		= &dccp_timeout[CT_DCCP_TIMEWAIT],
7172bc78049SPatrick McHardy 		.maxlen		= sizeof(unsigned int),
7182bc78049SPatrick McHardy 		.mode		= 0644,
7192bc78049SPatrick McHardy 		.proc_handler	= proc_dointvec_jiffies,
7202bc78049SPatrick McHardy 	},
7212bc78049SPatrick McHardy 	{
7222bc78049SPatrick McHardy 		.ctl_name	= CTL_UNNUMBERED,
7232bc78049SPatrick McHardy 		.procname	= "nf_conntrack_dccp_loose",
7242bc78049SPatrick McHardy 		.data		= &nf_ct_dccp_loose,
7252bc78049SPatrick McHardy 		.maxlen		= sizeof(nf_ct_dccp_loose),
7262bc78049SPatrick McHardy 		.mode		= 0644,
7272bc78049SPatrick McHardy 		.proc_handler	= proc_dointvec,
7282bc78049SPatrick McHardy 	},
7292bc78049SPatrick McHardy 	{
7302bc78049SPatrick McHardy 		.ctl_name	= 0,
7312bc78049SPatrick McHardy 	}
7322bc78049SPatrick McHardy };
7332bc78049SPatrick McHardy #endif /* CONFIG_SYSCTL */
7342bc78049SPatrick McHardy 
7352bc78049SPatrick McHardy static struct nf_conntrack_l4proto dccp_proto4 __read_mostly = {
7362bc78049SPatrick McHardy 	.l3proto		= AF_INET,
7372bc78049SPatrick McHardy 	.l4proto		= IPPROTO_DCCP,
7382bc78049SPatrick McHardy 	.name			= "dccp",
7392bc78049SPatrick McHardy 	.pkt_to_tuple		= dccp_pkt_to_tuple,
7402bc78049SPatrick McHardy 	.invert_tuple		= dccp_invert_tuple,
7412bc78049SPatrick McHardy 	.new			= dccp_new,
7422bc78049SPatrick McHardy 	.packet			= dccp_packet,
7432bc78049SPatrick McHardy 	.error			= dccp_error,
7442bc78049SPatrick McHardy 	.print_tuple		= dccp_print_tuple,
7452bc78049SPatrick McHardy 	.print_conntrack	= dccp_print_conntrack,
7462bc78049SPatrick McHardy #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
7472bc78049SPatrick McHardy 	.to_nlattr		= dccp_to_nlattr,
7482bc78049SPatrick McHardy 	.from_nlattr		= nlattr_to_dccp,
7492bc78049SPatrick McHardy 	.tuple_to_nlattr	= nf_ct_port_tuple_to_nlattr,
7502bc78049SPatrick McHardy 	.nlattr_to_tuple	= nf_ct_port_nlattr_to_tuple,
7512bc78049SPatrick McHardy 	.nla_policy		= nf_ct_port_nla_policy,
7522bc78049SPatrick McHardy #endif
7532bc78049SPatrick McHardy #ifdef CONFIG_SYSCTL
7542bc78049SPatrick McHardy 	.ctl_table_users	= &dccp_sysctl_table_users,
7552bc78049SPatrick McHardy 	.ctl_table_header	= &dccp_sysctl_header,
7562bc78049SPatrick McHardy 	.ctl_table		= dccp_sysctl_table,
7572bc78049SPatrick McHardy #endif
7582bc78049SPatrick McHardy };
7592bc78049SPatrick McHardy 
7602bc78049SPatrick McHardy static struct nf_conntrack_l4proto dccp_proto6 __read_mostly = {
7612bc78049SPatrick McHardy 	.l3proto		= AF_INET6,
7622bc78049SPatrick McHardy 	.l4proto		= IPPROTO_DCCP,
7632bc78049SPatrick McHardy 	.name			= "dccp",
7642bc78049SPatrick McHardy 	.pkt_to_tuple		= dccp_pkt_to_tuple,
7652bc78049SPatrick McHardy 	.invert_tuple		= dccp_invert_tuple,
7662bc78049SPatrick McHardy 	.new			= dccp_new,
7672bc78049SPatrick McHardy 	.packet			= dccp_packet,
7682bc78049SPatrick McHardy 	.error			= dccp_error,
7692bc78049SPatrick McHardy 	.print_tuple		= dccp_print_tuple,
7702bc78049SPatrick McHardy 	.print_conntrack	= dccp_print_conntrack,
7712bc78049SPatrick McHardy #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
7722bc78049SPatrick McHardy 	.to_nlattr		= dccp_to_nlattr,
7732bc78049SPatrick McHardy 	.from_nlattr		= nlattr_to_dccp,
7742bc78049SPatrick McHardy 	.tuple_to_nlattr	= nf_ct_port_tuple_to_nlattr,
7752bc78049SPatrick McHardy 	.nlattr_to_tuple	= nf_ct_port_nlattr_to_tuple,
7762bc78049SPatrick McHardy 	.nla_policy		= nf_ct_port_nla_policy,
7772bc78049SPatrick McHardy #endif
7782bc78049SPatrick McHardy #ifdef CONFIG_SYSCTL
7792bc78049SPatrick McHardy 	.ctl_table_users	= &dccp_sysctl_table_users,
7802bc78049SPatrick McHardy 	.ctl_table_header	= &dccp_sysctl_header,
7812bc78049SPatrick McHardy 	.ctl_table		= dccp_sysctl_table,
7822bc78049SPatrick McHardy #endif
7832bc78049SPatrick McHardy };
7842bc78049SPatrick McHardy 
7852bc78049SPatrick McHardy static int __init nf_conntrack_proto_dccp_init(void)
7862bc78049SPatrick McHardy {
7872bc78049SPatrick McHardy 	int err;
7882bc78049SPatrick McHardy 
7892bc78049SPatrick McHardy 	err = nf_conntrack_l4proto_register(&dccp_proto4);
7902bc78049SPatrick McHardy 	if (err < 0)
7912bc78049SPatrick McHardy 		goto err1;
7922bc78049SPatrick McHardy 
7932bc78049SPatrick McHardy 	err = nf_conntrack_l4proto_register(&dccp_proto6);
7942bc78049SPatrick McHardy 	if (err < 0)
7952bc78049SPatrick McHardy 		goto err2;
7962bc78049SPatrick McHardy 	return 0;
7972bc78049SPatrick McHardy 
7982bc78049SPatrick McHardy err2:
7992bc78049SPatrick McHardy 	nf_conntrack_l4proto_unregister(&dccp_proto4);
8002bc78049SPatrick McHardy err1:
8012bc78049SPatrick McHardy 	return err;
8022bc78049SPatrick McHardy }
8032bc78049SPatrick McHardy 
8042bc78049SPatrick McHardy static void __exit nf_conntrack_proto_dccp_fini(void)
8052bc78049SPatrick McHardy {
8062bc78049SPatrick McHardy 	nf_conntrack_l4proto_unregister(&dccp_proto6);
8072bc78049SPatrick McHardy 	nf_conntrack_l4proto_unregister(&dccp_proto4);
8082bc78049SPatrick McHardy }
8092bc78049SPatrick McHardy 
8102bc78049SPatrick McHardy module_init(nf_conntrack_proto_dccp_init);
8112bc78049SPatrick McHardy module_exit(nf_conntrack_proto_dccp_fini);
8122bc78049SPatrick McHardy 
8132bc78049SPatrick McHardy MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
8142bc78049SPatrick McHardy MODULE_DESCRIPTION("DCCP connection tracking protocol helper");
8152bc78049SPatrick McHardy MODULE_LICENSE("GPL");
816