예제 #1
0
/**
 * Install state of type 'type' for a dynamic session.
 * The hash table contains two type of rules:
 * - regular rules (O_KEEP_STATE)
 * - rules for sessions with limited number of sess per user
 *   (O_LIMIT). When they are created, the parent is
 *   increased by 1, and decreased on delete. In this case,
 *   the third parameter is the parent rule and not the chain.
 * - "parent" rules for the above (O_LIMIT_PARENT).
 */
static ipfw_dyn_rule *
add_dyn_rule(struct ipfw_flow_id *id, int i, uint8_t dyn_type,
    struct ip_fw *rule, uint16_t kidx)
{
	ipfw_dyn_rule *r;

	IPFW_BUCK_ASSERT(i);

	r = uma_zalloc(V_ipfw_dyn_rule_zone, M_NOWAIT | M_ZERO);
	if (r == NULL) {
		if (last_log != time_uptime) {
			last_log = time_uptime;
			log(LOG_DEBUG,
			    "ipfw: Cannot allocate dynamic state, "
			    "consider increasing net.inet.ip.fw.dyn_max\n");
		}
		return NULL;
	}
	ipfw_dyn_count++;

	/*
	 * refcount on parent is already incremented, so
	 * it is safe to use parent unlocked.
	 */
	if (dyn_type == O_LIMIT) {
		ipfw_dyn_rule *parent = (ipfw_dyn_rule *)rule;
		if ( parent->dyn_type != O_LIMIT_PARENT)
			panic("invalid parent");
		r->parent = parent;
		rule = parent->rule;
	}

	r->id = *id;
	r->expire = time_uptime + V_dyn_syn_lifetime;
	r->rule = rule;
	r->dyn_type = dyn_type;
	IPFW_ZERO_DYN_COUNTER(r);
	r->count = 0;
	r->kidx = kidx;
	r->bucket = i;
	r->next = V_ipfw_dyn_v[i].head;
	V_ipfw_dyn_v[i].head = r;
	DEB(print_dyn_rule(id, dyn_type, "add dyn entry", "total");)
	return r;
예제 #2
0
파일: ip_fw_dynamic.c 프로젝트: OpenKod/src
/*
 * Lookup a dynamic rule, locked version.
 */
static ipfw_dyn_rule *
lookup_dyn_rule_locked(struct ipfw_flow_id *pkt, int i, int *match_direction,
    struct tcphdr *tcp)
{
	/*
	 * Stateful ipfw extensions.
	 * Lookup into dynamic session queue.
	 */
#define MATCH_REVERSE	0
#define MATCH_FORWARD	1
#define MATCH_NONE	2
#define MATCH_UNKNOWN	3
	int dir = MATCH_NONE;
	ipfw_dyn_rule *prev, *q = NULL;

	IPFW_BUCK_ASSERT(i);

	for (prev = NULL, q = V_ipfw_dyn_v[i].head; q; prev = q, q = q->next) {
		if (q->dyn_type == O_LIMIT_PARENT && q->count)
			continue;

		if (pkt->proto != q->id.proto || q->dyn_type == O_LIMIT_PARENT)
			continue;

		if (IS_IP6_FLOW_ID(pkt)) {
			if (IN6_ARE_ADDR_EQUAL(&pkt->src_ip6, &q->id.src_ip6) &&
			    IN6_ARE_ADDR_EQUAL(&pkt->dst_ip6, &q->id.dst_ip6) &&
			    pkt->src_port == q->id.src_port &&
			    pkt->dst_port == q->id.dst_port) {
				dir = MATCH_FORWARD;
				break;
			}
			if (IN6_ARE_ADDR_EQUAL(&pkt->src_ip6, &q->id.dst_ip6) &&
			    IN6_ARE_ADDR_EQUAL(&pkt->dst_ip6, &q->id.src_ip6) &&
			    pkt->src_port == q->id.dst_port &&
			    pkt->dst_port == q->id.src_port) {
				dir = MATCH_REVERSE;
				break;
			}
		} else {
			if (pkt->src_ip == q->id.src_ip &&
			    pkt->dst_ip == q->id.dst_ip &&
			    pkt->src_port == q->id.src_port &&
			    pkt->dst_port == q->id.dst_port) {
				dir = MATCH_FORWARD;
				break;
			}
			if (pkt->src_ip == q->id.dst_ip &&
			    pkt->dst_ip == q->id.src_ip &&
			    pkt->src_port == q->id.dst_port &&
			    pkt->dst_port == q->id.src_port) {
				dir = MATCH_REVERSE;
				break;
			}
		}
	}
	if (q == NULL)
		goto done;	/* q = NULL, not found */

	if (prev != NULL) {	/* found and not in front */
		prev->next = q->next;
		q->next = V_ipfw_dyn_v[i].head;
		V_ipfw_dyn_v[i].head = q;
	}
	if (pkt->proto == IPPROTO_TCP) { /* update state according to flags */
		uint32_t ack;
		u_char flags = pkt->_flags & (TH_FIN | TH_SYN | TH_RST);

#define BOTH_SYN	(TH_SYN | (TH_SYN << 8))
#define BOTH_FIN	(TH_FIN | (TH_FIN << 8))
#define	TCP_FLAGS	(TH_FLAGS | (TH_FLAGS << 8))
#define	ACK_FWD		0x10000			/* fwd ack seen */
#define	ACK_REV		0x20000			/* rev ack seen */

		q->state |= (dir == MATCH_FORWARD) ? flags : (flags << 8);
		switch (q->state & TCP_FLAGS) {
		case TH_SYN:			/* opening */
			q->expire = time_uptime + V_dyn_syn_lifetime;
			break;

		case BOTH_SYN:			/* move to established */
		case BOTH_SYN | TH_FIN:		/* one side tries to close */
		case BOTH_SYN | (TH_FIN << 8):
#define _SEQ_GE(a,b) ((int)(a) - (int)(b) >= 0)
			if (tcp == NULL)
				break;

			ack = ntohl(tcp->th_ack);
			if (dir == MATCH_FORWARD) {
				if (q->ack_fwd == 0 ||
				    _SEQ_GE(ack, q->ack_fwd)) {
					q->ack_fwd = ack;
					q->state |= ACK_FWD;
				}
			} else {
				if (q->ack_rev == 0 ||
				    _SEQ_GE(ack, q->ack_rev)) {
					q->ack_rev = ack;
					q->state |= ACK_REV;
				}
			}
			if ((q->state & (ACK_FWD | ACK_REV)) ==
			    (ACK_FWD | ACK_REV)) {
				q->expire = time_uptime + V_dyn_ack_lifetime;
				q->state &= ~(ACK_FWD | ACK_REV);
			}
			break;

		case BOTH_SYN | BOTH_FIN:	/* both sides closed */
			if (V_dyn_fin_lifetime >= V_dyn_keepalive_period)
				V_dyn_fin_lifetime = V_dyn_keepalive_period - 1;
			q->expire = time_uptime + V_dyn_fin_lifetime;
			break;

		default:
#if 0
			/*
			 * reset or some invalid combination, but can also
			 * occur if we use keep-state the wrong way.
			 */
			if ( (q->state & ((TH_RST << 8)|TH_RST)) == 0)
				printf("invalid state: 0x%x\n", q->state);
#endif
			if (V_dyn_rst_lifetime >= V_dyn_keepalive_period)
				V_dyn_rst_lifetime = V_dyn_keepalive_period - 1;
			q->expire = time_uptime + V_dyn_rst_lifetime;
			break;
		}
	} else if (pkt->proto == IPPROTO_UDP) {
		q->expire = time_uptime + V_dyn_udp_lifetime;
	} else {
		/* other protocols */
		q->expire = time_uptime + V_dyn_short_lifetime;
	}
done:
	if (match_direction != NULL)
		*match_direction = dir;
	return (q);
}
예제 #3
0
/*
 * Lookup a dynamic rule, locked version.
 */
static ipfw_dyn_rule *
lookup_dyn_rule_locked(struct ipfw_flow_id *pkt, int i, int *match_direction,
    struct tcphdr *tcp)
{
	/*
	 * Stateful ipfw extensions.
	 * Lookup into dynamic session queue.
	 */
	ipfw_dyn_rule *prev, *q = NULL;
	int dir;

	IPFW_BUCK_ASSERT(i);

	dir = MATCH_NONE;
	for (prev = NULL, q = V_ipfw_dyn_v[i].head; q; prev = q, q = q->next) {
		if (q->dyn_type == O_LIMIT_PARENT && q->count)
			continue;

		if (pkt->proto != q->id.proto || q->dyn_type == O_LIMIT_PARENT)
			continue;

		if (IS_IP6_FLOW_ID(pkt)) {
			if (IN6_ARE_ADDR_EQUAL(&pkt->src_ip6, &q->id.src_ip6) &&
			    IN6_ARE_ADDR_EQUAL(&pkt->dst_ip6, &q->id.dst_ip6) &&
			    pkt->src_port == q->id.src_port &&
			    pkt->dst_port == q->id.dst_port) {
				dir = MATCH_FORWARD;
				break;
			}
			if (IN6_ARE_ADDR_EQUAL(&pkt->src_ip6, &q->id.dst_ip6) &&
			    IN6_ARE_ADDR_EQUAL(&pkt->dst_ip6, &q->id.src_ip6) &&
			    pkt->src_port == q->id.dst_port &&
			    pkt->dst_port == q->id.src_port) {
				dir = MATCH_REVERSE;
				break;
			}
		} else {
			if (pkt->src_ip == q->id.src_ip &&
			    pkt->dst_ip == q->id.dst_ip &&
			    pkt->src_port == q->id.src_port &&
			    pkt->dst_port == q->id.dst_port) {
				dir = MATCH_FORWARD;
				break;
			}
			if (pkt->src_ip == q->id.dst_ip &&
			    pkt->dst_ip == q->id.src_ip &&
			    pkt->src_port == q->id.dst_port &&
			    pkt->dst_port == q->id.src_port) {
				dir = MATCH_REVERSE;
				break;
			}
		}
	}
	if (q == NULL)
		goto done;	/* q = NULL, not found */

	if (prev != NULL) {	/* found and not in front */
		prev->next = q->next;
		q->next = V_ipfw_dyn_v[i].head;
		V_ipfw_dyn_v[i].head = q;
	}

	/* update state according to flags */
	dyn_update_proto_state(q, pkt, tcp, dir);
done:
	if (match_direction != NULL)
		*match_direction = dir;
	return (q);
}