/* * Check flow function * * p: packet data structure, same as the one found in snort. * flowFlags: data defined in the detection plugin for this rule option * * Returns: * > 0 : match found * = 0 : no match found * * Predefined constants: * (see sf_snort_plugin_api.h for more values) * RULE_MATCH - if packet flow matches rule * RULE_NOMATCH - if packet flow does not match rule * */ ENGINE_LINKAGE int checkFlow(void *p, FlowFlags *flowFlags) { SFSnortPacket *sp = (SFSnortPacket *) p; #define FLOW_DIRECTION_FLAGS (FLOW_ESTABLISHED | FLOW_TO_CLIENT | FLOW_TO_SERVER) /* Check that the flags set in the flow structure are the same ones * set in the packet -- covers established, and to/from client/server */ if ((sp->flags & (flowFlags->flags & FLOW_DIRECTION_FLAGS)) != (flowFlags->flags & FLOW_DIRECTION_FLAGS)) return RULE_NOMATCH; /* check if this rule only applies to reassembled */ if (flowFlags->flags & FLOW_ONLY_REASSEMBLED) { if ( !(sp->flags & FLAG_REBUILT_STREAM) #ifdef ENABLE_PAF && !PacketHasFullPDU(sp) #endif ) return RULE_NOMATCH; } /* check if this rule only applies to non-reassembled */ if ((flowFlags->flags & FLOW_IGNORE_REASSEMBLED) && (sp->flags & FLAG_REBUILT_STREAM)) return RULE_NOMATCH; return RULE_MATCH; }
/* Modbus rule evaluation callback. */ int ModbusRuleEval(void *raw_packet, const uint8_t **cursor, void *data) { SFSnortPacket *packet = (SFSnortPacket *)raw_packet; modbus_option_data_t *rule_data = (modbus_option_data_t *)data; modbus_session_data_t *session_data; /* The preprocessor only evaluates PAF-flushed PDUs. If the rule options don't check for this, they'll fire on stale session data when the original packet goes through before flushing. */ if (!PacketHasFullPDU(packet) && ModbusIsPafActive(packet)) return RULE_NOMATCH; session_data = (modbus_session_data_t *) _dpd.sessionAPI->get_application_data(packet->stream_session, PP_MODBUS); if ((packet->payload_size == 0 ) || (session_data == NULL)) { return RULE_NOMATCH; } switch (rule_data->type) { case MODBUS_FUNC: if (session_data->func == rule_data->arg) return RULE_MATCH; break; case MODBUS_UNIT: if (session_data->unit == rule_data->arg) return RULE_MATCH; break; case MODBUS_DATA: /* XXX: If a PDU contains only the MBAP + Function, should this option fail or set the cursor to the end of the payload? */ if (packet->payload_size < MODBUS_MIN_LEN) return RULE_NOMATCH; /* Modbus data is always directly after the function code. */ *cursor = (const uint8_t *) (packet->payload + MODBUS_MIN_LEN); _dpd.SetAltDetect((uint8_t *)*cursor, (uint16_t)(packet->payload_size - MODBUS_MIN_LEN)); return RULE_MATCH; } return RULE_NOMATCH; }
/* Main runtime entry point */ static void ProcessModbus(void *ipacketp, void *contextp) { SFSnortPacket *packetp = (SFSnortPacket *)ipacketp; modbus_session_data_t *sessp; PROFILE_VARS; /* Sanity checks. Should this preprocessor run? */ if (( !packetp ) || ( !packetp->payload ) || ( !packetp->payload_size ) || ( !IPH_IS_VALID(packetp) ) || ( !packetp->tcp_header )) { return; } PREPROC_PROFILE_START(modbusPerfStats); /* Fetch me a preprocessor config to use with this VLAN/subnet/etc.! */ modbus_eval_config = sfPolicyUserDataGetCurrent(modbus_context_id); /* Look for a previously-allocated session data. */ sessp = _dpd.streamAPI->get_application_data(packetp->stream_session_ptr, PP_MODBUS); if (sessp == NULL) { /* No existing session. Check those ports. */ if (ModbusPortCheck(modbus_eval_config, packetp) != MODBUS_OK) { PREPROC_PROFILE_END(modbusPerfStats); return; } } if ( !PacketHasFullPDU(packetp) ) { /* If a packet is rebuilt, but not a full PDU, then it's garbage that got flushed at the end of a stream. */ if ( packetp->flags & (FLAG_REBUILT_STREAM|FLAG_PDU_HEAD) ) { _dpd.alertAdd(GENERATOR_SPP_MODBUS, MODBUS_BAD_LENGTH, 1, 0, 3, MODBUS_BAD_LENGTH_STR, 0); } PREPROC_PROFILE_END(modbusPerfStats); return; } if (sessp == NULL) { /* Create session data and attach it to the Stream5 session */ sessp = ModbusCreateSessionData(packetp); if ( !sessp ) { PREPROC_PROFILE_END(modbusPerfStats); return; } } /* When pipelined Modbus PDUs appear in a single TCP segment, the detection engine caches the results of the rule options after evaluating on the first PDU. Setting this flag stops the caching. */ packetp->flags |= FLAG_ALLOW_MULTIPLE_DETECT; /* Do preprocessor-specific detection stuff here */ ModbusDecode(modbus_eval_config, packetp); /* That's the end! */ PREPROC_PROFILE_END(modbusPerfStats); }
/* Main runtime entry point */ static void ProcessDNP3(void *ipacketp, void *contextp) { SFSnortPacket *packetp = (SFSnortPacket *)ipacketp; MemBucket *tmp_bucket = NULL; dnp3_session_data_t *sessp = NULL; PROFILE_VARS; /* Sanity checks. Should this preprocessor run? */ if (( !packetp ) || ( !packetp->payload ) || ( !packetp->payload_size ) || ( !IPH_IS_VALID(packetp) ) || ( !packetp->tcp_header && !packetp->udp_header )) { return; } /* If TCP, require that PAF flushes full PDUs first. */ if (packetp->tcp_header && !PacketHasFullPDU(packetp)) return; PREPROC_PROFILE_START(dnp3PerfStats); /* When pipelined DNP3 PDUs appear in a single TCP segment or UDP packet, the detection engine caches the results of the rule options after evaluating on the first PDU. Setting this flag stops the caching. */ packetp->flags |= FLAG_ALLOW_MULTIPLE_DETECT; /* Fetch me a preprocessor config to use with this VLAN/subnet/etc.! */ dnp3_eval_config = sfPolicyUserDataGetCurrent(dnp3_context_id); /* Look for a previously-allocated session data. */ tmp_bucket = _dpd.streamAPI->get_application_data(packetp->stream_session_ptr, PP_DNP3); if (tmp_bucket == NULL) { /* No existing session. Check those ports. */ if (DNP3PortCheck(dnp3_eval_config, packetp) != DNP3_OK) { PREPROC_PROFILE_END(dnp3PerfStats); return; } /* Create session data and attach it to the Stream5 session */ tmp_bucket = DNP3CreateSessionData(packetp); if (tmp_bucket == NULL) { /* Mempool was full, don't process this session. */ static unsigned int times_mempool_alloc_failed = 0; /* Print a message, but only every 1000 times. Don't want to flood the log if there's a lot of DNP3 traffic. */ if (times_mempool_alloc_failed % 1000) { _dpd.logMsg("WARNING: DNP3 memcap exceeded.\n"); } times_mempool_alloc_failed++; PREPROC_PROFILE_END(dnp3PerfStats); return; } } sessp = (dnp3_session_data_t *) tmp_bucket->data; /* Set reassembly direction */ if (packetp->flags & FLAG_FROM_CLIENT) sessp->direction = DNP3_CLIENT; else sessp->direction = DNP3_SERVER; /* Do preprocessor-specific detection stuff here */ if (packetp->tcp_header) { /* Single PDU. PAF already split them up into separate pseudo-packets. */ DNP3FullReassembly(dnp3_eval_config, sessp, packetp, (uint8_t *)packetp->payload, packetp->payload_size); } else if (packetp->udp_header) { DNP3ProcessUDP(dnp3_eval_config, sessp, packetp); } /* That's the end! */ PREPROC_PROFILE_END(dnp3PerfStats); }