static struct proto_expectation_stack *proto_expectation_stack_alloc(struct proto *p, struct ptype *fwd_value, struct ptype *rev_value) { if (!p || !fwd_value) return NULL; struct proto_expectation_stack *es = malloc(sizeof(struct proto_expectation_stack)); if (!es) { pom_oom(sizeof(struct proto_expectation_stack)); return NULL; } memset(es, 0, sizeof(struct proto_expectation_stack)); es->proto = p; es->fields[POM_DIR_FWD] = ptype_alloc_from(fwd_value); if (!es->fields[POM_DIR_FWD]) { free(es); return NULL; } if (rev_value) { es->fields[POM_DIR_REV] = ptype_alloc_from(rev_value); if (!es->fields[POM_DIR_REV]) { ptype_cleanup(es->fields[POM_DIR_FWD]); free(es); return NULL; } } return es; }
static struct proto_expectation_stack *proto_expectation_stack_alloc(struct proto *p, struct ptype *fwd_value, struct ptype *rev_value) { if (!p || !fwd_value) { pomlog(POMLOG_ERR "Cannot allocate expectation with a forward nor reverse conntrack entry field value"); return NULL; } struct proto_expectation_stack *es = malloc(sizeof(struct proto_expectation_stack)); if (!es) { pom_oom(sizeof(struct proto_expectation_stack)); return NULL; } memset(es, 0, sizeof(struct proto_expectation_stack)); es->proto = p; es->fields[POM_DIR_FWD] = ptype_alloc_from(fwd_value); if (!es->fields[POM_DIR_FWD]) { free(es); return NULL; } if (rev_value) { es->fields[POM_DIR_REV] = ptype_alloc_from(rev_value); if (!es->fields[POM_DIR_REV]) { ptype_cleanup(es->fields[POM_DIR_FWD]); free(es); return NULL; } } return es; }
static int analyzer_smtp_event_fetch_common_data(struct analyzer_smtp_ce_priv *cpriv, struct proto_process_stack *stack, unsigned int stack_index, int server_direction) { struct proto_process_stack *l4_stack = &stack[stack_index - 1]; struct proto_process_stack *l3_stack = &stack[stack_index - 2]; int i; char *port_str = "dport"; if (server_direction == POM_DIR_REV) port_str = "sport"; for (i = 0; !cpriv->server_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 (!strcmp(name, port_str)) cpriv->server_port = *PTYPE_UINT16_GETVAL(l4_stack->pkt_info->fields_value[i]); } struct ptype *src = NULL, *dst = NULL; for (i = 0; !src || !dst; 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 (!src && !strcmp(name, "src")) src = l3_stack->pkt_info->fields_value[i]; else if (!dst && !strcmp(name, "dst")) dst = l3_stack->pkt_info->fields_value[i]; } if (server_direction == POM_DIR_FWD) { if (src) cpriv->client_addr = ptype_alloc_from(src); if (dst) cpriv->server_addr = ptype_alloc_from(dst); } else { if (src) cpriv->server_addr = ptype_alloc_from(src); if (dst) cpriv->client_addr = ptype_alloc_from(dst); } if (cpriv->server_addr) { char *host = dns_reverse_lookup_ptype(cpriv->server_addr); if (host) cpriv->server_host = host; } cpriv->common_data_fetched = 1; return POM_OK; }
int proto_expectation_set_field(struct proto_expectation *e, int stack_index, struct ptype *value, int direction) { struct proto_expectation_stack *es = NULL; int i; if (stack_index > 0) { es = e->head; for (i = 1; es && i < stack_index; i++) es = es->next; } else { stack_index = -stack_index; es = e->tail; for (i = 1; es && i < stack_index; i++) es = es->prev; } if (!es) { pomlog(POMLOG_ERR "Invalid stack index in the expectation"); return POM_ERR; } if (es->fields[direction]) { ptype_cleanup(es->fields[direction]); es->fields[direction] = NULL; } if (value) { es->fields[direction] = ptype_alloc_from(value); if (!es->fields[direction]) return POM_ERR; } return POM_OK; }
static int analyzer_smtp_event_fill_common_data(struct analyzer_smtp_ce_priv *cpriv, struct data *data) { if (cpriv->client_hello) { PTYPE_STRING_SETVAL(data[analyzer_smtp_common_client_hello].value, cpriv->client_hello); data_set(data[analyzer_smtp_common_client_hello]); } if (cpriv->server_hello) { PTYPE_STRING_SETVAL(data[analyzer_smtp_common_server_hello].value, cpriv->server_hello); data_set(data[analyzer_smtp_common_server_hello]); } if (cpriv->client_addr) { data[analyzer_smtp_common_client_addr].value = ptype_alloc_from(cpriv->client_addr); data[analyzer_smtp_common_client_addr].flags &= ~DATA_FLAG_NO_CLEAN; if (data[analyzer_smtp_common_client_addr].value) data_set(data[analyzer_smtp_common_client_addr]); } if (cpriv->server_addr) { data[analyzer_smtp_common_server_addr].value = ptype_alloc_from(cpriv->server_addr); data[analyzer_smtp_common_server_addr].flags &= ~DATA_FLAG_NO_CLEAN; if (data[analyzer_smtp_common_server_addr].value) data_set(data[analyzer_smtp_common_server_addr]); } if (cpriv->server_port) { PTYPE_UINT16_SETVAL(data[analyzer_smtp_common_server_port].value, cpriv->server_port); data_set(data[analyzer_smtp_common_server_port]); } if (cpriv->server_host) { PTYPE_STRING_SETVAL(data[analyzer_smtp_common_server_host].value, cpriv->server_host); data_set(data[analyzer_smtp_common_server_host]); } return POM_OK; }
int registry_set_param_value(struct registry_param *p, char *value) { if (!p || !value) return POM_ERR; if (p->flags & REGISTRY_PARAM_FLAG_IMMUTABLE) return POM_ERR; if (p->set_pre_callback && p->set_pre_callback(p->callback_priv, value) != POM_OK) { return POM_ERR; } core_pause_processing(); struct ptype *old_value = ptype_alloc_from(p->value); if (ptype_parse_val(p->value, value) != POM_OK) { core_resume_processing(); ptype_cleanup(old_value); return POM_ERR; } if (p->set_post_callback && p->set_post_callback(p->callback_priv, p->value) != POM_OK) { // Revert the old value ptype_copy(p->value, old_value); core_resume_processing(); ptype_cleanup(old_value); return POM_ERR; } core_resume_processing(); ptype_cleanup(old_value); 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) { conntrack_unlock(s->ce); 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 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; }
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 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; }