static int proto_ppp_pap_init(struct proto *proto, struct registry_instance *i) { if (proto_number_register("ppp", 0xc023, proto) != POM_OK) return POM_ERR; struct proto_ppp_pap_priv *priv = malloc(sizeof(struct proto_ppp_pap_priv)); if (!priv) { pom_oom(sizeof(struct proto_ppp_pap_priv)); return POM_ERR; } memset(priv, 0, sizeof(struct proto_ppp_pap_priv)); proto_set_priv(proto, priv); static struct data_item_reg evt_request_data_items[PROTO_PPP_PAP_EVT_REQUEST_DATA_COUNT] = { { 0 } }; evt_request_data_items[evt_ppp_pap_request_code].name = "code"; evt_request_data_items[evt_ppp_pap_request_code].value_type = ptype_get_type("uint8"); evt_request_data_items[evt_ppp_pap_request_identifier].name = "identifier"; evt_request_data_items[evt_ppp_pap_request_identifier].value_type = ptype_get_type("uint8"); evt_request_data_items[evt_ppp_pap_request_peer_id].name = "peer_id"; evt_request_data_items[evt_ppp_pap_request_peer_id].value_type = ptype_get_type("string"); evt_request_data_items[evt_ppp_pap_request_password].name = "password"; evt_request_data_items[evt_ppp_pap_request_password].value_type = ptype_get_type("string"); static struct data_reg evt_ppp_pap_request_data = { .items = evt_request_data_items, .data_count = PROTO_PPP_PAP_EVT_REQUEST_DATA_COUNT }; static struct event_reg_info proto_ppp_pap_request = { 0 }; proto_ppp_pap_request.source_name = "proto_ppp_pap"; proto_ppp_pap_request.source_obj = priv; proto_ppp_pap_request.name = "ppp_pap_request"; proto_ppp_pap_request.description = "PPP PAP Authentication request"; proto_ppp_pap_request.data_reg = &evt_ppp_pap_request_data; priv->evt_request = event_register(&proto_ppp_pap_request); if (!priv->evt_request) goto err; static struct data_item_reg evt_ack_nack_data_items[PROTO_PPP_PAP_EVT_ACK_NACK_DATA_COUNT] = { { 0 } }; evt_ack_nack_data_items[evt_ppp_pap_ack_nack_code].name = "code"; evt_ack_nack_data_items[evt_ppp_pap_ack_nack_code].value_type = ptype_get_type("uint8"); evt_ack_nack_data_items[evt_ppp_pap_ack_nack_identifier].name = "identifier"; evt_ack_nack_data_items[evt_ppp_pap_ack_nack_identifier].value_type = ptype_get_type("uint8"); evt_ack_nack_data_items[evt_ppp_pap_ack_nack_message].name = "message"; evt_ack_nack_data_items[evt_ppp_pap_ack_nack_message].value_type = ptype_get_type("string"); static struct data_reg evt_ppp_pap_ack_nack_data = { .items = evt_ack_nack_data_items, .data_count = PROTO_PPP_PAP_EVT_ACK_NACK_DATA_COUNT }; static struct event_reg_info proto_ppp_pap_ack_nack = { 0 }; proto_ppp_pap_ack_nack.source_name = "proto_ppp_pap"; proto_ppp_pap_ack_nack.source_obj = priv; proto_ppp_pap_ack_nack.name = "ppp_pap_ack_nack"; proto_ppp_pap_ack_nack.description = "PPP-PAP ACK/NACK"; proto_ppp_pap_ack_nack.data_reg = &evt_ppp_pap_ack_nack_data; priv->evt_ack_nack = event_register(&proto_ppp_pap_ack_nack); if (!priv->evt_ack_nack) goto err; priv->p_auth_timeout = ptype_alloc_unit("uint32", "seconds"); if (!priv->p_auth_timeout) goto err; struct registry_param *p = registry_new_param("auth_timeout", "60", priv->p_auth_timeout, "Authentication timeout", 0); if (proto_add_param(proto, p) != POM_OK) { registry_cleanup_param(p); goto err; } return POM_OK; err: proto_ppp_pap_cleanup(priv); return POM_ERR; }
int registry_config_save(char *config_name) { if (strlen(config_name) >= REGISTRY_CONFIG_NAME_MAX) { pomlog(POMLOG_ERR "Configuration name too long, max %u characters.", REGISTRY_CONFIG_NAME_MAX); return POM_ERR; } struct dataset_query *dsq_config_list = NULL, *dsq_config = NULL; struct datastore *sys_dstore = system_datastore(); if (!sys_dstore) return POM_ERR; struct datastore_connection *dc = datastore_connection_new(sys_dstore); if (!dc) return POM_ERR; dsq_config_list = datastore_dataset_query_open(sys_dstore, REGISTRY_CONFIG_LIST, registry_config_list_dataset_template, dc); if (!dsq_config_list) goto err; if (datastore_dataset_query_set_string_condition(dsq_config_list, 0, PTYPE_OP_EQ, config_name) != POM_OK) goto err; dsq_config = datastore_dataset_query_open(sys_dstore, REGISTRY_CONFIG, registry_config_dataset_template, dc); if (!dsq_config) goto err; if (datastore_transaction_begin(dc) != POM_OK) goto err; // Find out if we already have a config by that name int res = datastore_dataset_read_single(dsq_config_list); if (res == DATASET_QUERY_MORE) { // Delete existing stuff about this config if (datastore_dataset_query_set_uint64_condition(dsq_config, 0, PTYPE_OP_EQ, dsq_config_list->data_id) != POM_OK) goto err; if (datastore_dataset_delete(dsq_config_list) != DATASET_QUERY_OK) goto err; if (datastore_dataset_delete(dsq_config) != DATASET_QUERY_OK) goto err; } if (res < 0) goto err; // Add the config to the config list PTYPE_STRING_SETVAL(dsq_config_list->values[0].value, config_name); PTYPE_TIMESTAMP_SETVAL(dsq_config_list->values[1].value, pom_gettimeofday()); if (datastore_dataset_write(dsq_config_list) != DATASET_QUERY_OK) goto err; PTYPE_UINT64_SETVAL(dsq_config->values[0].value, dsq_config_list->data_id); registry_lock(); struct registry_class *cls; // Browse each class for (cls = registry_head; cls; cls = cls->next) { // Browse each instance of the class struct registry_instance *inst; for (inst = cls->instances; inst; inst = inst->next) { // Don't add the instance if it's not added by the user if (cls->instance_add) { // The system datastore will always exist if (inst == sys_dstore->reg_instance) continue; char *buff = malloc(strlen(cls->name) + 1 + strlen(inst->name) + 1); if (!buff) { pom_oom(strlen(cls->name) + 1 + strlen(inst->name) + 1); goto err_locked; } strcpy(buff, cls->name); strcat(buff, "."); strcat(buff, inst->name); PTYPE_STRING_SETVAL_P(dsq_config->values[1].value, buff); struct registry_param *p; for (p = inst->params; p && strcmp(p->name, "type"); p = p->next); if (p) { dsq_config->values[2].is_null = 0; char *type = PTYPE_STRING_GETVAL(p->value); PTYPE_STRING_SETVAL(dsq_config->values[2].value, type); } else { dsq_config->values[2].is_null = 1; } PTYPE_UINT8_SETVAL(dsq_config->values[3].value, registry_config_instance); if (datastore_dataset_write(dsq_config) != DATASET_QUERY_OK) goto err_locked; } // Browse the parametrers and add the non default ones struct registry_param *param; for (param = inst->params; param; param = param->next) { // Check if the parameter value is not the default one anymore if (param->default_value) { struct ptype *defval = ptype_alloc_from(param->value); if (!defval) goto err_locked; if (ptype_parse_val(defval, param->default_value) != POM_OK) { pomlog(POMLOG_ERR "Unable to parse default value !"); ptype_cleanup(defval); goto err_locked; } if (ptype_compare_val(PTYPE_OP_EQ, param->value, defval)) { // Param still has the default value, do nothing ptype_cleanup(defval); continue; } ptype_cleanup(defval); } char *buff = malloc(strlen(cls->name) + 1 + strlen(inst->name) + 1 + strlen(param->name) + 1); if (!buff) { pom_oom(strlen(cls->name) + 1 + strlen(inst->name) + 1 + strlen(param->name) + 1); goto err_locked; } strcpy(buff, cls->name); strcat(buff, "."); strcat(buff, inst->name); strcat(buff, "."); strcat(buff, param->name); PTYPE_STRING_SETVAL_P(dsq_config->values[1].value, buff); char *value = ptype_print_val_alloc(param->value, NULL); if (!value) goto err_locked; dsq_config->values[2].is_null = 0; PTYPE_STRING_SETVAL_P(dsq_config->values[2].value, value); PTYPE_UINT8_SETVAL(dsq_config->values[3].value, registry_config_instance_param); if (datastore_dataset_write(dsq_config) != DATASET_QUERY_OK) goto err_locked; } } } registry_config_serial++; registry_serial++; xmlrcpcmd_serial_inc(); registry_unlock(); if (datastore_transaction_commit(dc) != POM_OK) goto err; datastore_dataset_query_cleanup(dsq_config_list); datastore_dataset_query_cleanup(dsq_config); datastore_connection_release(dc); pomlog("Registry configuration saved as \"%s\"", config_name); return POM_OK; err_locked: registry_unlock(); err: if (dsq_config_list) datastore_dataset_query_cleanup(dsq_config_list); if (dsq_config) datastore_dataset_query_cleanup(dsq_config); if (dc) { datastore_transaction_rollback(dc); datastore_connection_release(dc); } return POM_ERR; }
int core_init(unsigned int num_threads) { core_registry_class = registry_add_class(CORE_REGISTRY); if (!core_registry_class) return POM_ERR; perf_pkt_queue = registry_class_add_perf(core_registry_class, "pkt_queue", registry_perf_type_gauge, "Number of packets in the queue waiting to be processed", "pkts"); perf_thread_active = registry_class_add_perf(core_registry_class, "active_thread", registry_perf_type_gauge, "Number of active threads", "threads"); if (!perf_pkt_queue || !perf_thread_active) return POM_OK; core_param_dump_pkt = ptype_alloc("bool"); if (!core_param_dump_pkt) return POM_ERR; core_param_offline_dns = ptype_alloc("bool"); if (!core_param_offline_dns) { ptype_cleanup(core_param_dump_pkt); core_param_dump_pkt = NULL; return POM_ERR; } core_param_reset_perf_on_restart = ptype_alloc("bool"); if (!core_param_reset_perf_on_restart) { ptype_cleanup(core_param_dump_pkt); core_param_dump_pkt = NULL; ptype_cleanup(core_param_offline_dns); core_param_offline_dns = NULL; return POM_ERR; } struct registry_param *param = registry_new_param("dump_pkt", "no", core_param_dump_pkt, "Dump packets to logs", REGISTRY_PARAM_FLAG_CLEANUP_VAL); if (registry_class_add_param(core_registry_class, param) != POM_OK) goto err; param = registry_new_param("offline_dns", "yes", core_param_offline_dns, "Enable offline DNS resolver", REGISTRY_PARAM_FLAG_CLEANUP_VAL); if (registry_class_add_param(core_registry_class, param) != POM_OK) goto err; param = registry_new_param("reset_perf_on_restart", "no", core_param_reset_perf_on_restart, "Reset performances when core restarts", REGISTRY_PARAM_FLAG_CLEANUP_VAL); if (registry_class_add_param(core_registry_class, param) != POM_OK) goto err; param = NULL; // Start the processing threads unsigned int num_cpu = sysconf(_SC_NPROCESSORS_ONLN) - 1; if (num_cpu < 1) { pomlog(POMLOG_WARN "Could not find the number of CPU, assuming %u", CORE_PROCESS_THREAD_DEFAULT); num_cpu = CORE_PROCESS_THREAD_DEFAULT; } if (num_threads < 1) num_threads = num_cpu; if (num_threads > num_cpu) pomlog(POMLOG_WARN "WARNING : Running more processing threads than available CPU is discouraged as it will cause issues by creating higher latencies and eventually dropping packets !!! You have been warned !"); if (num_threads > CORE_PROCESS_THREAD_MAX) num_threads = CORE_PROCESS_THREAD_MAX; core_num_threads = num_threads; pomlog(POMLOG_INFO "Starting %u processing thread(s)", core_num_threads); core_run = 1; memset(core_processing_threads, 0, sizeof(struct core_processing_thread*) * CORE_PROCESS_THREAD_MAX); unsigned int i; for (i = 0; i < core_num_threads; i++) { struct core_processing_thread *tmp = malloc(sizeof(struct core_processing_thread)); if (!tmp) { pom_oom(sizeof(struct core_processing_thread)); goto err; } memset(tmp, 0, sizeof(struct core_processing_thread)); tmp->thread_id = i; int res = pthread_mutex_init(&tmp->pkt_queue_lock, NULL); if (res) { pomlog(POMLOG_ERR "Error while initializing a thread pkt_queue lock : %s", pom_strerror(res)); free(tmp); goto err; } res = pthread_cond_init(&tmp->pkt_queue_cond, NULL); if (res) { pomlog(POMLOG_ERR "Error while initializing a thread pkt_queue condition : %s", pom_strerror(res)); pthread_mutex_destroy(&tmp->pkt_queue_lock); free(tmp); goto err; } if (pthread_create(&tmp->thread, NULL, core_processing_thread_func, tmp)) { pomlog(POMLOG_ERR "Error while creating a new processing thread : %s", pom_strerror(errno)); pthread_mutex_destroy(&tmp->pkt_queue_lock); pthread_cond_destroy(&tmp->pkt_queue_cond); free(tmp); goto err; } core_processing_threads[i] = tmp; } return POM_OK; err: if (param) registry_cleanup_param(param); core_cleanup(0); return POM_ERR; }
static int analyzer_rtp_pload_process(void *obj, struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) { struct analyzer *analyzer = obj; struct analyzer_rtp_priv *priv = analyzer->priv; struct proto_process_stack *pload_stack = &stack[stack_index]; struct proto_process_stack *s = &stack[stack_index - 1]; if (!s->ce) return POM_ERR; struct analyzer_rtp_ce_priv *cp = conntrack_get_priv(s->ce, obj); if (!cp) { cp = malloc(sizeof(struct analyzer_rtp_ce_priv)); if (!cp) { pom_oom(sizeof(struct analyzer_rtp_ce_priv)); return POM_ERR; } memset(cp, 0, sizeof(struct analyzer_rtp_ce_priv)); if (conntrack_add_priv(s->ce, obj, cp, analyzer_rtp_ce_cleanup) != POM_OK) return POM_ERR; } int dir = s->direction; if (!cp->evt[dir]) { cp->evt[dir] = event_alloc(priv->evt_rtp_stream); if (!cp->evt[dir]) return POM_ERR; struct data *evt_data = event_get_data(cp->evt[dir]); ptype_copy(evt_data[analyzer_rtp_stream_ssrc].value, s->pkt_info->fields_value[proto_rtp_field_ssrc]); data_set(evt_data[analyzer_rtp_stream_ssrc]); // For now we always assume RTP is over UDP or TCP if (stack_index > 2) { struct proto_process_stack *l4_stack = &stack[stack_index - 2]; unsigned int i; for (i = 0; !data_is_set(evt_data[analyzer_rtp_stream_src_port]) || !data_is_set(evt_data[analyzer_rtp_stream_dst_port]); i++) { struct proto_reg_info *l4_info = proto_get_info(l4_stack->proto); char *name = l4_info->pkt_fields[i].name; if (!name) break; if (!data_is_set(evt_data[analyzer_rtp_stream_src_port]) && !strcmp(name, "sport")) { ptype_copy(evt_data[analyzer_rtp_stream_src_port].value, l4_stack->pkt_info->fields_value[i]); data_set(evt_data[analyzer_rtp_stream_src_port]); } else if (!data_is_set(evt_data[analyzer_rtp_stream_dst_port]) && !strcmp(name, "dport")) { ptype_copy(evt_data[analyzer_rtp_stream_dst_port].value, l4_stack->pkt_info->fields_value[i]); data_set(evt_data[analyzer_rtp_stream_dst_port]); } } } if (stack_index > 3) { struct proto_process_stack *l3_stack = &stack[stack_index - 3]; unsigned int i; for (i = 0; !data_is_set(evt_data[analyzer_rtp_stream_src_addr]) || !data_is_set(evt_data[analyzer_rtp_stream_dst_addr]); i++) { struct proto_reg_info *l3_info = proto_get_info(l3_stack->proto); char *name = l3_info->pkt_fields[i].name; if (!name) break; if (!data_is_set(evt_data[analyzer_rtp_stream_src_addr]) && !strcmp(name, "src")) { evt_data[analyzer_rtp_stream_src_addr].value = ptype_alloc_from(l3_stack->pkt_info->fields_value[i]); if (evt_data[analyzer_rtp_stream_src_addr].value) data_set(evt_data[analyzer_rtp_stream_src_addr]); } else if (!data_is_set(evt_data[analyzer_rtp_stream_dst_addr]) && !strcmp(name, "dst")) { evt_data[analyzer_rtp_stream_dst_addr].value = ptype_alloc_from(l3_stack->pkt_info->fields_value[i]); if (evt_data[analyzer_rtp_stream_dst_addr].value) data_set(evt_data[analyzer_rtp_stream_dst_addr]); } } } struct proto *sess_proto = telephony_stream_info_get_sess_proto(s->ce); if (sess_proto) { struct proto_reg_info *proto_reg = proto_get_info(sess_proto); PTYPE_STRING_SETVAL(evt_data[analyzer_rtp_stream_sess_proto].value, proto_reg->name); data_set(evt_data[analyzer_rtp_stream_sess_proto]); } char *call_id = telephony_stream_info_get_call_id(s->ce); if (call_id) { PTYPE_STRING_SETVAL_P(evt_data[analyzer_rtp_stream_call_id].value, call_id); data_set(evt_data[analyzer_rtp_stream_call_id]); } if (event_process_begin(cp->evt[dir], stack, stack_index, p->ts) != POM_OK) return POM_ERR; } if (!cp->pload[dir]) { cp->pload[dir] = pload_alloc(cp->evt[dir], 0); if (!cp->pload[dir]) return POM_ERR; struct telephony_codec_info info = { 0 }; if (telephony_stream_info_get_codec(&info, stack, stack_index - 1) == POM_OK) { char *pload_type = telephony_codec_info_get_pload_type(&info); if (pload_type) pload_set_type(cp->pload[dir], pload_type); } } if (pload_append(cp->pload[dir], pload_stack->pload, pload_stack->plen) != POM_OK) return POM_ERR; return POM_OK; }
int registry_config_load(char *config_name) { struct dataset_query *dsq_config_list = NULL, *dsq_config = NULL; struct datastore *sys_dstore = system_datastore(); if (!sys_dstore) { pomlog(POMLOG_ERR "No system datastore declared"); return POM_ERR; } // Find what is the id corresponding to the name given if any dsq_config_list = datastore_dataset_query_open(sys_dstore, REGISTRY_CONFIG_LIST, registry_config_list_dataset_template, NULL); if (!dsq_config_list) goto err; if (datastore_dataset_query_set_string_condition(dsq_config_list, 0, PTYPE_OP_EQ, config_name) != POM_OK) goto err; int res = datastore_dataset_read_single(dsq_config_list); if (res < 0) goto err; if (res == DATASET_QUERY_OK) { pomlog(POMLOG_ERR "Configuration \"%s\" not found in the database", config_name); goto err; } uint64_t config_id = dsq_config_list->data_id; datastore_dataset_query_cleanup(dsq_config_list); dsq_config_list = NULL; // Fetch the config dsq_config = datastore_dataset_query_open(sys_dstore, REGISTRY_CONFIG, registry_config_dataset_template, NULL); if (!dsq_config) goto err; if (datastore_dataset_query_set_uint64_condition(dsq_config, 0, PTYPE_OP_EQ, config_id) != POM_OK) goto err; // Fetch the config in a convenient way if (datastore_dataset_query_set_order(dsq_config, 3, DATASET_READ_ORDER_ASC) != POM_OK) goto err; registry_lock(); // Reset the registry if (registry_config_reset() != POM_OK) goto err_locked; while ((res = datastore_dataset_read(dsq_config)) != DATASET_QUERY_OK) { if (res < 0) goto err_locked; if (dsq_config->values[1].is_null || dsq_config->values[3].is_null) { pomlog(POMLOG_ERR "Got NULL values while they were not supposed to be !"); goto err_locked; } enum registry_config_entry_types type = *PTYPE_UINT8_GETVAL(dsq_config->values[3].value); char *entry = PTYPE_STRING_GETVAL(dsq_config->values[1].value); char *value = NULL; if (!dsq_config->values[2].is_null) value = PTYPE_STRING_GETVAL(dsq_config->values[2].value); // Parse the entry char *name1 = strdup(entry); if (!name1) { pom_oom(strlen(entry) + 1); goto err_locked; } char *name2 = strchr(name1, '.'); if (!name2) { pomlog(POMLOG_ERR "Unparseable entry name \"%s\"", entry); free(name1); goto err_locked; } *name2 = 0; name2++; char *name3 = strchr(name2, '.'); if (name3) { *name3 = 0; name3++; } switch (type) { case registry_config_instance: { if (!value) { pomlog(POMLOG_WARN "Instance type not provided for entry %s, skipping", entry); break; } struct registry_class *cls = registry_find_class(name1); if (!cls) { pomlog(POMLOG_WARN "Cannot add instance %s to class %s as this class doesn't exists. skipping", name2, name1); break; } struct registry_instance *inst; for (inst = cls->instances; inst && strcmp(inst->name, name2); inst = inst->next); if (inst) { pomlog(POMLOG_WARN "Cannot add instance %s as it's already in the registry, skipping", entry); break; } if (!cls->instance_add) { pomlog(POMLOG_WARN "Cannot add instances to class %s as it doesn't support it. skipping", name1); break; } if (cls->instance_add(value, name2) != POM_OK) { pomlog(POMLOG_WARN "Unable to add instance %s of type %s, skipping", entry, value); break; } break; } case registry_config_instance_param: { if (!value) { pomlog(POMLOG_WARN "Parameter value not provided for entry %s, skipping", entry); break; } if (!name3) { pomlog(POMLOG_WARN "Parameter name not provided for entry %s, skipping", entry); break; } struct registry_instance *inst = registry_find_instance(name1, name2); if (!inst) { pomlog(POMLOG_WARN "Cannot find instance %s.%s to set parameter, skipping", name1, name2); break; } if (!strcmp(name3, "uid")) { // Special handling for uids if (registry_uid_assign(inst, value) != POM_OK) { pomlog(POMLOG_WARN "Error while setting the uid, skipping"); break; } } else if (!strcmp(name3, "running")) { // For now don't do anything for running } else { registry_set_param(inst, name3, value); } break; } default: pomlog(POMLOG_WARN "Unhandled configuration entry type %u for item %s", entry); } free(name1); } registry_classes_serial_inc(); registry_unlock(); datastore_dataset_query_cleanup(dsq_config); pomlog("Registry configuration \"%s\" loaded", config_name); return POM_OK; err_locked: registry_unlock(); err: if (dsq_config_list) datastore_dataset_query_cleanup(dsq_config_list); if (dsq_config) datastore_dataset_query_cleanup(dsq_config); return POM_ERR; }
static int analyzer_tftp_pkt_process(void *obj, struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) { struct analyzer_tftp_priv *priv = obj; struct proto_process_stack *s = &stack[stack_index]; struct proto_process_stack *s_prev = &stack[stack_index - 1]; uint16_t opcode = *PTYPE_UINT16_GETVAL(s_prev->pkt_info->fields_value[proto_tftp_field_opcode]); // Get the session struct conntrack_session *session = conntrack_session_get(s_prev->ce); if (!session) return POM_ERR; struct analyzer_tftp_session_priv *spriv = conntrack_session_get_priv(session, obj); if (!spriv) { // Add session priv if it is not done yet spriv = malloc(sizeof(struct analyzer_tftp_session_priv)); if (!spriv) { pom_oom(sizeof(struct analyzer_tftp_session_priv)); goto err; } memset(spriv, 0, sizeof(struct analyzer_tftp_session_priv)); if (conntrack_session_add_priv(session, obj, spriv, analyzer_tftp_session_priv_cleanup) != POM_OK) { free(spriv); goto err; } } void *pload = s->pload; uint32_t plen = s->plen; switch (opcode) { case tftp_rrq: case tftp_wrq: { if (plen < 3) return POM_OK; // Invalid packet // Find the filename // The below should always be valid as proto_tftp already checked this char *filename = pload; char *mode = memchr(filename, 0, plen - 1) + 1; struct analyzer_tftp_file *fq = malloc(sizeof(struct analyzer_tftp_file)); if (!fq) { pom_oom(sizeof(struct analyzer_tftp_file)); goto err; } memset(fq, 0, sizeof(struct analyzer_tftp_file)); // Get the port on which we expect this file // No need to check the IP as we got the session biding struct proto_process_stack *s_l4 = &stack[stack_index - 2]; unsigned int i; for (i = 0; !fq->port ; i++) { struct proto_reg_info *pinfo = proto_get_info(s_l4->proto); char *name = pinfo->pkt_fields[i].name; if (!name) { pomlog(POMLOG_ERR "Source port not found in RRQ/WRQ packets"); goto err; } if (!strcmp(name, "sport")) { fq->port = *PTYPE_UINT16_GETVAL(s_l4->pkt_info->fields_value[i]); break; } } fq->evt = event_alloc(priv->evt_file); if (!fq->evt) { free(fq); goto err; } struct data *evt_data = event_get_data(fq->evt); PTYPE_STRING_SETVAL(evt_data[analyzer_tftp_file_filename].value, filename); data_set(evt_data[analyzer_tftp_file_filename]); PTYPE_STRING_SETVAL(evt_data[analyzer_tftp_file_mode].value, mode); data_set(evt_data[analyzer_tftp_file_mode]); PTYPE_BOOL_SETVAL(evt_data[analyzer_tftp_file_write].value, opcode == tftp_wrq); data_set(evt_data[analyzer_tftp_file_write]); fq->next = spriv->files; if (fq->next) fq->next->prev = fq; spriv->files = fq; conntrack_session_unlock(session); event_process_begin(fq->evt, stack, stack_index, p->ts); break; } case tftp_data: { if (plen < sizeof(uint16_t)) return POM_OK; // Invalid packet struct analyzer_tftp_file *f = conntrack_get_priv(s_prev->ce, obj); struct data *evt_data = NULL; if (!f) { // The file is not yet associated to this connection // Find it in the queue struct proto_process_stack *s_l4 = &stack[stack_index - 2]; unsigned int i; uint16_t sport = 0, dport = 0; for (i = 0; !sport || !dport ; i++) { struct proto_reg_info *pinfo = proto_get_info(s_l4->proto); char *name = pinfo->pkt_fields[i].name; if (!name) { pomlog(POMLOG_ERR "Source port not found in data packets"); goto err; } if (!strcmp(name, "sport")) sport = *PTYPE_UINT16_GETVAL(s_l4->pkt_info->fields_value[i]); if (!strcmp(name, "dport")) dport = *PTYPE_UINT16_GETVAL(s_l4->pkt_info->fields_value[i]); } // Find the file in the session list for (f = spriv->files; ; f = f->next) { evt_data = event_get_data(f->evt); if (*PTYPE_BOOL_GETVAL(evt_data[analyzer_tftp_file_write].value)) { if (f->port == sport) break; } else { if (f->port == dport) break; } } if (!f) { pomlog(POMLOG_DEBUG "File not found in queued file request."); conntrack_session_unlock(session); return POM_OK; } // Remove the file from the queue and assign it to the conntrack if (f->prev) f->prev->next = f->next; else spriv->files = f->next; if (f->next) f->next->prev = f->prev; f->prev = NULL; f->next = NULL; // Create the payload buffer f->pload = pload_alloc(f->evt, PLOAD_FLAG_NEED_MAGIC); if (!f->pload) goto err; conntrack_add_priv(s_prev->ce, obj, f, analyzer_tftp_conntrack_priv_cleanup); } else { evt_data = event_get_data(f->evt); } conntrack_session_unlock(session); if (!f->pload) { pomlog(POMLOG_DEBUG "Ignoring extra packet"); return POM_OK; } // Discard the block ID pload += sizeof(uint16_t); plen -= sizeof(uint16_t); if (pload_append(f->pload, pload, plen) != POM_OK) goto err; uint32_t *size = PTYPE_UINT32_GETVAL(evt_data[analyzer_tftp_file_size].value); *size += plen; if (plen < ANALYZER_TFTP_BLK_SIZE) { // Got last packet ! data_set(evt_data[analyzer_tftp_file_size]); int res = pload_end(f->pload); res += event_process_end(f->evt); f->evt = NULL; f->pload = NULL; if (res) goto err; } break; } case tftp_error: { conntrack_session_unlock(session); struct analyzer_tftp_file *f = conntrack_get_priv(s_prev->ce, obj); if (f && f->pload) { int res = pload_end(f->pload); res += event_process_end(f->evt); f->pload = NULL; f->evt = NULL; if (res) goto err; } break; } default: conntrack_session_unlock(session); break; } return POM_OK; err: conntrack_session_unlock(session); return POM_ERR; }
int analyzer_ppp_pap_init(struct analyzer *analyzer) { struct analyzer_ppp_pap_priv *priv = malloc(sizeof(struct analyzer_ppp_pap_priv)); if (!priv) { pom_oom(sizeof(struct analyzer_ppp_pap_priv)); return POM_ERR; } memset(priv, 0, sizeof(struct analyzer_ppp_pap_priv)); analyzer->priv = priv; priv->evt_request = event_find("ppp_pap_request"); priv->evt_ack_nack = event_find("ppp_pap_ack_nack"); if (!priv->evt_request || !priv->evt_ack_nack) goto err; static struct data_item_reg evt_auth_data_items[ANALYZER_PPP_PAP_AUTH_DATA_COUNT] = { { 0 } }; evt_auth_data_items[analyzer_ppp_pap_auth_client].name = "client"; evt_auth_data_items[analyzer_ppp_pap_auth_client].flags = DATA_REG_FLAG_NO_ALLOC; evt_auth_data_items[analyzer_ppp_pap_auth_server].name = "server"; evt_auth_data_items[analyzer_ppp_pap_auth_server].flags = DATA_REG_FLAG_NO_ALLOC; evt_auth_data_items[analyzer_ppp_pap_auth_top_proto].name = "top_proto"; evt_auth_data_items[analyzer_ppp_pap_auth_top_proto].value_type = ptype_get_type("string"); evt_auth_data_items[analyzer_ppp_pap_auth_vlan].name = "vlan"; evt_auth_data_items[analyzer_ppp_pap_auth_vlan].flags = DATA_REG_FLAG_NO_ALLOC; evt_auth_data_items[analyzer_ppp_pap_auth_identifier].name = "identifier"; evt_auth_data_items[analyzer_ppp_pap_auth_identifier].value_type = ptype_get_type("uint8"); evt_auth_data_items[analyzer_ppp_pap_auth_success].name = "success"; evt_auth_data_items[analyzer_ppp_pap_auth_success].value_type = ptype_get_type("bool"); evt_auth_data_items[analyzer_ppp_pap_auth_peer_id].name = "peer_id"; evt_auth_data_items[analyzer_ppp_pap_auth_peer_id].value_type = ptype_get_type("string"); evt_auth_data_items[analyzer_ppp_pap_auth_password].name = "password"; evt_auth_data_items[analyzer_ppp_pap_auth_password].value_type = ptype_get_type("string"); static struct data_reg evt_auth_data = { .items = evt_auth_data_items, .data_count = ANALYZER_PPP_PAP_AUTH_DATA_COUNT }; static struct event_reg_info analyzer_ppp_pap_evt_auth = { 0 }; analyzer_ppp_pap_evt_auth.source_name = "analyzer_ppp_pap"; analyzer_ppp_pap_evt_auth.source_obj = analyzer; analyzer_ppp_pap_evt_auth.name = "ppp_pap_auth"; analyzer_ppp_pap_evt_auth.description = "PPP PAP MD5 authentication"; analyzer_ppp_pap_evt_auth.data_reg = &evt_auth_data; analyzer_ppp_pap_evt_auth.listeners_notify = analyzer_ppp_pap_event_listeners_notify; priv->evt_auth = event_register(&analyzer_ppp_pap_evt_auth); if (!priv->evt_auth) goto err; return POM_OK; err: analyzer_ppp_pap_cleanup(analyzer); return POM_ERR; }
static int analyzer_docsis_pkt_process(void *obj, struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) { struct analyzer *analyzer = obj; struct analyzer_docsis_priv *priv = analyzer->priv; struct proto_process_stack *s = &stack[stack_index]; uint8_t *type = PTYPE_UINT8_GETVAL(s->pkt_info->fields_value[proto_docsis_mgmt_field_type]); char *mac_dst = PTYPE_MAC_GETADDR(s->pkt_info->fields_value[proto_docsis_mgmt_field_dst]); // FIXME : improve this filtering at the source // Filter some useless messages we don't care about if (*type == MMT_UCD2 || *type == MMT_UCD3 || *type == MMT_MDD) return POM_OK; if (*type != MMT_RNG_RSP) { pomlog(POMLOG_DEBUG "Unhandled DOCSIS MGMT message type %u for destination mac %02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX", *type, mac_dst[0], mac_dst[1], mac_dst[2], mac_dst[3], mac_dst[4], mac_dst[5]); return POM_OK; } // Use the last bits for the modem ID uint16_t id = ntohs(*(uint16_t*) (mac_dst + 4)) & ANALYZER_DOCSIS_CM_MASK; pom_mutex_lock(&priv->lock); struct analyzer_docsis_cm *cm; for (cm = priv->cms[id]; cm; cm = cm->next) { if (!memcmp(cm->mac, mac_dst, sizeof(cm->mac))) break; } if (!cm) { // Cable modem not found ! cm = malloc(sizeof(struct analyzer_docsis_cm)); if (!cm) { pom_mutex_unlock(&priv->lock); pom_oom(sizeof(struct analyzer_docsis_cm)); return POM_ERR; } memset(cm, 0, sizeof(struct analyzer_docsis_cm)); cm->t = timer_alloc(cm, analyzer_docsis_cm_timeout); if (!cm->t) { pom_mutex_unlock(&priv->lock); free(cm); return POM_ERR; } cm->analyzer = analyzer; memcpy(cm->mac, mac_dst, sizeof(cm->mac)); cm->t4_multiplier = 1; cm->next = priv->cms[id]; if (cm->next) cm->next->prev = cm; priv->cms[id] = cm; // Announce the new CM if (event_has_listener(priv->evt_cm_new)) { struct event *evt = event_alloc(priv->evt_cm_new); if (!evt) { pom_mutex_unlock(&priv->lock); return POM_ERR; } struct data *evt_data = event_get_data(evt); PTYPE_MAC_SETADDR(evt_data[analyzer_docsis_cm_new_mac].value, cm->mac); data_set(evt_data[analyzer_docsis_cm_new_mac]); PTYPE_STRING_SETVAL(evt_data[analyzer_docsis_cm_new_input].value, p->input->name); data_set(evt_data[analyzer_docsis_cm_new_input]); if (event_process(evt, stack, stack_index, p->ts) != POM_OK) { pom_mutex_unlock(&priv->lock); return POM_ERR; } } } switch (*type) { case MMT_RNG_RSP: analyzer_docsis_pkt_parse_rng_rsp(priv, cm, p, stack, stack_index); break; // FIXME If ranging_status is 0 and we receive another msg, probably it's actually registered // and we need to call analyzer_docsis_reg_status_update(); } timer_queue_now(cm->t, T4_TIMEOUT * cm->t4_multiplier, p->ts); pom_mutex_unlock(&priv->lock); return POM_OK; }
static int analyzer_docsis_init(struct analyzer *analyzer) { struct analyzer_docsis_priv *priv = malloc(sizeof(struct analyzer_docsis_priv)); if (!priv) { pom_oom(sizeof(struct analyzer_docsis_priv)); return POM_ERR; } memset(priv, 0, sizeof(struct analyzer_docsis_priv)); analyzer->priv = priv; if (pthread_mutex_init(&priv->lock, NULL)) { pomlog(POMLOG_ERR "Error while initializing table lock : %s", pom_strerror(errno)); free(priv); return POM_ERR; } static struct data_item_reg evt_cm_new_data_items[ANALYZER_DOCSIS_EVT_CM_NEW_DATA_COUNT] = { { 0 } }; evt_cm_new_data_items[analyzer_docsis_cm_new_mac].name = "mac", evt_cm_new_data_items[analyzer_docsis_cm_new_mac].value_type = ptype_get_type("mac"); evt_cm_new_data_items[analyzer_docsis_cm_new_input].name = "input", evt_cm_new_data_items[analyzer_docsis_cm_new_input].value_type = ptype_get_type("string"); static struct data_reg evt_cm_new_data = { .items = evt_cm_new_data_items, .data_count = ANALYZER_DOCSIS_EVT_CM_NEW_DATA_COUNT }; static struct event_reg_info analyzer_docsis_evt_cm_new = { 0 }; analyzer_docsis_evt_cm_new.source_name = "analyzer_docsis"; analyzer_docsis_evt_cm_new.source_obj = analyzer; analyzer_docsis_evt_cm_new.name = "docsis_cm_new"; analyzer_docsis_evt_cm_new.description = "New cable modem found"; analyzer_docsis_evt_cm_new.data_reg = &evt_cm_new_data; analyzer_docsis_evt_cm_new.listeners_notify = analyzer_docsis_event_listeners_notify; priv->evt_cm_new = event_register(&analyzer_docsis_evt_cm_new); if (!priv->evt_cm_new) goto err; static struct data_item_reg evt_cm_reg_status_data_items[ANALYZER_DOCSIS_EVT_CM_REG_STATUS_DATA_COUNT] = { { 0 } }; evt_cm_reg_status_data_items[analyzer_docsis_cm_reg_status_old].name = "old_status", evt_cm_reg_status_data_items[analyzer_docsis_cm_reg_status_old].value_type = ptype_get_type("uint8"); evt_cm_reg_status_data_items[analyzer_docsis_cm_reg_status_new].name = "new_status", evt_cm_reg_status_data_items[analyzer_docsis_cm_reg_status_new].value_type = ptype_get_type("uint8"); evt_cm_reg_status_data_items[analyzer_docsis_cm_reg_status_mac].name = "mac"; evt_cm_reg_status_data_items[analyzer_docsis_cm_reg_status_mac].value_type = ptype_get_type("mac"); evt_cm_reg_status_data_items[analyzer_docsis_cm_reg_status_timeout].name = "timeout", evt_cm_reg_status_data_items[analyzer_docsis_cm_reg_status_timeout].value_type = ptype_get_type("uint8"); static struct data_reg evt_cm_reg_status_data = { .items = evt_cm_reg_status_data_items, .data_count = ANALYZER_DOCSIS_EVT_CM_REG_STATUS_DATA_COUNT }; static struct event_reg_info analyzer_docsis_evt_cm_reg_status = { 0 }; analyzer_docsis_evt_cm_reg_status.source_name = "analyzer_docsis"; analyzer_docsis_evt_cm_reg_status.source_obj = analyzer; analyzer_docsis_evt_cm_reg_status.name = "docsis_cm_reg_status"; analyzer_docsis_evt_cm_reg_status.description = "Cable modem registration status changed"; analyzer_docsis_evt_cm_reg_status.data_reg = &evt_cm_reg_status_data; analyzer_docsis_evt_cm_reg_status.listeners_notify = analyzer_docsis_event_listeners_notify; priv->evt_cm_reg_status = event_register(&analyzer_docsis_evt_cm_reg_status); if (!priv->evt_cm_reg_status) goto err; return POM_OK; err: analyzer_docsis_cleanup(analyzer); return POM_ERR; }
static int proto_ipv4_process(void *proto_priv, struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) { struct proto_process_stack *s = &stack[stack_index]; struct proto_process_stack *s_next = &stack[stack_index + 1]; struct ip* hdr = s->pload; unsigned int hdr_len = hdr->ip_hl * 4; if (s->plen < sizeof(struct ip) || // length smaller than header hdr->ip_hl < 5 || // ip header < 5 bytes ntohs(hdr->ip_len) < hdr_len || // datagram size < ip header length ntohs(hdr->ip_len) > s->plen) { // datagram size > given size return PROTO_INVALID; } PTYPE_IPV4_SETADDR(s->pkt_info->fields_value[proto_ipv4_field_src], hdr->ip_src); PTYPE_IPV4_SETADDR(s->pkt_info->fields_value[proto_ipv4_field_dst], hdr->ip_dst); PTYPE_UINT8_SETVAL(s->pkt_info->fields_value[proto_ipv4_field_tos], hdr->ip_tos); PTYPE_UINT8_SETVAL(s->pkt_info->fields_value[proto_ipv4_field_ttl], hdr->ip_ttl); // Handle conntrack stuff if (conntrack_get(stack, stack_index) != POM_OK) return PROTO_ERR; s_next->pload = s->pload + hdr_len; s_next->plen = ntohs(hdr->ip_len) - hdr_len; s_next->proto = proto_get_by_number(s->proto, hdr->ip_p); int res = POM_ERR; if (s->ce->children) { res = conntrack_delayed_cleanup(s->ce, 0, p->ts); } else { uint32_t *conntrack_timeout = PTYPE_UINT32_GETVAL(param_conntrack_timeout); res = conntrack_delayed_cleanup(s->ce, *conntrack_timeout, p->ts); } if (res == POM_ERR) { conntrack_unlock(s->ce); return PROTO_ERR; } uint16_t frag_off = ntohs(hdr->ip_off); // Check if packet is fragmented and need more handling if (frag_off & IP_DONT_FRAG) { conntrack_unlock(s->ce); return PROTO_OK; // Nothing to do } if (!(frag_off & IP_MORE_FRAG) && !(frag_off & IP_OFFSET_MASK)) { conntrack_unlock(s->ce); return PROTO_OK; // Nothing to do, full packet } uint16_t offset = (frag_off & IP_OFFSET_MASK) << 3; size_t frag_size = ntohs(hdr->ip_len) - (hdr->ip_hl * 4); // Ignore invalid fragments if (frag_size > 0xFFFF) { conntrack_unlock(s->ce); return PROTO_INVALID; } if (frag_size > s->plen + hdr_len) { conntrack_unlock(s->ce); return PROTO_INVALID; } // Account for one more fragment registry_perf_inc(perf_frags, 1); struct proto_ipv4_fragment *tmp = s->ce->priv; // Let's find the right buffer for (; tmp && tmp->id != hdr->ip_id; tmp = tmp->next); if (!tmp) { // Buffer not found, create it tmp = malloc(sizeof(struct proto_ipv4_fragment)); if (!tmp) { pom_oom(sizeof(struct proto_ipv4_fragment)); conntrack_unlock(s->ce); return PROTO_ERR; } memset(tmp, 0, sizeof(struct proto_ipv4_fragment)); tmp->t = conntrack_timer_alloc(s->ce, proto_ipv4_fragment_cleanup, tmp); if (!tmp->t) { conntrack_unlock(s->ce); free(tmp); return PROTO_ERR; } tmp->id = hdr->ip_id; if (!s_next->proto) { // Set processed flag so no attempt to process this will be done tmp->flags |= PROTO_IPV4_FLAG_PROCESSED; conntrack_unlock(s->ce); conntrack_timer_cleanup(tmp->t); free(tmp); return PROTO_STOP; } tmp->multipart = packet_multipart_alloc(s_next->proto, 0); if (!tmp->multipart) { conntrack_unlock(s->ce); conntrack_timer_cleanup(tmp->t); free(tmp); return PROTO_ERR; } tmp->next = s->ce->priv; if (tmp->next) tmp->next->prev = tmp; s->ce->priv = tmp; } // Fragment was already handled if (tmp->flags & PROTO_IPV4_FLAG_PROCESSED) { conntrack_unlock(s->ce); registry_perf_inc(perf_frags_dropped, 1); return PROTO_STOP; } // Add the fragment if (packet_multipart_add_packet(tmp->multipart, p, offset, frag_size, (s->pload - (void*)p->buff) + (hdr->ip_hl * 4)) != POM_OK) { conntrack_unlock(s->ce); packet_multipart_cleanup(tmp->multipart); conntrack_timer_cleanup(tmp->t); free(tmp); return PROTO_ERR; } tmp->count++; // Schedule the timeout for the fragment uint32_t *frag_timeout = PTYPE_UINT32_GETVAL(param_frag_timeout); conntrack_timer_queue(tmp->t, *frag_timeout, p->ts); if (!(frag_off & IP_MORE_FRAG)) tmp->flags |= PROTO_IPV4_FLAG_GOT_LAST; if ((tmp->flags & PROTO_IPV4_FLAG_GOT_LAST) && !tmp->multipart->gaps) tmp->flags |= PROTO_IPV4_FLAG_PROCESSED; conntrack_unlock(s->ce); if ((tmp->flags & PROTO_IPV4_FLAG_PROCESSED)) { int res = packet_multipart_process(tmp->multipart, stack, stack_index + 1); tmp->multipart = NULL; // Multipart will be cleared automatically if (res == PROTO_ERR) { return PROTO_ERR; } else if (res == PROTO_INVALID) { registry_perf_inc(perf_frags_dropped, tmp->count); } else { registry_perf_inc(perf_reassembled_pkts, 1); } } return PROTO_STOP; // Stop processing the packet }
static int input_pcap_dir_browse(struct input_pcap_priv *priv) { // Open the directory char *path = PTYPE_STRING_GETVAL(priv->tpriv.dir.p_dir); DIR *dir = opendir(path); if (!dir) { pomlog(POMLOG_ERR "Error while opening directory %s : %s", path, pom_strerror(errno)); return POM_ERR; } // Compile the regex used to match files char *match = PTYPE_STRING_GETVAL(priv->tpriv.dir.p_match); regex_t preg; int errcode = regcomp(&preg, match, REG_NOSUB); if (errcode) { char errbuf[256] = { 0 }; regerror(errcode, &preg, errbuf, sizeof(errbuf) - 1); pomlog(POMLOG_ERR "Error while compiling regex \"%s\" : %s", match, errbuf); return POM_ERR; } // Browse the given directory struct dirent *buf, *de; size_t len = offsetof(struct dirent, d_name) + pathconf(path, _PC_NAME_MAX) + 1; buf = malloc(len); int tot_files = 0; while (!priv->tpriv.dir.interrupt_scan) { int res = readdir_r(dir, buf, &de); if (res) { pomlog(POMLOG_ERR "Error while reading directory entry : %s", pom_strerror(errno)); regfree(&preg); free(buf); closedir(dir); return POM_ERR; } if (!de) break; // Match our file against regex if (regexec(&preg, buf->d_name, 1, NULL, 0)) { pomlog(POMLOG_DEBUG "Discarding file %s, regular expression not matched", buf->d_name); continue; } // Check if we already know about that file struct input_pcap_dir_file *tmp = priv->tpriv.dir.files; int found = 0; while (tmp) { if (!strcmp(tmp->filename, buf->d_name)) { found = 1; break; } tmp = tmp->next; } if (found) continue; // We don't know about this file, parse it char errbuf[PCAP_ERRBUF_SIZE + 1]; // Alloc the new file struct input_pcap_dir_file *cur = malloc(sizeof(struct input_pcap_dir_file)); if (!cur) { free(cur->full_path); regfree(&preg); pom_oom(sizeof(struct input_pcap_dir_file)); return POM_ERR; } memset(cur, 0, sizeof(struct input_pcap_dir_file)); cur->full_path = malloc(strlen(path) + strlen(buf->d_name) + 2); if (!cur->full_path) { regfree(&preg); pom_oom(strlen(path) + strlen(buf->d_name) + 2); return POM_ERR; } strcpy(cur->full_path, path); if (*cur->full_path && cur->full_path[strlen(cur->full_path) - 1] != '/') strcat(cur->full_path, "/"); cur->filename = cur->full_path + strlen(cur->full_path); strcat(cur->full_path, buf->d_name); // Get the time of the first packet pcap_t *p = pcap_open_offline(cur->full_path, errbuf); if (!p) { cur->next = priv->tpriv.dir.files; priv->tpriv.dir.files = cur; // Add at the begning in order not to process it again pomlog(POMLOG_WARN "Unable to open file %s : %s", cur->full_path, errbuf); continue; } if (input_pcap_set_filter(priv->p, PTYPE_STRING_GETVAL(priv->p_filter)) != POM_OK) { cur->next = priv->tpriv.dir.files; priv->tpriv.dir.files = cur; // Add at the begning in order not to process it again pomlog(POMLOG_WARN "Could not set filter on file %s", cur->full_path); continue; } const u_char *next_pkt; struct pcap_pkthdr *phdr; int result = pcap_next_ex(p, &phdr, &next_pkt); if (result <= 0) { cur->next = priv->tpriv.dir.files; priv->tpriv.dir.files = cur; // Add at the begning in order not to process it again pomlog(POMLOG_WARN "Could not read first packet from file %s", cur->full_path); free(cur->full_path); pcap_close(p); continue; } cur->first_pkt = pom_timeval_to_ptime(phdr->ts); pcap_close(p); // Add the packet at the right position tmp = priv->tpriv.dir.files; if (!tmp || (tmp && (cur->first_pkt < tmp->first_pkt))) { // Add at the begining cur->next = priv->tpriv.dir.files; priv->tpriv.dir.files = cur; } else { while (tmp->next) { if (cur->first_pkt < tmp->next->first_pkt) { // Add in the middle cur->next = tmp->next; tmp->next = cur; break; } tmp = tmp->next; } if (!tmp->next) { // Add at the end tmp->next = cur; } } pomlog(POMLOG_DEBUG "Added file %s to the list", cur->full_path); tot_files++; } regfree(&preg); free(buf); closedir(dir); if (priv->tpriv.dir.interrupt_scan) return 0; return tot_files; }
static int analyzer_multipart_pload_write(void *obj, void *p, void *data, size_t len) { struct analyzer_multipart_pload_priv *priv = p; if (priv->state == analyzer_multipart_pload_state_end) return POM_OK; if (priv->state == analyzer_multipart_pload_state_error) return POM_ERR; unsigned int line_len, remaining_len = len; while (remaining_len > 0) { // Because of the NOTE in RFC 2046 section 5.1.1, line start at CR void *cr = memchr(data + 1, '\r', remaining_len - 1); if (!cr || priv->last_line) { size_t add_len = remaining_len; if (cr) add_len = cr - data; size_t new_len = add_len + 1; if (priv->last_line) new_len += strlen(priv->last_line); if (priv->last_line_len < new_len) { char *new_last_line = realloc(priv->last_line, new_len); if (!new_last_line) { pom_oom(new_len); goto err; } if (!priv->last_line_len) new_last_line[0] = 0; priv->last_line_len = new_len; priv->last_line = new_last_line; } strncat(priv->last_line, data, add_len); priv->last_line[new_len - 1] = 0; if (!cr) break; // Process this line and continue to the next if (analyzer_multipart_pload_process_line(priv, priv->last_line, strlen(priv->last_line)) != POM_OK) goto err; // We need to process this part of the payload if (priv->state == analyzer_multipart_pload_state_content && priv->pload_start) { if (pload_append(priv->pload, priv->pload_start, priv->pload_end - priv->pload_start) != POM_OK) goto err; } priv->pload_start = NULL; priv->pload_end = NULL; free(priv->last_line); priv->last_line = NULL; priv->last_line_len = 0; data = cr; remaining_len -= add_len; continue; } line_len = cr - data; if (analyzer_multipart_pload_process_line(priv, data, line_len) != POM_OK) goto err; remaining_len -= line_len; data = cr; } if (priv->state == analyzer_multipart_pload_state_content && priv->pload_start) { if (pload_append(priv->pload, priv->pload_start, priv->pload_end - priv->pload_start) != POM_OK) goto err; } priv->pload_start = NULL; priv->pload_end = NULL; return POM_OK; err: if (priv->pload) { pload_end(priv->pload); priv->pload = NULL; } priv->state = analyzer_multipart_pload_state_error; return POM_ERR; }
static int analyzer_arp_init(struct analyzer *analyzer) { struct analyzer_arp_priv *priv = malloc(sizeof(struct analyzer_arp_priv)); if (!priv) { pom_oom(sizeof(struct analyzer_arp_priv)); return POM_ERR; } memset(priv, 0, sizeof(struct analyzer_arp_priv)); analyzer->priv = priv; if (pthread_mutex_init(&priv->lock, NULL)) { pomlog(POMLOG_ERR "Error while initializing table lock : %s", pom_strerror(errno)); free(priv); return POM_ERR; } priv->proto_vlan = proto_get("vlan"); if (!priv->proto_vlan) goto err; static struct data_item_reg evt_new_sta_data_items[ANALYZER_ARP_EVT_NEW_STA_DATA_COUNT] = { { 0 } }; evt_new_sta_data_items[analyzer_arp_new_sta_mac_addr].name = "mac_addr"; evt_new_sta_data_items[analyzer_arp_new_sta_mac_addr].value_type = ptype_get_type("mac"); evt_new_sta_data_items[analyzer_arp_new_sta_ip_addr].name = "ip_addr"; evt_new_sta_data_items[analyzer_arp_new_sta_ip_addr].value_type = ptype_get_type("ipv4"); evt_new_sta_data_items[analyzer_arp_new_sta_vlan].name = "vlan"; evt_new_sta_data_items[analyzer_arp_new_sta_vlan].value_type = ptype_get_type("uint16"); evt_new_sta_data_items[analyzer_arp_new_sta_input].name = "input"; evt_new_sta_data_items[analyzer_arp_new_sta_input].value_type = ptype_get_type("string"); static struct data_reg evt_new_sta_data = { .items = evt_new_sta_data_items, .data_count = ANALYZER_ARP_EVT_NEW_STA_DATA_COUNT }; static struct event_reg_info analyzer_arp_evt_new_sta = { 0 }; analyzer_arp_evt_new_sta.source_name = "analyzer_arp"; analyzer_arp_evt_new_sta.source_obj = analyzer; analyzer_arp_evt_new_sta.name = "arp_new_sta"; analyzer_arp_evt_new_sta.description = "New station found"; analyzer_arp_evt_new_sta.data_reg = &evt_new_sta_data; analyzer_arp_evt_new_sta.listeners_notify = analyzer_arp_event_listeners_notify; priv->evt_new_sta = event_register(&analyzer_arp_evt_new_sta); if (!priv->evt_new_sta) goto err; static struct data_item_reg evt_sta_changed_data_items[ANALYZER_ARP_EVT_STA_CHANGED_DATA_COUNT] = { { 0 } }; evt_sta_changed_data_items[analyzer_arp_sta_changed_old_mac_addr].name = "old_mac_addr"; evt_sta_changed_data_items[analyzer_arp_sta_changed_old_mac_addr].value_type = ptype_get_type("mac"); evt_sta_changed_data_items[analyzer_arp_sta_changed_new_mac_addr].name = "new_mac_addr"; evt_sta_changed_data_items[analyzer_arp_sta_changed_new_mac_addr].value_type = ptype_get_type("mac"); evt_sta_changed_data_items[analyzer_arp_sta_changed_ip_addr].name = "ip_addr"; evt_sta_changed_data_items[analyzer_arp_sta_changed_ip_addr].value_type = ptype_get_type("ipv4"); evt_sta_changed_data_items[analyzer_arp_sta_changed_vlan].name = "vlan"; evt_sta_changed_data_items[analyzer_arp_sta_changed_vlan].value_type = ptype_get_type("uint16"); evt_sta_changed_data_items[analyzer_arp_sta_changed_input].name = "input"; evt_sta_changed_data_items[analyzer_arp_sta_changed_input].value_type = ptype_get_type("string"); static struct data_reg evt_sta_changed_data = { .items = evt_sta_changed_data_items, .data_count = ANALYZER_ARP_EVT_STA_CHANGED_DATA_COUNT }; static struct event_reg_info analyzer_arp_evt_sta_changed = { 0 }; analyzer_arp_evt_sta_changed.source_name = "analyzer_arp"; analyzer_arp_evt_sta_changed.source_obj = analyzer; analyzer_arp_evt_sta_changed.name = "arp_sta_changed"; analyzer_arp_evt_sta_changed.description = "Station MAC address changed"; analyzer_arp_evt_sta_changed.data_reg = &evt_sta_changed_data; analyzer_arp_evt_sta_changed.listeners_notify = analyzer_arp_event_listeners_notify; priv->evt_sta_changed = event_register(&analyzer_arp_evt_sta_changed); if (!priv->evt_sta_changed) goto err; return POM_OK; err: analyzer_arp_cleanup(analyzer); return POM_ERR; }
static int analyzer_arp_pkt_process(void *obj, struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) { struct analyzer *analyzer = obj; struct analyzer_arp_priv *priv = analyzer->priv; struct proto_process_stack *s = &stack[stack_index]; struct proto_process_stack *s_prev = &stack[stack_index - 1]; struct in_addr arp_ip = PTYPE_IPV4_GETADDR(s->pkt_info->fields_value[proto_arp_field_sender_proto_addr]); // Discard bogon 0.0.0.0 if (!arp_ip.s_addr) return POM_OK; // Find that IP in the table uint32_t id = arp_ip.s_addr & ANALYZER_ARP_HOST_MASK; char *arp_mac = PTYPE_MAC_GETADDR(s->pkt_info->fields_value[proto_arp_field_sender_hw_addr]); uint16_t vlan = 0; if (s_prev->proto == priv->proto_vlan) vlan = *PTYPE_UINT16_GETVAL(s_prev->pkt_info->fields_value[proto_vlan_field_vid]); pom_mutex_lock(&priv->lock); struct analyzer_arp_host *host; for (host = priv->hosts[id]; host; host = host->next) { if (host->ip.s_addr == arp_ip.s_addr && host->vlan == vlan) break; } if (!host) { // Host not found ! host = malloc(sizeof(struct analyzer_arp_host)); if (!host) { pom_mutex_unlock(&priv->lock); pom_oom(sizeof(struct analyzer_arp_host)); return POM_ERR; } memset(host, 0, sizeof(struct analyzer_arp_host)); host->ip.s_addr = arp_ip.s_addr; memcpy(host->mac, arp_mac, sizeof(host->mac)); host->vlan = vlan; host->next = priv->hosts[id]; if (host->next) host->next->prev = host; priv->hosts[id] = host; pom_mutex_unlock(&priv->lock); // Announce the new station if (event_has_listener(priv->evt_new_sta)) { struct event *evt = event_alloc(priv->evt_new_sta); if (!evt) return POM_ERR; struct data *evt_data = evt->data; ptype_copy(evt_data[analyzer_arp_new_sta_mac_addr].value, s->pkt_info->fields_value[proto_arp_field_sender_hw_addr]); data_set(evt_data[analyzer_arp_new_sta_mac_addr]); ptype_copy(evt_data[analyzer_arp_new_sta_ip_addr].value, s->pkt_info->fields_value[proto_arp_field_sender_proto_addr]); data_set(evt_data[analyzer_arp_new_sta_ip_addr]); PTYPE_UINT16_SETVAL(evt_data[analyzer_arp_new_sta_vlan].value, vlan); data_set(evt_data[analyzer_arp_new_sta_vlan]); PTYPE_STRING_SETVAL(evt_data[analyzer_arp_new_sta_input].value, p->input->name); data_set(evt_data[analyzer_arp_new_sta_input]); if (event_process(evt, stack, stack_index) != POM_OK) return POM_ERR; } // Nothing else to do return POM_OK; } // Host was found, check mac if (memcmp(host->mac, arp_mac, sizeof(host->mac))) { if (event_has_listener(priv->evt_sta_changed)) { struct event *evt = event_alloc(priv->evt_sta_changed); if (!evt) { pom_mutex_unlock(&priv->lock); return POM_ERR; } struct data *evt_data = evt->data; PTYPE_MAC_SETADDR(evt_data[analyzer_arp_sta_changed_old_mac_addr].value, host->mac); data_set(evt_data[analyzer_arp_sta_changed_old_mac_addr]); ptype_copy(evt_data[analyzer_arp_sta_changed_new_mac_addr].value, s->pkt_info->fields_value[proto_arp_field_sender_hw_addr]); data_set(evt_data[analyzer_arp_sta_changed_new_mac_addr]); ptype_copy(evt_data[analyzer_arp_sta_changed_ip_addr].value, s->pkt_info->fields_value[proto_arp_field_sender_proto_addr]); data_set(evt_data[analyzer_arp_sta_changed_ip_addr]); PTYPE_UINT16_SETVAL(evt_data[analyzer_arp_sta_changed_vlan].value, vlan); data_set(evt_data[analyzer_arp_sta_changed_vlan]); PTYPE_STRING_SETVAL(evt_data[analyzer_arp_sta_changed_input].value, p->input->name); data_set(evt_data[analyzer_arp_sta_changed_input]); if (event_process(evt, stack, stack_index) != POM_OK) { pom_mutex_unlock(&priv->lock); return POM_ERR; } } memcpy(host->mac, arp_mac, sizeof(host->mac)); } pom_mutex_unlock(&priv->lock); return POM_OK; }
int output_log_xml_open(void *output_priv) { struct output_log_xml_priv *priv = output_priv; if (addon_log_xml_open(priv) != POM_OK) return POM_ERR; if (!strlen(PTYPE_STRING_GETVAL(priv->p_source))) { pomlog(POMLOG_ERR "You need to specify a source for this output"); goto err; } char *src = strdup(PTYPE_STRING_GETVAL(priv->p_source)); if (!src) { pom_oom(strlen(PTYPE_STRING_GETVAL(priv->p_source))); goto err; } char *token, *saveptr, *str = src; for (; ; str = NULL) { token = strtok_r(str, ", ", &saveptr); if (!token) break; struct event_reg *evt = event_find(token); if (!evt) { pomlog(POMLOG_WARN "Event \"%s\" does not exists", token); continue; } struct output_log_xml_evt *evt_lst = malloc(sizeof(struct output_log_xml_evt)); if (!evt_lst) { pom_oom(sizeof(struct output_log_xml_evt)); free(src); goto err; } memset(evt_lst, 0, sizeof(struct output_log_xml_evt)); evt_lst->evt = evt; // Start listening to the event if (event_listener_register(evt, priv, NULL, output_log_xml_process) != POM_OK) { free(evt_lst); free(src); goto err; } evt_lst->next = priv->evt_lst; priv->evt_lst = evt_lst; } free(src); if (!priv->evt_lst) goto err; return POM_OK; err: output_log_xml_close(priv); return POM_ERR; }
int packet_stream_force_dequeue(struct packet_stream *stream) { struct packet_stream_pkt *p = NULL; unsigned int next_dir = 0; while (1) { if (!stream->head[POM_DIR_FWD] && !stream->head[POM_DIR_REV]) return POM_OK; if (!stream->head[POM_DIR_FWD]) { next_dir = POM_DIR_REV; } else if (!stream->head[POM_DIR_REV]) { next_dir = POM_DIR_FWD; } else { // We have packets in both direction, lets see which one we'll process first int i; for (i = 0; i < POM_DIR_TOT; i++) { int r = POM_DIR_REVERSE(i); struct packet_stream_pkt *a = stream->head[i], *b = stream->head[r]; uint32_t end_seq = a->seq + a->plen; if ((end_seq <= b->ack && b->ack - end_seq < PACKET_HALF_SEQ) || (b->ack > end_seq && end_seq - b->ack > PACKET_HALF_SEQ)) break; } if (i == POM_DIR_TOT) { // There is a gap in both direction // Process the first packet received struct packet *a = stream->head[POM_DIR_FWD]->pkt, *b = stream->head[POM_DIR_REV]->pkt; if (a->ts.tv_sec < b->ts.tv_sec || (a->ts.tv_sec == b->ts.tv_sec && a->ts.tv_usec < b->ts.tv_usec)) { next_dir = POM_DIR_FWD; } else { next_dir = POM_DIR_REV; } debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : processing next by timestamp", pthread_self(), stream, stream->head[next_dir]->pkt->ts.tv_sec, stream->head[next_dir]->pkt->ts.tv_usec, stream->head[next_dir]->seq, stream->head[next_dir]->ack); } else { next_dir = i; } } p = stream->head[next_dir]; if (p->next) p->next->prev = NULL; else stream->tail[next_dir] = NULL; stream->head[next_dir] = p->next; stream->cur_buff_size -= p->plen; if (packet_stream_is_packet_old_dupe(stream, p, next_dir)) { packet_stream_free_packet(p); } else { break; } } if (packet_stream_remove_dupe_bytes(stream, p, next_dir) == POM_ERR) return POM_ERR; uint32_t gap = p->seq - stream->cur_seq[next_dir]; int res = PROTO_OK; if (gap) { if (gap < stream->max_buff_size) { debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : filling gap of %u", pthread_self(), stream, p->pkt->ts.tv_sec, p->pkt->ts.tv_usec, p->seq, p->ack, gap); uint32_t gap_step = gap; if (gap_step > 2048) gap_step = 2048; void *zero = malloc(gap_step); if (!zero) { pom_oom(gap_step); return POM_ERR; } memset(zero, 0, gap_step); struct proto_process_stack *s = &p->stack[p->stack_index]; uint32_t plen_old = s->plen; void *pload_old = s->pload; uint32_t pos; for (pos = 0; pos < gap; pos += gap_step) { if (pos + gap_step < gap) s->plen = gap_step; else s->plen = gap - pos; s->pload = zero; res = stream->handler(stream->ce, p->pkt, p->stack, p->stack_index); s->direction = next_dir; if (res == PROTO_ERR) break; } free(zero); s->pload = pload_old; s->plen = plen_old; } else { debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : gap of %u too big. not filling", pthread_self(), stream, p->pkt->ts.tv_sec, p->pkt->ts.tv_usec, p->seq, p->ack, gap); } } if (res != PROTO_ERR) { debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : process forced", pthread_self(), stream, p->pkt->ts.tv_sec, p->pkt->ts.tv_usec, p->seq, p->ack); res = stream->handler(stream->ce, p->pkt, p->stack, p->stack_index); } stream->cur_seq[next_dir] = p->seq + p->plen; stream->cur_ack[next_dir] = p->ack; packet_stream_free_packet(p); if (res == PROTO_ERR) return POM_ERR; // See if we can process additional packets // Check if additional packets can be processed while ((p = packet_stream_get_next(stream, &next_dir))) { debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : process additional", pthread_self(), stream, p->pkt->ts.tv_sec, p->pkt->ts.tv_usec, p->seq, p->ack); if (stream->handler(stream->ce, p->pkt, p->stack, p->stack_index) == PROTO_ERR) return POM_ERR; stream->cur_seq[next_dir] += p->plen; stream->cur_ack[next_dir] = p->ack; packet_stream_free_packet(p); } return POM_OK; }
int timer_queue(struct timer *t, unsigned int expiry) { pom_mutex_lock(&timer_main_lock); // Timer is still queued, dequeue it if (t->queue) { if (t->prev) { t->prev->next = t->next; } else { t->queue->head = t->next; if (t->queue->head) t->queue->head->prev = NULL; } if (t->next) { t->next->prev = t->prev; } else { t->queue->tail = t->prev; if (t->queue->tail) t->queue->tail->next = NULL; } t->queue = NULL; t->prev = NULL; t->next = NULL; } struct timer_queue *tq = timer_queues; // First find the right queue or create it if (!tq) { // There is no queue yet tq = malloc(sizeof(struct timer_queue)); if (!tq) { pom_mutex_unlock(&timer_main_lock); pom_oom(sizeof(struct timer_queue)); return POM_ERR; } memset(tq, 0, sizeof(struct timer_queue)); timer_queues = tq; tq->expiry = expiry; } else { while (tq) { if (tq->expiry == expiry) { // The right queue already exists break; } else if (tq->expiry > expiry) { // The right queue doesn't exists and we are too far in the list struct timer_queue *tmp; tmp = malloc(sizeof(struct timer_queue)); if (!tmp) { pom_oom(sizeof(struct timer_queue)); pom_mutex_unlock(&timer_main_lock); return POM_ERR; } memset(tmp, 0, sizeof(struct timer_queue)); tmp->prev = tq->prev; tmp->next = tq; tq->prev = tmp; if (tmp->prev) tmp->prev->next = tmp; else timer_queues = tmp; tq = tmp; tq->expiry = expiry; break; } else if (!tq->next) { // Looks like we are at the end of our list struct timer_queue *tmp; tmp = malloc(sizeof(struct timer_queue)); if (!tmp) { pom_oom(sizeof(struct timer_queue)); pom_mutex_unlock(&timer_main_lock); return POM_ERR; } memset(tmp, 0, sizeof(struct timer_queue)); tmp->prev = tq; tq->next = tmp; tq = tmp; tq->expiry = expiry; break; } tq = tq->next; } } // Now we can queue the timer if (tq->head == NULL) { tq->head = t; tq->tail = t; } else { t->prev = tq->tail; tq->tail = t; t->prev->next = t; } // Update the expiry time core_get_clock(&t->expires); t->expires.tv_sec += expiry; t->queue = tq; pom_mutex_unlock(&timer_main_lock); return POM_OK; }
int packet_stream_parser_get_line(struct packet_stream_parser *sp, char **line, unsigned int *len) { if (!line || !len) return POM_ERR; // Find the next line return in the current payload char *pload = sp->pload; int str_len = sp->plen, tmp_len = 0; char *lf = memchr(pload, '\n', sp->plen); if (!lf) { if (sp->buff) { memmove(sp->buff, sp->pload, sp->plen); sp->buff_pos = sp->plen; } else { sp->buff = malloc(sp->plen); if (!sp->buff) { pom_oom(sp->plen); return POM_ERR; } memcpy(sp->buff, sp->pload, sp->plen); sp->buff_len = sp->plen; sp->buff_pos = sp->plen; } // \n not found *line = NULL; *len = 0; debug_stream_parser("entry %p, no line found", sp); return POM_OK; } tmp_len = lf - pload; str_len = tmp_len + 1; if (lf > pload && *(lf - 1) == '\r') tmp_len--; sp->plen -= str_len; if (!sp->plen) sp->pload = NULL; else sp->pload += str_len; // Trim the string while (*pload == ' ' && tmp_len) { pload++; tmp_len--; } while (pload[tmp_len] == ' ' && tmp_len) tmp_len--; *line = pload; *len = tmp_len; debug_stream_parser("entry %p, got line of %u bytes", sp, tmp_len); return POM_OK; }
int analyzer_ppp_pap_event_process_begin(struct event *evt, void *obj, struct proto_process_stack *stack, unsigned int stack_index) { struct analyzer *analyzer = obj; struct analyzer_ppp_pap_priv *apriv = analyzer->priv; struct proto_process_stack *s = &stack[stack_index]; if (!s->ce) return PROTO_ERR; conntrack_lock(s->ce); struct ptype *src = NULL, *dst = NULL; struct analyzer_ppp_pap_ce_priv *cpriv = conntrack_get_priv(s->ce, analyzer); if (!cpriv) { cpriv = malloc(sizeof(struct analyzer_ppp_pap_ce_priv)); if (!cpriv) { pom_oom(sizeof(struct analyzer_ppp_pap_ce_priv)); goto err; } memset(cpriv, 0, sizeof(struct analyzer_ppp_pap_ce_priv)); if (conntrack_add_priv(s->ce, analyzer, cpriv, analyzer_ppp_pap_ce_priv_cleanup) != POM_OK) { free(cpriv); goto err; } // Try to find the source and destination unsigned int i = 0; for (i = 1; i <= 4; i++) { struct proto_process_stack *prev_stack = &stack[stack_index - i]; if (!prev_stack->proto) break; struct proto_reg_info *info = proto_get_info(prev_stack->proto); if (!strcmp(info->name, "vlan")) { cpriv->vlan = ptype_alloc_from(prev_stack->pkt_info->fields_value[proto_vlan_field_vid]); if (!cpriv->vlan) return POM_ERR; } unsigned int j; for (j = 0; !src || !dst; j++) { struct proto_reg_info *prev_info = proto_get_info(prev_stack->proto); if (!prev_info->pkt_fields) break; char *name = prev_info->pkt_fields[j].name; if (!name) break; if (!src && !strcmp(name, "src")) src = prev_stack->pkt_info->fields_value[j]; else if (!dst && !strcmp(name, "dst")) dst = prev_stack->pkt_info->fields_value[j]; } if (src || dst) break; } struct proto_process_stack *prev_stack = &stack[stack_index - 2]; if (prev_stack->proto) { struct proto_reg_info *info = proto_get_info(prev_stack->proto); cpriv->top_proto = info->name; } } struct event_reg *evt_reg = event_get_reg(evt); int dir = POM_DIR_UNK; if (evt_reg == apriv->evt_request) { if (!cpriv->evt_request) { event_refcount_inc(evt); cpriv->evt_request = evt; } dir = POM_DIR_FWD; } else { if (!cpriv->evt_ack_nack) { event_refcount_inc(evt); cpriv->evt_ack_nack = evt; } dir = POM_DIR_REV; } if (src && dst && dir != POM_DIR_UNK) { if (dir == POM_DIR_FWD) { cpriv->client = ptype_alloc_from(src); cpriv->server = ptype_alloc_from(dst); } else { cpriv->client = ptype_alloc_from(dst); cpriv->server = ptype_alloc_from(src); } } int res = POM_OK; if (cpriv->evt_request && cpriv->evt_ack_nack) res = analyzer_ppp_pap_finalize(apriv, cpriv); conntrack_unlock(s->ce); return res; err: conntrack_unlock(s->ce); return POM_ERR; }
int packet_multipart_add_packet(struct packet_multipart *multipart, struct packet *pkt, size_t offset, size_t len, size_t pkt_buff_offset) { struct packet_multipart_pkt *tmp = multipart->tail; // Check where to add the packet while (tmp) { if (tmp->offset + tmp->len <= offset) break; // Packet is after is one if (tmp->offset == offset) { if (tmp->len != len) pomlog(POMLOG_WARN "Size missmatch for packet already in the buffer"); return POM_OK; } tmp = tmp->prev; } struct packet_multipart_pkt *res = malloc(sizeof(struct packet_multipart_pkt)); if (!res) { pom_oom(sizeof(struct packet_multipart_pkt)); return POM_ERR; } memset(res, 0, sizeof(struct packet_multipart_pkt)); res->offset = offset; res->pkt_buff_offset = pkt_buff_offset; res->len = len; // Copy the packet res->pkt = packet_clone(pkt, multipart->flags); if (!res->pkt) { free(res); return POM_ERR; } multipart->cur += len; if (tmp) { // Packet is after this one, add it res->prev = tmp; res->next = tmp->next; tmp->next = res; if (res->next) { res->next->prev = res; if ((res->next->offset == res->offset + res->len) && (res->prev->offset + res->prev->len == res->offset)) // A gap was filled multipart->gaps--; else if ((res->next->offset > res->offset + res->len) && (res->prev->offset + res->prev->len < res->offset)) // A gap was created multipart->gaps++; } else { multipart->tail = res; } return POM_OK; } else { // Add it at the head res->next = multipart->head; if (res->next) res->next->prev = res; else multipart->tail = res; multipart->head = res; if (res->offset) { // There is a gap at the begining multipart->gaps++; } else if (res->next && res->len == res->next->offset) { // Gap filled multipart->gaps--; } } return POM_OK; }
static int proto_eap_init(struct proto *proto, struct registry_instance *i) { if (proto_number_register("8021x", 0x0, proto) != POM_OK || proto_number_register("ppp", 0xc227, proto) != POM_OK) return POM_ERR; struct proto_eap_priv *priv = malloc(sizeof(struct proto_eap_priv)); if (!priv) { pom_oom(sizeof(struct proto_eap_priv)); return POM_ERR; } memset(priv, 0, sizeof(struct proto_eap_priv)); proto_set_priv(proto, priv); priv->p_timeout = ptype_alloc_unit("uint32", "seconds"); if (!priv->p_timeout) goto err; struct registry_param *p = registry_new_param("timeout", "60", priv->p_timeout, "Transaction timeout", 0); if (registry_instance_add_param(i, p) != POM_OK) { registry_cleanup_param(p); goto err; } static struct data_item_reg evt_identity_data_items[PROTO_EAP_EVT_IDENTITY_DATA_COUNT] = { { 0 } }; evt_identity_data_items[evt_eap_common_identifier].name = "identifier"; evt_identity_data_items[evt_eap_common_identifier].value_type = ptype_get_type("uint8"); evt_identity_data_items[evt_eap_common_code].name = "code"; evt_identity_data_items[evt_eap_common_code].value_type = ptype_get_type("uint8"); evt_identity_data_items[evt_eap_identity_identity].name = "identity"; evt_identity_data_items[evt_eap_identity_identity].value_type = ptype_get_type("string"); static struct data_reg evt_eap_identity_data = { .items = evt_identity_data_items, .data_count = PROTO_EAP_EVT_IDENTITY_DATA_COUNT }; static struct event_reg_info proto_eap_identity = { 0 }; proto_eap_identity.source_name = "proto_eap"; proto_eap_identity.source_obj = priv; proto_eap_identity.name = "eap_identity"; proto_eap_identity.description = "EAP Identity"; proto_eap_identity.data_reg = &evt_eap_identity_data; priv->evt_identity = event_register(&proto_eap_identity); if (!priv->evt_identity) goto err; static struct data_item_reg evt_md5_challenge_data_items[PROTO_EAP_EVT_MD5_CHALLENGE_DATA_COUNT] = { { 0 } }; evt_md5_challenge_data_items[evt_eap_common_identifier].name = "identifier"; evt_md5_challenge_data_items[evt_eap_common_identifier].value_type = ptype_get_type("uint8"); evt_md5_challenge_data_items[evt_eap_common_code].name = "code"; evt_md5_challenge_data_items[evt_eap_common_code].value_type = ptype_get_type("uint8"); evt_md5_challenge_data_items[evt_eap_md5_challenge_value].name = "value"; evt_md5_challenge_data_items[evt_eap_md5_challenge_value].value_type = ptype_get_type("bytes"); evt_md5_challenge_data_items[evt_eap_md5_challenge_name].name = "name"; evt_md5_challenge_data_items[evt_eap_md5_challenge_name].value_type = ptype_get_type("string"); static struct data_reg evt_eap_md5_challenge_data = { .items = evt_md5_challenge_data_items, .data_count = PROTO_EAP_EVT_MD5_CHALLENGE_DATA_COUNT }; static struct event_reg_info proto_eap_md5_challenge = { 0 }; proto_eap_md5_challenge.source_name = "proto_eap"; proto_eap_md5_challenge.source_obj = priv; proto_eap_md5_challenge.name = "eap_md5_challenge"; proto_eap_md5_challenge.description = "EAP MD5-Challenge"; proto_eap_md5_challenge.data_reg = &evt_eap_md5_challenge_data; priv->evt_md5_challenge = event_register(&proto_eap_md5_challenge); if (!priv->evt_md5_challenge) goto err; static struct data_item_reg evt_success_failure_data_items[PROTO_EAP_EVT_SUCCESS_FAILURE_DATA_COUNT] = { { 0 } }; evt_success_failure_data_items[evt_eap_common_identifier].name = "identifier"; evt_success_failure_data_items[evt_eap_common_identifier].value_type = ptype_get_type("uint8"); evt_success_failure_data_items[evt_eap_success_failure_success].name = "success"; evt_success_failure_data_items[evt_eap_success_failure_success].value_type = ptype_get_type("bool"); static struct data_reg evt_eap_success_failure_data = { .items = evt_success_failure_data_items, .data_count = PROTO_EAP_EVT_SUCCESS_FAILURE_DATA_COUNT }; static struct event_reg_info proto_eap_success_failure = { 0 }; proto_eap_success_failure.source_name = "proto_eap"; proto_eap_success_failure.source_obj = priv; proto_eap_success_failure.name = "eap_success_failure"; proto_eap_success_failure.description = "EAP Success/Failure"; proto_eap_success_failure.data_reg = &evt_eap_success_failure_data; priv->evt_success_failure = event_register(&proto_eap_success_failure); if (!priv->evt_success_failure) goto err; return POM_OK; err: proto_eap_cleanup(priv); return POM_ERR; }
int packet_buffer_pool_get(struct packet *pkt, size_t size, size_t align_offset) { if (align_offset >= PACKET_BUFFER_ALIGNMENT) { pomlog(POMLOG_ERR "Alignment offset too big"); return POM_ERR; } size_t tot_size = size + align_offset + PACKET_BUFFER_ALIGNMENT; if (tot_size > packet_buffer_pool_size[PACKET_BUFFER_POOL_COUNT - 1]) { pomlog(POMLOG_ERR "Requested size too big : %llu", size); return POM_ERR; } int i; for (i = 0; i < PACKET_BUFFER_POOL_COUNT && packet_buffer_pool_size[i] < tot_size; i++); struct packet_buffer *pb = NULL; pom_mutex_lock(&packet_buffer_pool_mutex); if (!packet_buffer_pool[i].unused) { // Allocate a new one size_t alloc_size = packet_buffer_pool_size[i] + sizeof(struct packet_buffer); pb = malloc(alloc_size); if (!pb) { pom_mutex_unlock(&packet_buffer_pool_mutex); pom_oom(alloc_size); return POM_ERR; } memset(pb, 0, alloc_size); pb->base_buff = (void*)pb + sizeof(struct packet_buffer); pb->aligned_buff = (void*) (((long)pb->base_buff & ~(PACKET_BUFFER_ALIGNMENT - 1)) + PACKET_BUFFER_ALIGNMENT + align_offset); pb->pool_id = i; } else { // Reuse an unused one pb = packet_buffer_pool[i].unused; if (pb->next) pb->next->prev = NULL; packet_buffer_pool[i].unused = pb->next; } // Put this one in the used list pb->next = packet_buffer_pool[i].used; if (pb->next) pb->next->prev = pb; packet_buffer_pool[i].used = pb; pom_mutex_unlock(&packet_buffer_pool_mutex); pkt->pkt_buff = pb; pkt->len = size; pkt->buff = pb->aligned_buff; return POM_OK; }
static int analyzer_rtp_init(struct analyzer *analyzer) { struct analyzer_rtp_priv *priv = malloc(sizeof(struct analyzer_rtp_priv)); if (!priv) { pom_oom(sizeof(struct analyzer_rtp_priv)); return POM_ERR; } memset(priv, 0, sizeof(struct analyzer_rtp_priv)); analyzer->priv = priv; priv->proto_rtp = proto_get("rtp"); if (!priv->proto_rtp) goto err; static struct data_item_reg evt_rtp_stream_data_items[ANALYZER_RTP_STREAM_DATA_COUNT] = { { 0 } }; evt_rtp_stream_data_items[analyzer_rtp_stream_src_addr].name = "src_addr"; evt_rtp_stream_data_items[analyzer_rtp_stream_src_addr].flags = DATA_REG_FLAG_NO_ALLOC; evt_rtp_stream_data_items[analyzer_rtp_stream_dst_addr].name = "dst_addr"; evt_rtp_stream_data_items[analyzer_rtp_stream_dst_addr].flags = DATA_REG_FLAG_NO_ALLOC; evt_rtp_stream_data_items[analyzer_rtp_stream_src_port].name = "src_port"; evt_rtp_stream_data_items[analyzer_rtp_stream_src_port].value_type = ptype_get_type("uint16"); evt_rtp_stream_data_items[analyzer_rtp_stream_dst_port].name = "dst_port"; evt_rtp_stream_data_items[analyzer_rtp_stream_dst_port].value_type = ptype_get_type("uint16"); evt_rtp_stream_data_items[analyzer_rtp_stream_sess_proto].name = "sess_proto"; evt_rtp_stream_data_items[analyzer_rtp_stream_sess_proto].value_type = ptype_get_type("string"); evt_rtp_stream_data_items[analyzer_rtp_stream_call_id].name = "call_id"; evt_rtp_stream_data_items[analyzer_rtp_stream_call_id].value_type = ptype_get_type("string"); evt_rtp_stream_data_items[analyzer_rtp_stream_ssrc].name = "ssrc"; evt_rtp_stream_data_items[analyzer_rtp_stream_ssrc].value_type = ptype_get_type("uint32"); static struct data_reg evt_rtp_stream_data = { .items = evt_rtp_stream_data_items, .data_count = ANALYZER_RTP_STREAM_DATA_COUNT }; static struct event_reg_info analyzer_rtp_evt_stream = { 0 }; analyzer_rtp_evt_stream.source_name = "analyzer_rtp"; analyzer_rtp_evt_stream.source_obj = analyzer; analyzer_rtp_evt_stream.name = "rtp_stream"; analyzer_rtp_evt_stream.description = "RTP stream in a single direction"; analyzer_rtp_evt_stream.data_reg = &evt_rtp_stream_data; analyzer_rtp_evt_stream.flags = EVENT_REG_FLAG_PAYLOAD; analyzer_rtp_evt_stream.listeners_notify = analyzer_rtp_event_listeners_notify; analyzer_rtp_evt_stream.cleanup = analyzer_rtp_stream_event_cleanup; priv->evt_rtp_stream = event_register(&analyzer_rtp_evt_stream); if (!priv->evt_rtp_stream) goto err; return POM_OK; err: analyzer_rtp_cleanup(analyzer); return POM_ERR; }
int packet_stream_process_packet(struct packet_stream *stream, struct packet *pkt, struct proto_process_stack *stack, unsigned int stack_index, uint32_t seq, uint32_t ack) { if (!stream || !pkt || !stack) return PROTO_ERR; debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : start", pthread_self(), stream, pkt->ts.tv_sec, pkt->ts.tv_usec, seq, ack); struct proto_process_stack *cur_stack = &stack[stack_index]; int direction = cur_stack->direction; int must_wait = 0; pom_mutex_lock(&stream->wait_lock); int res = pthread_mutex_trylock(&stream->lock); if (res == EBUSY) { // Already locked, let's wait a bit must_wait = 1; } else if (res) { pomlog(POMLOG_ERR "Error while locking packet stream lock : %s", pom_strerror(errno)); abort(); return POM_ERR; } else { // We got the processing lock. But was it really this thread's turn ? struct packet_stream_thread_wait *tmp = stream->wait_list_head; // A thread with a packet preceding ours is waiting if (tmp && (tmp->ts.tv_sec < pkt->ts.tv_sec || (tmp->ts.tv_sec == pkt->ts.tv_sec && tmp->ts.tv_usec < pkt->ts.tv_usec))) { // No it wasn't, release it and signal the right thread must_wait = 2; pom_mutex_unlock(&stream->lock); debug_stream("thread %p, entry %p : signaling thread %p", pthread_self(), stream, stream->wait_list_head->thread); pthread_cond_broadcast(&stream->wait_list_head->cond); } else { // Yes it was. YAY ! pom_mutex_unlock(&stream->wait_lock); } } if (must_wait) { // Add ourself in the waiting list struct packet_stream_thread_wait *lst = NULL; if (stream->wait_list_unused) { lst = stream->wait_list_unused; stream->wait_list_unused = lst->next; lst->next = NULL; } else { lst = malloc(sizeof(struct packet_stream_thread_wait)); if (!lst) { pom_oom(sizeof(struct packet_stream_thread_wait)); pom_mutex_unlock(&stream->wait_lock); return POM_ERR; } memset(lst, 0, sizeof(struct packet_stream_thread_wait)); if (pthread_cond_init(&lst->cond, NULL)) { pomlog(POMLOG_ERR "Error while initializing wait list condition : %s", pom_strerror(errno)); free(lst); return POM_ERR; } } memcpy(&lst->ts, &pkt->ts, sizeof(struct timeval)); lst->thread = pthread_self(); struct packet_stream_thread_wait *tmp; for (tmp = stream->wait_list_head; tmp && (tmp->ts.tv_sec < lst->ts.tv_sec || (tmp->ts.tv_sec == lst->ts.tv_sec && tmp->ts.tv_usec < lst->ts.tv_usec)); tmp = tmp->next); if (tmp) { lst->prev = tmp->prev; if (lst->prev) lst->prev->next = lst; else stream->wait_list_head = lst; lst->next = tmp; lst->next->prev = lst; } else { lst->prev = stream->wait_list_tail; if (lst->prev) lst->prev->next = lst; else stream->wait_list_head = lst; stream->wait_list_tail = lst; } while (1) { debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : waiting", pthread_self(), stream, pkt->ts.tv_sec, pkt->ts.tv_usec, seq, ack); if (pthread_cond_wait(&lst->cond, &stream->wait_lock)) { pomlog(POMLOG_ERR "Error while waiting for the packet stream wait cond : %s", pom_strerror(errno)); abort(); return POM_ERR; } if (stream->wait_list_head != lst) { // There is a small chance that another stream lock stream->wait_lock while pthread_cond_wait acquires it // If we are not the right thread, then simply signal the right one and wait again for our turn debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : wrong thread woke up", pthread_self(), stream, pkt->ts.tv_sec, pkt->ts.tv_usec, seq, ack); pthread_cond_broadcast(&stream->wait_list_head->cond); continue; } break; } tmp = stream->wait_list_head; stream->wait_list_head = tmp->next; if (stream->wait_list_head) stream->wait_list_head->prev = NULL; else stream->wait_list_tail = NULL; tmp->next = stream->wait_list_unused; tmp->prev = NULL; stream->wait_list_unused = tmp; pom_mutex_unlock(&stream->wait_lock); pom_mutex_lock(&stream->lock); } debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : start locked", pthread_self(), stream, pkt->ts.tv_sec, pkt->ts.tv_usec, seq, ack); // Update the stream flags if (stream->flags & PACKET_FLAG_STREAM_BIDIR) { // Update flags if (direction == POM_DIR_FWD && !(stream->flags & PACKET_FLAG_STREAM_GOT_FWD_DIR)) { stream->flags |= PACKET_FLAG_STREAM_GOT_FWD_DIR; } else if (direction == POM_DIR_REV && !(stream->flags & PACKET_FLAG_STREAM_GOT_REV_DIR)) { stream->flags |= PACKET_FLAG_STREAM_GOT_REV_DIR; } } // Put this packet in our struct packet_stream_pkt struct packet_stream_pkt spkt = {0}; spkt.pkt = pkt; spkt.seq = seq; spkt.ack = ack; spkt.plen = cur_stack->plen; spkt.stack = stack; spkt.stack_index = stack_index; // Check if the packet is worth processing uint32_t cur_seq = stream->cur_seq[direction]; if (cur_seq != seq) { if (packet_stream_is_packet_old_dupe(stream, &spkt, direction)) { // cur_seq is after the end of the packet, discard it packet_stream_end_process_packet(stream); debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : discard", pthread_self(), stream, pkt->ts.tv_sec, pkt->ts.tv_usec, seq, ack); return PROTO_OK; } if (packet_stream_remove_dupe_bytes(stream, &spkt, direction) == POM_ERR) { packet_stream_end_process_packet(stream); return PROTO_ERR; } } // Ok let's process it then // Check if it is the packet we're waiting for if (packet_stream_is_packet_next(stream, &spkt, direction)) { // Process it stream->cur_seq[direction] += cur_stack->plen; stream->cur_ack[direction] = ack; debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : process", pthread_self(), stream, pkt->ts.tv_sec, pkt->ts.tv_usec, seq, ack); int res = stream->handler(stream->ce, pkt, stack, stack_index); if (res == PROTO_ERR) { packet_stream_end_process_packet(stream); return PROTO_ERR; } // Check if additional packets can be processed struct packet_stream_pkt *p = NULL; unsigned int cur_dir = direction, additional_processed = 0; while ((p = packet_stream_get_next(stream, &cur_dir))) { debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : process additional", pthread_self(), stream, p->pkt->ts.tv_sec, p->pkt->ts.tv_usec, p->seq, p->ack); if (stream->handler(stream->ce, p->pkt, p->stack, p->stack_index) == POM_ERR) { packet_stream_end_process_packet(stream); return PROTO_ERR; } stream->cur_seq[cur_dir] += p->plen; stream->cur_ack[cur_dir] = p->ack; packet_stream_free_packet(p); additional_processed = 1; } if (additional_processed) { if (!stream->head[POM_DIR_FWD] && !stream->head[POM_DIR_REV]) conntrack_timer_dequeue(stream->t); else conntrack_timer_queue(stream->t, stream->same_dir_timeout); } packet_stream_end_process_packet(stream); debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : done processed", pthread_self(), stream, pkt->ts.tv_sec, pkt->ts.tv_usec, seq, ack); return res; } // Queue the packet then debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : queue", pthread_self(), stream, pkt->ts.tv_sec, pkt->ts.tv_usec, seq, ack); struct packet_stream_pkt *p = malloc(sizeof(struct packet_stream_pkt)); if (!p) { pom_oom(sizeof(struct packet_stream_pkt)); packet_stream_end_process_packet(stream); return PROTO_ERR; } memset(p, 0 , sizeof(struct packet_stream_pkt)); if (cur_stack->plen) { // No need to backup this if there is no payload p->pkt = packet_clone(pkt, stream->flags); if (!p->pkt) { packet_stream_end_process_packet(stream); free(p); return PROTO_ERR; } p->stack = core_stack_backup(stack, pkt, p->pkt); if (!p->stack) { packet_stream_end_process_packet(stream); packet_pool_release(p->pkt); free(p); return PROTO_ERR; } } p->plen = cur_stack->plen; p->seq = seq; p->ack = ack; p->stack_index = stack_index; if (!stream->tail[direction]) { stream->head[direction] = p; stream->tail[direction] = p; } else { struct packet_stream_pkt *tmp = stream->tail[direction]; while ( tmp && ((tmp->seq >= seq && tmp->seq - seq < PACKET_HALF_SEQ) || (tmp->seq <= seq && seq - tmp->seq > PACKET_HALF_SEQ))) { tmp = tmp->prev; } if (!tmp) { // Packet goes at the begining of the list p->next = stream->head[direction]; if (p->next) p->next->prev = p; else stream->tail[direction] = p; stream->head[direction] = p; } else { // Insert the packet after the current one p->next = tmp->next; p->prev = tmp; if (p->next) p->next->prev = p; else stream->tail[direction] = p; tmp->next = p; } } stream->cur_buff_size += cur_stack->plen; if (stream->cur_buff_size >= stream->max_buff_size) { // Buffer overflow debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : buffer overflow, forced dequeue", pthread_self(), stream, pkt->ts.tv_sec, pkt->ts.tv_usec, seq, ack); if (packet_stream_force_dequeue(stream) != POM_OK) { packet_stream_end_process_packet(stream); return POM_ERR; } if (stream->t) conntrack_timer_dequeue(stream->t); } // Add timeout if (stream->t && (stream->head[POM_DIR_FWD] || stream->head[POM_DIR_REV])) conntrack_timer_queue(stream->t, stream->same_dir_timeout); packet_stream_end_process_packet(stream); debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : done queued", pthread_self(), stream, pkt->ts.tv_sec, pkt->ts.tv_usec, seq, ack); return PROTO_OK; }
struct registry_config_entry* registry_config_list() { struct dataset_query *dsq_config_list = NULL; struct registry_config_entry *list = NULL; ssize_t list_size = 0; struct datastore *sys_dstore = system_datastore(); if (!sys_dstore) return NULL; struct datastore_connection *dc = datastore_connection_new(sys_dstore); if (!dc) return NULL; dsq_config_list = datastore_dataset_query_open(sys_dstore, REGISTRY_CONFIG_LIST, registry_config_list_dataset_template, dc); if (!dsq_config_list) goto err; int res; while ((res = datastore_dataset_read(dsq_config_list)) != DATASET_QUERY_OK) { if (res < 0) goto err; if (dsq_config_list->values[0].is_null || dsq_config_list->values[1].is_null) { pomlog(POMLOG_ERR "Got NULL values while they were not supposed to be !"); goto err; } struct registry_config_entry *new_list = realloc(list, sizeof(struct registry_config_entry) * (list_size + 2)); if (!new_list) { pom_oom(sizeof(struct registry_config_entry) * (list_size + 2)); goto err; } list = new_list; memset(&list[list_size], 0, sizeof(struct registry_config_entry) * 2); char *name = PTYPE_STRING_GETVAL(dsq_config_list->values[0].value); strncpy(list[list_size].name, name, REGISTRY_CONFIG_NAME_MAX - 1); list[list_size].ts = *PTYPE_TIMESTAMP_GETVAL(dsq_config_list->values[1].value); list_size++; } datastore_dataset_query_cleanup(dsq_config_list); datastore_connection_release(dc); return list; err: if (dsq_config_list) datastore_dataset_query_cleanup(dsq_config_list); if (dc) datastore_connection_release(dc); if (list) free(list); return NULL; }
static int proto_smtp_process(void *proto_priv, struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) { struct proto_process_stack *s = &stack[stack_index]; struct proto_process_stack *s_next = &stack[stack_index + 1]; if (conntrack_get_unique_from_parent(stack, stack_index) != POM_OK) { pomlog(POMLOG_ERR "Could not get conntrack entry"); return PROTO_ERR; } // There should no need to keep the lock here since we are in the packet_stream lock from proto_tcp conntrack_unlock(s->ce); struct proto_smtp_priv *ppriv = proto_priv; struct proto_smtp_conntrack_priv *priv = s->ce->priv; if (!priv) { priv = malloc(sizeof(struct proto_smtp_conntrack_priv)); if (!priv) { pom_oom(sizeof(struct proto_smtp_conntrack_priv)); return PROTO_ERR; } memset(priv, 0, sizeof(struct proto_smtp_conntrack_priv)); priv->parser[POM_DIR_FWD] = packet_stream_parser_alloc(SMTP_MAX_LINE, PACKET_STREAM_PARSER_FLAG_TRIM); if (!priv->parser[POM_DIR_FWD]) { free(priv); return PROTO_ERR; } priv->parser[POM_DIR_REV] = packet_stream_parser_alloc(SMTP_MAX_LINE, PACKET_STREAM_PARSER_FLAG_TRIM); if (!priv->parser[POM_DIR_REV]) { packet_stream_parser_cleanup(priv->parser[POM_DIR_FWD]); free(priv); return PROTO_ERR; } priv->server_direction = POM_DIR_UNK; s->ce->priv = priv; } if (priv->flags & PROTO_SMTP_FLAG_INVALID) return PROTO_OK; struct packet_stream_parser *parser = priv->parser[s->direction]; if (packet_stream_parser_add_payload(parser, s->pload, s->plen) != POM_OK) return PROTO_ERR; char *line = NULL; size_t len = 0; while (1) { // Some check to do prior to parse the payload if (s->direction == POM_DIR_REVERSE(priv->server_direction)) { if (priv->flags & PROTO_SMTP_FLAG_STARTTLS) { // Last command was a STARTTLS command, this is the TLS negociation // Since we can't parse this, mark it as invalid priv->flags |= PROTO_SMTP_FLAG_INVALID; return PROTO_OK; } else if (priv->flags & PROTO_SMTP_FLAG_CLIENT_DATA) { // We are receiving payload data, check where the end is void *pload; size_t plen; packet_stream_parser_get_remaining(parser, &pload, &plen); if (!plen) return PROTO_OK; // Look for the "<CR><LF>.<CR><LF>" sequence if (priv->data_end_pos > 0) { // The previous packet ended with something that might be the final sequence // Check if we have the rest int i, found = 1; for (i = 0; i < PROTO_SMTP_DATA_END_LEN - priv->data_end_pos && i <= plen; i++) { if (*(char*)(pload + i) != PROTO_SMTP_DATA_END[priv->data_end_pos + i]) { found = 0; break; } } if (found) { // If we have already processed the dot after <CR><LF> there is no way to remove it // Thus we mark this connection as invalid. Most MTA will send at worst the last // 3 bytes of the end sequence in a sequence packet if (i != plen || (priv->data_end_pos >= 2 && plen < 3)) { pomlog(POMLOG_DEBUG "The final line was not at the of a packet as expected !"); priv->flags |= PROTO_SMTP_FLAG_INVALID; event_process_end(priv->data_evt); priv->data_evt = NULL; return PROTO_OK; } s_next->pload = pload; s_next->plen = plen - PROTO_SMTP_DATA_END_LEN + 2; // The last line return is part of the payload priv->flags |= PROTO_SMTP_FLAG_CLIENT_DATA_END; priv->flags &= ~PROTO_SMTP_FLAG_CLIENT_DATA; priv->data_end_pos = 0; return PROTO_OK; } priv->data_end_pos = 0; } char *dotline = pom_strnstr(pload, PROTO_SMTP_DATA_END, plen); if (dotline) { if (pload + plen - PROTO_SMTP_DATA_END_LEN != dotline) { pomlog(POMLOG_DEBUG "The final line was not at the of a packet as expected !"); priv->flags |= PROTO_SMTP_FLAG_INVALID; event_process_end(priv->data_evt); priv->data_evt = NULL; return PROTO_OK; } s_next->pload = pload; s_next->plen = plen - PROTO_SMTP_DATA_END_LEN + 2; // The last line return is part of the payload priv->flags |= PROTO_SMTP_FLAG_CLIENT_DATA_END; priv->flags &= ~PROTO_SMTP_FLAG_CLIENT_DATA; } else { // Check if the end of the payload contains part of the "<CR><LF>.<CR><LF>" sequence int i, found = 0; for (i = 1 ; (i < PROTO_SMTP_DATA_END_LEN) && (i <= plen); i++) { if (!memcmp(pload + plen - i, PROTO_SMTP_DATA_END, i)) { found = 1; break; } } if (found) priv->data_end_pos = i; s_next->pload = pload; s_next->plen = plen; } return PROTO_OK; } } // Process commands if (packet_stream_parser_get_line(parser, &line, &len) != POM_OK) return PROTO_ERR; if (!line) return PROTO_OK; if (!len) // Probably a missed packet return PROTO_OK; // Try to find the server direction if (priv->server_direction == POM_DIR_UNK) { unsigned int code = atoi(line); if (code > 0) { priv->server_direction = s->direction; } else { priv->server_direction = POM_DIR_REVERSE(s->direction); } } if (s->direction == priv->server_direction) { // Parse the response code and generate the event if ((len < 5) || // Server response is 3 digit error code, a space or hyphen and then at least one letter of text (line[3] != ' ' && line[3] != '-')) { pomlog(POMLOG_DEBUG "Too short or invalid response from server"); priv->flags |= PROTO_SMTP_FLAG_INVALID; return POM_OK; } int code = atoi(line); if (code == 0) { pomlog(POMLOG_DEBUG "Invalid response from server"); priv->flags |= PROTO_SMTP_FLAG_INVALID; return POM_OK; } if (event_has_listener(ppriv->evt_reply)) { struct data *evt_data = NULL; if (priv->reply_evt) { evt_data = event_get_data(priv->reply_evt); uint16_t cur_code = *PTYPE_UINT16_GETVAL(evt_data[proto_smtp_reply_code].value); if (cur_code != code) { pomlog(POMLOG_WARN "Multiline code not the same as previous line : %hu -> %hu", cur_code, code); event_process_end(priv->reply_evt); priv->reply_evt = NULL; } } if (!priv->reply_evt) { priv->reply_evt = event_alloc(ppriv->evt_reply); if (!priv->reply_evt) return PROTO_ERR; evt_data = event_get_data(priv->reply_evt); PTYPE_UINT16_SETVAL(evt_data[proto_smtp_reply_code].value, code); data_set(evt_data[proto_smtp_reply_code]); } if (len > 4) { struct ptype *txt = ptype_alloc("string"); if (!txt) return PROTO_ERR; PTYPE_STRING_SETVAL_N(txt, line + 4, len - 4); if (data_item_add_ptype(evt_data, proto_smtp_reply_text, strdup("text"), txt) != POM_OK) return PROTO_ERR; } if (!event_is_started(priv->reply_evt)) event_process_begin(priv->reply_evt, stack, stack_index, p->ts); } if (line[3] != '-') { // Last line in the response if (priv->reply_evt) { event_process_end(priv->reply_evt); priv->reply_evt = NULL; } } if (priv->flags & PROTO_SMTP_FLAG_STARTTLS) { // The last command was STARTTLS priv->flags &= ~PROTO_SMTP_FLAG_STARTTLS; if (code == 220) { // TLS has the go, we can't parse from now so mark as invalid priv->flags |= PROTO_SMTP_FLAG_INVALID; return POM_OK; } } } else { // Client command if (len < 4) { // Client commands are at least 4 bytes long pomlog(POMLOG_DEBUG "Too short or invalid query from client"); priv->flags |= PROTO_SMTP_FLAG_INVALID; return POM_OK; } // Make sure it's a command by checking it's at least a four letter word int i; for (i = 0; i < 4; i++) { // In some case it can also be a base64 encoded word if (! ((line[i] >= 'A' && line[i] <= 'Z') || (line[i] >= 'a' && line[i] <= 'z') || (line[i] >= '0' && line [i] <= '9') || line[i] == '=')) break; } if ((i < 4)) { pomlog(POMLOG_DEBUG "Recieved invalid client command"); priv->flags |= PROTO_SMTP_FLAG_INVALID; return POM_OK; } if (!strncasecmp(line, "DATA", strlen("DATA")) && len == strlen("DATA")) { priv->flags |= PROTO_SMTP_FLAG_CLIENT_DATA; } else if (!strncasecmp(line, "STARTTLS", strlen("STARTTLS")) && len == strlen("STARTTLS")) { priv->flags |= PROTO_SMTP_FLAG_STARTTLS; } if (event_has_listener(ppriv->evt_cmd)) { struct event *evt = event_alloc(ppriv->evt_cmd); if (!evt) return PROTO_ERR; size_t cmdlen = len; char *space = memchr(line, ' ', len); if (space) cmdlen = space - line; struct data *evt_data = event_get_data(evt); PTYPE_STRING_SETVAL_N(evt_data[proto_smtp_cmd_name].value, line, cmdlen); data_set(evt_data[proto_smtp_cmd_name]); if (space) { PTYPE_STRING_SETVAL_N(evt_data[proto_smtp_cmd_arg].value, space + 1, len - 1 - cmdlen); data_set(evt_data[proto_smtp_cmd_arg]); } if (priv->flags & PROTO_SMTP_FLAG_CLIENT_DATA) { // The event ends at the end of the message priv->data_evt = evt; return event_process_begin(evt, stack, stack_index, p->ts); } else { return event_process(evt, stack, stack_index, p->ts); } } } } return PROTO_OK; }
int core_queue_packet(struct packet *p, unsigned int flags, unsigned int thread_affinity) { // Update the counters registry_perf_inc(p->input->perf_pkts_in, 1); registry_perf_inc(p->input->perf_bytes_in, p->len); if (!core_run) return POM_ERR; debug_core("Queuing packet %p (%u.%06u)", p, pom_ptime_sec(p->ts), pom_ptime_usec(p->ts)); // Find the right thread to queue to struct core_processing_thread *t = NULL; if (flags & CORE_QUEUE_HAS_THREAD_AFFINITY) { t = core_processing_threads[thread_affinity % core_num_threads]; pom_mutex_lock(&t->pkt_queue_lock); } else { static volatile unsigned int start = 0; unsigned int i; while (1) { unsigned int thread_id = start; for (i = 0; i < core_num_threads; i++) { thread_id++; if (thread_id >= core_num_threads) thread_id -= core_num_threads; t = core_processing_threads[thread_id]; int res = pthread_mutex_trylock(&t->pkt_queue_lock); if (res == EBUSY) { // Thread is busy, go to the next one continue; } else if (res) { pomlog(POMLOG_ERR "Error while locking a processing thread pkt_queue mutex : %s", pom_strerror(res)); abort(); return POM_ERR; } // We've got the lock, check if it's ok to queue here if (t->pkt_count < CORE_THREAD_PKT_QUEUE_MAX) { // Use this thread break; } // Too many packets pending in this thread, go to the next one pom_mutex_unlock(&t->pkt_queue_lock); } if (i < core_num_threads) { // We locked on a thread start = thread_id; break; } // No thread found if (core_pkt_queue_count >= ((CORE_THREAD_PKT_QUEUE_MAX - 1) * core_num_threads)) { // Queue full if (flags & CORE_QUEUE_DROP_IF_FULL) { // TODO add dropped stats debug_core("Dropped packet %p (%u.%06u) to thread %u", p, pom_ptime_sec(p->ts), pom_ptime_usec(p->ts)); return POM_OK; } // We're not going to drop this. Wait then debug_core("All queues full. Waiting ..."); pom_mutex_lock(&core_pkt_queue_wait_lock); // Recheck the count after locking if (core_pkt_queue_count >= ((CORE_THREAD_PKT_QUEUE_MAX - 1) * core_num_threads)) { int res = pthread_cond_wait(&core_pkt_queue_wait_cond, &core_pkt_queue_wait_lock); if (res) { pomlog(POMLOG_ERR "Error while waiting for the core pkt_queue condition : %s", pom_strerror(res)); abort(); } } pom_mutex_unlock(&core_pkt_queue_wait_lock); } } } // We've got the thread's lock, add it to the queue struct core_packet_queue *tmp = NULL; if (t->pkt_queue_unused) { tmp = t->pkt_queue_unused; t->pkt_queue_unused = tmp->next; } else { tmp = malloc(sizeof(struct core_packet_queue)); if (!tmp) { pom_mutex_unlock(&t->pkt_queue_lock); pom_oom(sizeof(struct core_packet_queue)); return POM_ERR; } } tmp->pkt = p; tmp->next = NULL; if (t->pkt_queue_tail) { t->pkt_queue_tail->next = tmp; } else { t->pkt_queue_head = tmp; // The queue was empty, we need to signal it int res = pthread_cond_signal(&t->pkt_queue_cond); if (res) { pomlog(POMLOG_ERR "Error while signaling the thread pkt_queue restart condition : %s", pom_strerror(res)); abort(); return POM_ERR; } } t->pkt_queue_tail = tmp; t->pkt_count++; __sync_fetch_and_add(&core_pkt_queue_count, 1); registry_perf_inc(perf_pkt_queue, 1); debug_core("Queued packet %p (%u.%06u) to thread %u", p, pom_ptime_sec(p->ts), pom_ptime_usec(p->ts), t->thread_id); pom_mutex_unlock(&t->pkt_queue_lock); return POM_OK; }
static int proto_smtp_init(struct proto *proto, struct registry_instance *i) { if (proto_number_register("tcp", 25, proto) != POM_OK || proto_number_register("tcp", 587, proto) != POM_OK) return POM_ERR; struct proto_smtp_priv *priv = malloc(sizeof(struct proto_smtp_priv)); if (!priv) { pom_oom(sizeof(struct proto_smtp_priv)); return POM_ERR; } memset(priv, 0, sizeof(struct proto_smtp_priv)); proto_set_priv(proto, priv); // Register the smtp_cmd event static struct data_item_reg evt_cmd_data_items[PROTO_SMTP_EVT_CMD_DATA_COUNT] = { { 0 } }; evt_cmd_data_items[proto_smtp_cmd_name].name = "name"; evt_cmd_data_items[proto_smtp_cmd_name].value_type = ptype_get_type("string"); evt_cmd_data_items[proto_smtp_cmd_arg].name = "arg"; evt_cmd_data_items[proto_smtp_cmd_arg].value_type = ptype_get_type("string"); static struct data_reg evt_cmd_data = { .items = evt_cmd_data_items, .data_count = PROTO_SMTP_EVT_CMD_DATA_COUNT }; static struct event_reg_info proto_smtp_evt_cmd = { 0 }; proto_smtp_evt_cmd.source_name = "proto_smtp"; proto_smtp_evt_cmd.source_obj = proto; proto_smtp_evt_cmd.name = "smtp_cmd"; proto_smtp_evt_cmd.description = "SMTP command from the client"; proto_smtp_evt_cmd.data_reg = &evt_cmd_data; priv->evt_cmd = event_register(&proto_smtp_evt_cmd); if (!priv->evt_cmd) goto err; // Register the smtp_reply event static struct data_item_reg evt_reply_data_items[PROTO_SMTP_EVT_CMD_DATA_COUNT] = { { 0 } }; evt_reply_data_items[proto_smtp_reply_code].name = "code"; evt_reply_data_items[proto_smtp_reply_code].value_type = ptype_get_type("uint16"); evt_reply_data_items[proto_smtp_reply_text].name = "text"; evt_reply_data_items[proto_smtp_reply_text].flags = DATA_REG_FLAG_LIST; static struct data_reg evt_reply_data = { .items = evt_reply_data_items, .data_count = PROTO_SMTP_EVT_REPLY_DATA_COUNT }; static struct event_reg_info proto_smtp_evt_reply = { 0 }; proto_smtp_evt_reply.source_name = "proto_smtp"; proto_smtp_evt_reply.source_obj = proto; proto_smtp_evt_reply.name = "smtp_reply"; proto_smtp_evt_reply.description = "SMTP command from the client"; proto_smtp_evt_reply.data_reg = &evt_reply_data; priv->evt_reply = event_register(&proto_smtp_evt_reply); if (!priv->evt_reply) goto err; return POM_OK; err: proto_smtp_cleanup(priv); return POM_ERR; }
int proto_register(struct proto_reg_info *reg_info) { if (reg_info->api_ver != PROTO_API_VER) { pomlog(POMLOG_ERR "Cannot register proto as API version differ : expected %u got %u", PROTO_API_VER, reg_info->api_ver); return POM_ERR; } // Check if the protocol already exists struct proto *proto; for (proto = proto_head; proto && strcmp(proto->info->name, reg_info->name); proto = proto->next); if (proto) return POM_ERR; // Allocate the protocol proto = malloc(sizeof(struct proto)); if (!proto) { pom_oom(sizeof(struct proto)); return POM_ERR; } memset(proto, 0, sizeof(struct proto)); proto->info = reg_info; proto->id = proto_count; proto_count++; if (reg_info->number_class) { proto->number_class = proto_number_class_get(reg_info->number_class); if (!proto->number_class) goto err_proto; } int res = pthread_rwlock_init(&proto->expectation_lock, NULL); if (res) { pomlog(POMLOG_ERR "Error while initializing the proto_expectation rwlock : %s", pom_strerror(res)); goto err_proto; } res = pthread_rwlock_init(&proto->listeners_lock, NULL); if (res) { pomlog(POMLOG_ERR "Error while initializing the proto_listeners rwlock : %s", pom_strerror(res)); goto err_lock1; } proto->reg_instance = registry_add_instance(proto_registry_class, reg_info->name); if (!proto->reg_instance) { pomlog(POMLOG_ERR "Error while adding the registry instanc for protocol %s", reg_info->name); goto err_lock; } // Allocate the conntrack table if (reg_info->ct_info) { proto->ct = conntrack_table_alloc(reg_info->ct_info->default_table_size, (reg_info->ct_info->rev_pkt_field_id == -1 ? 0 : 1)); if (!proto->ct) { pomlog(POMLOG_ERR "Error while allocating conntrack tables"); goto err_registry; } proto->perf_conn_cur = registry_instance_add_perf(proto->reg_instance, "conn_cur", registry_perf_type_gauge, "Current number of monitored connection", "connections"); proto->perf_conn_tot = registry_instance_add_perf(proto->reg_instance, "conn_tot", registry_perf_type_counter, "Total number of connections", "connections"); proto->perf_conn_hash_col = registry_instance_add_perf(proto->reg_instance, "conn_hash_col", registry_perf_type_counter, "Total number of conntrack hash collisions", "collisions"); if (!proto->perf_conn_cur || !proto->perf_conn_tot || !proto->perf_conn_hash_col) goto err_conntrack; } proto->perf_pkts = registry_instance_add_perf(proto->reg_instance, "pkts", registry_perf_type_counter, "Number of packets processed", "pkts"); proto->perf_bytes = registry_instance_add_perf(proto->reg_instance, "bytes", registry_perf_type_counter, "Number of bytes processed", "bytes"); proto->perf_expt_pending = registry_instance_add_perf(proto->reg_instance, "expectations_pending", registry_perf_type_gauge, "Number of expectations pending", "expectations"); proto->perf_expt_matched = registry_instance_add_perf(proto->reg_instance, "expectations_matched", registry_perf_type_counter, "Number of expectations matched", "expectations"); if (!proto->perf_pkts || !proto->perf_bytes || !proto->perf_expt_pending || !proto->perf_expt_matched) goto err_conntrack; if (reg_info->init) { if (reg_info->init(proto, proto->reg_instance) == POM_ERR) { pomlog(POMLOG_ERR "Error while registering proto %s", reg_info->name); goto err_conntrack; } } mod_refcount_inc(reg_info->mod); proto->next = proto_head; if (proto->next) proto->next->prev = proto; proto_head = proto; pomlog(POMLOG_DEBUG "Proto %s registered", reg_info->name); return POM_OK; err_conntrack: // Remove proto number if any proto_number_unregister(proto); conntrack_table_cleanup(proto->ct); err_registry: registry_remove_instance(proto->reg_instance); err_lock: pthread_rwlock_destroy(&proto->listeners_lock); err_lock1: pthread_rwlock_destroy(&proto->expectation_lock); err_proto: free(proto); return POM_ERR; }
int conntrack_get(struct proto_process_stack *stack, unsigned int stack_index) { struct proto_process_stack *s = &stack[stack_index]; struct proto_process_stack *s_prev = &stack[stack_index - 1]; struct proto_process_stack *s_next = &stack[stack_index + 1]; if (s->ce) return POM_OK; if (!s->proto || !s->proto->info->ct_info) return POM_ERR; struct ptype *fwd_value = s->pkt_info->fields_value[s->proto->info->ct_info->fwd_pkt_field_id]; if (!fwd_value) return POM_ERR; struct ptype *rev_value = NULL; if (s->proto->info->ct_info->rev_pkt_field_id != CONNTRACK_PKT_FIELD_NONE) { rev_value = s->pkt_info->fields_value[s->proto->info->ct_info->rev_pkt_field_id]; if (!rev_value) return POM_ERR; } struct conntrack_tables *ct = s->proto->ct; uint32_t hash = conntrack_hash(fwd_value, rev_value, s_prev->ce) % ct->table_size; // Lock the specific hash while browsing for a conntrack pom_mutex_lock(&ct->locks[hash]); // Try to find the conntrack in the forward table // Check if we can find this entry in the forward way if (ct->table[hash]) { s->ce = conntrack_find(ct->table[hash], fwd_value, rev_value, s_prev->ce); if (s->ce) { s->direction = POM_DIR_FWD; s_next->direction = POM_DIR_FWD; pom_mutex_lock(&s->ce->lock); s->ce->refcount++; pom_mutex_unlock(&ct->locks[hash]); return POM_OK;; } } // It wasn't found in the forward way, maybe in the reverse direction ? if (rev_value) { s->ce = conntrack_find(ct->table[hash], rev_value, fwd_value, s_prev->ce); if (s->ce) { s->direction = POM_DIR_REV; s_next->direction = POM_DIR_REV; pom_mutex_lock(&s->ce->lock); s->ce->refcount++; pom_mutex_unlock(&ct->locks[hash]); return POM_OK; } } // It's not found in the reverse direction either, let's create it then if (s_prev->direction == POM_DIR_REV && rev_value) { // This indicates that the parent conntrack matched in a reverse direction // Let's keep directions consistent and swap fwd and rev values struct ptype *tmp = rev_value; rev_value = fwd_value; fwd_value = tmp; } // Alloc the conntrack entry struct conntrack_entry *ce = malloc(sizeof(struct conntrack_entry)); if (!ce) { pom_mutex_unlock(&ct->locks[hash]); pom_oom(sizeof(struct conntrack_entry)); return POM_ERR; } memset(ce, 0, sizeof(struct conntrack_entry)); if (pom_mutex_init_type(&ce->lock, PTHREAD_MUTEX_ERRORCHECK) != POM_OK) { pom_mutex_unlock(&ct->locks[hash]); free(ce); return POM_ERR; } struct conntrack_node_list *child = NULL; // We shouldn't have to check if the parent still exists as it // is supposed to have a refcount since conntrack_get is called after // the parent's conntrack_get was called and before conntrack_refcount_dec // was called by core_process_stack. if (s_prev->ce) { child = malloc(sizeof(struct conntrack_node_list)); if (!child) { pthread_mutex_destroy(&ce->lock); pom_mutex_unlock(&ct->locks[hash]); free(ce); pom_oom(sizeof(struct conntrack_node_list)); return POM_ERR; } memset(child, 0, sizeof(struct conntrack_node_list)); child->ce = ce; child->ct = s->proto->ct; child->hash = hash; ce->parent = malloc(sizeof(struct conntrack_node_list)); if (!ce->parent) { pthread_mutex_destroy(&ce->lock); pom_mutex_unlock(&ct->locks[hash]); free(child); free(ce); pom_oom(sizeof(struct conntrack_node_list)); return POM_ERR; } ce->parent->ce = s_prev->ce; ce->parent->ct = s_prev->ce->proto->ct; ce->parent->hash = s_prev->ce->hash; } ce->proto = s->proto; ce->hash = hash; struct conntrack_list *lst = NULL; ce->fwd_value = ptype_alloc_from(fwd_value); if (!ce->fwd_value) goto err; if (rev_value) { ce->rev_value = ptype_alloc_from(rev_value); if (!ce->rev_value) goto err; } // Alloc the list node lst = malloc(sizeof(struct conntrack_list)); if (!lst) { ptype_cleanup(ce->fwd_value); pom_oom(sizeof(struct conntrack_list)); goto err; } memset(lst, 0, sizeof(struct conntrack_list)); lst->ce = ce; // Insert in the conntrack table lst->next = ct->table[hash]; if (lst->next) { lst->next->prev = lst; registry_perf_inc(s->proto->perf_conn_hash_col, 1); } ct->table[hash] = lst; // Add the child to the parent if any if (child) { pom_mutex_lock(&s_prev->ce->lock); if (!s_prev->ce->refcount) pomlog(POMLOG_WARN "Internal error, the parent is supposed to have a refcount > 0"); child->next = s_prev->ce->children; if (child->next) child->next->prev = child; s_prev->ce->children = child; pom_mutex_unlock(&s_prev->ce->lock); } // Unlock the table if (s_prev->ce) { debug_conntrack("Allocated conntrack %p with parent %p", ce, s_prev->ce); } else { debug_conntrack("Allocated conntrack %p with no parent", ce); } pom_mutex_lock(&ce->lock); ce->refcount++; pom_mutex_unlock(&ct->locks[hash]); s->ce = ce; s->direction = s_prev->direction; // Propagate the direction to the payload as well s_next->direction = s->direction; registry_perf_inc(ce->proto->perf_conn_cur, 1); registry_perf_inc(ce->proto->perf_conn_tot, 1); return POM_OK; err: pom_mutex_unlock(&ct->locks[hash]); pthread_mutex_destroy(&ce->lock); if (child) free(child); if (lst) free(lst); if (ce->parent) free(ce->parent); if (ce->fwd_value) ptype_cleanup(ce->fwd_value); if (ce->rev_value) ptype_cleanup(ce->rev_value); free(ce); return POM_ERR; }