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.23 2020/05/30 14:16:56 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 */ 59typedef struct { 60 int timeouts[NPF_ANY_CONN_NSTATES]; 61 int gre_timeout; 62} npf_state_params_t; 63 64/* 65 * Generic FSM. 66 */ 67static const uint8_t npf_generic_fsm[NPF_ANY_CONN_NSTATES][2] = { 68 [NPF_ANY_CONN_CLOSED] = { 69 [NPF_FLOW_FORW] = NPF_ANY_CONN_NEW, 70 }, 71 [NPF_ANY_CONN_NEW] = { 72 [NPF_FLOW_FORW] = NPF_ANY_CONN_NEW, 73 [NPF_FLOW_BACK] = NPF_ANY_CONN_ESTABLISHED, 74 }, 75 [NPF_ANY_CONN_ESTABLISHED] = { 76 [NPF_FLOW_FORW] = NPF_ANY_CONN_ESTABLISHED, 77 [NPF_FLOW_BACK] = NPF_ANY_CONN_ESTABLISHED, 78 }, 79}; 80 81/* 82 * State sampler for debugging. 83 */ 84#if defined(_NPF_TESTING) 85static void (*npf_state_sample)(npf_state_t *, bool) = NULL; 86#define NPF_STATE_SAMPLE(n, r) if (npf_state_sample) (*npf_state_sample)(n, r); 87#else 88#define NPF_STATE_SAMPLE(n, r) 89#endif 90 91void 92npf_state_sysinit(npf_t *npf) 93{ 94 npf_state_params_t *params = npf_param_allocgroup(npf, 95 NPF_PARAMS_GENERIC_STATE, sizeof(npf_state_params_t)); 96 npf_param_t param_map[] = { 97 /* 98 * Generic timeout (in seconds). 99 */ 100 { 101 "state.generic.timeout.closed", 102 ¶ms->timeouts[NPF_ANY_CONN_CLOSED], 103 .default_val = 0, 104 .min = 0, .max = INT_MAX 105 }, 106 { 107 "state.generic.timeout.new", 108 ¶ms->timeouts[NPF_ANY_CONN_NEW], 109 .default_val = 30, 110 .min = 0, .max = INT_MAX 111 }, 112 { 113 "state.generic.timeout.established", 114 ¶ms->timeouts[NPF_ANY_CONN_ESTABLISHED], 115 .default_val = 60, 116 .min = 0, .max = INT_MAX 117 }, 118 { 119 "state.generic.timeout.gre", 120 ¶ms->gre_timeout, 121 .default_val = 24 * 60 * 60, 122 .min = 0, .max = INT_MAX 123 }, 124 }; 125 npf_param_register(npf, param_map, __arraycount(param_map)); 126 npf_state_tcp_sysinit(npf); 127} 128 129void 130npf_state_sysfini(npf_t *npf) 131{ 132 const size_t len = sizeof(npf_state_params_t); 133 npf_param_freegroup(npf, NPF_PARAMS_GENERIC_STATE, len); 134 npf_state_tcp_sysfini(npf); 135} 136 137/* 138 * npf_state_init: initialise the state structure. 139 * 140 * Should normally be called on a first packet, which also determines the 141 * direction in a case of connection-orientated protocol. Returns true on 142 * success and false otherwise (e.g. if protocol is not supported). 143 */ 144bool 145npf_state_init(npf_cache_t *npc, npf_state_t *nst) 146{ 147 const int proto = npc->npc_proto; 148 bool ret; 149 150 KASSERT(npf_iscached(npc, NPC_IP46)); 151 KASSERT(npf_iscached(npc, NPC_LAYER4)); 152 153 memset(nst, 0, sizeof(npf_state_t)); 154 155 switch (proto) { 156 case IPPROTO_TCP: 157 /* Pass to TCP state tracking engine. */ 158 ret = npf_state_tcp(npc, nst, NPF_FLOW_FORW); 159 break; 160 case IPPROTO_UDP: 161 case IPPROTO_ICMP: 162 case IPPROTO_GRE: 163 /* Generic. */ 164 nst->nst_state = npf_generic_fsm[nst->nst_state][NPF_FLOW_FORW]; 165 ret = true; 166 break; 167 default: 168 ret = false; 169 } 170 NPF_STATE_SAMPLE(nst, ret); 171 return ret; 172} 173 174void 175npf_state_destroy(npf_state_t *nst) 176{ 177 nst->nst_state = 0; 178} 179 180/* 181 * npf_state_inspect: inspect the packet according to the protocol state. 182 * 183 * Return true if packet is considered to match the state (e.g. for TCP, 184 * the packet belongs to the tracked connection) and false otherwise. 185 */ 186bool 187npf_state_inspect(npf_cache_t *npc, npf_state_t *nst, const npf_flow_t flow) 188{ 189 const int proto = npc->npc_proto; 190 bool ret; 191 192 switch (proto) { 193 case IPPROTO_TCP: 194 /* Pass to TCP state tracking engine. */ 195 ret = npf_state_tcp(npc, nst, flow); 196 break; 197 case IPPROTO_UDP: 198 case IPPROTO_ICMP: 199 case IPPROTO_GRE: 200 /* Generic. */ 201 nst->nst_state = npf_generic_fsm[nst->nst_state][flow]; 202 ret = true; 203 break; 204 default: 205 ret = false; 206 } 207 NPF_STATE_SAMPLE(nst, ret); 208 209 return ret; 210} 211 212/* 213 * npf_state_etime: return the expiration time depending on the state. 214 */ 215int 216npf_state_etime(npf_t *npf, const npf_state_t *nst, const int proto) 217{ 218 const npf_state_params_t *params; 219 const unsigned state = nst->nst_state; 220 int timeout = 0; 221 222 switch (proto) { 223 case IPPROTO_TCP: 224 /* Pass to TCP state tracking engine. */ 225 timeout = npf_state_tcp_timeout(npf, nst); 226 break; 227 case IPPROTO_UDP: 228 case IPPROTO_ICMP: 229 /* Generic. */ 230 params = npf->params[NPF_PARAMS_GENERIC_STATE]; 231 timeout = params->timeouts[state]; 232 break; 233 case IPPROTO_GRE: 234 params = npf->params[NPF_PARAMS_GENERIC_STATE]; 235 timeout = params->gre_timeout; 236 break; 237 default: 238 KASSERT(false); 239 } 240 return timeout; 241} 242 243void 244npf_state_dump(const npf_state_t *nst) 245{ 246#if defined(DDB) || defined(_NPF_TESTING) 247 const npf_tcpstate_t *fst = &nst->nst_tcpst[0]; 248 const npf_tcpstate_t *tst = &nst->nst_tcpst[1]; 249 250 printf("\tstate (%p) %d:\n\t\t" 251 "F { end %u maxend %u mwin %u wscale %u }\n\t\t" 252 "T { end %u maxend %u mwin %u wscale %u }\n", 253 nst, nst->nst_state, 254 fst->nst_end, fst->nst_maxend, fst->nst_maxwin, fst->nst_wscale, 255 tst->nst_end, tst->nst_maxend, tst->nst_maxwin, tst->nst_wscale 256 ); 257#endif 258} 259 260#if defined(_NPF_TESTING) 261void 262npf_state_setsampler(void (*func)(npf_state_t *, bool)) 263{ 264 npf_state_sample = func; 265} 266#endif 267