Example #1
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;
}
Example #2
0
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;
}
Example #3
0
int proto_expectation_timeout(void *priv, ptime now) {

	struct proto_expectation *e = priv;

	proto_expectation_remove(e);
	proto_expectation_cleanup(e);

	return POM_OK;
}
Example #4
0
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;
}
Example #5
0
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;
}
Example #6
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;
}
Example #7
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;
}
Example #8
0
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;
}