int proto_process(struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) { struct proto_process_stack *s = &stack[stack_index]; struct proto *proto = s->proto; if (!proto || !proto->info->process) return PROTO_ERR; int res = proto->info->process(proto->priv, p, stack, stack_index); registry_perf_inc(proto->perf_pkts, 1); registry_perf_inc(proto->perf_bytes, s->plen); if (res != PROTO_OK) return res; // Process the expectations ! pom_rwlock_rlock(&proto->expectation_lock); struct proto_expectation *e = proto->expectations; while (e) { int expt_dir = POM_DIR_UNK; struct proto_expectation_stack *es = e->tail; struct ptype *fwd_value = s->pkt_info->fields_value[s->proto->info->ct_info->fwd_pkt_field_id]; struct ptype *rev_value = s->pkt_info->fields_value[s->proto->info->ct_info->rev_pkt_field_id]; if ((!es->fields[POM_DIR_FWD] || ptype_compare_val(PTYPE_OP_EQ, es->fields[POM_DIR_FWD], fwd_value)) && (!es->fields[POM_DIR_REV] || ptype_compare_val(PTYPE_OP_EQ, es->fields[POM_DIR_REV], rev_value))) { // Expectation matched the forward direction expt_dir = POM_DIR_FWD; } else if ((!es->fields[POM_DIR_FWD] || ptype_compare_val(PTYPE_OP_EQ, es->fields[POM_DIR_FWD], rev_value)) && (!es->fields[POM_DIR_REV] || ptype_compare_val(PTYPE_OP_EQ, es->fields[POM_DIR_REV], fwd_value))) { // Expectation matched the reverse direction expt_dir = POM_DIR_REV; } if (expt_dir == POM_DIR_UNK) { // Expectation not matched e = e->next; continue; } es = es->prev; int stack_index_tmp = stack_index - 1; while (es) { struct proto_process_stack *s_tmp = &stack[stack_index_tmp]; if (s_tmp->proto != es->proto) { e = e->next; continue; } fwd_value = s_tmp->pkt_info->fields_value[s_tmp->proto->info->ct_info->fwd_pkt_field_id]; rev_value = s_tmp->pkt_info->fields_value[s_tmp->proto->info->ct_info->rev_pkt_field_id]; if (expt_dir == POM_DIR_FWD) { if ((es->fields[POM_DIR_FWD] && !ptype_compare_val(PTYPE_OP_EQ, es->fields[POM_DIR_FWD], fwd_value)) || (es->fields[POM_DIR_REV] && !ptype_compare_val(PTYPE_OP_EQ, es->fields[POM_DIR_REV], rev_value))) { e = e->next; continue; } } else { if ((es->fields[POM_DIR_FWD] && !ptype_compare_val(PTYPE_OP_EQ, es->fields[POM_DIR_FWD], rev_value)) || (es->fields[POM_DIR_REV] && !ptype_compare_val(PTYPE_OP_EQ, es->fields[POM_DIR_REV], fwd_value))) { e = e->next; continue; } } es = es->prev; stack_index_tmp--; } // Expectation matched ! // Relock with write access pom_rwlock_unlock(&proto->expectation_lock); pom_rwlock_wlock(&proto->expectation_lock); debug_expectation("Expectation %p matched !", e); // Remove it from the list if (e->next) e->next->prev = e->prev; if (e->prev) e->prev->next = e->next; else proto->expectations = e->next; struct proto_process_stack *s_next = &stack[stack_index + 1]; s_next->proto = e->proto; if (conntrack_get_unique_from_parent(stack, stack_index + 1) != POM_OK) { proto_expectation_cleanup(e); return PROTO_ERR; } s_next->ce->priv = e->priv; if (conntrack_session_bind(s_next->ce, e->session)) { proto_expectation_cleanup(e); return PROTO_ERR; } registry_perf_dec(e->proto->perf_expt_pending, 1); registry_perf_inc(e->proto->perf_expt_matched, 1); proto_expectation_cleanup(e); conntrack_unlock(s_next->ce); break; } pom_rwlock_unlock(&proto->expectation_lock); return res; }
int proto_process(struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) { struct proto_process_stack *s = &stack[stack_index]; struct proto *proto = s->proto; if (!proto || !proto->info->process) return PROTO_ERR; int res = proto->info->process(proto->priv, p, stack, stack_index); registry_perf_inc(proto->perf_pkts, 1); registry_perf_inc(proto->perf_bytes, s->plen); if (res != PROTO_OK) return res; int matched = 0; // Process the expectations ! pom_rwlock_rlock(&proto->expectation_lock); struct proto_expectation *e = NULL; for (e = proto->expectations; e; e = e->next) { if (e->flags & PROTO_EXPECTATION_FLAG_MATCHED) { // Another thread already matched the expectation, continue continue; } // Bit one means it matches the forward direction // Bit two means it matches the reverse direction int expt_dir = 3; struct proto_expectation_stack *es = e->tail; int stack_index_tmp = stack_index; while (es) { struct proto_process_stack *s_tmp = &stack[stack_index_tmp]; if (s_tmp->proto != es->proto) { expt_dir = 0; break; } struct ptype *fwd_value = s_tmp->pkt_info->fields_value[s_tmp->proto->info->ct_info->fwd_pkt_field_id]; struct ptype *rev_value = s_tmp->pkt_info->fields_value[s_tmp->proto->info->ct_info->rev_pkt_field_id]; if (expt_dir & 1) { if ((es->fields[POM_DIR_FWD] && !ptype_compare_val(PTYPE_OP_EQ, es->fields[POM_DIR_FWD], fwd_value)) || (es->fields[POM_DIR_REV] && !ptype_compare_val(PTYPE_OP_EQ, es->fields[POM_DIR_REV], rev_value))) { expt_dir &= ~1; // It doesn't match in the forward direction } } if (expt_dir & 2) { if ((es->fields[POM_DIR_FWD] && !ptype_compare_val(PTYPE_OP_EQ, es->fields[POM_DIR_FWD], rev_value)) || (es->fields[POM_DIR_REV] && !ptype_compare_val(PTYPE_OP_EQ, es->fields[POM_DIR_REV], fwd_value))) { expt_dir &= ~2; } } if (!expt_dir) break; es = es->prev; stack_index_tmp--; } if (expt_dir) { // It matched if (!(__sync_fetch_and_or(&e->flags, PROTO_EXPECTATION_FLAG_MATCHED) & PROTO_EXPECTATION_FLAG_MATCHED)) { // Something matched matched++; } } } pom_rwlock_unlock(&proto->expectation_lock); if (!matched) return POM_OK; // At least one expectation matched ! debug_expectation("%u expectation matched !", matched); // Relock with write access pom_rwlock_wlock(&proto->expectation_lock); e = proto->expectations; while (e) { struct proto_expectation *cur = e; e = e->next; if (!(cur->flags & PROTO_EXPECTATION_FLAG_MATCHED)) continue; // Remove the expectation from the conntrack if (cur->next) cur->next->prev = cur->prev; if (cur->prev) cur->prev->next = cur->next; else proto->expectations = cur->next; // Remove matched and queued flags __sync_fetch_and_and(&cur->flags, ~(PROTO_EXPECTATION_FLAG_MATCHED | PROTO_EXPECTATION_FLAG_QUEUED)); struct proto_process_stack *s_next = &stack[stack_index + 1]; s_next->proto = cur->proto; if (conntrack_get_unique_from_parent(stack, stack_index + 1) != POM_OK) { proto_expectation_cleanup(cur); continue; } if (!s_next->ce->priv) { s_next->ce->priv = cur->priv; // Prevent cleanup of private data while cleaning the expectation cur->priv = NULL; } if (cur->session) { if (conntrack_session_bind(s_next->ce, cur->session)) { proto_expectation_cleanup(cur); continue; } } registry_perf_dec(cur->proto->perf_expt_pending, 1); registry_perf_inc(cur->proto->perf_expt_matched, 1); if (cur->match_callback) { // Call the callback with the conntrack locked cur->match_callback(cur, cur->callback_priv, s_next->ce); // Nullify callback_priv so it doesn't get cleaned up cur->callback_priv = NULL; } if (cur->expiry) { // The expectation was added using 'add_and_cleanup' function proto_expectation_cleanup(cur); } conntrack_unlock(s_next->ce); } pom_rwlock_unlock(&proto->expectation_lock); return res; }