int proto_expectation_expiry(void *priv, ptime now) { struct proto_expectation *e = priv; struct proto *proto = e->tail->proto; timer_cleanup(e->expiry); pom_rwlock_wlock(&proto->expectation_lock); if (e->next) e->next->prev = e->prev; if (e->prev) e->prev->next = e->next; else proto->expectations = e->next; pom_rwlock_unlock(&proto->expectation_lock); if (e->priv && proto->info->ct_info->cleanup_handler) { if (proto->info->ct_info->cleanup_handler(e->priv) != POM_OK) pomlog(POMLOG_WARN "Unable to free the conntrack priv of the proto_expectation"); } registry_perf_dec(e->proto->perf_expt_pending, 1); proto_expectation_cleanup(e); return POM_OK; }
struct proto_expectation *proto_expectation_alloc_from_conntrack(struct conntrack_entry *ce, struct proto *proto, void *priv) { struct proto_expectation *e = proto_expectation_alloc(proto, priv); if (!e) return NULL; if (!ce->fwd_value && !ce->rev_value) // This is a unique conntrack, match the upper layer instead ce = ce->parent->ce; while (1) { if (proto_expectation_prepend(e, ce->proto, ce->fwd_value, ce->rev_value) != POM_OK) { proto_expectation_cleanup(e); return NULL; } if (!ce->parent) break; ce = ce->parent->ce; } return e; }
int proto_expectation_timeout(void *priv, ptime now) { struct proto_expectation *e = priv; proto_expectation_remove(e); proto_expectation_cleanup(e); return POM_OK; }
int proto_finish() { struct proto *proto; // Cleanup the expectations first for (proto = proto_head; proto; proto = proto->next) { while (proto->expectations) { struct proto_expectation *e = proto->expectations; proto->expectations = e->next; proto_expectation_cleanup(e); } } // Cleanup the conntracks for (proto = proto_head; proto; proto = proto->next) { conntrack_table_empty(proto->ct); } return POM_OK; }
struct proto_expectation *proto_expectation_alloc_from_conntrack(struct conntrack_entry *ce, struct proto *proto, void *priv) { struct proto_expectation *e = proto_expectation_alloc(proto, priv); if (!e) return NULL; while (1) { if (proto_expectation_prepend(e, ce->proto, ce->fwd_value, ce->rev_value) != POM_OK) { proto_expectation_cleanup(e); return NULL; } if (!ce->parent) break; ce = ce->parent->ce; } return e; }
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; }
static int proto_tftp_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_prev = &stack[stack_index - 1]; 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 a conntrack entry"); return PROTO_ERR; } struct proto_tftp_conntrack_priv *priv = s->ce->priv; if (!priv) { priv = malloc(sizeof(struct proto_tftp_conntrack_priv)); if (!priv) { pom_oom(sizeof(struct proto_tftp_conntrack_priv)); conntrack_unlock(s->ce); return POM_ERR; } memset(priv, 0, sizeof(struct proto_tftp_conntrack_priv)); s->ce->priv = priv; } if (priv->flags & PROTO_TFTP_CONN_INVALID) { conntrack_unlock(s->ce); return PROTO_INVALID; } void *pload = s->pload; uint32_t plen = s->plen; // proto_tftp only process up to the opcode field // afterwards, it's up to the analyzer to parse the rest uint16_t opcode = ntohs(*((uint16_t*)pload)); PTYPE_UINT16_SETVAL(s->pkt_info->fields_value[proto_tftp_field_opcode], opcode); pload += sizeof(uint16_t); plen -= sizeof(uint16_t); s_next->pload = pload; s_next->plen = plen; switch (opcode) { case tftp_rrq: case tftp_wrq: { // Find the filename char *filename = pload; char *mode = memchr(filename, 0, plen - 1); if (!mode) { priv->flags |= PROTO_TFTP_CONN_INVALID; conntrack_unlock(s->ce); debug_tftp("End of filename not found in read/write request"); return PROTO_INVALID; } mode++; ssize_t filename_len = mode - filename; char *end = memchr(mode, 0, plen - filename_len); if (!end) { priv->flags |= PROTO_TFTP_CONN_INVALID; conntrack_unlock(s->ce); debug_tftp("End of mode not found in read/write request"); return PROTO_INVALID; } debug_tftp("Got read/write request for filename \"%s\" with mode \"%s\"", filename, mode); struct conntrack_session *session = conntrack_session_get(s->ce); if (!session) { conntrack_unlock(s->ce); return POM_ERR; } // We don't need to do anything with the session conntrack_session_unlock(session); struct proto_expectation *expt = proto_expectation_alloc_from_conntrack(s_prev->ce, proto_tftp, NULL); if (!expt) { conntrack_unlock(s->ce); return PROTO_ERR; } proto_expectation_set_field(expt, -1, NULL, POM_DIR_REV); if (proto_expectation_add(expt, session, PROTO_TFTP_EXPT_TIMER, p->ts) != POM_OK) { conntrack_unlock(s->ce); proto_expectation_cleanup(expt); return PROTO_ERR; } break; } case tftp_data: { if (plen < 2) { priv->flags |= PROTO_TFTP_CONN_INVALID; conntrack_unlock(s->ce); return PROTO_INVALID; } uint16_t block_id = ntohs(*((uint16_t*)(pload))); int set_start_seq = 0; if (!priv->stream) { priv->stream = stream_alloc(PROTO_TFTP_STREAM_BUFF, s->ce, 0, proto_tftp_process_payload); if (!priv->stream) { conntrack_unlock(s->ce); return PROTO_ERR; } stream_set_timeout(priv->stream, PROTO_TFTP_PKT_TIMER); set_start_seq = 1; } conntrack_unlock(s->ce); if (set_start_seq) stream_set_start_seq(priv->stream, s->direction, PROTO_TFTP_BLK_SIZE + 2); int res = stream_process_packet(priv->stream, p, stack, stack_index + 1, block_id * (PROTO_TFTP_BLK_SIZE + 2), 0); return (res == PROTO_OK ? PROTO_STOP : res); } case tftp_ack: // Nothing to do break; case tftp_error: // An error occured, cleanup this conntrack soon conntrack_delayed_cleanup(s->ce, 1, p->ts); break; default: priv->flags |= PROTO_TFTP_CONN_INVALID; conntrack_unlock(s->ce); return PROTO_INVALID; } conntrack_delayed_cleanup(s->ce, PROTO_TFTP_PKT_TIMER, p->ts); conntrack_unlock(s->ce); return PROTO_OK; }