/* 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; // preconditions - what we registered for assert(IsTCP(packetp) && packetp->payload && packetp->payload_size); 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.sessionAPI->get_application_data(packetp->stream_session, 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) && ModbusIsPafActive(packetp) ) { if ( sessp ) { sessp->unit = 0; sessp->func = 0; } /* 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 Stream 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 */ if (ModbusDecode(modbus_eval_config, packetp) == MODBUS_FAIL) { sessp->unit = 0; sessp->func = 0; } /* That's the end! */ PREPROC_PROFILE_END(modbusPerfStats); }