Пример #1
0
int event_listener_unregister(struct event_reg *evt_reg, void *obj) {

	struct event_listener *lst;
	for (lst = evt_reg->listeners; lst && lst->obj != obj; lst = lst->next);

	if (!lst) {
		pomlog(POMLOG_ERR "Object %p not found in the listeners list of event %s",  obj, evt_reg->info->name);
		return POM_ERR;
	}

	if (lst->next)
		lst->next->prev = lst->prev;
	
	if (lst->prev)
		lst->prev->next = lst->next;
	else
		evt_reg->listeners = lst->next;

	free(lst);

	if (!evt_reg->listeners) {
		if (evt_reg->info->listeners_notify && evt_reg->info->listeners_notify(evt_reg->info->source_obj, evt_reg, 0) != POM_OK) {
			pomlog(POMLOG_WARN "Error while notifying event object that it has no listeners");
		}
	}

	registry_perf_dec(evt_reg->perf_listeners, 1);

	return POM_OK;
}
Пример #2
0
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;
}
Пример #3
0
int proto_expectation_remove(struct proto_expectation *e) {

	struct proto *proto = e->tail->proto;
	pom_rwlock_wlock(&proto->expectation_lock);

	if (!(e->flags & PROTO_EXPECTATION_FLAG_QUEUED)) {
		pom_rwlock_unlock(&proto->expectation_lock);
		return POM_ERR;
	}

	if (!e->next && !e->prev && proto->expectations != e) {
		// The expectation is not queued
		pom_rwlock_unlock(&proto->expectation_lock);
		return POM_OK;
	}

	if (e->next)
		e->next->prev = e->prev;
	if (e->prev)
		e->prev->next = e->next;
	else
		proto->expectations = e->next;

	__sync_fetch_and_and(&e->flags, ~PROTO_EXPECTATION_FLAG_QUEUED);

	pom_rwlock_unlock(&proto->expectation_lock);

	registry_perf_dec(e->proto->perf_expt_pending, 1);

	return POM_OK;
}
Пример #4
0
int timer_cleanup(struct timer *t) {

	if (t->queue)
		timer_dequeue(t);

	free(t);
	
	registry_perf_dec(perf_timer_allocated, 1);

	return POM_OK;
}
Пример #5
0
int event_process_end(struct event *evt) {

	debug_event("Processing event end %s", evt->reg->info->name);

	if (!(evt->flags & EVENT_FLAG_PROCESS_BEGAN)) {
		pomlog(POMLOG_ERR "Internal error: event %s processing hasn't begun", evt->reg->info->name);
		event_cleanup(evt);
		return POM_ERR;
	}

	if (evt->flags & EVENT_FLAG_PROCESS_DONE) {
		pomlog(POMLOG_ERR "Internal error: event %s has already been processed entirely", evt->reg->info->name);
		event_cleanup(evt);
		return POM_ERR;
	}


	struct event_listener *lst;
	for (lst = evt->reg->listeners; lst; lst = lst->next) {
		if (lst->process_end && lst->process_end(evt, lst->obj) != POM_OK) {
			pomlog(POMLOG_WARN "An error occured while processing event %s", evt->reg->info->name);
		}
	}

	for (lst = evt->tmp_listeners; lst; lst = lst->next) {
		if (lst->process_end && lst->process_end(evt, lst->obj) != POM_OK) {
			pomlog(POMLOG_WARN "An error occured while processing event %s", evt->reg->info->name);
		}
		registry_perf_dec(evt->reg->perf_listeners, 1);
	}
	
	evt->ce = NULL;

	evt->flags |= EVENT_FLAG_PROCESS_DONE;

	registry_perf_dec(evt->reg->perf_ongoing, 1);
	registry_perf_inc(evt->reg->perf_processed, 1);

	return event_refcount_dec(evt);
}
Пример #6
0
int output_file_pload_close(void *output_priv, void *pload_instance_priv) {

	struct output_file_pload_priv *ppriv = pload_instance_priv;
	int fd = ppriv->fd;
	pomlog(POMLOG_DEBUG "File %s closed", ppriv->filename);
	free(ppriv->filename);
	free(ppriv);

	struct output_file_priv *priv = output_priv;
	if (priv) {
		if (priv->perf_files_open)
			registry_perf_dec(priv->perf_files_open, 1);
		if (priv->perf_files_closed)
			registry_perf_inc(priv->perf_files_closed, 1);
	}

	return close(fd);
}
Пример #7
0
int timer_dequeue(struct timer *t) {

	// First let's check if it's the one at the begining of the queue

	pom_mutex_lock(&timer_main_lock);

	if (!t->queue) {
		pomlog(POMLOG_WARN "Warning, timer %p was already dequeued", t);
		pom_mutex_unlock(&timer_main_lock);
		return POM_OK;
	}

	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;
		
	}


	// Make sure this timer will not reference anything

	t->prev = NULL;
	t->next = NULL;
	t->queue = NULL;
	pom_mutex_unlock(&timer_main_lock);

	registry_perf_dec(perf_timer_queued, 1);

	return POM_OK;
}
Пример #8
0
int packet_release(struct packet *p) {

    // Release the multipart
    struct packet_multipart *multipart = __sync_fetch_and_and(&p->multipart, 0);
    if (multipart && packet_multipart_cleanup(multipart) != POM_OK)
        return POM_ERR;

    // The packet refcount will be 0 afterwards
    // We can clean up the buffer if any
    if (p->refcount > 1) {
        __sync_fetch_and_sub(&p->refcount, 1);
        return POM_OK;
    }

    if (p->pkt_buff)
        packet_buffer_release(p->pkt_buff);

    registry_perf_dec(perf_pkt_in_use, 1);
    free(p);

    return POM_OK;
}
Пример #9
0
int timers_process() {


	static int processing = 0;

	int res = pthread_mutex_trylock(&timer_main_lock);
	if (res == EBUSY) {
		// Already locked, give up
		return POM_OK;
	} else if (res) {
		// Something went wrong
		pomlog(POMLOG_ERR "Error while trying to lock the main timer lock : %s", pom_strerror(res));
		abort();
		return POM_ERR;
	}

	// Another thread is already processing the timers, drop out
	if (processing) {
		pom_mutex_unlock(&timer_main_lock);
		return POM_OK;
	}

	processing = 1;

	ptime now = core_get_clock();

	struct timer_queue *tq;
	tq = timer_queues;

	while (tq) {
		while (tq->head && (tq->head->expires < now)) {
				
			// Dequeue the timer
			struct timer *tmp = tq->head;
			tq->head = tq->head->next;
			if (tq->head)
				tq->head->prev = NULL;
			else
				tq->tail = NULL;

			tmp->next = NULL;
			tmp->prev = NULL;
			tmp->queue = NULL;
			pom_mutex_unlock(&timer_main_lock);
			registry_perf_dec(perf_timer_queued, 1);

			// Process it
			debug_timer( "Timer 0x%lx reached. Starting handler ...", (unsigned long) tmp);
			if ((*tmp->handler) (tmp->priv, now) != POM_OK) {
				return POM_ERR;
			}

			registry_perf_inc(perf_timer_processed, 1);

			pom_mutex_lock(&timer_main_lock);

		}
		tq = tq->next;

	}

	processing = 0;

	pom_mutex_unlock(&timer_main_lock);

	return POM_OK;
}
Пример #10
0
void packet_buffer_release(struct packet_buffer *pb) {

    registry_perf_dec(perf_pkt_buff, pb->buff_size);
    free(pb);
}
Пример #11
0
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;
}
Пример #12
0
void *core_processing_thread_func(void *priv) {

	struct core_processing_thread *tpriv = priv;

	if (packet_info_pool_init()) {
		halt("Error while initializing the packet_info_pool", 1);
		return NULL;
	}

	registry_perf_inc(perf_thread_active, 1);

	pom_mutex_lock(&tpriv->pkt_queue_lock);

	while (core_run) {
		
		while (!tpriv->pkt_queue_head) {
			// We are not active while waiting for a packet
			registry_perf_dec(perf_thread_active, 1);

			debug_core("thread %u : waiting", tpriv->thread_id);

			if (registry_perf_getval(perf_thread_active) == 0) {
				if (core_get_state() == core_state_finishing)
					core_set_state(core_state_idle);
			}

			if (!core_run) {
				pom_mutex_unlock(&tpriv->pkt_queue_lock);
				goto end;
			}

			int res = pthread_cond_wait(&tpriv->pkt_queue_cond, &tpriv->pkt_queue_lock);
			if (res) {
				pomlog(POMLOG_ERR "Error while waiting for restart condition : %s", pom_strerror(res));
				abort();
				return NULL;
			}
			registry_perf_inc(perf_thread_active, 1);
		}


		// Dequeue a packet
		struct core_packet_queue *tmp = tpriv->pkt_queue_head;
		tpriv->pkt_queue_head = tmp->next;
		if (!tpriv->pkt_queue_head)
			tpriv->pkt_queue_tail = NULL;


		// Add it to the unused list
		tmp->next = tpriv->pkt_queue_unused;
		tpriv->pkt_queue_unused = tmp;

		tpriv->pkt_count--;

		registry_perf_dec(perf_pkt_queue, 1);

		__sync_fetch_and_sub(&core_pkt_queue_count, 1);

		if (tpriv->pkt_count < CORE_THREAD_PKT_QUEUE_MIN) {

			pom_mutex_lock(&core_pkt_queue_wait_lock);
			// Tell the input processes that they can continue queuing packets
			int res = pthread_cond_broadcast(&core_pkt_queue_wait_cond);
			if (res) {
				pomlog(POMLOG_ERR "Error while signaling the main pkt_queue condition : %s", pom_strerror(res));
				abort();
			}
			pom_mutex_unlock(&core_pkt_queue_wait_lock);
		}

		// Keep track of our packet
		struct packet *pkt = tmp->pkt;

		debug_core("thread %u : Processing packet %p (%u.%06u)", tpriv->thread_id, pkt, pom_ptime_sec(pkt->ts), pom_ptime_usec(pkt->ts));
		pom_mutex_unlock(&tpriv->pkt_queue_lock);

		// Lock the processing lock
		pom_rwlock_rlock(&core_processing_lock);

		// Update the current clock
		if (core_clock[tpriv->thread_id] < pkt->ts) // Make sure we keep it monotonous
			core_clock[tpriv->thread_id] = pkt->ts;

		//pomlog(POMLOG_DEBUG "Thread %u processing ...", pthread_self());
		if (core_process_packet(pkt) == POM_ERR) {
			core_run = 0;
			pom_rwlock_unlock(&core_processing_lock);
			break;
		}

		// Process timers
		if (timers_process() != POM_OK) {
			pom_rwlock_unlock(&core_processing_lock);
			break;
		}

		pom_rwlock_unlock(&core_processing_lock);

		if (packet_release(pkt) != POM_OK) {
			pomlog(POMLOG_ERR "Error while releasing the packet");
			break;
		}
		
		debug_core("thread %u : Processed packet %p (%u.%06u)", tpriv->thread_id, pkt, pom_ptime_sec(pkt->ts), pom_ptime_usec(pkt->ts));
		// Re-lock our queue for the next run
		pom_mutex_lock(&tpriv->pkt_queue_lock);

	}

	halt("Processing thread encountered an error", 1);
end:
	packet_info_pool_cleanup();

	return NULL;
}
Пример #13
0
int conntrack_cleanup(struct conntrack_tables *ct, uint32_t hash, struct conntrack_entry *ce) {

	// Remove the conntrack from the conntrack table
	pom_mutex_lock(&ct->locks[hash]);

	// Try to find the conntrack in the list
	struct conntrack_list *lst = NULL;

	for (lst = ct->table[hash]; lst && lst->ce != ce; lst = lst->next);

	if (!lst) {
		pom_mutex_unlock(&ct->locks[hash]);
		pomlog(POMLOG_ERR "Trying to cleanup a non existing conntrack : %p", ce);
		return POM_OK;
	}

	conntrack_lock(ce);
	if (ce->refcount) {
		debug_conntrack(POMLOG_ERR "Conntrack %p is still being referenced : %u !", ce, ce->refcount);
		conntrack_delayed_cleanup(ce, 1, core_get_clock_last());
		conntrack_unlock(ce);
		pom_mutex_unlock(&ct->locks[hash]);
		return POM_OK;
	}


	if (lst->prev)
		lst->prev->next = lst->next;
	else
		ct->table[hash] = lst->next;

	if (lst->next)
		lst->next->prev = lst->prev;

	free(lst);

	pom_mutex_unlock(&ct->locks[hash]);

	if (ce->cleanup_timer && ce->cleanup_timer != (void *) -1) {
		conntrack_timer_cleanup(ce->cleanup_timer);
		ce->cleanup_timer = (void *) -1; // Mark that the conntrack is being cleaned up
	}

	// Once the conntrack is removed from the hash table, it will not be referenced ever again
	conntrack_unlock(ce);

	if (ce->parent) {
		debug_conntrack("Cleaning up conntrack %p, with parent %p", ce, ce->parent->ce);
	} else {
		debug_conntrack("Cleaning up conntrack %p, with no parent", ce);
	}

	
	if (ce->parent) {
		// Remove the child from the parent
		
		// Make sure the parent still exists
		uint32_t hash = ce->parent->hash;
		pom_mutex_lock(&ce->parent->ct->locks[hash]);
		
		for (lst = ce->parent->ct->table[hash]; lst && lst->ce != ce->parent->ce; lst = lst->next);

		if (lst) {

			conntrack_lock(ce->parent->ce);
			struct conntrack_node_list *tmp = ce->parent->ce->children;

			for (; tmp && tmp->ce != ce; tmp = tmp->next);

			if (tmp) {
				if (tmp->prev)
					tmp->prev->next = tmp->next;
				else
					ce->parent->ce->children = tmp->next;

				if (tmp->next)
					tmp->next->prev = tmp->prev;

				free(tmp);
			} else {
				pomlog(POMLOG_WARN "Conntrack %s not found in parent's %s children list", ce, ce->parent->ce);
			}

			if (!ce->parent->ce->children) // Parent has no child anymore, clean it up after some time
				conntrack_delayed_cleanup(ce->parent->ce, CONNTRACK_CHILDLESS_TIMEOUT, core_get_clock_last());

			conntrack_unlock(ce->parent->ce);
		} else {
			debug_conntrack("Parent conntrack %p not found while cleaning child %p !", ce->parent->ce, ce);
		}

		pom_mutex_unlock(&ce->parent->ct->locks[hash]);

		free(ce->parent);
	}

	if (ce->session)
		conntrack_session_refcount_dec(ce->session);

	// Cleanup private stuff from the conntrack
	if (ce->priv && ce->proto->info->ct_info->cleanup_handler) {
		if (ce->proto->info->ct_info->cleanup_handler(ce->priv) != POM_OK)
			pomlog(POMLOG_WARN "Unable to free the private memory of a conntrack");
	}

	// Cleanup the priv_list
	struct conntrack_priv_list *priv_lst = ce->priv_list;
	while (priv_lst) {
		if (priv_lst->cleanup) {
			if (priv_lst->cleanup(priv_lst->obj, priv_lst->priv) != POM_OK)
				pomlog(POMLOG_WARN "Error while cleaning up private objects in conntrack_entry");
		}
		ce->priv_list = priv_lst->next;
		free(priv_lst);
		priv_lst = ce->priv_list;

	}


	// Cleanup the children
	while (ce->children) {
		struct conntrack_node_list *child = ce->children;
		ce->children = child->next;

		if (conntrack_cleanup(child->ct, child->hash, child->ce) != POM_OK) 
			return POM_ERR;

		free(child);
	}

	
	if (ce->fwd_value)
		ptype_cleanup(ce->fwd_value);
	if (ce->rev_value)
		ptype_cleanup(ce->rev_value);

	pthread_mutex_destroy(&ce->lock);

	registry_perf_dec(ce->proto->perf_conn_cur, 1);

	free(ce);

	return POM_OK;
}
Пример #14
0
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;
}