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 
191546000fSCyrill Gorcunov #include <net/net_namespace.h>
201546000fSCyrill Gorcunov #include <net/netns/generic.h>
211546000fSCyrill Gorcunov 
222bc78049SPatrick McHardy #include <linux/netfilter/nfnetlink_conntrack.h>
232bc78049SPatrick McHardy #include <net/netfilter/nf_conntrack.h>
242bc78049SPatrick McHardy #include <net/netfilter/nf_conntrack_l4proto.h>
252bc78049SPatrick McHardy #include <net/netfilter/nf_log.h>
262bc78049SPatrick McHardy 
272bc78049SPatrick McHardy static DEFINE_RWLOCK(dccp_lock);
282bc78049SPatrick McHardy 
292bc78049SPatrick McHardy /* Timeouts are based on values from RFC4340:
302bc78049SPatrick McHardy  *
312bc78049SPatrick McHardy  * - REQUEST:
322bc78049SPatrick McHardy  *
332bc78049SPatrick McHardy  *   8.1.2. Client Request
342bc78049SPatrick McHardy  *
352bc78049SPatrick McHardy  *   A client MAY give up on its DCCP-Requests after some time
362bc78049SPatrick McHardy  *   (3 minutes, for example).
372bc78049SPatrick McHardy  *
382bc78049SPatrick McHardy  * - RESPOND:
392bc78049SPatrick McHardy  *
402bc78049SPatrick McHardy  *   8.1.3. Server Response
412bc78049SPatrick McHardy  *
422bc78049SPatrick McHardy  *   It MAY also leave the RESPOND state for CLOSED after a timeout of
432bc78049SPatrick McHardy  *   not less than 4MSL (8 minutes);
442bc78049SPatrick McHardy  *
452bc78049SPatrick McHardy  * - PARTOPEN:
462bc78049SPatrick McHardy  *
472bc78049SPatrick McHardy  *   8.1.5. Handshake Completion
482bc78049SPatrick McHardy  *
492bc78049SPatrick McHardy  *   If the client remains in PARTOPEN for more than 4MSL (8 minutes),
502bc78049SPatrick McHardy  *   it SHOULD reset the connection with Reset Code 2, "Aborted".
512bc78049SPatrick McHardy  *
522bc78049SPatrick McHardy  * - OPEN:
532bc78049SPatrick McHardy  *
542bc78049SPatrick McHardy  *   The DCCP timestamp overflows after 11.9 hours. If the connection
552bc78049SPatrick McHardy  *   stays idle this long the sequence number won't be recognized
562bc78049SPatrick McHardy  *   as valid anymore.
572bc78049SPatrick McHardy  *
582bc78049SPatrick McHardy  * - CLOSEREQ/CLOSING:
592bc78049SPatrick McHardy  *
602bc78049SPatrick McHardy  *   8.3. Termination
612bc78049SPatrick McHardy  *
622bc78049SPatrick McHardy  *   The retransmission timer should initially be set to go off in two
632bc78049SPatrick McHardy  *   round-trip times and should back off to not less than once every
642bc78049SPatrick McHardy  *   64 seconds ...
652bc78049SPatrick McHardy  *
662bc78049SPatrick McHardy  * - TIMEWAIT:
672bc78049SPatrick McHardy  *
682bc78049SPatrick McHardy  *   4.3. States
692bc78049SPatrick McHardy  *
702bc78049SPatrick McHardy  *   A server or client socket remains in this state for 2MSL (4 minutes)
712bc78049SPatrick McHardy  *   after the connection has been town down, ...
722bc78049SPatrick McHardy  */
732bc78049SPatrick McHardy 
742bc78049SPatrick McHardy #define DCCP_MSL (2 * 60 * HZ)
752bc78049SPatrick McHardy 
762bc78049SPatrick McHardy static const char * const dccp_state_names[] = {
772bc78049SPatrick McHardy 	[CT_DCCP_NONE]		= "NONE",
782bc78049SPatrick McHardy 	[CT_DCCP_REQUEST]	= "REQUEST",
792bc78049SPatrick McHardy 	[CT_DCCP_RESPOND]	= "RESPOND",
802bc78049SPatrick McHardy 	[CT_DCCP_PARTOPEN]	= "PARTOPEN",
812bc78049SPatrick McHardy 	[CT_DCCP_OPEN]		= "OPEN",
822bc78049SPatrick McHardy 	[CT_DCCP_CLOSEREQ]	= "CLOSEREQ",
832bc78049SPatrick McHardy 	[CT_DCCP_CLOSING]	= "CLOSING",
842bc78049SPatrick McHardy 	[CT_DCCP_TIMEWAIT]	= "TIMEWAIT",
852bc78049SPatrick McHardy 	[CT_DCCP_IGNORE]	= "IGNORE",
862bc78049SPatrick McHardy 	[CT_DCCP_INVALID]	= "INVALID",
872bc78049SPatrick McHardy };
882bc78049SPatrick McHardy 
892bc78049SPatrick McHardy #define sNO	CT_DCCP_NONE
902bc78049SPatrick McHardy #define sRQ	CT_DCCP_REQUEST
912bc78049SPatrick McHardy #define sRS	CT_DCCP_RESPOND
922bc78049SPatrick McHardy #define sPO	CT_DCCP_PARTOPEN
932bc78049SPatrick McHardy #define sOP	CT_DCCP_OPEN
942bc78049SPatrick McHardy #define sCR	CT_DCCP_CLOSEREQ
952bc78049SPatrick McHardy #define sCG	CT_DCCP_CLOSING
962bc78049SPatrick McHardy #define sTW	CT_DCCP_TIMEWAIT
972bc78049SPatrick McHardy #define sIG	CT_DCCP_IGNORE
982bc78049SPatrick McHardy #define sIV	CT_DCCP_INVALID
992bc78049SPatrick McHardy 
1002bc78049SPatrick McHardy /*
1012bc78049SPatrick McHardy  * DCCP state transistion table
1022bc78049SPatrick McHardy  *
1032bc78049SPatrick McHardy  * The assumption is the same as for TCP tracking:
1042bc78049SPatrick McHardy  *
1052bc78049SPatrick McHardy  * We are the man in the middle. All the packets go through us but might
1062bc78049SPatrick McHardy  * get lost in transit to the destination. It is assumed that the destination
1072bc78049SPatrick McHardy  * can't receive segments we haven't seen.
1082bc78049SPatrick McHardy  *
1092bc78049SPatrick McHardy  * The following states exist:
1102bc78049SPatrick McHardy  *
1112bc78049SPatrick McHardy  * NONE:	Initial state, expecting Request
1122bc78049SPatrick McHardy  * REQUEST:	Request seen, waiting for Response from server
1132bc78049SPatrick McHardy  * RESPOND:	Response from server seen, waiting for Ack from client
1142bc78049SPatrick McHardy  * PARTOPEN:	Ack after Response seen, waiting for packet other than Response,
1152bc78049SPatrick McHardy  * 		Reset or Sync from server
1162bc78049SPatrick McHardy  * OPEN:	Packet other than Response, Reset or Sync seen
1172bc78049SPatrick McHardy  * CLOSEREQ:	CloseReq from server seen, expecting Close from client
1182bc78049SPatrick McHardy  * CLOSING:	Close seen, expecting Reset
1192bc78049SPatrick McHardy  * TIMEWAIT:	Reset seen
1202bc78049SPatrick McHardy  * IGNORE:	Not determinable whether packet is valid
1212bc78049SPatrick McHardy  *
1222bc78049SPatrick McHardy  * Some states exist only on one side of the connection: REQUEST, RESPOND,
1232bc78049SPatrick McHardy  * PARTOPEN, CLOSEREQ. For the other side these states are equivalent to
1242bc78049SPatrick McHardy  * the one it was in before.
1252bc78049SPatrick McHardy  *
1262bc78049SPatrick McHardy  * Packets are marked as ignored (sIG) if we don't know if they're valid
1272bc78049SPatrick McHardy  * (for example a reincarnation of a connection we didn't notice is dead
1282bc78049SPatrick McHardy  * already) and the server may send back a connection closing Reset or a
1292bc78049SPatrick McHardy  * Response. They're also used for Sync/SyncAck packets, which we don't
1302bc78049SPatrick McHardy  * care about.
1312bc78049SPatrick McHardy  */
1322bc78049SPatrick McHardy static const u_int8_t
1332bc78049SPatrick McHardy dccp_state_table[CT_DCCP_ROLE_MAX + 1][DCCP_PKT_SYNCACK + 1][CT_DCCP_MAX + 1] = {
1342bc78049SPatrick McHardy 	[CT_DCCP_ROLE_CLIENT] = {
1352bc78049SPatrick McHardy 		[DCCP_PKT_REQUEST] = {
1362bc78049SPatrick McHardy 		/*
1372bc78049SPatrick McHardy 		 * sNO -> sRQ		Regular Request
1382bc78049SPatrick McHardy 		 * sRQ -> sRQ		Retransmitted Request or reincarnation
1392bc78049SPatrick McHardy 		 * sRS -> sRS		Retransmitted Request (apparently Response
1402bc78049SPatrick McHardy 		 * 			got lost after we saw it) or reincarnation
1412bc78049SPatrick McHardy 		 * sPO -> sIG		Ignore, conntrack might be out of sync
1422bc78049SPatrick McHardy 		 * sOP -> sIG		Ignore, conntrack might be out of sync
1432bc78049SPatrick McHardy 		 * sCR -> sIG		Ignore, conntrack might be out of sync
1442bc78049SPatrick McHardy 		 * sCG -> sIG		Ignore, conntrack might be out of sync
1452bc78049SPatrick McHardy 		 * sTW -> sRQ		Reincarnation
1462bc78049SPatrick McHardy 		 *
1472bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO. sOP, sCR, sCG, sTW, */
1482bc78049SPatrick McHardy 			sRQ, sRQ, sRS, sIG, sIG, sIG, sIG, sRQ,
1492bc78049SPatrick McHardy 		},
1502bc78049SPatrick McHardy 		[DCCP_PKT_RESPONSE] = {
1512bc78049SPatrick McHardy 		/*
1522bc78049SPatrick McHardy 		 * sNO -> sIV		Invalid
1532bc78049SPatrick McHardy 		 * sRQ -> sIG		Ignore, might be response to ignored Request
1542bc78049SPatrick McHardy 		 * sRS -> sIG		Ignore, might be response to ignored Request
1552bc78049SPatrick McHardy 		 * sPO -> sIG		Ignore, might be response to ignored Request
1562bc78049SPatrick McHardy 		 * sOP -> sIG		Ignore, might be response to ignored Request
1572bc78049SPatrick McHardy 		 * sCR -> sIG		Ignore, might be response to ignored Request
1582bc78049SPatrick McHardy 		 * sCG -> sIG		Ignore, might be response to ignored Request
1592bc78049SPatrick McHardy 		 * sTW -> sIV		Invalid, reincarnation in reverse direction
1602bc78049SPatrick McHardy 		 *			goes through sRQ
1612bc78049SPatrick McHardy 		 *
1622bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
1632bc78049SPatrick McHardy 			sIV, sIG, sIG, sIG, sIG, sIG, sIG, sIV,
1642bc78049SPatrick McHardy 		},
1652bc78049SPatrick McHardy 		[DCCP_PKT_ACK] = {
1662bc78049SPatrick McHardy 		/*
1672bc78049SPatrick McHardy 		 * sNO -> sIV		No connection
1682bc78049SPatrick McHardy 		 * sRQ -> sIV		No connection
1692bc78049SPatrick McHardy 		 * sRS -> sPO		Ack for Response, move to PARTOPEN (8.1.5.)
1702bc78049SPatrick McHardy 		 * sPO -> sPO		Retransmitted Ack for Response, remain in PARTOPEN
1712bc78049SPatrick McHardy 		 * sOP -> sOP		Regular ACK, remain in OPEN
1722bc78049SPatrick McHardy 		 * sCR -> sCR		Ack in CLOSEREQ MAY be processed (8.3.)
1732bc78049SPatrick McHardy 		 * sCG -> sCG		Ack in CLOSING MAY be processed (8.3.)
1742bc78049SPatrick McHardy 		 * sTW -> sIV
1752bc78049SPatrick McHardy 		 *
1762bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
1772bc78049SPatrick McHardy 			sIV, sIV, sPO, sPO, sOP, sCR, sCG, sIV
1782bc78049SPatrick McHardy 		},
1792bc78049SPatrick McHardy 		[DCCP_PKT_DATA] = {
1802bc78049SPatrick McHardy 		/*
1812bc78049SPatrick McHardy 		 * sNO -> sIV		No connection
1822bc78049SPatrick McHardy 		 * sRQ -> sIV		No connection
1832bc78049SPatrick McHardy 		 * sRS -> sIV		No connection
1842bc78049SPatrick McHardy 		 * sPO -> sIV		MUST use DataAck in PARTOPEN state (8.1.5.)
1852bc78049SPatrick McHardy 		 * sOP -> sOP		Regular Data packet
1862bc78049SPatrick McHardy 		 * sCR -> sCR		Data in CLOSEREQ MAY be processed (8.3.)
1872bc78049SPatrick McHardy 		 * sCG -> sCG		Data in CLOSING MAY be processed (8.3.)
1882bc78049SPatrick McHardy 		 * sTW -> sIV
1892bc78049SPatrick McHardy 		 *
1902bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
1912bc78049SPatrick McHardy 			sIV, sIV, sIV, sIV, sOP, sCR, sCG, sIV,
1922bc78049SPatrick McHardy 		},
1932bc78049SPatrick McHardy 		[DCCP_PKT_DATAACK] = {
1942bc78049SPatrick McHardy 		/*
1952bc78049SPatrick McHardy 		 * sNO -> sIV		No connection
1962bc78049SPatrick McHardy 		 * sRQ -> sIV		No connection
1972bc78049SPatrick McHardy 		 * sRS -> sPO		Ack for Response, move to PARTOPEN (8.1.5.)
1982bc78049SPatrick McHardy 		 * sPO -> sPO		Remain in PARTOPEN state
1992bc78049SPatrick McHardy 		 * sOP -> sOP		Regular DataAck packet in OPEN state
2002bc78049SPatrick McHardy 		 * sCR -> sCR		DataAck in CLOSEREQ MAY be processed (8.3.)
2012bc78049SPatrick McHardy 		 * sCG -> sCG		DataAck in CLOSING MAY be processed (8.3.)
2022bc78049SPatrick McHardy 		 * sTW -> sIV
2032bc78049SPatrick McHardy 		 *
2042bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
2052bc78049SPatrick McHardy 			sIV, sIV, sPO, sPO, sOP, sCR, sCG, sIV
2062bc78049SPatrick McHardy 		},
2072bc78049SPatrick McHardy 		[DCCP_PKT_CLOSEREQ] = {
2082bc78049SPatrick McHardy 		/*
2092bc78049SPatrick McHardy 		 * CLOSEREQ may only be sent by the server.
2102bc78049SPatrick McHardy 		 *
2112bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
2122bc78049SPatrick McHardy 			sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV
2132bc78049SPatrick McHardy 		},
2142bc78049SPatrick McHardy 		[DCCP_PKT_CLOSE] = {
2152bc78049SPatrick McHardy 		/*
2162bc78049SPatrick McHardy 		 * sNO -> sIV		No connection
2172bc78049SPatrick McHardy 		 * sRQ -> sIV		No connection
2182bc78049SPatrick McHardy 		 * sRS -> sIV		No connection
2192bc78049SPatrick McHardy 		 * sPO -> sCG		Client-initiated close
2202bc78049SPatrick McHardy 		 * sOP -> sCG		Client-initiated close
2212bc78049SPatrick McHardy 		 * sCR -> sCG		Close in response to CloseReq (8.3.)
2222bc78049SPatrick McHardy 		 * sCG -> sCG		Retransmit
2232bc78049SPatrick McHardy 		 * sTW -> sIV		Late retransmit, already in TIME_WAIT
2242bc78049SPatrick McHardy 		 *
2252bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
2262bc78049SPatrick McHardy 			sIV, sIV, sIV, sCG, sCG, sCG, sIV, sIV
2272bc78049SPatrick McHardy 		},
2282bc78049SPatrick McHardy 		[DCCP_PKT_RESET] = {
2292bc78049SPatrick McHardy 		/*
2302bc78049SPatrick McHardy 		 * sNO -> sIV		No connection
2312bc78049SPatrick McHardy 		 * sRQ -> sTW		Sync received or timeout, SHOULD send Reset (8.1.1.)
2322bc78049SPatrick McHardy 		 * sRS -> sTW		Response received without Request
2332bc78049SPatrick McHardy 		 * sPO -> sTW		Timeout, SHOULD send Reset (8.1.5.)
2342bc78049SPatrick McHardy 		 * sOP -> sTW		Connection reset
2352bc78049SPatrick McHardy 		 * sCR -> sTW		Connection reset
2362bc78049SPatrick McHardy 		 * sCG -> sTW		Connection reset
2372bc78049SPatrick McHardy 		 * sTW -> sIG		Ignore (don't refresh timer)
2382bc78049SPatrick McHardy 		 *
2392bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
2402bc78049SPatrick McHardy 			sIV, sTW, sTW, sTW, sTW, sTW, sTW, sIG
2412bc78049SPatrick McHardy 		},
2422bc78049SPatrick McHardy 		[DCCP_PKT_SYNC] = {
2432bc78049SPatrick McHardy 		/*
2442bc78049SPatrick McHardy 		 * We currently ignore Sync packets
2452bc78049SPatrick McHardy 		 *
2462bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
2472bc78049SPatrick McHardy 			sIG, sIG, sIG, sIG, sIG, sIG, sIG, sIG,
2482bc78049SPatrick McHardy 		},
2492bc78049SPatrick McHardy 		[DCCP_PKT_SYNCACK] = {
2502bc78049SPatrick McHardy 		/*
2512bc78049SPatrick McHardy 		 * We currently ignore SyncAck packets
2522bc78049SPatrick McHardy 		 *
2532bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
2542bc78049SPatrick McHardy 			sIG, sIG, sIG, sIG, sIG, sIG, sIG, sIG,
2552bc78049SPatrick McHardy 		},
2562bc78049SPatrick McHardy 	},
2572bc78049SPatrick McHardy 	[CT_DCCP_ROLE_SERVER] = {
2582bc78049SPatrick McHardy 		[DCCP_PKT_REQUEST] = {
2592bc78049SPatrick McHardy 		/*
2602bc78049SPatrick McHardy 		 * sNO -> sIV		Invalid
2612bc78049SPatrick McHardy 		 * sRQ -> sIG		Ignore, conntrack might be out of sync
2622bc78049SPatrick McHardy 		 * sRS -> sIG		Ignore, conntrack might be out of sync
2632bc78049SPatrick McHardy 		 * sPO -> sIG		Ignore, conntrack might be out of sync
2642bc78049SPatrick McHardy 		 * sOP -> sIG		Ignore, conntrack might be out of sync
2652bc78049SPatrick McHardy 		 * sCR -> sIG		Ignore, conntrack might be out of sync
2662bc78049SPatrick McHardy 		 * sCG -> sIG		Ignore, conntrack might be out of sync
2672bc78049SPatrick McHardy 		 * sTW -> sRQ		Reincarnation, must reverse roles
2682bc78049SPatrick McHardy 		 *
2692bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
2702bc78049SPatrick McHardy 			sIV, sIG, sIG, sIG, sIG, sIG, sIG, sRQ
2712bc78049SPatrick McHardy 		},
2722bc78049SPatrick McHardy 		[DCCP_PKT_RESPONSE] = {
2732bc78049SPatrick McHardy 		/*
2742bc78049SPatrick McHardy 		 * sNO -> sIV		Response without Request
2752bc78049SPatrick McHardy 		 * sRQ -> sRS		Response to clients Request
2762bc78049SPatrick McHardy 		 * sRS -> sRS		Retransmitted Response (8.1.3. SHOULD NOT)
2772bc78049SPatrick McHardy 		 * sPO -> sIG		Response to an ignored Request or late retransmit
2782bc78049SPatrick McHardy 		 * sOP -> sIG		Ignore, might be response to ignored Request
2792bc78049SPatrick McHardy 		 * sCR -> sIG		Ignore, might be response to ignored Request
2802bc78049SPatrick McHardy 		 * sCG -> sIG		Ignore, might be response to ignored Request
2812bc78049SPatrick McHardy 		 * sTW -> sIV		Invalid, Request from client in sTW moves to sRQ
2822bc78049SPatrick McHardy 		 *
2832bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
2842bc78049SPatrick McHardy 			sIV, sRS, sRS, sIG, sIG, sIG, sIG, sIV
2852bc78049SPatrick McHardy 		},
2862bc78049SPatrick McHardy 		[DCCP_PKT_ACK] = {
2872bc78049SPatrick McHardy 		/*
2882bc78049SPatrick McHardy 		 * sNO -> sIV		No connection
2892bc78049SPatrick McHardy 		 * sRQ -> sIV		No connection
2902bc78049SPatrick McHardy 		 * sRS -> sIV		No connection
2912bc78049SPatrick McHardy 		 * sPO -> sOP		Enter OPEN state (8.1.5.)
2922bc78049SPatrick McHardy 		 * sOP -> sOP		Regular Ack in OPEN state
2932bc78049SPatrick McHardy 		 * sCR -> sIV		Waiting for Close from client
2942bc78049SPatrick McHardy 		 * sCG -> sCG		Ack in CLOSING MAY be processed (8.3.)
2952bc78049SPatrick McHardy 		 * sTW -> sIV
2962bc78049SPatrick McHardy 		 *
2972bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
2982bc78049SPatrick McHardy 			sIV, sIV, sIV, sOP, sOP, sIV, sCG, sIV
2992bc78049SPatrick McHardy 		},
3002bc78049SPatrick McHardy 		[DCCP_PKT_DATA] = {
3012bc78049SPatrick McHardy 		/*
3022bc78049SPatrick McHardy 		 * sNO -> sIV		No connection
3032bc78049SPatrick McHardy 		 * sRQ -> sIV		No connection
3042bc78049SPatrick McHardy 		 * sRS -> sIV		No connection
3052bc78049SPatrick McHardy 		 * sPO -> sOP		Enter OPEN state (8.1.5.)
3062bc78049SPatrick McHardy 		 * sOP -> sOP		Regular Data packet in OPEN state
3072bc78049SPatrick McHardy 		 * sCR -> sIV		Waiting for Close from client
3082bc78049SPatrick McHardy 		 * sCG -> sCG		Data in CLOSING MAY be processed (8.3.)
3092bc78049SPatrick McHardy 		 * sTW -> sIV
3102bc78049SPatrick McHardy 		 *
3112bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
3122bc78049SPatrick McHardy 			sIV, sIV, sIV, sOP, sOP, sIV, sCG, sIV
3132bc78049SPatrick McHardy 		},
3142bc78049SPatrick McHardy 		[DCCP_PKT_DATAACK] = {
3152bc78049SPatrick McHardy 		/*
3162bc78049SPatrick McHardy 		 * sNO -> sIV		No connection
3172bc78049SPatrick McHardy 		 * sRQ -> sIV		No connection
3182bc78049SPatrick McHardy 		 * sRS -> sIV		No connection
3192bc78049SPatrick McHardy 		 * sPO -> sOP		Enter OPEN state (8.1.5.)
3202bc78049SPatrick McHardy 		 * sOP -> sOP		Regular DataAck in OPEN state
3212bc78049SPatrick McHardy 		 * sCR -> sIV		Waiting for Close from client
3222bc78049SPatrick McHardy 		 * sCG -> sCG		Data in CLOSING MAY be processed (8.3.)
3232bc78049SPatrick McHardy 		 * sTW -> sIV
3242bc78049SPatrick McHardy 		 *
3252bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
3262bc78049SPatrick McHardy 			sIV, sIV, sIV, sOP, sOP, sIV, sCG, sIV
3272bc78049SPatrick McHardy 		},
3282bc78049SPatrick McHardy 		[DCCP_PKT_CLOSEREQ] = {
3292bc78049SPatrick McHardy 		/*
3302bc78049SPatrick McHardy 		 * sNO -> sIV		No connection
3312bc78049SPatrick McHardy 		 * sRQ -> sIV		No connection
3322bc78049SPatrick McHardy 		 * sRS -> sIV		No connection
3332bc78049SPatrick McHardy 		 * sPO -> sOP -> sCR	Move directly to CLOSEREQ (8.1.5.)
3342bc78049SPatrick McHardy 		 * sOP -> sCR		CloseReq in OPEN state
3352bc78049SPatrick McHardy 		 * sCR -> sCR		Retransmit
3362bc78049SPatrick McHardy 		 * sCG -> sCR		Simultaneous close, client sends another Close
3372bc78049SPatrick McHardy 		 * sTW -> sIV		Already closed
3382bc78049SPatrick McHardy 		 *
3392bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
3402bc78049SPatrick McHardy 			sIV, sIV, sIV, sCR, sCR, sCR, sCR, sIV
3412bc78049SPatrick McHardy 		},
3422bc78049SPatrick McHardy 		[DCCP_PKT_CLOSE] = {
3432bc78049SPatrick McHardy 		/*
3442bc78049SPatrick McHardy 		 * sNO -> sIV		No connection
3452bc78049SPatrick McHardy 		 * sRQ -> sIV		No connection
3462bc78049SPatrick McHardy 		 * sRS -> sIV		No connection
3472bc78049SPatrick McHardy 		 * sPO -> sOP -> sCG	Move direcly to CLOSING
3482bc78049SPatrick McHardy 		 * sOP -> sCG		Move to CLOSING
3492bc78049SPatrick McHardy 		 * sCR -> sIV		Close after CloseReq is invalid
3502bc78049SPatrick McHardy 		 * sCG -> sCG		Retransmit
3512bc78049SPatrick McHardy 		 * sTW -> sIV		Already closed
3522bc78049SPatrick McHardy 		 *
3532bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
3542bc78049SPatrick McHardy 			sIV, sIV, sIV, sCG, sCG, sIV, sCG, sIV
3552bc78049SPatrick McHardy 		},
3562bc78049SPatrick McHardy 		[DCCP_PKT_RESET] = {
3572bc78049SPatrick McHardy 		/*
3582bc78049SPatrick McHardy 		 * sNO -> sIV		No connection
3592bc78049SPatrick McHardy 		 * sRQ -> sTW		Reset in response to Request
3602bc78049SPatrick McHardy 		 * sRS -> sTW		Timeout, SHOULD send Reset (8.1.3.)
3612bc78049SPatrick McHardy 		 * sPO -> sTW		Timeout, SHOULD send Reset (8.1.3.)
3622bc78049SPatrick McHardy 		 * sOP -> sTW
3632bc78049SPatrick McHardy 		 * sCR -> sTW
3642bc78049SPatrick McHardy 		 * sCG -> sTW
3652bc78049SPatrick McHardy 		 * sTW -> sIG		Ignore (don't refresh timer)
3662bc78049SPatrick McHardy 		 *
3672bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW, sTW */
3682bc78049SPatrick McHardy 			sIV, sTW, sTW, sTW, sTW, sTW, sTW, sTW, sIG
3692bc78049SPatrick McHardy 		},
3702bc78049SPatrick McHardy 		[DCCP_PKT_SYNC] = {
3712bc78049SPatrick McHardy 		/*
3722bc78049SPatrick McHardy 		 * We currently ignore Sync packets
3732bc78049SPatrick McHardy 		 *
3742bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
3752bc78049SPatrick McHardy 			sIG, sIG, sIG, sIG, sIG, sIG, sIG, sIG,
3762bc78049SPatrick McHardy 		},
3772bc78049SPatrick McHardy 		[DCCP_PKT_SYNCACK] = {
3782bc78049SPatrick McHardy 		/*
3792bc78049SPatrick McHardy 		 * We currently ignore SyncAck packets
3802bc78049SPatrick McHardy 		 *
3812bc78049SPatrick McHardy 		 *	sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
3822bc78049SPatrick McHardy 			sIG, sIG, sIG, sIG, sIG, sIG, sIG, sIG,
3832bc78049SPatrick McHardy 		},
3842bc78049SPatrick McHardy 	},
3852bc78049SPatrick McHardy };
3862bc78049SPatrick McHardy 
3871546000fSCyrill Gorcunov /* this module per-net specifics */
3881546000fSCyrill Gorcunov static int dccp_net_id;
3891546000fSCyrill Gorcunov struct dccp_net {
3901546000fSCyrill Gorcunov 	int dccp_loose;
3911546000fSCyrill Gorcunov 	unsigned int dccp_timeout[CT_DCCP_MAX + 1];
3921546000fSCyrill Gorcunov #ifdef CONFIG_SYSCTL
3931546000fSCyrill Gorcunov 	struct ctl_table_header *sysctl_header;
3941546000fSCyrill Gorcunov 	struct ctl_table *sysctl_table;
3951546000fSCyrill Gorcunov #endif
3961546000fSCyrill Gorcunov };
3971546000fSCyrill Gorcunov 
3981546000fSCyrill Gorcunov static inline struct dccp_net *dccp_pernet(struct net *net)
3991546000fSCyrill Gorcunov {
4001546000fSCyrill Gorcunov 	return net_generic(net, dccp_net_id);
4011546000fSCyrill Gorcunov }
4021546000fSCyrill Gorcunov 
40309f263cdSJan Engelhardt static bool dccp_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff,
4042bc78049SPatrick McHardy 			      struct nf_conntrack_tuple *tuple)
4052bc78049SPatrick McHardy {
4062bc78049SPatrick McHardy 	struct dccp_hdr _hdr, *dh;
4072bc78049SPatrick McHardy 
4082bc78049SPatrick McHardy 	dh = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
4092bc78049SPatrick McHardy 	if (dh == NULL)
41009f263cdSJan Engelhardt 		return false;
4112bc78049SPatrick McHardy 
4122bc78049SPatrick McHardy 	tuple->src.u.dccp.port = dh->dccph_sport;
4132bc78049SPatrick McHardy 	tuple->dst.u.dccp.port = dh->dccph_dport;
41409f263cdSJan Engelhardt 	return true;
4152bc78049SPatrick McHardy }
4162bc78049SPatrick McHardy 
41709f263cdSJan Engelhardt static bool dccp_invert_tuple(struct nf_conntrack_tuple *inv,
4182bc78049SPatrick McHardy 			      const struct nf_conntrack_tuple *tuple)
4192bc78049SPatrick McHardy {
4202bc78049SPatrick McHardy 	inv->src.u.dccp.port = tuple->dst.u.dccp.port;
4212bc78049SPatrick McHardy 	inv->dst.u.dccp.port = tuple->src.u.dccp.port;
42209f263cdSJan Engelhardt 	return true;
4232bc78049SPatrick McHardy }
4242bc78049SPatrick McHardy 
42509f263cdSJan Engelhardt static bool dccp_new(struct nf_conn *ct, const struct sk_buff *skb,
4262bc78049SPatrick McHardy 		     unsigned int dataoff)
4272bc78049SPatrick McHardy {
428c2a2c7e0SAlexey Dobriyan 	struct net *net = nf_ct_net(ct);
4291546000fSCyrill Gorcunov 	struct dccp_net *dn;
4302bc78049SPatrick McHardy 	struct dccp_hdr _dh, *dh;
4312bc78049SPatrick McHardy 	const char *msg;
4322bc78049SPatrick McHardy 	u_int8_t state;
4332bc78049SPatrick McHardy 
4342bc78049SPatrick McHardy 	dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &dh);
4352bc78049SPatrick McHardy 	BUG_ON(dh == NULL);
4362bc78049SPatrick McHardy 
4372bc78049SPatrick McHardy 	state = dccp_state_table[CT_DCCP_ROLE_CLIENT][dh->dccph_type][CT_DCCP_NONE];
4382bc78049SPatrick McHardy 	switch (state) {
4392bc78049SPatrick McHardy 	default:
4401546000fSCyrill Gorcunov 		dn = dccp_pernet(net);
4411546000fSCyrill Gorcunov 		if (dn->dccp_loose == 0) {
4422bc78049SPatrick McHardy 			msg = "nf_ct_dccp: not picking up existing connection ";
4432bc78049SPatrick McHardy 			goto out_invalid;
4442bc78049SPatrick McHardy 		}
4452bc78049SPatrick McHardy 	case CT_DCCP_REQUEST:
4462bc78049SPatrick McHardy 		break;
4472bc78049SPatrick McHardy 	case CT_DCCP_INVALID:
4482bc78049SPatrick McHardy 		msg = "nf_ct_dccp: invalid state transition ";
4492bc78049SPatrick McHardy 		goto out_invalid;
4502bc78049SPatrick McHardy 	}
4512bc78049SPatrick McHardy 
4522bc78049SPatrick McHardy 	ct->proto.dccp.role[IP_CT_DIR_ORIGINAL] = CT_DCCP_ROLE_CLIENT;
4532bc78049SPatrick McHardy 	ct->proto.dccp.role[IP_CT_DIR_REPLY] = CT_DCCP_ROLE_SERVER;
4542bc78049SPatrick McHardy 	ct->proto.dccp.state = CT_DCCP_NONE;
45509f263cdSJan Engelhardt 	return true;
4562bc78049SPatrick McHardy 
4572bc78049SPatrick McHardy out_invalid:
458c2a2c7e0SAlexey Dobriyan 	if (LOG_INVALID(net, IPPROTO_DCCP))
4595e8fbe2aSPatrick McHardy 		nf_log_packet(nf_ct_l3num(ct), 0, skb, NULL, NULL, NULL, msg);
46009f263cdSJan Engelhardt 	return false;
4612bc78049SPatrick McHardy }
4622bc78049SPatrick McHardy 
4632bc78049SPatrick McHardy static u64 dccp_ack_seq(const struct dccp_hdr *dh)
4642bc78049SPatrick McHardy {
4652bc78049SPatrick McHardy 	const struct dccp_hdr_ack_bits *dhack;
4662bc78049SPatrick McHardy 
4672bc78049SPatrick McHardy 	dhack = (void *)dh + __dccp_basic_hdr_len(dh);
4682bc78049SPatrick McHardy 	return ((u64)ntohs(dhack->dccph_ack_nr_high) << 32) +
4692bc78049SPatrick McHardy 		     ntohl(dhack->dccph_ack_nr_low);
4702bc78049SPatrick McHardy }
4712bc78049SPatrick McHardy 
4722bc78049SPatrick McHardy static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb,
4732bc78049SPatrick McHardy 		       unsigned int dataoff, enum ip_conntrack_info ctinfo,
47476108ceaSJan Engelhardt 		       u_int8_t pf, unsigned int hooknum)
4752bc78049SPatrick McHardy {
476c2a2c7e0SAlexey Dobriyan 	struct net *net = nf_ct_net(ct);
4771546000fSCyrill Gorcunov 	struct dccp_net *dn;
4782bc78049SPatrick McHardy 	enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
4792bc78049SPatrick McHardy 	struct dccp_hdr _dh, *dh;
4802bc78049SPatrick McHardy 	u_int8_t type, old_state, new_state;
4812bc78049SPatrick McHardy 	enum ct_dccp_roles role;
4822bc78049SPatrick McHardy 
4832bc78049SPatrick McHardy 	dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &dh);
4842bc78049SPatrick McHardy 	BUG_ON(dh == NULL);
4852bc78049SPatrick McHardy 	type = dh->dccph_type;
4862bc78049SPatrick McHardy 
4872bc78049SPatrick McHardy 	if (type == DCCP_PKT_RESET &&
4882bc78049SPatrick McHardy 	    !test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
4892bc78049SPatrick McHardy 		/* Tear down connection immediately if only reply is a RESET */
490718d4ad9SFabian Hugelshofer 		nf_ct_kill_acct(ct, ctinfo, skb);
4912bc78049SPatrick McHardy 		return NF_ACCEPT;
4922bc78049SPatrick McHardy 	}
4932bc78049SPatrick McHardy 
4942bc78049SPatrick McHardy 	write_lock_bh(&dccp_lock);
4952bc78049SPatrick McHardy 
4962bc78049SPatrick McHardy 	role = ct->proto.dccp.role[dir];
4972bc78049SPatrick McHardy 	old_state = ct->proto.dccp.state;
4982bc78049SPatrick McHardy 	new_state = dccp_state_table[role][type][old_state];
4992bc78049SPatrick McHardy 
5002bc78049SPatrick McHardy 	switch (new_state) {
5012bc78049SPatrick McHardy 	case CT_DCCP_REQUEST:
5022bc78049SPatrick McHardy 		if (old_state == CT_DCCP_TIMEWAIT &&
5032bc78049SPatrick McHardy 		    role == CT_DCCP_ROLE_SERVER) {
5042bc78049SPatrick McHardy 			/* Reincarnation in the reverse direction: reopen and
5052bc78049SPatrick McHardy 			 * reverse client/server roles. */
5062bc78049SPatrick McHardy 			ct->proto.dccp.role[dir] = CT_DCCP_ROLE_CLIENT;
5072bc78049SPatrick McHardy 			ct->proto.dccp.role[!dir] = CT_DCCP_ROLE_SERVER;
5082bc78049SPatrick McHardy 		}
5092bc78049SPatrick McHardy 		break;
5102bc78049SPatrick McHardy 	case CT_DCCP_RESPOND:
5112bc78049SPatrick McHardy 		if (old_state == CT_DCCP_REQUEST)
5122bc78049SPatrick McHardy 			ct->proto.dccp.handshake_seq = dccp_hdr_seq(dh);
5132bc78049SPatrick McHardy 		break;
5142bc78049SPatrick McHardy 	case CT_DCCP_PARTOPEN:
5152bc78049SPatrick McHardy 		if (old_state == CT_DCCP_RESPOND &&
5162bc78049SPatrick McHardy 		    type == DCCP_PKT_ACK &&
5172bc78049SPatrick McHardy 		    dccp_ack_seq(dh) == ct->proto.dccp.handshake_seq)
5182bc78049SPatrick McHardy 			set_bit(IPS_ASSURED_BIT, &ct->status);
5192bc78049SPatrick McHardy 		break;
5202bc78049SPatrick McHardy 	case CT_DCCP_IGNORE:
5212bc78049SPatrick McHardy 		/*
5222bc78049SPatrick McHardy 		 * Connection tracking might be out of sync, so we ignore
5232bc78049SPatrick McHardy 		 * packets that might establish a new connection and resync
5242bc78049SPatrick McHardy 		 * if the server responds with a valid Response.
5252bc78049SPatrick McHardy 		 */
5262bc78049SPatrick McHardy 		if (ct->proto.dccp.last_dir == !dir &&
5272bc78049SPatrick McHardy 		    ct->proto.dccp.last_pkt == DCCP_PKT_REQUEST &&
5282bc78049SPatrick McHardy 		    type == DCCP_PKT_RESPONSE) {
5292bc78049SPatrick McHardy 			ct->proto.dccp.role[!dir] = CT_DCCP_ROLE_CLIENT;
5302bc78049SPatrick McHardy 			ct->proto.dccp.role[dir] = CT_DCCP_ROLE_SERVER;
5312bc78049SPatrick McHardy 			ct->proto.dccp.handshake_seq = dccp_hdr_seq(dh);
5322bc78049SPatrick McHardy 			new_state = CT_DCCP_RESPOND;
5332bc78049SPatrick McHardy 			break;
5342bc78049SPatrick McHardy 		}
5352bc78049SPatrick McHardy 		ct->proto.dccp.last_dir = dir;
5362bc78049SPatrick McHardy 		ct->proto.dccp.last_pkt = type;
5372bc78049SPatrick McHardy 
5382bc78049SPatrick McHardy 		write_unlock_bh(&dccp_lock);
539c2a2c7e0SAlexey Dobriyan 		if (LOG_INVALID(net, IPPROTO_DCCP))
5402bc78049SPatrick McHardy 			nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
5412bc78049SPatrick McHardy 				      "nf_ct_dccp: invalid packet ignored ");
5422bc78049SPatrick McHardy 		return NF_ACCEPT;
5432bc78049SPatrick McHardy 	case CT_DCCP_INVALID:
5442bc78049SPatrick McHardy 		write_unlock_bh(&dccp_lock);
545c2a2c7e0SAlexey Dobriyan 		if (LOG_INVALID(net, IPPROTO_DCCP))
5462bc78049SPatrick McHardy 			nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
5472bc78049SPatrick McHardy 				      "nf_ct_dccp: invalid state transition ");
5482bc78049SPatrick McHardy 		return -NF_ACCEPT;
5492bc78049SPatrick McHardy 	}
5502bc78049SPatrick McHardy 
5512bc78049SPatrick McHardy 	ct->proto.dccp.last_dir = dir;
5522bc78049SPatrick McHardy 	ct->proto.dccp.last_pkt = type;
5532bc78049SPatrick McHardy 	ct->proto.dccp.state = new_state;
5542bc78049SPatrick McHardy 	write_unlock_bh(&dccp_lock);
5551546000fSCyrill Gorcunov 
5561546000fSCyrill Gorcunov 	dn = dccp_pernet(net);
5571546000fSCyrill Gorcunov 	nf_ct_refresh_acct(ct, ctinfo, skb, dn->dccp_timeout[new_state]);
5582bc78049SPatrick McHardy 
5592bc78049SPatrick McHardy 	return NF_ACCEPT;
5602bc78049SPatrick McHardy }
5612bc78049SPatrick McHardy 
56274c51a14SAlexey Dobriyan static int dccp_error(struct net *net, struct sk_buff *skb,
56374c51a14SAlexey Dobriyan 		      unsigned int dataoff, enum ip_conntrack_info *ctinfo,
56474c51a14SAlexey Dobriyan 		      u_int8_t pf, unsigned int hooknum)
5652bc78049SPatrick McHardy {
5662bc78049SPatrick McHardy 	struct dccp_hdr _dh, *dh;
5672bc78049SPatrick McHardy 	unsigned int dccp_len = skb->len - dataoff;
5682bc78049SPatrick McHardy 	unsigned int cscov;
5692bc78049SPatrick McHardy 	const char *msg;
5702bc78049SPatrick McHardy 
5712bc78049SPatrick McHardy 	dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &dh);
5722bc78049SPatrick McHardy 	if (dh == NULL) {
5732bc78049SPatrick McHardy 		msg = "nf_ct_dccp: short packet ";
5742bc78049SPatrick McHardy 		goto out_invalid;
5752bc78049SPatrick McHardy 	}
5762bc78049SPatrick McHardy 
5772bc78049SPatrick McHardy 	if (dh->dccph_doff * 4 < sizeof(struct dccp_hdr) ||
5782bc78049SPatrick McHardy 	    dh->dccph_doff * 4 > dccp_len) {
5792bc78049SPatrick McHardy 		msg = "nf_ct_dccp: truncated/malformed packet ";
5802bc78049SPatrick McHardy 		goto out_invalid;
5812bc78049SPatrick McHardy 	}
5822bc78049SPatrick McHardy 
5832bc78049SPatrick McHardy 	cscov = dccp_len;
5842bc78049SPatrick McHardy 	if (dh->dccph_cscov) {
5852bc78049SPatrick McHardy 		cscov = (dh->dccph_cscov - 1) * 4;
5862bc78049SPatrick McHardy 		if (cscov > dccp_len) {
5872bc78049SPatrick McHardy 			msg = "nf_ct_dccp: bad checksum coverage ";
5882bc78049SPatrick McHardy 			goto out_invalid;
5892bc78049SPatrick McHardy 		}
5902bc78049SPatrick McHardy 	}
5912bc78049SPatrick McHardy 
592c04d0552SAlexey Dobriyan 	if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING &&
5932bc78049SPatrick McHardy 	    nf_checksum_partial(skb, hooknum, dataoff, cscov, IPPROTO_DCCP,
5942bc78049SPatrick McHardy 				pf)) {
5952bc78049SPatrick McHardy 		msg = "nf_ct_dccp: bad checksum ";
5962bc78049SPatrick McHardy 		goto out_invalid;
5972bc78049SPatrick McHardy 	}
5982bc78049SPatrick McHardy 
5992bc78049SPatrick McHardy 	if (dh->dccph_type >= DCCP_PKT_INVALID) {
6002bc78049SPatrick McHardy 		msg = "nf_ct_dccp: reserved packet type ";
6012bc78049SPatrick McHardy 		goto out_invalid;
6022bc78049SPatrick McHardy 	}
6032bc78049SPatrick McHardy 
6042bc78049SPatrick McHardy 	return NF_ACCEPT;
6052bc78049SPatrick McHardy 
6062bc78049SPatrick McHardy out_invalid:
607c2a2c7e0SAlexey Dobriyan 	if (LOG_INVALID(net, IPPROTO_DCCP))
6082bc78049SPatrick McHardy 		nf_log_packet(pf, 0, skb, NULL, NULL, NULL, msg);
6092bc78049SPatrick McHardy 	return -NF_ACCEPT;
6102bc78049SPatrick McHardy }
6112bc78049SPatrick McHardy 
6122bc78049SPatrick McHardy static int dccp_print_tuple(struct seq_file *s,
6132bc78049SPatrick McHardy 			    const struct nf_conntrack_tuple *tuple)
6142bc78049SPatrick McHardy {
6152bc78049SPatrick McHardy 	return seq_printf(s, "sport=%hu dport=%hu ",
6162bc78049SPatrick McHardy 			  ntohs(tuple->src.u.dccp.port),
6172bc78049SPatrick McHardy 			  ntohs(tuple->dst.u.dccp.port));
6182bc78049SPatrick McHardy }
6192bc78049SPatrick McHardy 
6202bc78049SPatrick McHardy static int dccp_print_conntrack(struct seq_file *s, const struct nf_conn *ct)
6212bc78049SPatrick McHardy {
6222bc78049SPatrick McHardy 	return seq_printf(s, "%s ", dccp_state_names[ct->proto.dccp.state]);
6232bc78049SPatrick McHardy }
6242bc78049SPatrick McHardy 
6252bc78049SPatrick McHardy #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
6262bc78049SPatrick McHardy static int dccp_to_nlattr(struct sk_buff *skb, struct nlattr *nla,
6272bc78049SPatrick McHardy 			  const struct nf_conn *ct)
6282bc78049SPatrick McHardy {
6292bc78049SPatrick McHardy 	struct nlattr *nest_parms;
6302bc78049SPatrick McHardy 
6312bc78049SPatrick McHardy 	read_lock_bh(&dccp_lock);
6322bc78049SPatrick McHardy 	nest_parms = nla_nest_start(skb, CTA_PROTOINFO_DCCP | NLA_F_NESTED);
6332bc78049SPatrick McHardy 	if (!nest_parms)
6342bc78049SPatrick McHardy 		goto nla_put_failure;
6352bc78049SPatrick McHardy 	NLA_PUT_U8(skb, CTA_PROTOINFO_DCCP_STATE, ct->proto.dccp.state);
6362bc78049SPatrick McHardy 	nla_nest_end(skb, nest_parms);
6372bc78049SPatrick McHardy 	read_unlock_bh(&dccp_lock);
6382bc78049SPatrick McHardy 	return 0;
6392bc78049SPatrick McHardy 
6402bc78049SPatrick McHardy nla_put_failure:
6412bc78049SPatrick McHardy 	read_unlock_bh(&dccp_lock);
6422bc78049SPatrick McHardy 	return -1;
6432bc78049SPatrick McHardy }
6442bc78049SPatrick McHardy 
6452bc78049SPatrick McHardy static const struct nla_policy dccp_nla_policy[CTA_PROTOINFO_DCCP_MAX + 1] = {
6462bc78049SPatrick McHardy 	[CTA_PROTOINFO_DCCP_STATE]	= { .type = NLA_U8 },
6472bc78049SPatrick McHardy };
6482bc78049SPatrick McHardy 
6492bc78049SPatrick McHardy static int nlattr_to_dccp(struct nlattr *cda[], struct nf_conn *ct)
6502bc78049SPatrick McHardy {
6512bc78049SPatrick McHardy 	struct nlattr *attr = cda[CTA_PROTOINFO_DCCP];
6522bc78049SPatrick McHardy 	struct nlattr *tb[CTA_PROTOINFO_DCCP_MAX + 1];
6532bc78049SPatrick McHardy 	int err;
6542bc78049SPatrick McHardy 
6552bc78049SPatrick McHardy 	if (!attr)
6562bc78049SPatrick McHardy 		return 0;
6572bc78049SPatrick McHardy 
6582bc78049SPatrick McHardy 	err = nla_parse_nested(tb, CTA_PROTOINFO_DCCP_MAX, attr,
6592bc78049SPatrick McHardy 			       dccp_nla_policy);
6602bc78049SPatrick McHardy 	if (err < 0)
6612bc78049SPatrick McHardy 		return err;
6622bc78049SPatrick McHardy 
6632bc78049SPatrick McHardy 	if (!tb[CTA_PROTOINFO_DCCP_STATE] ||
6642bc78049SPatrick McHardy 	    nla_get_u8(tb[CTA_PROTOINFO_DCCP_STATE]) >= CT_DCCP_IGNORE)
6652bc78049SPatrick McHardy 		return -EINVAL;
6662bc78049SPatrick McHardy 
6672bc78049SPatrick McHardy 	write_lock_bh(&dccp_lock);
6682bc78049SPatrick McHardy 	ct->proto.dccp.state = nla_get_u8(tb[CTA_PROTOINFO_DCCP_STATE]);
6692bc78049SPatrick McHardy 	write_unlock_bh(&dccp_lock);
6702bc78049SPatrick McHardy 	return 0;
6712bc78049SPatrick McHardy }
672a400c30eSHolger Eitzenberger 
673a400c30eSHolger Eitzenberger static int dccp_nlattr_size(void)
674a400c30eSHolger Eitzenberger {
675a400c30eSHolger Eitzenberger 	return nla_total_size(0)	/* CTA_PROTOINFO_DCCP */
676a400c30eSHolger Eitzenberger 		+ nla_policy_len(dccp_nla_policy, CTA_PROTOINFO_DCCP_MAX + 1);
677a400c30eSHolger Eitzenberger }
6782bc78049SPatrick McHardy #endif
6792bc78049SPatrick McHardy 
6802bc78049SPatrick McHardy #ifdef CONFIG_SYSCTL
6811546000fSCyrill Gorcunov /* template, data assigned later */
6821546000fSCyrill Gorcunov static struct ctl_table dccp_sysctl_table[] = {
6832bc78049SPatrick McHardy 	{
6842bc78049SPatrick McHardy 		.ctl_name	= CTL_UNNUMBERED,
6852bc78049SPatrick McHardy 		.procname	= "nf_conntrack_dccp_timeout_request",
6862bc78049SPatrick McHardy 		.maxlen		= sizeof(unsigned int),
6872bc78049SPatrick McHardy 		.mode		= 0644,
6882bc78049SPatrick McHardy 		.proc_handler	= proc_dointvec_jiffies,
6892bc78049SPatrick McHardy 	},
6902bc78049SPatrick McHardy 	{
6912bc78049SPatrick McHardy 		.ctl_name	= CTL_UNNUMBERED,
6922bc78049SPatrick McHardy 		.procname	= "nf_conntrack_dccp_timeout_respond",
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_partopen",
7002bc78049SPatrick McHardy 		.maxlen		= sizeof(unsigned int),
7012bc78049SPatrick McHardy 		.mode		= 0644,
7022bc78049SPatrick McHardy 		.proc_handler	= proc_dointvec_jiffies,
7032bc78049SPatrick McHardy 	},
7042bc78049SPatrick McHardy 	{
7052bc78049SPatrick McHardy 		.ctl_name	= CTL_UNNUMBERED,
7062bc78049SPatrick McHardy 		.procname	= "nf_conntrack_dccp_timeout_open",
7072bc78049SPatrick McHardy 		.maxlen		= sizeof(unsigned int),
7082bc78049SPatrick McHardy 		.mode		= 0644,
7092bc78049SPatrick McHardy 		.proc_handler	= proc_dointvec_jiffies,
7102bc78049SPatrick McHardy 	},
7112bc78049SPatrick McHardy 	{
7122bc78049SPatrick McHardy 		.ctl_name	= CTL_UNNUMBERED,
7132bc78049SPatrick McHardy 		.procname	= "nf_conntrack_dccp_timeout_closereq",
7142bc78049SPatrick McHardy 		.maxlen		= sizeof(unsigned int),
7152bc78049SPatrick McHardy 		.mode		= 0644,
7162bc78049SPatrick McHardy 		.proc_handler	= proc_dointvec_jiffies,
7172bc78049SPatrick McHardy 	},
7182bc78049SPatrick McHardy 	{
7192bc78049SPatrick McHardy 		.ctl_name	= CTL_UNNUMBERED,
7202bc78049SPatrick McHardy 		.procname	= "nf_conntrack_dccp_timeout_closing",
7212bc78049SPatrick McHardy 		.maxlen		= sizeof(unsigned int),
7222bc78049SPatrick McHardy 		.mode		= 0644,
7232bc78049SPatrick McHardy 		.proc_handler	= proc_dointvec_jiffies,
7242bc78049SPatrick McHardy 	},
7252bc78049SPatrick McHardy 	{
7262bc78049SPatrick McHardy 		.ctl_name	= CTL_UNNUMBERED,
7272bc78049SPatrick McHardy 		.procname	= "nf_conntrack_dccp_timeout_timewait",
7282bc78049SPatrick McHardy 		.maxlen		= sizeof(unsigned int),
7292bc78049SPatrick McHardy 		.mode		= 0644,
7302bc78049SPatrick McHardy 		.proc_handler	= proc_dointvec_jiffies,
7312bc78049SPatrick McHardy 	},
7322bc78049SPatrick McHardy 	{
7332bc78049SPatrick McHardy 		.ctl_name	= CTL_UNNUMBERED,
7342bc78049SPatrick McHardy 		.procname	= "nf_conntrack_dccp_loose",
7351546000fSCyrill Gorcunov 		.maxlen		= sizeof(int),
7362bc78049SPatrick McHardy 		.mode		= 0644,
7372bc78049SPatrick McHardy 		.proc_handler	= proc_dointvec,
7382bc78049SPatrick McHardy 	},
7392bc78049SPatrick McHardy 	{
7402bc78049SPatrick McHardy 		.ctl_name	= 0,
7412bc78049SPatrick McHardy 	}
7422bc78049SPatrick McHardy };
7432bc78049SPatrick McHardy #endif /* CONFIG_SYSCTL */
7442bc78049SPatrick McHardy 
7452bc78049SPatrick McHardy static struct nf_conntrack_l4proto dccp_proto4 __read_mostly = {
7462bc78049SPatrick McHardy 	.l3proto		= AF_INET,
7472bc78049SPatrick McHardy 	.l4proto		= IPPROTO_DCCP,
7482bc78049SPatrick McHardy 	.name			= "dccp",
7492bc78049SPatrick McHardy 	.pkt_to_tuple		= dccp_pkt_to_tuple,
7502bc78049SPatrick McHardy 	.invert_tuple		= dccp_invert_tuple,
7512bc78049SPatrick McHardy 	.new			= dccp_new,
7522bc78049SPatrick McHardy 	.packet			= dccp_packet,
7532bc78049SPatrick McHardy 	.error			= dccp_error,
7542bc78049SPatrick McHardy 	.print_tuple		= dccp_print_tuple,
7552bc78049SPatrick McHardy 	.print_conntrack	= dccp_print_conntrack,
7562bc78049SPatrick McHardy #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
7572bc78049SPatrick McHardy 	.to_nlattr		= dccp_to_nlattr,
758a400c30eSHolger Eitzenberger 	.nlattr_size		= dccp_nlattr_size,
7592bc78049SPatrick McHardy 	.from_nlattr		= nlattr_to_dccp,
7602bc78049SPatrick McHardy 	.tuple_to_nlattr	= nf_ct_port_tuple_to_nlattr,
761a400c30eSHolger Eitzenberger 	.nlattr_tuple_size	= nf_ct_port_nlattr_tuple_size,
7622bc78049SPatrick McHardy 	.nlattr_to_tuple	= nf_ct_port_nlattr_to_tuple,
7632bc78049SPatrick McHardy 	.nla_policy		= nf_ct_port_nla_policy,
7642bc78049SPatrick McHardy #endif
7652bc78049SPatrick McHardy };
7662bc78049SPatrick McHardy 
7672bc78049SPatrick McHardy static struct nf_conntrack_l4proto dccp_proto6 __read_mostly = {
7682bc78049SPatrick McHardy 	.l3proto		= AF_INET6,
7692bc78049SPatrick McHardy 	.l4proto		= IPPROTO_DCCP,
7702bc78049SPatrick McHardy 	.name			= "dccp",
7712bc78049SPatrick McHardy 	.pkt_to_tuple		= dccp_pkt_to_tuple,
7722bc78049SPatrick McHardy 	.invert_tuple		= dccp_invert_tuple,
7732bc78049SPatrick McHardy 	.new			= dccp_new,
7742bc78049SPatrick McHardy 	.packet			= dccp_packet,
7752bc78049SPatrick McHardy 	.error			= dccp_error,
7762bc78049SPatrick McHardy 	.print_tuple		= dccp_print_tuple,
7772bc78049SPatrick McHardy 	.print_conntrack	= dccp_print_conntrack,
7782bc78049SPatrick McHardy #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
7792bc78049SPatrick McHardy 	.to_nlattr		= dccp_to_nlattr,
7805ff48294SPatrick McHardy 	.nlattr_size		= dccp_nlattr_size,
7812bc78049SPatrick McHardy 	.from_nlattr		= nlattr_to_dccp,
7822bc78049SPatrick McHardy 	.tuple_to_nlattr	= nf_ct_port_tuple_to_nlattr,
783a400c30eSHolger Eitzenberger 	.nlattr_tuple_size	= nf_ct_port_nlattr_tuple_size,
7842bc78049SPatrick McHardy 	.nlattr_to_tuple	= nf_ct_port_nlattr_to_tuple,
7852bc78049SPatrick McHardy 	.nla_policy		= nf_ct_port_nla_policy,
7862bc78049SPatrick McHardy #endif
7871546000fSCyrill Gorcunov };
7881546000fSCyrill Gorcunov 
7891546000fSCyrill Gorcunov static __net_init int dccp_net_init(struct net *net)
7901546000fSCyrill Gorcunov {
7911546000fSCyrill Gorcunov 	struct dccp_net *dn;
7921546000fSCyrill Gorcunov 	int err;
7931546000fSCyrill Gorcunov 
7941546000fSCyrill Gorcunov 	dn = kmalloc(sizeof(*dn), GFP_KERNEL);
7951546000fSCyrill Gorcunov 	if (!dn)
7961546000fSCyrill Gorcunov 		return -ENOMEM;
7971546000fSCyrill Gorcunov 
7981546000fSCyrill Gorcunov 	/* default values */
7991546000fSCyrill Gorcunov 	dn->dccp_loose = 1;
8001546000fSCyrill Gorcunov 	dn->dccp_timeout[CT_DCCP_REQUEST]	= 2 * DCCP_MSL;
8011546000fSCyrill Gorcunov 	dn->dccp_timeout[CT_DCCP_RESPOND]	= 4 * DCCP_MSL;
8021546000fSCyrill Gorcunov 	dn->dccp_timeout[CT_DCCP_PARTOPEN]	= 4 * DCCP_MSL;
8031546000fSCyrill Gorcunov 	dn->dccp_timeout[CT_DCCP_OPEN]		= 12 * 3600 * HZ;
8041546000fSCyrill Gorcunov 	dn->dccp_timeout[CT_DCCP_CLOSEREQ]	= 64 * HZ;
8051546000fSCyrill Gorcunov 	dn->dccp_timeout[CT_DCCP_CLOSING]	= 64 * HZ;
8061546000fSCyrill Gorcunov 	dn->dccp_timeout[CT_DCCP_TIMEWAIT]	= 2 * DCCP_MSL;
8071546000fSCyrill Gorcunov 
8081546000fSCyrill Gorcunov 	err = net_assign_generic(net, dccp_net_id, dn);
8091546000fSCyrill Gorcunov 	if (err)
8101546000fSCyrill Gorcunov 		goto out;
8111546000fSCyrill Gorcunov 
8122bc78049SPatrick McHardy #ifdef CONFIG_SYSCTL
8131546000fSCyrill Gorcunov 	err = -ENOMEM;
8141546000fSCyrill Gorcunov 	dn->sysctl_table = kmemdup(dccp_sysctl_table,
8151546000fSCyrill Gorcunov 			sizeof(dccp_sysctl_table), GFP_KERNEL);
8161546000fSCyrill Gorcunov 	if (!dn->sysctl_table)
8171546000fSCyrill Gorcunov 		goto out;
8181546000fSCyrill Gorcunov 
8191546000fSCyrill Gorcunov 	dn->sysctl_table[0].data = &dn->dccp_timeout[CT_DCCP_REQUEST];
8201546000fSCyrill Gorcunov 	dn->sysctl_table[1].data = &dn->dccp_timeout[CT_DCCP_RESPOND];
8211546000fSCyrill Gorcunov 	dn->sysctl_table[2].data = &dn->dccp_timeout[CT_DCCP_PARTOPEN];
8221546000fSCyrill Gorcunov 	dn->sysctl_table[3].data = &dn->dccp_timeout[CT_DCCP_OPEN];
8231546000fSCyrill Gorcunov 	dn->sysctl_table[4].data = &dn->dccp_timeout[CT_DCCP_CLOSEREQ];
8241546000fSCyrill Gorcunov 	dn->sysctl_table[5].data = &dn->dccp_timeout[CT_DCCP_CLOSING];
8251546000fSCyrill Gorcunov 	dn->sysctl_table[6].data = &dn->dccp_timeout[CT_DCCP_TIMEWAIT];
8261546000fSCyrill Gorcunov 	dn->sysctl_table[7].data = &dn->dccp_loose;
8271546000fSCyrill Gorcunov 
8281546000fSCyrill Gorcunov 	dn->sysctl_header = register_net_sysctl_table(net,
8291546000fSCyrill Gorcunov 			nf_net_netfilter_sysctl_path, dn->sysctl_table);
8301546000fSCyrill Gorcunov 	if (!dn->sysctl_header) {
8311546000fSCyrill Gorcunov 		kfree(dn->sysctl_table);
8321546000fSCyrill Gorcunov 		goto out;
8331546000fSCyrill Gorcunov 	}
8342bc78049SPatrick McHardy #endif
8351546000fSCyrill Gorcunov 
8361546000fSCyrill Gorcunov 	return 0;
8371546000fSCyrill Gorcunov 
8381546000fSCyrill Gorcunov out:
8391546000fSCyrill Gorcunov 	kfree(dn);
8401546000fSCyrill Gorcunov 	return err;
8411546000fSCyrill Gorcunov }
8421546000fSCyrill Gorcunov 
8431546000fSCyrill Gorcunov static __net_exit void dccp_net_exit(struct net *net)
8441546000fSCyrill Gorcunov {
8451546000fSCyrill Gorcunov 	struct dccp_net *dn = dccp_pernet(net);
8461546000fSCyrill Gorcunov #ifdef CONFIG_SYSCTL
8471546000fSCyrill Gorcunov 	unregister_net_sysctl_table(dn->sysctl_header);
8481546000fSCyrill Gorcunov 	kfree(dn->sysctl_table);
8491546000fSCyrill Gorcunov #endif
8501546000fSCyrill Gorcunov 	kfree(dn);
8511546000fSCyrill Gorcunov 
8521546000fSCyrill Gorcunov 	net_assign_generic(net, dccp_net_id, NULL);
8531546000fSCyrill Gorcunov }
8541546000fSCyrill Gorcunov 
8551546000fSCyrill Gorcunov static struct pernet_operations dccp_net_ops = {
8561546000fSCyrill Gorcunov 	.init = dccp_net_init,
8571546000fSCyrill Gorcunov 	.exit = dccp_net_exit,
8582bc78049SPatrick McHardy };
8592bc78049SPatrick McHardy 
8602bc78049SPatrick McHardy static int __init nf_conntrack_proto_dccp_init(void)
8612bc78049SPatrick McHardy {
8622bc78049SPatrick McHardy 	int err;
8632bc78049SPatrick McHardy 
8641546000fSCyrill Gorcunov 	err = register_pernet_gen_subsys(&dccp_net_id, &dccp_net_ops);
8652bc78049SPatrick McHardy 	if (err < 0)
8662bc78049SPatrick McHardy 		goto err1;
8672bc78049SPatrick McHardy 
8681546000fSCyrill Gorcunov 	err = nf_conntrack_l4proto_register(&dccp_proto4);
8692bc78049SPatrick McHardy 	if (err < 0)
8702bc78049SPatrick McHardy 		goto err2;
8711546000fSCyrill Gorcunov 
8721546000fSCyrill Gorcunov 	err = nf_conntrack_l4proto_register(&dccp_proto6);
8731546000fSCyrill Gorcunov 	if (err < 0)
8741546000fSCyrill Gorcunov 		goto err3;
8752bc78049SPatrick McHardy 	return 0;
8762bc78049SPatrick McHardy 
8771546000fSCyrill Gorcunov err3:
8782bc78049SPatrick McHardy 	nf_conntrack_l4proto_unregister(&dccp_proto4);
8791546000fSCyrill Gorcunov err2:
8801546000fSCyrill Gorcunov 	unregister_pernet_gen_subsys(dccp_net_id, &dccp_net_ops);
8812bc78049SPatrick McHardy err1:
8822bc78049SPatrick McHardy 	return err;
8832bc78049SPatrick McHardy }
8842bc78049SPatrick McHardy 
8852bc78049SPatrick McHardy static void __exit nf_conntrack_proto_dccp_fini(void)
8862bc78049SPatrick McHardy {
8871546000fSCyrill Gorcunov 	unregister_pernet_gen_subsys(dccp_net_id, &dccp_net_ops);
8882bc78049SPatrick McHardy 	nf_conntrack_l4proto_unregister(&dccp_proto6);
8892bc78049SPatrick McHardy 	nf_conntrack_l4proto_unregister(&dccp_proto4);
8902bc78049SPatrick McHardy }
8912bc78049SPatrick McHardy 
8922bc78049SPatrick McHardy module_init(nf_conntrack_proto_dccp_init);
8932bc78049SPatrick McHardy module_exit(nf_conntrack_proto_dccp_fini);
8942bc78049SPatrick McHardy 
8952bc78049SPatrick McHardy MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
8962bc78049SPatrick McHardy MODULE_DESCRIPTION("DCCP connection tracking protocol helper");
8972bc78049SPatrick McHardy MODULE_LICENSE("GPL");
898