xref: /netbsd/sys/net/npf/npf_state.c (revision d6939920)
197b932f1Srmind /*-
238bb64a8Srmind  * Copyright (c) 2010-2012 The NetBSD Foundation, Inc.
397b932f1Srmind  * All rights reserved.
497b932f1Srmind  *
597b932f1Srmind  * This material is based upon work partially supported by The
697b932f1Srmind  * NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
797b932f1Srmind  *
897b932f1Srmind  * Redistribution and use in source and binary forms, with or without
997b932f1Srmind  * modification, are permitted provided that the following conditions
1097b932f1Srmind  * are met:
1197b932f1Srmind  * 1. Redistributions of source code must retain the above copyright
1297b932f1Srmind  *    notice, this list of conditions and the following disclaimer.
1397b932f1Srmind  * 2. Redistributions in binary form must reproduce the above copyright
1497b932f1Srmind  *    notice, this list of conditions and the following disclaimer in the
1597b932f1Srmind  *    documentation and/or other materials provided with the distribution.
1697b932f1Srmind  *
1797b932f1Srmind  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
1897b932f1Srmind  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
1997b932f1Srmind  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2097b932f1Srmind  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2197b932f1Srmind  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2297b932f1Srmind  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2397b932f1Srmind  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2497b932f1Srmind  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2597b932f1Srmind  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2697b932f1Srmind  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2797b932f1Srmind  * POSSIBILITY OF SUCH DAMAGE.
2897b932f1Srmind  */
2997b932f1Srmind 
3097b932f1Srmind /*
31c6d74635Srmind  * NPF state engine to track connection.
3297b932f1Srmind  */
3397b932f1Srmind 
340473fe8bSchristos #ifdef _KERNEL
3597b932f1Srmind #include <sys/cdefs.h>
36*d6939920Srmind __KERNEL_RCSID(0, "$NetBSD: npf_state.c,v 1.23 2020/05/30 14:16:56 rmind Exp $");
3797b932f1Srmind 
3897b932f1Srmind #include <sys/param.h>
3997b932f1Srmind #include <sys/systm.h>
4097b932f1Srmind #include <sys/mutex.h>
410473fe8bSchristos #endif
4297b932f1Srmind 
4397b932f1Srmind #include "npf_impl.h"
4497b932f1Srmind 
45255cf3c4Srmind /*
46c6d74635Srmind  * Generic connection states and timeout table.
47255cf3c4Srmind  *
48f49c13e6Srmind  * Note: used for connection-less protocols.
49255cf3c4Srmind  */
50255cf3c4Srmind 
51c6d74635Srmind #define	NPF_ANY_CONN_CLOSED		0
52c6d74635Srmind #define	NPF_ANY_CONN_NEW		1
53c6d74635Srmind #define	NPF_ANY_CONN_ESTABLISHED	2
54c6d74635Srmind #define	NPF_ANY_CONN_NSTATES		3
55255cf3c4Srmind 
567e3fb338Srmind /*
577e3fb338Srmind  * Parameters.
587e3fb338Srmind  */
597e3fb338Srmind typedef struct {
607e3fb338Srmind 	int		timeouts[NPF_ANY_CONN_NSTATES];
61*d6939920Srmind 	int		gre_timeout;
627e3fb338Srmind } npf_state_params_t;
637e3fb338Srmind 
647e3fb338Srmind /*
657e3fb338Srmind  * Generic FSM.
667e3fb338Srmind  */
67c6d74635Srmind static const uint8_t npf_generic_fsm[NPF_ANY_CONN_NSTATES][2] = {
68c6d74635Srmind 	[NPF_ANY_CONN_CLOSED] = {
69c6d74635Srmind 		[NPF_FLOW_FORW]		= NPF_ANY_CONN_NEW,
70255cf3c4Srmind 	},
71c6d74635Srmind 	[NPF_ANY_CONN_NEW] = {
72c6d74635Srmind 		[NPF_FLOW_FORW]		= NPF_ANY_CONN_NEW,
73c6d74635Srmind 		[NPF_FLOW_BACK]		= NPF_ANY_CONN_ESTABLISHED,
74255cf3c4Srmind 	},
75c6d74635Srmind 	[NPF_ANY_CONN_ESTABLISHED] = {
76c6d74635Srmind 		[NPF_FLOW_FORW]		= NPF_ANY_CONN_ESTABLISHED,
77c6d74635Srmind 		[NPF_FLOW_BACK]		= NPF_ANY_CONN_ESTABLISHED,
78255cf3c4Srmind 	},
7997b932f1Srmind };
8097b932f1Srmind 
8138bb64a8Srmind /*
8261ec0f9cSrmind  * State sampler for debugging.
8361ec0f9cSrmind  */
84c0ea6c8dSchristos #if defined(_NPF_TESTING)
8561ec0f9cSrmind static void (*npf_state_sample)(npf_state_t *, bool) = NULL;
8661ec0f9cSrmind #define	NPF_STATE_SAMPLE(n, r) if (npf_state_sample) (*npf_state_sample)(n, r);
8761ec0f9cSrmind #else
8861ec0f9cSrmind #define	NPF_STATE_SAMPLE(n, r)
8961ec0f9cSrmind #endif
9061ec0f9cSrmind 
917e3fb338Srmind void
npf_state_sysinit(npf_t * npf)927e3fb338Srmind npf_state_sysinit(npf_t *npf)
937e3fb338Srmind {
947e3fb338Srmind 	npf_state_params_t *params = npf_param_allocgroup(npf,
957e3fb338Srmind 	    NPF_PARAMS_GENERIC_STATE, sizeof(npf_state_params_t));
967e3fb338Srmind 	npf_param_t param_map[] = {
977e3fb338Srmind 		/*
987e3fb338Srmind 		 * Generic timeout (in seconds).
997e3fb338Srmind 		 */
1007e3fb338Srmind 		{
1017e3fb338Srmind 			"state.generic.timeout.closed",
1027e3fb338Srmind 			&params->timeouts[NPF_ANY_CONN_CLOSED],
1037e3fb338Srmind 			.default_val = 0,
1047e3fb338Srmind 			.min = 0, .max = INT_MAX
1057e3fb338Srmind 		},
1067e3fb338Srmind 		{
1077e3fb338Srmind 			"state.generic.timeout.new",
1087e3fb338Srmind 			&params->timeouts[NPF_ANY_CONN_NEW],
1097e3fb338Srmind 			.default_val = 30,
1107e3fb338Srmind 			.min = 0, .max = INT_MAX
1117e3fb338Srmind 		},
1127e3fb338Srmind 		{
1137e3fb338Srmind 			"state.generic.timeout.established",
1147e3fb338Srmind 			&params->timeouts[NPF_ANY_CONN_ESTABLISHED],
1157e3fb338Srmind 			.default_val = 60,
1167e3fb338Srmind 			.min = 0, .max = INT_MAX
1177e3fb338Srmind 		},
118*d6939920Srmind 		{
119*d6939920Srmind 			"state.generic.timeout.gre",
120*d6939920Srmind 			&params->gre_timeout,
121*d6939920Srmind 			.default_val = 24 * 60 * 60,
122*d6939920Srmind 			.min = 0, .max = INT_MAX
123*d6939920Srmind 		},
1247e3fb338Srmind 	};
1257e3fb338Srmind 	npf_param_register(npf, param_map, __arraycount(param_map));
1267e3fb338Srmind 	npf_state_tcp_sysinit(npf);
1277e3fb338Srmind }
1287e3fb338Srmind 
1297e3fb338Srmind void
npf_state_sysfini(npf_t * npf)1307e3fb338Srmind npf_state_sysfini(npf_t *npf)
1317e3fb338Srmind {
1327e3fb338Srmind 	const size_t len = sizeof(npf_state_params_t);
1337e3fb338Srmind 	npf_param_freegroup(npf, NPF_PARAMS_GENERIC_STATE, len);
1347e3fb338Srmind 	npf_state_tcp_sysfini(npf);
1357e3fb338Srmind }
1367e3fb338Srmind 
13761ec0f9cSrmind /*
13838bb64a8Srmind  * npf_state_init: initialise the state structure.
13938bb64a8Srmind  *
14038bb64a8Srmind  * Should normally be called on a first packet, which also determines the
14138bb64a8Srmind  * direction in a case of connection-orientated protocol.  Returns true on
14238bb64a8Srmind  * success and false otherwise (e.g. if protocol is not supported).
14338bb64a8Srmind  */
14497b932f1Srmind bool
npf_state_init(npf_cache_t * npc,npf_state_t * nst)145d5cb4211Srmind npf_state_init(npf_cache_t *npc, npf_state_t *nst)
14697b932f1Srmind {
14737527ec6Srmind 	const int proto = npc->npc_proto;
148255cf3c4Srmind 	bool ret;
14997b932f1Srmind 
150e8a3af9dSzoltan 	KASSERT(npf_iscached(npc, NPC_IP46));
151e8a3af9dSzoltan 	KASSERT(npf_iscached(npc, NPC_LAYER4));
152b7b64eb5Srmind 
153255cf3c4Srmind 	memset(nst, 0, sizeof(npf_state_t));
154b7b64eb5Srmind 
155255cf3c4Srmind 	switch (proto) {
156255cf3c4Srmind 	case IPPROTO_TCP:
157255cf3c4Srmind 		/* Pass to TCP state tracking engine. */
158d5cb4211Srmind 		ret = npf_state_tcp(npc, nst, NPF_FLOW_FORW);
159255cf3c4Srmind 		break;
160255cf3c4Srmind 	case IPPROTO_UDP:
161255cf3c4Srmind 	case IPPROTO_ICMP:
162*d6939920Srmind 	case IPPROTO_GRE:
163255cf3c4Srmind 		/* Generic. */
164255cf3c4Srmind 		nst->nst_state = npf_generic_fsm[nst->nst_state][NPF_FLOW_FORW];
165255cf3c4Srmind 		ret = true;
166255cf3c4Srmind 		break;
167255cf3c4Srmind 	default:
168255cf3c4Srmind 		ret = false;
16997b932f1Srmind 	}
1708b3f8af5Srmind 	NPF_STATE_SAMPLE(nst, ret);
171255cf3c4Srmind 	return ret;
17297b932f1Srmind }
17397b932f1Srmind 
17497b932f1Srmind void
npf_state_destroy(npf_state_t * nst)17597b932f1Srmind npf_state_destroy(npf_state_t *nst)
17697b932f1Srmind {
177255cf3c4Srmind 	nst->nst_state = 0;
17897b932f1Srmind }
17997b932f1Srmind 
18038bb64a8Srmind /*
18138bb64a8Srmind  * npf_state_inspect: inspect the packet according to the protocol state.
18238bb64a8Srmind  *
18338bb64a8Srmind  * Return true if packet is considered to match the state (e.g. for TCP,
18438bb64a8Srmind  * the packet belongs to the tracked connection) and false otherwise.
18538bb64a8Srmind  */
18697b932f1Srmind bool
npf_state_inspect(npf_cache_t * npc,npf_state_t * nst,const npf_flow_t flow)187*d6939920Srmind npf_state_inspect(npf_cache_t *npc, npf_state_t *nst, const npf_flow_t flow)
18897b932f1Srmind {
18937527ec6Srmind 	const int proto = npc->npc_proto;
19097b932f1Srmind 	bool ret;
19197b932f1Srmind 
19297b932f1Srmind 	switch (proto) {
19397b932f1Srmind 	case IPPROTO_TCP:
194255cf3c4Srmind 		/* Pass to TCP state tracking engine. */
195*d6939920Srmind 		ret = npf_state_tcp(npc, nst, flow);
196255cf3c4Srmind 		break;
197255cf3c4Srmind 	case IPPROTO_UDP:
198255cf3c4Srmind 	case IPPROTO_ICMP:
199*d6939920Srmind 	case IPPROTO_GRE:
200255cf3c4Srmind 		/* Generic. */
201*d6939920Srmind 		nst->nst_state = npf_generic_fsm[nst->nst_state][flow];
202255cf3c4Srmind 		ret = true;
20397b932f1Srmind 		break;
20497b932f1Srmind 	default:
205255cf3c4Srmind 		ret = false;
20697b932f1Srmind 	}
2078b3f8af5Srmind 	NPF_STATE_SAMPLE(nst, ret);
208255cf3c4Srmind 
20997b932f1Srmind 	return ret;
21097b932f1Srmind }
21197b932f1Srmind 
21293471725Srmind /*
2137e3fb338Srmind  * npf_state_etime: return the expiration time depending on the state.
21493471725Srmind  */
21597b932f1Srmind int
npf_state_etime(npf_t * npf,const npf_state_t * nst,const int proto)2167e3fb338Srmind npf_state_etime(npf_t *npf, const npf_state_t *nst, const int proto)
21797b932f1Srmind {
2187e3fb338Srmind 	const npf_state_params_t *params;
2197e3fb338Srmind 	const unsigned state = nst->nst_state;
220255cf3c4Srmind 	int timeout = 0;
22197b932f1Srmind 
222255cf3c4Srmind 	switch (proto) {
223255cf3c4Srmind 	case IPPROTO_TCP:
224255cf3c4Srmind 		/* Pass to TCP state tracking engine. */
2257e3fb338Srmind 		timeout = npf_state_tcp_timeout(npf, nst);
226255cf3c4Srmind 		break;
227255cf3c4Srmind 	case IPPROTO_UDP:
228255cf3c4Srmind 	case IPPROTO_ICMP:
229255cf3c4Srmind 		/* Generic. */
2307e3fb338Srmind 		params = npf->params[NPF_PARAMS_GENERIC_STATE];
2317e3fb338Srmind 		timeout = params->timeouts[state];
232255cf3c4Srmind 		break;
233*d6939920Srmind 	case IPPROTO_GRE:
234*d6939920Srmind 		params = npf->params[NPF_PARAMS_GENERIC_STATE];
235*d6939920Srmind 		timeout = params->gre_timeout;
236*d6939920Srmind 		break;
237255cf3c4Srmind 	default:
238255cf3c4Srmind 		KASSERT(false);
23997b932f1Srmind 	}
240255cf3c4Srmind 	return timeout;
24197b932f1Srmind }
24297b932f1Srmind 
24397b932f1Srmind void
npf_state_dump(const npf_state_t * nst)244f75d4435Srmind npf_state_dump(const npf_state_t *nst)
24597b932f1Srmind {
24634931850Syamt #if defined(DDB) || defined(_NPF_TESTING)
247f75d4435Srmind 	const npf_tcpstate_t *fst = &nst->nst_tcpst[0];
248f75d4435Srmind 	const npf_tcpstate_t *tst = &nst->nst_tcpst[1];
24997b932f1Srmind 
25097b932f1Srmind 	printf("\tstate (%p) %d:\n\t\t"
251255cf3c4Srmind 	    "F { end %u maxend %u mwin %u wscale %u }\n\t\t"
252255cf3c4Srmind 	    "T { end %u maxend %u mwin %u wscale %u }\n",
25397b932f1Srmind 	    nst, nst->nst_state,
254255cf3c4Srmind 	    fst->nst_end, fst->nst_maxend, fst->nst_maxwin, fst->nst_wscale,
255255cf3c4Srmind 	    tst->nst_end, tst->nst_maxend, tst->nst_maxwin, tst->nst_wscale
25697b932f1Srmind 	);
25797b932f1Srmind #endif
25834931850Syamt }
25961ec0f9cSrmind 
260c0ea6c8dSchristos #if defined(_NPF_TESTING)
26161ec0f9cSrmind void
npf_state_setsampler(void (* func)(npf_state_t *,bool))26261ec0f9cSrmind npf_state_setsampler(void (*func)(npf_state_t *, bool))
26361ec0f9cSrmind {
26461ec0f9cSrmind 	npf_state_sample = func;
26561ec0f9cSrmind }
26661ec0f9cSrmind #endif
267