xref: /netbsd-src/sys/net/npf/npf_state.c (revision e6c7e151de239c49d2e38720a061ed9d1fa99309)
1 /*-
2  * Copyright (c) 2010-2012 The NetBSD Foundation, Inc.
3  * All rights reserved.
4  *
5  * This material is based upon work partially supported by The
6  * NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 /*
31  * NPF state engine to track connection.
32  */
33 
34 #ifdef _KERNEL
35 #include <sys/cdefs.h>
36 __KERNEL_RCSID(0, "$NetBSD: npf_state.c,v 1.22 2019/07/23 00:52:01 rmind Exp $");
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/mutex.h>
41 #endif
42 
43 #include "npf_impl.h"
44 
45 /*
46  * Generic connection states and timeout table.
47  *
48  * Note: used for connection-less protocols.
49  */
50 
51 #define	NPF_ANY_CONN_CLOSED		0
52 #define	NPF_ANY_CONN_NEW		1
53 #define	NPF_ANY_CONN_ESTABLISHED	2
54 #define	NPF_ANY_CONN_NSTATES		3
55 
56 /*
57  * Parameters.
58  */
59 typedef struct {
60 	int		timeouts[NPF_ANY_CONN_NSTATES];
61 } npf_state_params_t;
62 
63 /*
64  * Generic FSM.
65  */
66 static const uint8_t npf_generic_fsm[NPF_ANY_CONN_NSTATES][2] = {
67 	[NPF_ANY_CONN_CLOSED] = {
68 		[NPF_FLOW_FORW]		= NPF_ANY_CONN_NEW,
69 	},
70 	[NPF_ANY_CONN_NEW] = {
71 		[NPF_FLOW_FORW]		= NPF_ANY_CONN_NEW,
72 		[NPF_FLOW_BACK]		= NPF_ANY_CONN_ESTABLISHED,
73 	},
74 	[NPF_ANY_CONN_ESTABLISHED] = {
75 		[NPF_FLOW_FORW]		= NPF_ANY_CONN_ESTABLISHED,
76 		[NPF_FLOW_BACK]		= NPF_ANY_CONN_ESTABLISHED,
77 	},
78 };
79 
80 /*
81  * State sampler for debugging.
82  */
83 #if defined(_NPF_TESTING)
84 static void (*npf_state_sample)(npf_state_t *, bool) = NULL;
85 #define	NPF_STATE_SAMPLE(n, r) if (npf_state_sample) (*npf_state_sample)(n, r);
86 #else
87 #define	NPF_STATE_SAMPLE(n, r)
88 #endif
89 
90 void
91 npf_state_sysinit(npf_t *npf)
92 {
93 	npf_state_params_t *params = npf_param_allocgroup(npf,
94 	    NPF_PARAMS_GENERIC_STATE, sizeof(npf_state_params_t));
95 	npf_param_t param_map[] = {
96 		/*
97 		 * Generic timeout (in seconds).
98 		 */
99 		{
100 			"state.generic.timeout.closed",
101 			&params->timeouts[NPF_ANY_CONN_CLOSED],
102 			.default_val = 0,
103 			.min = 0, .max = INT_MAX
104 		},
105 		{
106 			"state.generic.timeout.new",
107 			&params->timeouts[NPF_ANY_CONN_NEW],
108 			.default_val = 30,
109 			.min = 0, .max = INT_MAX
110 		},
111 		{
112 			"state.generic.timeout.established",
113 			&params->timeouts[NPF_ANY_CONN_ESTABLISHED],
114 			.default_val = 60,
115 			.min = 0, .max = INT_MAX
116 		},
117 	};
118 	npf_param_register(npf, param_map, __arraycount(param_map));
119 	npf_state_tcp_sysinit(npf);
120 }
121 
122 void
123 npf_state_sysfini(npf_t *npf)
124 {
125 	const size_t len = sizeof(npf_state_params_t);
126 	npf_param_freegroup(npf, NPF_PARAMS_GENERIC_STATE, len);
127 	npf_state_tcp_sysfini(npf);
128 }
129 
130 /*
131  * npf_state_init: initialise the state structure.
132  *
133  * Should normally be called on a first packet, which also determines the
134  * direction in a case of connection-orientated protocol.  Returns true on
135  * success and false otherwise (e.g. if protocol is not supported).
136  */
137 bool
138 npf_state_init(npf_cache_t *npc, npf_state_t *nst)
139 {
140 	const int proto = npc->npc_proto;
141 	bool ret;
142 
143 	KASSERT(npf_iscached(npc, NPC_IP46));
144 	KASSERT(npf_iscached(npc, NPC_LAYER4));
145 
146 	memset(nst, 0, sizeof(npf_state_t));
147 
148 	switch (proto) {
149 	case IPPROTO_TCP:
150 		/* Pass to TCP state tracking engine. */
151 		ret = npf_state_tcp(npc, nst, NPF_FLOW_FORW);
152 		break;
153 	case IPPROTO_UDP:
154 	case IPPROTO_ICMP:
155 		/* Generic. */
156 		nst->nst_state = npf_generic_fsm[nst->nst_state][NPF_FLOW_FORW];
157 		ret = true;
158 		break;
159 	default:
160 		ret = false;
161 	}
162 	NPF_STATE_SAMPLE(nst, ret);
163 	return ret;
164 }
165 
166 void
167 npf_state_destroy(npf_state_t *nst)
168 {
169 	nst->nst_state = 0;
170 }
171 
172 /*
173  * npf_state_inspect: inspect the packet according to the protocol state.
174  *
175  * Return true if packet is considered to match the state (e.g. for TCP,
176  * the packet belongs to the tracked connection) and false otherwise.
177  */
178 bool
179 npf_state_inspect(npf_cache_t *npc, npf_state_t *nst, const bool forw)
180 {
181 	const int proto = npc->npc_proto;
182 	const int di = forw ? NPF_FLOW_FORW : NPF_FLOW_BACK;
183 	bool ret;
184 
185 	switch (proto) {
186 	case IPPROTO_TCP:
187 		/* Pass to TCP state tracking engine. */
188 		ret = npf_state_tcp(npc, nst, di);
189 		break;
190 	case IPPROTO_UDP:
191 	case IPPROTO_ICMP:
192 		/* Generic. */
193 		nst->nst_state = npf_generic_fsm[nst->nst_state][di];
194 		ret = true;
195 		break;
196 	default:
197 		ret = false;
198 	}
199 	NPF_STATE_SAMPLE(nst, ret);
200 
201 	return ret;
202 }
203 
204 /*
205  * npf_state_etime: return the expiration time depending on the state.
206  */
207 int
208 npf_state_etime(npf_t *npf, const npf_state_t *nst, const int proto)
209 {
210 	const npf_state_params_t *params;
211 	const unsigned state = nst->nst_state;
212 	int timeout = 0;
213 
214 	switch (proto) {
215 	case IPPROTO_TCP:
216 		/* Pass to TCP state tracking engine. */
217 		timeout = npf_state_tcp_timeout(npf, nst);
218 		break;
219 	case IPPROTO_UDP:
220 	case IPPROTO_ICMP:
221 		/* Generic. */
222 		params = npf->params[NPF_PARAMS_GENERIC_STATE];
223 		timeout = params->timeouts[state];
224 		break;
225 	default:
226 		KASSERT(false);
227 	}
228 	return timeout;
229 }
230 
231 void
232 npf_state_dump(const npf_state_t *nst)
233 {
234 #if defined(DDB) || defined(_NPF_TESTING)
235 	const npf_tcpstate_t *fst = &nst->nst_tcpst[0];
236 	const npf_tcpstate_t *tst = &nst->nst_tcpst[1];
237 
238 	printf("\tstate (%p) %d:\n\t\t"
239 	    "F { end %u maxend %u mwin %u wscale %u }\n\t\t"
240 	    "T { end %u maxend %u mwin %u wscale %u }\n",
241 	    nst, nst->nst_state,
242 	    fst->nst_end, fst->nst_maxend, fst->nst_maxwin, fst->nst_wscale,
243 	    tst->nst_end, tst->nst_maxend, tst->nst_maxwin, tst->nst_wscale
244 	);
245 #endif
246 }
247 
248 #if defined(_NPF_TESTING)
249 void
250 npf_state_setsampler(void (*func)(npf_state_t *, bool))
251 {
252 	npf_state_sample = func;
253 }
254 #endif
255