1 /****************************************************************************
2 *
3 * Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
4 * Copyright (C) 2004-2013 Sourcefire, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License Version 2 as
8 * published by the Free Software Foundation. You may not use, modify or
9 * distribute this program under any other version of the GNU General
10 * Public License.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 ****************************************************************************/
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include "sf_types.h"
28 #include "snort_debug.h"
29 #include "decode.h"
30 #include "mstring.h"
31 #include "sfxhash.h"
32 #include "util.h"
33 #include "memory_stats.h"
34
35 #include "spp_session.h"
36 #include "session_api.h"
37 #include "snort_session.h"
38
39 #include "stream_common.h"
40 #include "snort_stream_tcp.h"
41 #include "snort_stream_udp.h"
42 #include "snort_stream_icmp.h"
43
44 #include "parser.h"
45
46 #include "reg_test.h"
47
48 #include "profiler.h"
49 #include "sfPolicy.h"
50 #ifdef PERF_PROFILING
51 PreprocStats s5IcmpPerfStats;
52 #endif
53
54 /* client/server ip/port dereference */
55 #define icmp_sender_ip lwSsn->client_ip
56 #define icmp_responder_ip lwSsn->server_ip
57
58 /* D A T A S T R U C T U R E S ***********************************/
59 typedef struct _IcmpSession
60 {
61 SessionControlBlock *lwSsn;
62
63 uint32_t echo_count;
64
65 struct timeval ssn_time;
66
67 } IcmpSession;
68
69
70 /* G L O B A L S **************************************************/
71 static SessionCache* icmp_lws_cache = NULL;
72
73 /* P R O T O T Y P E S ********************************************/
74 static void StreamParseIcmpArgs(char *, StreamIcmpPolicy *);
75 static void StreamPrintIcmpConfig(StreamIcmpPolicy *);
76 static int ProcessIcmpUnreach(Packet *p);
77 static int ProcessIcmpEcho(Packet *p);
78
StreamInitIcmp(void)79 void StreamInitIcmp( void )
80 {
81 /* Finally ICMP */
82 if(icmp_lws_cache == NULL)
83 {
84 icmp_lws_cache = session_api->init_session_cache( SESSION_PROTO_ICMP,
85 sizeof( IcmpSession ),
86 NULL );
87 }
88 }
89
StreamIcmpPolicyInit(StreamIcmpConfig * config,char * args)90 void StreamIcmpPolicyInit(StreamIcmpConfig *config, char *args)
91 {
92 if (config == NULL)
93 return;
94
95 config->num_policies++;
96
97 StreamParseIcmpArgs(args, &config->default_policy);
98
99 StreamPrintIcmpConfig(&config->default_policy);
100 }
101
StreamParseIcmpArgs(char * args,StreamIcmpPolicy * s5IcmpPolicy)102 static void StreamParseIcmpArgs(char *args, StreamIcmpPolicy *s5IcmpPolicy)
103 {
104 char **toks;
105 int num_toks;
106 int i;
107 char **stoks = NULL;
108 int s_toks;
109 char *endPtr = NULL;
110
111 s5IcmpPolicy->session_timeout = STREAM_DEFAULT_SSN_TIMEOUT;
112 //s5IcmpPolicy->flags = 0;
113
114 if(args != NULL && strlen(args) != 0)
115 {
116 toks = mSplit(args, ",", 0, &num_toks, 0);
117
118 for (i = 0; i < num_toks; i++)
119 {
120 stoks = mSplit(toks[i], " ", 2, &s_toks, 0);
121
122 if (s_toks == 0)
123 {
124 FatalError("%s(%d) => Missing parameter in Stream ICMP config.\n",
125 file_name, file_line);
126 }
127
128 if(!strcasecmp(stoks[0], "timeout"))
129 {
130 if(stoks[1])
131 {
132 s5IcmpPolicy->session_timeout = strtoul(stoks[1], &endPtr, 10);
133 }
134
135 if (!stoks[1] || (endPtr == &stoks[1][0]) || *endPtr)
136 {
137 FatalError("%s(%d) => Invalid timeout in config file. Integer parameter required.\n",
138 file_name, file_line);
139 }
140
141 if ((s5IcmpPolicy->session_timeout > STREAM_MAX_SSN_TIMEOUT) ||
142 (s5IcmpPolicy->session_timeout < STREAM_MIN_SSN_TIMEOUT))
143 {
144 FatalError("%s(%d) => Invalid timeout in config file. "
145 "Must be between %d and %d\n",
146 file_name, file_line,
147 STREAM_MIN_SSN_TIMEOUT, STREAM_MAX_SSN_TIMEOUT);
148 }
149 if (s_toks > 2)
150 {
151 FatalError("%s(%d) => Invalid Stream ICMP Policy option. Missing comma?\n",
152 file_name, file_line);
153 }
154 }
155 else
156 {
157 FatalError("%s(%d) => Invalid Stream ICMP policy option\n",
158 file_name, file_line);
159 }
160
161 mSplitFree(&stoks, s_toks);
162 }
163
164 mSplitFree(&toks, num_toks);
165 }
166 }
167
StreamPrintIcmpConfig(StreamIcmpPolicy * s5IcmpPolicy)168 static void StreamPrintIcmpConfig(StreamIcmpPolicy *s5IcmpPolicy)
169 {
170 LogMessage("Stream ICMP Policy config:\n");
171 LogMessage(" Timeout: %d seconds\n", s5IcmpPolicy->session_timeout);
172 //LogMessage(" Flags: 0x%X\n", s5UdpPolicy->flags);
173 //IpAddrSetPrint(" Bound Addresses:", s5UdpPolicy->bound_addrs);
174
175 }
176
IcmpSessionCleanup(SessionControlBlock * ssn)177 void IcmpSessionCleanup(SessionControlBlock *ssn)
178 {
179 IcmpSession *icmpssn = NULL;
180
181 if (ssn->ha_state.session_flags & SSNFLAG_PRUNED)
182 {
183 CloseStreamSession(&sfBase, SESSION_CLOSED_PRUNED);
184 }
185 else if (ssn->ha_state.session_flags & SSNFLAG_TIMEDOUT)
186 {
187 CloseStreamSession(&sfBase, SESSION_CLOSED_TIMEDOUT);
188 }
189 else
190 {
191 CloseStreamSession(&sfBase, SESSION_CLOSED_NORMALLY);
192 }
193
194 if (ssn->proto_specific_data)
195 icmpssn = ssn->proto_specific_data->data;
196
197 if (!icmpssn)
198 {
199 /* Huh? */
200 return;
201 }
202
203 /* Cleanup the proto specific data */
204 session_api->free_protocol_session_pool( SESSION_PROTO_ICMP, ssn );
205 ssn->proto_specific_data = NULL;
206
207 StreamResetFlowBits(ssn);
208 session_api->free_application_data(ssn);
209
210 s5stats.icmp_sessions_released++;
211 s5stats.active_icmp_sessions--;
212 }
213
StreamGetIcmpPrunes(void)214 uint32_t StreamGetIcmpPrunes(void)
215 {
216 if( icmp_lws_cache )
217 return session_api->get_session_prune_count( SESSION_PROTO_ICMP );
218 else
219 return s5stats.icmp_prunes;
220 }
221
StreamResetIcmpPrunes(void)222 void StreamResetIcmpPrunes(void)
223 {
224 session_api->reset_session_prune_count( SESSION_PROTO_ICMP );
225 }
226
StreamResetIcmp(void)227 void StreamResetIcmp(void)
228 {
229 session_api->purge_session_cache(icmp_lws_cache);
230 session_api->clean_protocol_session_pool( SESSION_PROTO_ICMP );
231 }
232
StreamCleanIcmp(void)233 void StreamCleanIcmp(void)
234 {
235 if ( icmp_lws_cache )
236 s5stats.icmp_prunes = session_api->get_session_prune_count( SESSION_PROTO_ICMP );
237
238 /* Clean up session cache */
239 session_api->delete_session_cache( SESSION_PROTO_ICMP );
240 icmp_lws_cache = NULL;
241 }
242
StreamIcmpConfigFree(StreamIcmpConfig * config)243 void StreamIcmpConfigFree(StreamIcmpConfig *config)
244 {
245 if (config == NULL)
246 return;
247
248 SnortPreprocFree(config, sizeof(StreamIcmpConfig),
249 PP_STREAM, PP_MEM_CATEGORY_CONFIG);
250 }
251
StreamVerifyIcmpConfig(StreamIcmpConfig * config,tSfPolicyId policy_id)252 int StreamVerifyIcmpConfig(StreamIcmpConfig *config, tSfPolicyId policy_id)
253 {
254 if (config == NULL)
255 return -1;
256
257 if (!icmp_lws_cache)
258 return -1;
259
260 if (config->num_policies == 0)
261 return -1;
262
263 return 0;
264 }
265
StreamProcessIcmp(Packet * p)266 int StreamProcessIcmp(Packet *p)
267 {
268 int status = 0;
269
270 switch (p->icmph->type)
271 {
272 case ICMP_DEST_UNREACH:
273 s5stats.icmp_unreachable++;
274 if (p->icmph->code != ICMP_FRAG_NEEDED)
275 {
276 status = ProcessIcmpUnreach(p);
277 }
278 else
279 {
280 s5stats.icmp_unreachable_code4++;
281 }
282 break;
283
284 case ICMP_ECHO:
285 case ICMP_ECHOREPLY:
286 status = ProcessIcmpEcho(p);
287 break;
288
289 default:
290 /* We only handle the above ICMP messages with stream5 */
291 break;
292 }
293
294 return status;
295 }
296
ProcessIcmpUnreach(Packet * p)297 static int ProcessIcmpUnreach(Packet *p)
298 {
299 /* Handle ICMP unreachable */
300 SessionKey skey;
301 SessionControlBlock *ssn = NULL;
302 uint16_t sport;
303 uint16_t dport;
304 sfaddr_t *src;
305 sfaddr_t *dst;
306
307 /* No "orig" IP Header */
308 if (!p->orig_iph)
309 return 0;
310
311 /* Get TCP/UDP/ICMP session from original protocol/port info
312 * embedded in the ICMP Unreach message. This is already decoded
313 * in p->orig_foo. TCP/UDP ports are decoded as p->orig_sp/dp.
314 */
315 skey.protocol = GET_ORIG_IPH_PROTO(p);
316 sport = p->orig_sp;
317 dport = p->orig_dp;
318
319 src = GET_ORIG_SRC(p);
320 dst = GET_ORIG_DST(p);
321
322 if (sfip_fast_lt6(src, dst))
323 {
324 COPY4(skey.ip_l, sfaddr_get_ip6_ptr(src));
325 skey.port_l = sport;
326 COPY4(skey.ip_h, sfaddr_get_ip6_ptr(dst));
327 skey.port_h = dport;
328 }
329 else if (IP_EQUALITY(src, dst))
330 {
331 COPY4(skey.ip_l, sfaddr_get_ip6_ptr(src));
332 COPY4(skey.ip_h, skey.ip_l);
333 if (sport < dport)
334 {
335 skey.port_l = sport;
336 skey.port_h = dport;
337 }
338 else
339 {
340 skey.port_l = dport;
341 skey.port_h = sport;
342 }
343 }
344 else
345 {
346 COPY4(skey.ip_l, sfaddr_get_ip6_ptr(dst));
347 COPY4(skey.ip_h, sfaddr_get_ip6_ptr(src));
348 skey.port_l = dport;
349 skey.port_h = sport;
350 }
351
352 if (p->vh)
353 skey.vlan_tag = (uint16_t)VTH_VLAN(p->vh);
354 else
355 skey.vlan_tag = 0;
356
357 switch (skey.protocol)
358 {
359 case IPPROTO_TCP:
360 /* Lookup a TCP session */
361 ssn = GetLWTcpSession(&skey);
362 break;
363 case IPPROTO_UDP:
364 /* Lookup a UDP session */
365 ssn = GetLWUdpSession(&skey);
366 break;
367 case IPPROTO_ICMP:
368 /* Lookup a ICMP session */
369 ssn = session_api->get_session_by_key(icmp_lws_cache, &skey);
370 break;
371 }
372
373 if (ssn)
374 {
375 /* Mark this session as dead. */
376 DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
377 "Marking session as dead, per ICMP Unreachable!\n"););
378 ssn->ha_state.session_flags |= SSNFLAG_DROP_CLIENT;
379 ssn->ha_state.session_flags |= SSNFLAG_DROP_SERVER;
380 ssn->session_state |= STREAM_STATE_UNREACH;
381 s5stats.active_icmp_sessions++;
382 }
383
384 return 0;
385 }
386
ProcessIcmpEcho(Packet * p)387 static int ProcessIcmpEcho(Packet *p)
388 {
389 //SessionKey skey;
390 //SessionControlBlock *ssn = NULL;
391 s5stats.active_icmp_sessions++;
392
393 return 0;
394 }
395
IcmpUpdateDirection(SessionControlBlock * ssn,char dir,sfaddr_t * ip,uint16_t port)396 void IcmpUpdateDirection(SessionControlBlock *ssn, char dir, sfaddr_t* ip, uint16_t port)
397 {
398 IcmpSession *icmpssn = ssn->proto_specific_data->data;
399 sfaddr_t tmpIp;
400
401 if (!icmpssn)
402 {
403 /* Huh? */
404 return;
405 }
406
407 if (IP_EQUALITY(&icmpssn->icmp_sender_ip, ip))
408 {
409 if ((dir == SSN_DIR_FROM_SENDER) && (ssn->ha_state.direction == SSN_DIR_FROM_SENDER))
410 {
411 /* Direction already set as SENDER */
412 return;
413 }
414 }
415 else if (IP_EQUALITY(&icmpssn->icmp_responder_ip, ip))
416 {
417 if ((dir == SSN_DIR_FROM_RESPONDER) && (ssn->ha_state.direction == SSN_DIR_FROM_RESPONDER))
418 {
419 /* Direction already set as RESPONDER */
420 return;
421 }
422 }
423
424 /* Swap them -- leave ssn->ha_state.direction the same */
425 tmpIp = icmpssn->icmp_sender_ip;
426 icmpssn->icmp_sender_ip = icmpssn->icmp_responder_ip;
427 icmpssn->icmp_responder_ip = tmpIp;
428 }
429
430 #ifdef SNORT_RELOAD
SessionICMPReload(uint32_t max_sessions,uint16_t pruningTimeout,uint16_t nominalTimeout)431 void SessionICMPReload(uint32_t max_sessions, uint16_t pruningTimeout, uint16_t nominalTimeout)
432 {
433 SessionReload(icmp_lws_cache, max_sessions, pruningTimeout, nominalTimeout
434 #ifdef REG_TEST
435 , "ICMP"
436 #endif
437 );
438 }
439
SessionICMPReloadAdjust(unsigned maxWork)440 unsigned SessionICMPReloadAdjust(unsigned maxWork)
441 {
442 return SessionProtocolReloadAdjust(icmp_lws_cache, session_configuration->max_icmp_sessions,
443 maxWork, 0
444 #ifdef REG_TEST
445 , "ICMP"
446 #endif
447 );
448 }
449 #endif
450
get_icmp_used_mempool()451 size_t get_icmp_used_mempool()
452 {
453 if (icmp_lws_cache && icmp_lws_cache->protocol_session_pool)
454 return icmp_lws_cache->protocol_session_pool->used_memory;
455
456 return 0;
457 }
458