Esempio n. 1
0
int
pf_map_addr(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr,
    struct pf_addr *naddr, struct pf_addr *init_addr, struct pf_src_node **sns,
    struct pf_pool *rpool, enum pf_sn_types type)
{
	unsigned char		 hash[16];
	struct pf_addr		 faddr;
	struct pf_addr		*raddr = &rpool->addr.v.a.addr;
	struct pf_addr		*rmask = &rpool->addr.v.a.mask;
	u_int64_t		 states;
	u_int16_t		 weight;
	u_int64_t		 load;
	u_int64_t		 cload;

	if (sns[type] == NULL && rpool->opts & PF_POOL_STICKYADDR &&
	    (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE &&
	    pf_map_addr_sticky(af, r, saddr, naddr, sns, rpool, type) == 0)
		return (0);

	if (rpool->addr.type == PF_ADDR_NOROUTE)
		return (1);
	if (rpool->addr.type == PF_ADDR_DYNIFTL) {
		switch (af) {
#ifdef INET
		case AF_INET:
			if (rpool->addr.p.dyn->pfid_acnt4 < 1 &&
			    ((rpool->opts & PF_POOL_TYPEMASK) !=
			    PF_POOL_ROUNDROBIN) &&
			    ((rpool->opts & PF_POOL_TYPEMASK) !=
			    PF_POOL_LEASTSTATES))
				return (1);
			raddr = &rpool->addr.p.dyn->pfid_addr4;
			rmask = &rpool->addr.p.dyn->pfid_mask4;
			break;
#endif /* INET */
#ifdef INET6
		case AF_INET6:
			if (rpool->addr.p.dyn->pfid_acnt6 < 1 &&
			    ((rpool->opts & PF_POOL_TYPEMASK) !=
			    PF_POOL_ROUNDROBIN) &&
			    ((rpool->opts & PF_POOL_TYPEMASK) !=
			    PF_POOL_LEASTSTATES))
				return (1);
			raddr = &rpool->addr.p.dyn->pfid_addr6;
			rmask = &rpool->addr.p.dyn->pfid_mask6;
			break;
#endif /* INET6 */
		}
	} else if (rpool->addr.type == PF_ADDR_TABLE) {
		if (((rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_ROUNDROBIN) &&
		    ((rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_LEASTSTATES))
			return (1); /* unsupported */
	} else {
		raddr = &rpool->addr.v.a.addr;
		rmask = &rpool->addr.v.a.mask;
	}

	switch (rpool->opts & PF_POOL_TYPEMASK) {
	case PF_POOL_NONE:
		PF_ACPY(naddr, raddr, af);
		break;
	case PF_POOL_BITMASK:
		PF_POOLMASK(naddr, raddr, rmask, saddr, af);
		break;
	case PF_POOL_RANDOM:
		if (init_addr != NULL && PF_AZERO(init_addr, af)) {
			switch (af) {
#ifdef INET
			case AF_INET:
				rpool->counter.addr32[0] = htonl(arc4random());
				break;
#endif /* INET */
#ifdef INET6
			case AF_INET6:
				if (rmask->addr32[3] != 0xffffffff)
					rpool->counter.addr32[3] =
					    htonl(arc4random());
				else
					break;
				if (rmask->addr32[2] != 0xffffffff)
					rpool->counter.addr32[2] =
					    htonl(arc4random());
				else
					break;
				if (rmask->addr32[1] != 0xffffffff)
					rpool->counter.addr32[1] =
					    htonl(arc4random());
				else
					break;
				if (rmask->addr32[0] != 0xffffffff)
					rpool->counter.addr32[0] =
					    htonl(arc4random());
				break;
#endif /* INET6 */
			}
			PF_POOLMASK(naddr, raddr, rmask, &rpool->counter, af);
			PF_ACPY(init_addr, naddr, af);

		} else {
			PF_AINC(&rpool->counter, af);
			PF_POOLMASK(naddr, raddr, rmask, &rpool->counter, af);
		}
		break;
	case PF_POOL_SRCHASH:
		pf_hash(saddr, (struct pf_addr *)&hash, &rpool->key, af);
		PF_POOLMASK(naddr, raddr, rmask, (struct pf_addr *)&hash, af);
		break;
	case PF_POOL_ROUNDROBIN:
		if (rpool->addr.type == PF_ADDR_TABLE ||
		    rpool->addr.type == PF_ADDR_DYNIFTL) {
			if (pfr_pool_get(rpool, &raddr, &rmask, af)) {
				/*
				 * reset counter in case its value
				 * has been removed from the pool.
				 */
				bzero(&rpool->counter, sizeof(rpool->counter));
				if (pfr_pool_get(rpool, &raddr, &rmask, af))
					return (1);
			}
		} else if (pf_match_addr(0, raddr, rmask, &rpool->counter, af))
			return (1);

		/* iterate over table if it contains entries which are weighted */
		if ((rpool->addr.type == PF_ADDR_TABLE &&
		    rpool->addr.p.tbl->pfrkt_refcntcost > 0) ||
		    (rpool->addr.type == PF_ADDR_DYNIFTL &&
		    rpool->addr.p.dyn->pfid_kt->pfrkt_refcntcost > 0)) {
			do {
				if (rpool->addr.type == PF_ADDR_TABLE ||
				    rpool->addr.type == PF_ADDR_DYNIFTL) {
					if (pfr_pool_get(rpool,
					    &raddr, &rmask, af))
						return (1);
				} else {
					log(LOG_ERR, "pf: pf_map_addr: "
					    "weighted RR failure");
					return (1);
				}
				if (rpool->weight >= rpool->curweight)
					break;
				PF_AINC(&rpool->counter, af);
			} while (1);
 
			weight = rpool->weight;
		}

		PF_ACPY(naddr, &rpool->counter, af);
		if (init_addr != NULL && PF_AZERO(init_addr, af))
			PF_ACPY(init_addr, naddr, af);
		PF_AINC(&rpool->counter, af);
		break;
	case PF_POOL_LEASTSTATES:
		/* retrieve an address first */
		if (rpool->addr.type == PF_ADDR_TABLE ||
		    rpool->addr.type == PF_ADDR_DYNIFTL) {
			if (pfr_pool_get(rpool, &raddr, &rmask, af)) {
				/* see PF_POOL_ROUNDROBIN */
				bzero(&rpool->counter, sizeof(rpool->counter));
				if (pfr_pool_get(rpool, &raddr, &rmask, af))
					return (1);
			}
		} else if (pf_match_addr(0, raddr, rmask, &rpool->counter, af))
			return (1);

		states = rpool->states;
		weight = rpool->weight;

		if ((rpool->addr.type == PF_ADDR_TABLE &&
		    rpool->addr.p.tbl->pfrkt_refcntcost > 0) ||
		    (rpool->addr.type == PF_ADDR_DYNIFTL &&
		    rpool->addr.p.dyn->pfid_kt->pfrkt_refcntcost > 0))
			load = ((UINT16_MAX * rpool->states) / rpool->weight);
		else
			load = states;

		PF_ACPY(&faddr, &rpool->counter, af);

		PF_ACPY(naddr, &rpool->counter, af);
		if (init_addr != NULL && PF_AZERO(init_addr, af))
			PF_ACPY(init_addr, naddr, af);

		/*
		 * iterate *once* over whole table and find destination with
		 * least connection
		 */
		do  {
			PF_AINC(&rpool->counter, af);
			if (rpool->addr.type == PF_ADDR_TABLE ||
			    rpool->addr.type == PF_ADDR_DYNIFTL) {
				if (pfr_pool_get(rpool, &raddr, &rmask, af))
					return (1);
			} else if (pf_match_addr(0, raddr, rmask,
			    &rpool->counter, af))
				return (1);

			if ((rpool->addr.type == PF_ADDR_TABLE &&
			    rpool->addr.p.tbl->pfrkt_refcntcost > 0) ||
			    (rpool->addr.type == PF_ADDR_DYNIFTL &&
			    rpool->addr.p.dyn->pfid_kt->pfrkt_refcntcost > 0))
				cload = ((UINT16_MAX * rpool->states)
					/ rpool->weight);
			else
				cload = rpool->states;

			/* find lc minimum */
			if (cload < load) {
				states = rpool->states;
				weight = rpool->weight;
				load = cload;

				PF_ACPY(naddr, &rpool->counter, af);
				if (init_addr != NULL &&
				    PF_AZERO(init_addr, af))
				    PF_ACPY(init_addr, naddr, af);
			}
		} while (pf_match_addr(1, &faddr, rmask, &rpool->counter, af) &&
		    (states > 0));

		if (rpool->addr.type == PF_ADDR_TABLE) {
			if (pfr_states_increase(rpool->addr.p.tbl,
			    naddr, af) == -1) {
				if (pf_status.debug >= LOG_DEBUG) {
					log(LOG_DEBUG,"pf: pf_map_addr: "
					    "selected address ");
					pf_print_host(naddr, 0, af);
					addlog(". Failed to increase count!\n");
				}
				return (1);
			}
		} else if (rpool->addr.type == PF_ADDR_DYNIFTL) {
			if (pfr_states_increase(rpool->addr.p.dyn->pfid_kt,
			    naddr, af) == -1) {
				if (pf_status.debug >= LOG_DEBUG) {
					log(LOG_DEBUG, "pf: pf_map_addr: "
					    "selected address ");
					pf_print_host(naddr, 0, af);
					addlog(". Failed to increase count!\n");
				}
				return (1);
			}
		}
		break;
	}

	if (rpool->opts & PF_POOL_STICKYADDR) {
		if (sns[type] != NULL) {
			pf_remove_src_node(sns[type]);
			sns[type] = NULL;
		}
		if (pf_insert_src_node(&sns[type], r, type, af, saddr, naddr,
		    0))
			return (1);
	}

	if (pf_status.debug >= LOG_NOTICE &&
	    (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) {
		log(LOG_NOTICE, "pf: pf_map_addr: selected address ");
		pf_print_host(naddr, 0, af);
		if ((rpool->opts & PF_POOL_TYPEMASK) ==
		    PF_POOL_LEASTSTATES)
			addlog(" with state count %llu", states);
		if ((rpool->addr.type == PF_ADDR_TABLE &&
		    rpool->addr.p.tbl->pfrkt_refcntcost > 0) ||
		    (rpool->addr.type == PF_ADDR_DYNIFTL &&
		    rpool->addr.p.dyn->pfid_kt->pfrkt_refcntcost > 0))
			addlog(" with weight %u", weight);
		addlog("\n");
	}

	return (0);
}
Esempio n. 2
0
int
pf_map_addr(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr,
    struct pf_addr *naddr, struct pf_addr *init_addr, struct pf_src_node **sns,
    struct pf_pool *rpool, enum pf_sn_types type)
{
	unsigned char		 hash[16];
	struct pf_addr		*raddr = &rpool->cur->addr.v.a.addr;
	struct pf_addr		*rmask = &rpool->cur->addr.v.a.mask;
	struct pf_pooladdr	*acur = rpool->cur;
	struct pf_src_node	 k;

	if (sns[type] == NULL && rpool->opts & PF_POOL_STICKYADDR &&
	    (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) {
		k.af = af;
		k.type = type;
		PF_ACPY(&k.addr, saddr, af);
		k.rule.ptr = r;
		pf_status.scounters[SCNT_SRC_NODE_SEARCH]++;
		sns[type] = RB_FIND(pf_src_tree, &tree_src_tracking, &k);
		if (sns[type] != NULL) {
			if (!PF_AZERO(&(sns[type])->raddr, af))
				PF_ACPY(naddr, &(sns[type])->raddr, af);
			if (pf_status.debug >= PF_DEBUG_MISC) {
				printf("pf_map_addr: src tracking (%u) maps ",
				    type);
				pf_print_host(&k.addr, 0, af);
				printf(" to ");
				pf_print_host(naddr, 0, af);
				printf("\n");
			}
			return (0);
		}
	}

	if (rpool->cur->addr.type == PF_ADDR_NOROUTE)
		return (1);
	if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) {
		switch (af) {
#ifdef INET
		case AF_INET:
			if (rpool->cur->addr.p.dyn->pfid_acnt4 < 1 &&
			    (rpool->opts & PF_POOL_TYPEMASK) !=
			    PF_POOL_ROUNDROBIN)
				return (1);
			 raddr = &rpool->cur->addr.p.dyn->pfid_addr4;
			 rmask = &rpool->cur->addr.p.dyn->pfid_mask4;
			break;
#endif /* INET */
#ifdef INET6
		case AF_INET6:
			if (rpool->cur->addr.p.dyn->pfid_acnt6 < 1 &&
			    (rpool->opts & PF_POOL_TYPEMASK) !=
			    PF_POOL_ROUNDROBIN)
				return (1);
			raddr = &rpool->cur->addr.p.dyn->pfid_addr6;
			rmask = &rpool->cur->addr.p.dyn->pfid_mask6;
			break;
#endif /* INET6 */
		}
	} else if (rpool->cur->addr.type == PF_ADDR_TABLE) {
		if ((rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_ROUNDROBIN)
			return (1); /* unsupported */
	} else {
		raddr = &rpool->cur->addr.v.a.addr;
		rmask = &rpool->cur->addr.v.a.mask;
	}

	switch (rpool->opts & PF_POOL_TYPEMASK) {
	case PF_POOL_NONE:
		PF_ACPY(naddr, raddr, af);
		break;
	case PF_POOL_BITMASK:
		PF_POOLMASK(naddr, raddr, rmask, saddr, af);
		break;
	case PF_POOL_RANDOM:
		if (init_addr != NULL && PF_AZERO(init_addr, af)) {
			switch (af) {
#ifdef INET
			case AF_INET:
				rpool->counter.addr32[0] = htonl(arc4random());
				break;
#endif /* INET */
#ifdef INET6
			case AF_INET6:
				if (rmask->addr32[3] != 0xffffffff)
					rpool->counter.addr32[3] =
					    htonl(arc4random());
				else
					break;
				if (rmask->addr32[2] != 0xffffffff)
					rpool->counter.addr32[2] =
					    htonl(arc4random());
				else
					break;
				if (rmask->addr32[1] != 0xffffffff)
					rpool->counter.addr32[1] =
					    htonl(arc4random());
				else
					break;
				if (rmask->addr32[0] != 0xffffffff)
					rpool->counter.addr32[0] =
					    htonl(arc4random());
				break;
#endif /* INET6 */
			}
			PF_POOLMASK(naddr, raddr, rmask, &rpool->counter, af);
			PF_ACPY(init_addr, naddr, af);

		} else {
			PF_AINC(&rpool->counter, af);
			PF_POOLMASK(naddr, raddr, rmask, &rpool->counter, af);
		}
		break;
	case PF_POOL_SRCHASH:
		pf_hash(saddr, (struct pf_addr *)&hash, &rpool->key, af);
		PF_POOLMASK(naddr, raddr, rmask, (struct pf_addr *)&hash, af);
		break;
	case PF_POOL_ROUNDROBIN:
		if (rpool->cur->addr.type == PF_ADDR_TABLE) {
			if (!pfr_pool_get(rpool->cur->addr.p.tbl,
			    &rpool->tblidx, &rpool->counter,
			    &raddr, &rmask, af))
				goto get_addr;
		} else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) {
			if (!pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt,
			    &rpool->tblidx, &rpool->counter,
			    &raddr, &rmask, af))
				goto get_addr;
		} else if (pf_match_addr(0, raddr, rmask, &rpool->counter, af))
			goto get_addr;

	try_next:
		if ((rpool->cur = TAILQ_NEXT(rpool->cur, entries)) == NULL)
			rpool->cur = TAILQ_FIRST(&rpool->list);
		if (rpool->cur->addr.type == PF_ADDR_TABLE) {
			rpool->tblidx = -1;
			if (pfr_pool_get(rpool->cur->addr.p.tbl,
			    &rpool->tblidx, &rpool->counter,
			    &raddr, &rmask, af)) {
				/* table contains no address of type 'af' */
				if (rpool->cur != acur)
					goto try_next;
				return (1);
			}
		} else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) {
			rpool->tblidx = -1;
			if (pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt,
			    &rpool->tblidx, &rpool->counter,
			    &raddr, &rmask, af)) {
				/* table contains no address of type 'af' */
				if (rpool->cur != acur)
					goto try_next;
				return (1);
			}
		} else {
			raddr = &rpool->cur->addr.v.a.addr;
			rmask = &rpool->cur->addr.v.a.mask;
			PF_ACPY(&rpool->counter, raddr, af);
		}

	get_addr:
		PF_ACPY(naddr, &rpool->counter, af);
		if (init_addr != NULL && PF_AZERO(init_addr, af))
			PF_ACPY(init_addr, naddr, af);
		PF_AINC(&rpool->counter, af);
		break;
	}

	if (rpool->opts & PF_POOL_STICKYADDR) {
		if (sns[type] != NULL) {
			pf_remove_src_node(sns[type]);
			sns[type] = NULL;
		}
		if (pf_insert_src_node(&sns[type], r, type, af, saddr, naddr,
		    0))
			return (1);
	}

	if (pf_status.debug >= PF_DEBUG_NOISY &&
	    (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) {
		printf("pf_map_addr: selected address ");
		pf_print_host(naddr, 0, af);
		printf("\n");
	}

	return (0);
}