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 ¶ms->timeouts[NPF_ANY_CONN_CLOSED], 102 .default_val = 0, 103 .min = 0, .max = INT_MAX 104 }, 105 { 106 "state.generic.timeout.new", 107 ¶ms->timeouts[NPF_ANY_CONN_NEW], 108 .default_val = 30, 109 .min = 0, .max = INT_MAX 110 }, 111 { 112 "state.generic.timeout.established", 113 ¶ms->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