xref: /openbsd-src/sys/net/pf_lb.c (revision e8d816753b9af4d465375cdb9e00ae6f5f109a36)
1*e8d81675Ssashan /*	$OpenBSD: pf_lb.c,v 1.74 2023/05/10 22:42:51 sashan Exp $ */
254f930a6Spyr 
354f930a6Spyr /*
454f930a6Spyr  * Copyright (c) 2001 Daniel Hartmeier
554f930a6Spyr  * Copyright (c) 2002 - 2008 Henning Brauer
654f930a6Spyr  * All rights reserved.
754f930a6Spyr  *
854f930a6Spyr  * Redistribution and use in source and binary forms, with or without
954f930a6Spyr  * modification, are permitted provided that the following conditions
1054f930a6Spyr  * are met:
1154f930a6Spyr  *
1254f930a6Spyr  *    - Redistributions of source code must retain the above copyright
1354f930a6Spyr  *      notice, this list of conditions and the following disclaimer.
1454f930a6Spyr  *    - Redistributions in binary form must reproduce the above
1554f930a6Spyr  *      copyright notice, this list of conditions and the following
1654f930a6Spyr  *      disclaimer in the documentation and/or other materials provided
1754f930a6Spyr  *      with the distribution.
1854f930a6Spyr  *
1954f930a6Spyr  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2054f930a6Spyr  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2154f930a6Spyr  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
2254f930a6Spyr  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
2354f930a6Spyr  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2454f930a6Spyr  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2554f930a6Spyr  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2654f930a6Spyr  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
2754f930a6Spyr  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2854f930a6Spyr  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
2954f930a6Spyr  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3054f930a6Spyr  * POSSIBILITY OF SUCH DAMAGE.
3154f930a6Spyr  *
3254f930a6Spyr  * Effort sponsored in part by the Defense Advanced Research Projects
3354f930a6Spyr  * Agency (DARPA) and Air Force Research Laboratory, Air Force
3454f930a6Spyr  * Materiel Command, USAF, under agreement number F30602-01-2-0537.
3554f930a6Spyr  *
3654f930a6Spyr  */
3754f930a6Spyr 
3854f930a6Spyr #include "bpfilter.h"
3954f930a6Spyr #include "pflog.h"
4054f930a6Spyr #include "pfsync.h"
4154f930a6Spyr #include "pflow.h"
4254f930a6Spyr 
4354f930a6Spyr #include <sys/param.h>
4454f930a6Spyr #include <sys/systm.h>
4554f930a6Spyr #include <sys/mbuf.h>
4654f930a6Spyr #include <sys/filio.h>
4754f930a6Spyr #include <sys/socket.h>
4854f930a6Spyr #include <sys/socketvar.h>
4954f930a6Spyr #include <sys/kernel.h>
5054f930a6Spyr #include <sys/time.h>
5154f930a6Spyr #include <sys/pool.h>
5254f930a6Spyr #include <sys/rwlock.h>
53a2fdc13dSmcbride #include <sys/syslog.h>
54d387fefaSzinke #include <sys/stdint.h>
5554f930a6Spyr 
56a558d13eStedu #include <crypto/siphash.h>
5754f930a6Spyr 
5854f930a6Spyr #include <net/if.h>
5954f930a6Spyr #include <net/bpf.h>
60c5a64272Sbluhm #include <net/route.h>
6154f930a6Spyr 
6254f930a6Spyr #include <netinet/in.h>
6354f930a6Spyr #include <netinet/ip.h>
64ab417b3aSbluhm #include <netinet/in_pcb.h>
6554f930a6Spyr #include <netinet/ip_var.h>
66ab417b3aSbluhm #include <netinet/ip_icmp.h>
67ab417b3aSbluhm #include <netinet/icmp_var.h>
6854f930a6Spyr #include <netinet/tcp.h>
6954f930a6Spyr #include <netinet/tcp_seq.h>
7054f930a6Spyr #include <netinet/tcp_timer.h>
71ab417b3aSbluhm #include <netinet/udp.h>
7254f930a6Spyr #include <netinet/udp_var.h>
7354f930a6Spyr #include <netinet/if_ether.h>
74ab417b3aSbluhm 
75ab417b3aSbluhm #ifdef INET6
76ab417b3aSbluhm #include <netinet/ip6.h>
77ab417b3aSbluhm #include <netinet/icmp6.h>
78ab417b3aSbluhm #endif /* INET6 */
7954f930a6Spyr 
8054f930a6Spyr #include <net/pfvar.h>
81ab417b3aSbluhm #include <net/pfvar_priv.h>
82ecaa3a52Ssashan 
83ecaa3a52Ssashan #if NPFLOG > 0
8454f930a6Spyr #include <net/if_pflog.h>
85ecaa3a52Ssashan #endif	/* NPFLOG > 0 */
86ecaa3a52Ssashan 
87ecaa3a52Ssashan #if NPFLOW > 0
8854f930a6Spyr #include <net/if_pflow.h>
89ecaa3a52Ssashan #endif	/* NPFLOW > 0 */
9054f930a6Spyr 
9154f930a6Spyr #if NPFSYNC > 0
9254f930a6Spyr #include <net/if_pfsync.h>
9354f930a6Spyr #endif /* NPFSYNC > 0 */
9454f930a6Spyr 
95252a0552Sreyk u_int64_t		 pf_hash(struct pf_addr *, struct pf_addr *,
9654f930a6Spyr 			    struct pf_poolhashkey *, sa_family_t);
97ccf63ac6Shenning int			 pf_get_sport(struct pf_pdesc *, struct pf_rule *,
98ccf63ac6Shenning 			    struct pf_addr *, u_int16_t *, u_int16_t,
99ccf63ac6Shenning 			    u_int16_t, struct pf_src_node **);
1004cb186b8Syasuoka int			 pf_map_addr_states_increase(sa_family_t,
1014cb186b8Syasuoka 				struct pf_pool *, struct pf_addr *);
10297326e01Sclaudio int			 pf_get_transaddr_af(struct pf_rule *,
10397326e01Sclaudio 			    struct pf_pdesc *, struct pf_src_node **);
1043f915362Smarkus int			 pf_map_addr_sticky(sa_family_t, struct pf_rule *,
1053f915362Smarkus 			    struct pf_addr *, struct pf_addr *,
1063f915362Smarkus 			    struct pf_src_node **, struct pf_pool *,
1073f915362Smarkus 			    enum pf_sn_types);
10854f930a6Spyr 
109252a0552Sreyk u_int64_t
pf_hash(struct pf_addr * inaddr,struct pf_addr * hash,struct pf_poolhashkey * key,sa_family_t af)11054f930a6Spyr pf_hash(struct pf_addr *inaddr, struct pf_addr *hash,
11154f930a6Spyr     struct pf_poolhashkey *key, sa_family_t af)
11254f930a6Spyr {
113252a0552Sreyk 	uint64_t res = 0;
114a558d13eStedu #ifdef INET6
115a558d13eStedu 	union {
116a558d13eStedu 		uint64_t hash64;
117a558d13eStedu 		uint32_t hash32[2];
118a558d13eStedu 	} h;
1199b00340fSsashan #endif	/* INET6 */
12054f930a6Spyr 
12154f930a6Spyr 	switch (af) {
12254f930a6Spyr 	case AF_INET:
123252a0552Sreyk 		res = SipHash24((SIPHASH_KEY *)key,
124a558d13eStedu 		    &inaddr->addr32[0], sizeof(inaddr->addr32[0]));
125252a0552Sreyk 		hash->addr32[0] = res;
12654f930a6Spyr 		break;
12754f930a6Spyr #ifdef INET6
12854f930a6Spyr 	case AF_INET6:
129252a0552Sreyk 		res = SipHash24((SIPHASH_KEY *)key, &inaddr->addr32[0],
130a558d13eStedu 		    4 * sizeof(inaddr->addr32[0]));
131252a0552Sreyk 		h.hash64 = res;
132a558d13eStedu 		hash->addr32[0] = h.hash32[0];
133a558d13eStedu 		hash->addr32[1] = h.hash32[1];
134a558d13eStedu 		/*
135a558d13eStedu 		 * siphash isn't big enough, but flipping it around is
136a558d13eStedu 		 * good enough here.
137a558d13eStedu 		 */
138a558d13eStedu 		hash->addr32[2] = ~h.hash32[1];
139a558d13eStedu 		hash->addr32[3] = ~h.hash32[0];
14054f930a6Spyr 		break;
14154f930a6Spyr #endif /* INET6 */
1429b00340fSsashan 	default:
1439b00340fSsashan 		unhandled_af(af);
14454f930a6Spyr 	}
145252a0552Sreyk 	return (res);
14654f930a6Spyr }
14754f930a6Spyr 
14854f930a6Spyr int
pf_get_sport(struct pf_pdesc * pd,struct pf_rule * r,struct pf_addr * naddr,u_int16_t * nport,u_int16_t low,u_int16_t high,struct pf_src_node ** sn)149ccf63ac6Shenning pf_get_sport(struct pf_pdesc *pd, struct pf_rule *r,
15054f930a6Spyr     struct pf_addr *naddr, u_int16_t *nport, u_int16_t low, u_int16_t high,
151ccf63ac6Shenning     struct pf_src_node **sn)
15254f930a6Spyr {
15354f930a6Spyr 	struct pf_state_key_cmp	key;
15454f930a6Spyr 	struct pf_addr		init_addr;
15554f930a6Spyr 	u_int16_t		cut;
1564af3c109Sbluhm 	int			dir = (pd->dir == PF_IN) ? PF_OUT : PF_IN;
1574af3c109Sbluhm 	int			sidx = pd->sidx;
1584af3c109Sbluhm 	int			didx = pd->didx;
15954f930a6Spyr 
160568ff528Shenning 	memset(&init_addr, 0, sizeof(init_addr));
16197326e01Sclaudio 	if (pf_map_addr(pd->naf, r, &pd->nsaddr, naddr, &init_addr, sn, &r->nat,
162c3e2fff9Shenning 	    PF_SN_NAT))
16354f930a6Spyr 		return (1);
16454f930a6Spyr 
165c5a64272Sbluhm 	if (pd->proto == IPPROTO_ICMP) {
166c5a64272Sbluhm 		if (pd->ndport == htons(ICMP_ECHO)) {
16754f930a6Spyr 			low = 1;
16854f930a6Spyr 			high = 65535;
16970bf7555Smcbride 		} else
17070bf7555Smcbride 			return (0);	/* Don't try to modify non-echo ICMP */
17154f930a6Spyr 	}
172c5a64272Sbluhm #ifdef INET6
173c5a64272Sbluhm 	if (pd->proto == IPPROTO_ICMPV6) {
174c5a64272Sbluhm 		if (pd->ndport == htons(ICMP6_ECHO_REQUEST)) {
175c5a64272Sbluhm 			low = 1;
176c5a64272Sbluhm 			high = 65535;
177c5a64272Sbluhm 		} else
178c5a64272Sbluhm 			return (0);	/* Don't try to modify non-echo ICMP */
179c5a64272Sbluhm 	}
180c5a64272Sbluhm #endif /* INET6 */
18154f930a6Spyr 
18254f930a6Spyr 	do {
18397326e01Sclaudio 		key.af = pd->naf;
184ccf63ac6Shenning 		key.proto = pd->proto;
185ccf63ac6Shenning 		key.rdomain = pd->rdomain;
186492cf661Skn 		pf_addrcpy(&key.addr[didx], &pd->ndaddr, key.af);
187492cf661Skn 		pf_addrcpy(&key.addr[sidx], naddr, key.af);
1884af3c109Sbluhm 		key.port[didx] = pd->ndport;
18954f930a6Spyr 
19054f930a6Spyr 		/*
19154f930a6Spyr 		 * port search; start random, step;
19254f930a6Spyr 		 * similar 2 portloop in in_pcbbind
19354f930a6Spyr 		 */
194ccf63ac6Shenning 		if (!(pd->proto == IPPROTO_TCP || pd->proto == IPPROTO_UDP ||
1957da8ba26Smikeb 		    pd->proto == IPPROTO_ICMP || pd->proto == IPPROTO_ICMPV6)) {
1960c449d89Ssthen 			/* XXX bug: icmp states dont use the id on both
1970c449d89Ssthen 			 * XXX sides (traceroute -I through nat) */
1984af3c109Sbluhm 			key.port[sidx] = pd->nsport;
199*e8d81675Ssashan 			key.hash = pf_pkt_hash(key.af, key.proto, &key.addr[0],
200*e8d81675Ssashan 			    &key.addr[1], key.port[0], key.port[1]);
2014af3c109Sbluhm 			if (pf_find_state_all(&key, dir, NULL) == NULL) {
202ccf63ac6Shenning 				*nport = pd->nsport;
20354f930a6Spyr 				return (0);
2040c449d89Ssthen 			}
20554f930a6Spyr 		} else if (low == 0 && high == 0) {
2064af3c109Sbluhm 			key.port[sidx] = pd->nsport;
207*e8d81675Ssashan 			key.hash = pf_pkt_hash(key.af, key.proto, &key.addr[0],
208*e8d81675Ssashan 			    &key.addr[1], key.port[0], key.port[1]);
2094af3c109Sbluhm 			if (pf_find_state_all(&key, dir, NULL) == NULL) {
210ccf63ac6Shenning 				*nport = pd->nsport;
21154f930a6Spyr 				return (0);
2120c449d89Ssthen 			}
21354f930a6Spyr 		} else if (low == high) {
2144af3c109Sbluhm 			key.port[sidx] = htons(low);
215*e8d81675Ssashan 			key.hash = pf_pkt_hash(key.af, key.proto, &key.addr[0],
216*e8d81675Ssashan 			    &key.addr[1], key.port[0], key.port[1]);
2174af3c109Sbluhm 			if (pf_find_state_all(&key, dir, NULL) == NULL) {
21854f930a6Spyr 				*nport = htons(low);
21954f930a6Spyr 				return (0);
22054f930a6Spyr 			}
22154f930a6Spyr 		} else {
222425f1cf2Sbluhm 			u_int32_t tmp;
22354f930a6Spyr 
22454f930a6Spyr 			if (low > high) {
22554f930a6Spyr 				tmp = low;
22654f930a6Spyr 				low = high;
22754f930a6Spyr 				high = tmp;
22854f930a6Spyr 			}
22954f930a6Spyr 			/* low < high */
23054f930a6Spyr 			cut = arc4random_uniform(1 + high - low) + low;
23154f930a6Spyr 			/* low <= cut <= high */
232425f1cf2Sbluhm 			for (tmp = cut; tmp <= high && tmp <= 0xffff; ++tmp) {
2334af3c109Sbluhm 				key.port[sidx] = htons(tmp);
234*e8d81675Ssashan 				key.hash = pf_pkt_hash(key.af, key.proto,
235*e8d81675Ssashan 				    &key.addr[0], &key.addr[1], key.port[0],
236*e8d81675Ssashan 				    key.port[1]);
2374af3c109Sbluhm 				if (pf_find_state_all(&key, dir, NULL) ==
238ccf63ac6Shenning 				    NULL && !in_baddynamic(tmp, pd->proto)) {
23954f930a6Spyr 					*nport = htons(tmp);
24054f930a6Spyr 					return (0);
24154f930a6Spyr 				}
24254f930a6Spyr 			}
243425f1cf2Sbluhm 			tmp = cut;
244425f1cf2Sbluhm 			for (tmp -= 1; tmp >= low && tmp <= 0xffff; --tmp) {
2454af3c109Sbluhm 				key.port[sidx] = htons(tmp);
246*e8d81675Ssashan 				key.hash = pf_pkt_hash(key.af, key.proto,
247*e8d81675Ssashan 				    &key.addr[0], &key.addr[1], key.port[0],
248*e8d81675Ssashan 				    key.port[1]);
2494af3c109Sbluhm 				if (pf_find_state_all(&key, dir, NULL) ==
250ccf63ac6Shenning 				    NULL && !in_baddynamic(tmp, pd->proto)) {
25154f930a6Spyr 					*nport = htons(tmp);
25254f930a6Spyr 					return (0);
25354f930a6Spyr 				}
25454f930a6Spyr 			}
25554f930a6Spyr 		}
25654f930a6Spyr 
2570ef3d4feShenning 		switch (r->nat.opts & PF_POOL_TYPEMASK) {
25854f930a6Spyr 		case PF_POOL_RANDOM:
25954f930a6Spyr 		case PF_POOL_ROUNDROBIN:
260bcb11948Szinke 		case PF_POOL_LEASTSTATES:
261e34cb833Smikeb 			/*
262e34cb833Smikeb 			 * pick a different source address since we're out
263e34cb833Smikeb 			 * of free port choices for the current one.
264e34cb833Smikeb 			 */
26597326e01Sclaudio 			if (pf_map_addr(pd->naf, r, &pd->nsaddr, naddr,
266ccf63ac6Shenning 			    &init_addr, sn, &r->nat, PF_SN_NAT))
26754f930a6Spyr 				return (1);
26854f930a6Spyr 			break;
26954f930a6Spyr 		case PF_POOL_NONE:
27054f930a6Spyr 		case PF_POOL_SRCHASH:
27154f930a6Spyr 		case PF_POOL_BITMASK:
27254f930a6Spyr 		default:
27354f930a6Spyr 			return (1);
27454f930a6Spyr 		}
27597326e01Sclaudio 	} while (! PF_AEQ(&init_addr, naddr, pd->naf) );
27654f930a6Spyr 	return (1);					/* none available */
27754f930a6Spyr }
27854f930a6Spyr 
27954f930a6Spyr int
pf_map_addr_sticky(sa_family_t af,struct pf_rule * r,struct pf_addr * saddr,struct pf_addr * naddr,struct pf_src_node ** sns,struct pf_pool * rpool,enum pf_sn_types type)2803f915362Smarkus pf_map_addr_sticky(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr,
2813f915362Smarkus     struct pf_addr *naddr, struct pf_src_node **sns, struct pf_pool *rpool,
2823f915362Smarkus     enum pf_sn_types type)
28354f930a6Spyr {
2843f915362Smarkus 	struct pf_addr		*raddr, *rmask, *cached;
2853f915362Smarkus 	struct pf_state		*s;
28654f930a6Spyr 	struct pf_src_node	 k;
2873f915362Smarkus 	int			 valid;
28854f930a6Spyr 
28954f930a6Spyr 	k.af = af;
290c3e2fff9Shenning 	k.type = type;
291492cf661Skn 	pf_addrcpy(&k.addr, saddr, af);
29254f930a6Spyr 	k.rule.ptr = r;
29354f930a6Spyr 	pf_status.scounters[SCNT_SRC_NODE_SEARCH]++;
294342c264fSdlg 	sns[type] = RB_FIND(pf_src_tree, &tree_src_tracking, &k);
2953f915362Smarkus 	if (sns[type] == NULL)
2963f915362Smarkus 		return (-1);
2973f915362Smarkus 
2983f915362Smarkus 	/* check if the cached entry is still valid */
2993f915362Smarkus 	cached = &(sns[type])->raddr;
3003f915362Smarkus 	valid = 0;
3013f915362Smarkus 	if (PF_AZERO(cached, af)) {
3023f915362Smarkus 		valid = 1;
3033f915362Smarkus 	} else if (rpool->addr.type == PF_ADDR_DYNIFTL) {
3043f915362Smarkus 		if (pfr_kentry_byaddr(rpool->addr.p.dyn->pfid_kt, cached,
3053f915362Smarkus 		    af, 0))
3063f915362Smarkus 			valid = 1;
3073f915362Smarkus 	} else if (rpool->addr.type == PF_ADDR_TABLE) {
3083f915362Smarkus 		if (pfr_kentry_byaddr(rpool->addr.p.tbl, cached, af, 0))
3093f915362Smarkus 			valid = 1;
3103f915362Smarkus 	} else if (rpool->addr.type != PF_ADDR_NOROUTE) {
3113f915362Smarkus 		raddr = &rpool->addr.v.a.addr;
3123f915362Smarkus 		rmask = &rpool->addr.v.a.mask;
3133f915362Smarkus 		valid = pf_match_addr(0, raddr, rmask, cached, af);
3143f915362Smarkus 	}
3153f915362Smarkus 	if (!valid) {
3163f915362Smarkus 		if (pf_status.debug >= LOG_DEBUG) {
3173f915362Smarkus 			log(LOG_DEBUG, "pf: pf_map_addr: "
3183f915362Smarkus 			    "stale src tracking (%u) ", type);
3193f915362Smarkus 			pf_print_host(&k.addr, 0, af);
3203f915362Smarkus 			addlog(" to ");
3213f915362Smarkus 			pf_print_host(cached, 0, af);
3223f915362Smarkus 			addlog("\n");
3233f915362Smarkus 		}
3243f915362Smarkus 		if (sns[type]->states != 0) {
3253f915362Smarkus 			/* XXX expensive */
326e9311d0bSdlg 			RBT_FOREACH(s, pf_state_tree_id, &tree_id)
327e9311d0bSdlg 				pf_state_rm_src_node(s, sns[type]);
3283f915362Smarkus 		}
3293f915362Smarkus 		sns[type]->expire = 1;
3303f915362Smarkus 		pf_remove_src_node(sns[type]);
3313f915362Smarkus 		sns[type] = NULL;
3323f915362Smarkus 		return (-1);
3333f915362Smarkus 	}
3344cb186b8Syasuoka 
335511c53b1Syasuoka 
336511c53b1Syasuoka 	if (!PF_AZERO(cached, af)) {
337511c53b1Syasuoka 		pf_addrcpy(naddr, cached, af);
338511c53b1Syasuoka 		if ((rpool->opts & PF_POOL_TYPEMASK) == PF_POOL_LEASTSTATES &&
339511c53b1Syasuoka 		    pf_map_addr_states_increase(af, rpool, cached) == -1)
3404cb186b8Syasuoka 			return (-1);
3414cb186b8Syasuoka 	}
342a2fdc13dSmcbride 	if (pf_status.debug >= LOG_DEBUG) {
343a2fdc13dSmcbride 		log(LOG_DEBUG, "pf: pf_map_addr: "
344a2fdc13dSmcbride 		    "src tracking (%u) maps ", type);
34554f930a6Spyr 		pf_print_host(&k.addr, 0, af);
346a2fdc13dSmcbride 		addlog(" to ");
34754f930a6Spyr 		pf_print_host(naddr, 0, af);
348a2fdc13dSmcbride 		addlog("\n");
34954f930a6Spyr 	}
350b4470a7bSyasuoka 
351b4470a7bSyasuoka 	if (sns[type]->kif != NULL)
352b4470a7bSyasuoka 		rpool->kif = sns[type]->kif;
353b4470a7bSyasuoka 
35454f930a6Spyr 	return (0);
35554f930a6Spyr }
3563f915362Smarkus 
357c2364f2aSsashan uint32_t
pf_rand_addr(uint32_t mask)358c2364f2aSsashan pf_rand_addr(uint32_t mask)
359c2364f2aSsashan {
360c2364f2aSsashan 	uint32_t addr;
361c2364f2aSsashan 
362c2364f2aSsashan 	mask = ~ntohl(mask);
363c2364f2aSsashan 	addr = arc4random_uniform(mask + 1);
364c2364f2aSsashan 
365c2364f2aSsashan 	return (htonl(addr));
366c2364f2aSsashan }
367c2364f2aSsashan 
3683f915362Smarkus 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)3693f915362Smarkus pf_map_addr(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr,
3703f915362Smarkus     struct pf_addr *naddr, struct pf_addr *init_addr, struct pf_src_node **sns,
3713f915362Smarkus     struct pf_pool *rpool, enum pf_sn_types type)
3723f915362Smarkus {
3734bd0ce1dSjan 	struct pf_addr		 hash;
3743f915362Smarkus 	struct pf_addr		 faddr;
3753f915362Smarkus 	struct pf_addr		*raddr = &rpool->addr.v.a.addr;
3763f915362Smarkus 	struct pf_addr		*rmask = &rpool->addr.v.a.mask;
37726b62979Syasuoka 	struct pfr_ktable	*kt;
3784cb186b8Syasuoka 	struct pfi_kif		*kif;
3793f915362Smarkus 	u_int64_t		 states;
3803f915362Smarkus 	u_int16_t		 weight;
3813f915362Smarkus 	u_int64_t		 load;
3823f915362Smarkus 	u_int64_t		 cload;
383252a0552Sreyk 	u_int64_t		 hashidx;
384252a0552Sreyk 	int			 cnt;
3853f915362Smarkus 
3863f915362Smarkus 	if (sns[type] == NULL && rpool->opts & PF_POOL_STICKYADDR &&
3873f915362Smarkus 	    (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE &&
3883f915362Smarkus 	    pf_map_addr_sticky(af, r, saddr, naddr, sns, rpool, type) == 0)
3893f915362Smarkus 		return (0);
39054f930a6Spyr 
39136754172Smcbride 	if (rpool->addr.type == PF_ADDR_NOROUTE)
39254f930a6Spyr 		return (1);
39336754172Smcbride 	if (rpool->addr.type == PF_ADDR_DYNIFTL) {
39454f930a6Spyr 		switch (af) {
39554f930a6Spyr 		case AF_INET:
39636754172Smcbride 			if (rpool->addr.p.dyn->pfid_acnt4 < 1 &&
397252a0552Sreyk 			    !PF_POOL_DYNTYPE(rpool->opts))
39854f930a6Spyr 				return (1);
39936754172Smcbride 			raddr = &rpool->addr.p.dyn->pfid_addr4;
40036754172Smcbride 			rmask = &rpool->addr.p.dyn->pfid_mask4;
40154f930a6Spyr 			break;
40254f930a6Spyr #ifdef INET6
40354f930a6Spyr 		case AF_INET6:
40436754172Smcbride 			if (rpool->addr.p.dyn->pfid_acnt6 < 1 &&
405252a0552Sreyk 			    !PF_POOL_DYNTYPE(rpool->opts))
40654f930a6Spyr 				return (1);
40736754172Smcbride 			raddr = &rpool->addr.p.dyn->pfid_addr6;
40836754172Smcbride 			rmask = &rpool->addr.p.dyn->pfid_mask6;
40954f930a6Spyr 			break;
41054f930a6Spyr #endif /* INET6 */
4119b00340fSsashan 		default:
4129b00340fSsashan 			unhandled_af(af);
41354f930a6Spyr 		}
41436754172Smcbride 	} else if (rpool->addr.type == PF_ADDR_TABLE) {
415252a0552Sreyk 		if (!PF_POOL_DYNTYPE(rpool->opts))
41654f930a6Spyr 			return (1); /* unsupported */
41754f930a6Spyr 	} else {
41836754172Smcbride 		raddr = &rpool->addr.v.a.addr;
41936754172Smcbride 		rmask = &rpool->addr.v.a.mask;
42054f930a6Spyr 	}
42154f930a6Spyr 
42254f930a6Spyr 	switch (rpool->opts & PF_POOL_TYPEMASK) {
42354f930a6Spyr 	case PF_POOL_NONE:
424492cf661Skn 		pf_addrcpy(naddr, raddr, af);
42554f930a6Spyr 		break;
42654f930a6Spyr 	case PF_POOL_BITMASK:
427492cf661Skn 		pf_poolmask(naddr, raddr, rmask, saddr, af);
42854f930a6Spyr 		break;
42954f930a6Spyr 	case PF_POOL_RANDOM:
43026b62979Syasuoka 		if (rpool->addr.type == PF_ADDR_TABLE ||
43126b62979Syasuoka 		    rpool->addr.type == PF_ADDR_DYNIFTL) {
43226b62979Syasuoka 			if (rpool->addr.type == PF_ADDR_TABLE)
43326b62979Syasuoka 				kt = rpool->addr.p.tbl;
434c30ec822Syasuoka 			else
43526b62979Syasuoka 				kt = rpool->addr.p.dyn->pfid_kt;
43626b62979Syasuoka 			kt = pfr_ktable_select_active(kt);
43726b62979Syasuoka 			if (kt == NULL)
438252a0552Sreyk 				return (1);
43926b62979Syasuoka 
44026b62979Syasuoka 			cnt = kt->pfrkt_cnt;
441c30ec822Syasuoka 			if (cnt == 0)
442c30ec822Syasuoka 				rpool->tblidx = 0;
443c30ec822Syasuoka 			else
444252a0552Sreyk 				rpool->tblidx = (int)arc4random_uniform(cnt);
445252a0552Sreyk 			memset(&rpool->counter, 0, sizeof(rpool->counter));
446252a0552Sreyk 			if (pfr_pool_get(rpool, &raddr, &rmask, af))
447252a0552Sreyk 				return (1);
448492cf661Skn 			pf_addrcpy(naddr, &rpool->counter, af);
449252a0552Sreyk 		} else if (init_addr != NULL && PF_AZERO(init_addr, af)) {
45054f930a6Spyr 			switch (af) {
45154f930a6Spyr 			case AF_INET:
452c2364f2aSsashan 				rpool->counter.addr32[0] = pf_rand_addr(
453c2364f2aSsashan 				    rmask->addr32[0]);
45454f930a6Spyr 				break;
45554f930a6Spyr #ifdef INET6
45654f930a6Spyr 			case AF_INET6:
45754f930a6Spyr 				if (rmask->addr32[3] != 0xffffffff)
458c2364f2aSsashan 					rpool->counter.addr32[3] = pf_rand_addr(
459c2364f2aSsashan 					    rmask->addr32[3]);
46054f930a6Spyr 				else
46154f930a6Spyr 					break;
46254f930a6Spyr 				if (rmask->addr32[2] != 0xffffffff)
463c2364f2aSsashan 					rpool->counter.addr32[2] = pf_rand_addr(
464c2364f2aSsashan 					    rmask->addr32[2]);
46554f930a6Spyr 				else
46654f930a6Spyr 					break;
46754f930a6Spyr 				if (rmask->addr32[1] != 0xffffffff)
468c2364f2aSsashan 					rpool->counter.addr32[1] = pf_rand_addr(
469c2364f2aSsashan 					    rmask->addr32[1]);
47054f930a6Spyr 				else
47154f930a6Spyr 					break;
47254f930a6Spyr 				if (rmask->addr32[0] != 0xffffffff)
473c2364f2aSsashan 					rpool->counter.addr32[0] = pf_rand_addr(
474c2364f2aSsashan 					    rmask->addr32[0]);
47554f930a6Spyr 				break;
47654f930a6Spyr #endif /* INET6 */
4779b00340fSsashan 			default:
4789b00340fSsashan 				unhandled_af(af);
47954f930a6Spyr 			}
480492cf661Skn 			pf_poolmask(naddr, raddr, rmask, &rpool->counter, af);
481492cf661Skn 			pf_addrcpy(init_addr, naddr, af);
48254f930a6Spyr 
48354f930a6Spyr 		} else {
484492cf661Skn 			pf_addr_inc(&rpool->counter, af);
485492cf661Skn 			pf_poolmask(naddr, raddr, rmask, &rpool->counter, af);
48654f930a6Spyr 		}
48754f930a6Spyr 		break;
48854f930a6Spyr 	case PF_POOL_SRCHASH:
4894bd0ce1dSjan 		hashidx = pf_hash(saddr, &hash, &rpool->key, af);
49026b62979Syasuoka 
49126b62979Syasuoka 		if (rpool->addr.type == PF_ADDR_TABLE ||
49226b62979Syasuoka 		    rpool->addr.type == PF_ADDR_DYNIFTL) {
49326b62979Syasuoka 			if (rpool->addr.type == PF_ADDR_TABLE)
49426b62979Syasuoka 				kt = rpool->addr.p.tbl;
495c30ec822Syasuoka 			else
49626b62979Syasuoka 				kt = rpool->addr.p.dyn->pfid_kt;
49726b62979Syasuoka 			kt = pfr_ktable_select_active(kt);
49826b62979Syasuoka 			if (kt == NULL)
499252a0552Sreyk 				return (1);
50026b62979Syasuoka 
50126b62979Syasuoka 			cnt = kt->pfrkt_cnt;
502c30ec822Syasuoka 			if (cnt == 0)
503c30ec822Syasuoka 				rpool->tblidx = 0;
504c30ec822Syasuoka 			else
505252a0552Sreyk 				rpool->tblidx = (int)(hashidx % cnt);
506252a0552Sreyk 			memset(&rpool->counter, 0, sizeof(rpool->counter));
507252a0552Sreyk 			if (pfr_pool_get(rpool, &raddr, &rmask, af))
508252a0552Sreyk 				return (1);
509492cf661Skn 			pf_addrcpy(naddr, &rpool->counter, af);
510252a0552Sreyk 		} else {
5114bd0ce1dSjan 			pf_poolmask(naddr, raddr, rmask, &hash, af);
512252a0552Sreyk 		}
51354f930a6Spyr 		break;
51454f930a6Spyr 	case PF_POOL_ROUNDROBIN:
5152b27abb2Smarkus 		if (rpool->addr.type == PF_ADDR_TABLE ||
5162b27abb2Smarkus 		    rpool->addr.type == PF_ADDR_DYNIFTL) {
51760152e75Smarkus 			if (pfr_pool_get(rpool, &raddr, &rmask, af)) {
51860152e75Smarkus 				/*
51960152e75Smarkus 				 * reset counter in case its value
52060152e75Smarkus 				 * has been removed from the pool.
52160152e75Smarkus 				 */
522568ff528Shenning 				memset(&rpool->counter, 0,
523568ff528Shenning 				    sizeof(rpool->counter));
5242b27abb2Smarkus 				if (pfr_pool_get(rpool, &raddr, &rmask, af))
52536754172Smcbride 					return (1);
52660152e75Smarkus 			}
527308aaa40Ssashan 		} else if (PF_AZERO(&rpool->counter, af)) {
528308aaa40Ssashan 			/*
529c2364f2aSsashan 			 * fall back to POOL_NONE if there is a single host
530c2364f2aSsashan 			 * address in pool.
531308aaa40Ssashan 			 */
53256efcf3bSbenno 			if (af == AF_INET &&
53356efcf3bSbenno 			    rmask->addr32[0] == INADDR_BROADCAST) {
534308aaa40Ssashan 				pf_addrcpy(naddr, raddr, af);
535308aaa40Ssashan 				break;
536c2364f2aSsashan 			}
53756efcf3bSbenno #ifdef INET6
53856efcf3bSbenno 			if (af == AF_INET6 &&
53956efcf3bSbenno 			    IN6_ARE_ADDR_EQUAL(&rmask->v6, &in6mask128)) {
54056efcf3bSbenno 				pf_addrcpy(naddr, raddr, af);
54156efcf3bSbenno 				break;
54256efcf3bSbenno 			}
54356efcf3bSbenno #endif
54454f930a6Spyr 		} else if (pf_match_addr(0, raddr, rmask, &rpool->counter, af))
54554f930a6Spyr 			return (1);
54654f930a6Spyr 
547cbdc262eSmcbride 		/* iterate over table if it contains entries which are weighted */
54842af0bf3Smcbride 		if ((rpool->addr.type == PF_ADDR_TABLE &&
54942af0bf3Smcbride 		    rpool->addr.p.tbl->pfrkt_refcntcost > 0) ||
55042af0bf3Smcbride 		    (rpool->addr.type == PF_ADDR_DYNIFTL &&
55142af0bf3Smcbride 		    rpool->addr.p.dyn->pfid_kt->pfrkt_refcntcost > 0)) {
552cbdc262eSmcbride 			do {
5532b27abb2Smarkus 				if (rpool->addr.type == PF_ADDR_TABLE ||
5542b27abb2Smarkus 				    rpool->addr.type == PF_ADDR_DYNIFTL) {
5552b27abb2Smarkus 					if (pfr_pool_get(rpool,
5562b27abb2Smarkus 					    &raddr, &rmask, af))
557cbdc262eSmcbride 						return (1);
55842af0bf3Smcbride 				} else {
55942af0bf3Smcbride 					log(LOG_ERR, "pf: pf_map_addr: "
56042af0bf3Smcbride 					    "weighted RR failure");
561cbdc262eSmcbride 					return (1);
56242af0bf3Smcbride 				}
56342af0bf3Smcbride 				if (rpool->weight >= rpool->curweight)
56442af0bf3Smcbride 					break;
565492cf661Skn 				pf_addr_inc(&rpool->counter, af);
56642af0bf3Smcbride 			} while (1);
567cbdc262eSmcbride 
568cbdc262eSmcbride 			weight = rpool->weight;
569cbdc262eSmcbride 		}
570cbdc262eSmcbride 
571c2364f2aSsashan 		pf_poolmask(naddr, raddr, rmask, &rpool->counter, af);
57254f930a6Spyr 		if (init_addr != NULL && PF_AZERO(init_addr, af))
573c2364f2aSsashan 			pf_addrcpy(init_addr, &rpool->counter, af);
574492cf661Skn 		pf_addr_inc(&rpool->counter, af);
57554f930a6Spyr 		break;
576bcb11948Szinke 	case PF_POOL_LEASTSTATES:
577bcb11948Szinke 		/* retrieve an address first */
5782b27abb2Smarkus 		if (rpool->addr.type == PF_ADDR_TABLE ||
5792b27abb2Smarkus 		    rpool->addr.type == PF_ADDR_DYNIFTL) {
58060152e75Smarkus 			if (pfr_pool_get(rpool, &raddr, &rmask, af)) {
58160152e75Smarkus 				/* see PF_POOL_ROUNDROBIN */
582568ff528Shenning 				memset(&rpool->counter, 0,
583568ff528Shenning 				    sizeof(rpool->counter));
5842b27abb2Smarkus 				if (pfr_pool_get(rpool, &raddr, &rmask, af))
585bcb11948Szinke 					return (1);
58660152e75Smarkus 			}
587bcb11948Szinke 		} else if (pf_match_addr(0, raddr, rmask, &rpool->counter, af))
588bcb11948Szinke 			return (1);
589bcb11948Szinke 
590bcb11948Szinke 		states = rpool->states;
591d387fefaSzinke 		weight = rpool->weight;
5924cb186b8Syasuoka 		kif = rpool->kif;
593d387fefaSzinke 
594d387fefaSzinke 		if ((rpool->addr.type == PF_ADDR_TABLE &&
595d387fefaSzinke 		    rpool->addr.p.tbl->pfrkt_refcntcost > 0) ||
596d387fefaSzinke 		    (rpool->addr.type == PF_ADDR_DYNIFTL &&
597d387fefaSzinke 		    rpool->addr.p.dyn->pfid_kt->pfrkt_refcntcost > 0))
598d387fefaSzinke 			load = ((UINT16_MAX * rpool->states) / rpool->weight);
599d387fefaSzinke 		else
600d387fefaSzinke 			load = states;
601bcb11948Szinke 
602492cf661Skn 		pf_addrcpy(&faddr, &rpool->counter, af);
603bcb11948Szinke 
604492cf661Skn 		pf_addrcpy(naddr, &rpool->counter, af);
605bcb11948Szinke 		if (init_addr != NULL && PF_AZERO(init_addr, af))
606492cf661Skn 			pf_addrcpy(init_addr, naddr, af);
607bcb11948Szinke 
608bcb11948Szinke 		/*
609bcb11948Szinke 		 * iterate *once* over whole table and find destination with
610bcb11948Szinke 		 * least connection
611bcb11948Szinke 		 */
612cbdc262eSmcbride 		do  {
613492cf661Skn 			pf_addr_inc(&rpool->counter, af);
6142b27abb2Smarkus 			if (rpool->addr.type == PF_ADDR_TABLE ||
6152b27abb2Smarkus 			    rpool->addr.type == PF_ADDR_DYNIFTL) {
6162b27abb2Smarkus 				if (pfr_pool_get(rpool, &raddr, &rmask, af))
617bcb11948Szinke 					return (1);
618cbdc262eSmcbride 			} else if (pf_match_addr(0, raddr, rmask,
619cbdc262eSmcbride 			    &rpool->counter, af))
620cbdc262eSmcbride 				return (1);
621bcb11948Szinke 
622d387fefaSzinke 			if ((rpool->addr.type == PF_ADDR_TABLE &&
623d387fefaSzinke 			    rpool->addr.p.tbl->pfrkt_refcntcost > 0) ||
624d387fefaSzinke 			    (rpool->addr.type == PF_ADDR_DYNIFTL &&
625d387fefaSzinke 			    rpool->addr.p.dyn->pfid_kt->pfrkt_refcntcost > 0))
626d387fefaSzinke 				cload = ((UINT16_MAX * rpool->states)
627d387fefaSzinke 					/ rpool->weight);
628d387fefaSzinke 			else
629d387fefaSzinke 				cload = rpool->states;
630d387fefaSzinke 
631bcb11948Szinke 			/* find lc minimum */
632d387fefaSzinke 			if (cload < load) {
633bcb11948Szinke 				states = rpool->states;
634d387fefaSzinke 				weight = rpool->weight;
6354cb186b8Syasuoka 				kif = rpool->kif;
636d387fefaSzinke 				load = cload;
637bcb11948Szinke 
638492cf661Skn 				pf_addrcpy(naddr, &rpool->counter, af);
639bcb11948Szinke 				if (init_addr != NULL &&
640bcb11948Szinke 				    PF_AZERO(init_addr, af))
641492cf661Skn 				    pf_addrcpy(init_addr, naddr, af);
642bcb11948Szinke 			}
643cbdc262eSmcbride 		} while (pf_match_addr(1, &faddr, rmask, &rpool->counter, af) &&
644cbdc262eSmcbride 		    (states > 0));
645bcb11948Szinke 
6464cb186b8Syasuoka 		if (pf_map_addr_states_increase(af, rpool, naddr) == -1)
647bcb11948Szinke 			return (1);
6484cb186b8Syasuoka 		/* revert the kif which was set by pfr_pool_get() */
6494cb186b8Syasuoka 		rpool->kif = kif;
650bcb11948Szinke 		break;
65154f930a6Spyr 	}
652c3e2fff9Shenning 
653c3e2fff9Shenning 	if (rpool->opts & PF_POOL_STICKYADDR) {
654c3e2fff9Shenning 		if (sns[type] != NULL) {
655c3e2fff9Shenning 			pf_remove_src_node(sns[type]);
656c3e2fff9Shenning 			sns[type] = NULL;
657c3e2fff9Shenning 		}
658b4470a7bSyasuoka 		if (pf_insert_src_node(&sns[type], r, type, af, saddr, naddr,
659b4470a7bSyasuoka 		    rpool->kif))
660c3e2fff9Shenning 			return (1);
661c3e2fff9Shenning 	}
66254f930a6Spyr 
663c50ff513Ssthen 	if (pf_status.debug >= LOG_INFO &&
66454f930a6Spyr 	    (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) {
665c50ff513Ssthen 		log(LOG_INFO, "pf: pf_map_addr: selected address ");
66654f930a6Spyr 		pf_print_host(naddr, 0, af);
667bcb11948Szinke 		if ((rpool->opts & PF_POOL_TYPEMASK) ==
668bcb11948Szinke 		    PF_POOL_LEASTSTATES)
66994b88d7eSmiod 			addlog(" with state count %llu", states);
670d387fefaSzinke 		if ((rpool->addr.type == PF_ADDR_TABLE &&
67142af0bf3Smcbride 		    rpool->addr.p.tbl->pfrkt_refcntcost > 0) ||
67242af0bf3Smcbride 		    (rpool->addr.type == PF_ADDR_DYNIFTL &&
673d387fefaSzinke 		    rpool->addr.p.dyn->pfid_kt->pfrkt_refcntcost > 0))
674cbdc262eSmcbride 			addlog(" with weight %u", weight);
675a2fdc13dSmcbride 		addlog("\n");
67654f930a6Spyr 	}
67754f930a6Spyr 
67854f930a6Spyr 	return (0);
67954f930a6Spyr }
68054f930a6Spyr 
6810ef3d4feShenning int
pf_map_addr_states_increase(sa_family_t af,struct pf_pool * rpool,struct pf_addr * naddr)6824cb186b8Syasuoka pf_map_addr_states_increase(sa_family_t af, struct pf_pool *rpool,
6834cb186b8Syasuoka     struct pf_addr *naddr)
6844cb186b8Syasuoka {
6854cb186b8Syasuoka 	if (rpool->addr.type == PF_ADDR_TABLE) {
6864cb186b8Syasuoka 		if (pfr_states_increase(rpool->addr.p.tbl,
6874cb186b8Syasuoka 		    naddr, af) == -1) {
6884cb186b8Syasuoka 			if (pf_status.debug >= LOG_DEBUG) {
6894cb186b8Syasuoka 				log(LOG_DEBUG,
6904cb186b8Syasuoka 				    "pf: pf_map_addr_states_increase: "
6914cb186b8Syasuoka 				    "selected address ");
6924cb186b8Syasuoka 				pf_print_host(naddr, 0, af);
6934cb186b8Syasuoka 				addlog(". Failed to increase count!\n");
6944cb186b8Syasuoka 			}
695511c53b1Syasuoka 			return (-1);
6964cb186b8Syasuoka 		}
6974cb186b8Syasuoka 	} else if (rpool->addr.type == PF_ADDR_DYNIFTL) {
6984cb186b8Syasuoka 		if (pfr_states_increase(rpool->addr.p.dyn->pfid_kt,
6994cb186b8Syasuoka 		    naddr, af) == -1) {
7004cb186b8Syasuoka 			if (pf_status.debug >= LOG_DEBUG) {
7014cb186b8Syasuoka 				log(LOG_DEBUG,
7024cb186b8Syasuoka 				    "pf: pf_map_addr_states_increase: "
7034cb186b8Syasuoka 				    "selected address ");
7044cb186b8Syasuoka 				pf_print_host(naddr, 0, af);
7054cb186b8Syasuoka 				addlog(". Failed to increase count!\n");
7064cb186b8Syasuoka 			}
707511c53b1Syasuoka 			return (-1);
7084cb186b8Syasuoka 		}
7094cb186b8Syasuoka 	}
7104cb186b8Syasuoka 	return (0);
7114cb186b8Syasuoka }
7124cb186b8Syasuoka 
7134cb186b8Syasuoka int
pf_get_transaddr(struct pf_rule * r,struct pf_pdesc * pd,struct pf_src_node ** sns,struct pf_rule ** nr)714ccf63ac6Shenning pf_get_transaddr(struct pf_rule *r, struct pf_pdesc *pd,
715cbdc262eSmcbride     struct pf_src_node **sns, struct pf_rule **nr)
71654f930a6Spyr {
7170ef3d4feShenning 	struct pf_addr	naddr;
7186d03c693Sbluhm 	u_int16_t	nport;
71954f930a6Spyr 
720f1029a73Sbluhm #ifdef INET6
72197326e01Sclaudio 	if (pd->af != pd->naf)
72297326e01Sclaudio 		return (pf_get_transaddr_af(r, pd, sns));
723f1029a73Sbluhm #endif /* INET6 */
72497326e01Sclaudio 
72536754172Smcbride 	if (r->nat.addr.type != PF_ADDR_NONE) {
7267d3e2ec5Sclaudio 		/* XXX is this right? what if rtable is changed at the same
7277d3e2ec5Sclaudio 		 * XXX time? where do I need to figure out the sport? */
7286d03c693Sbluhm 		nport = 0;
729ccf63ac6Shenning 		if (pf_get_sport(pd, r, &naddr, &nport,
730ccf63ac6Shenning 		    r->nat.proxy_port[0], r->nat.proxy_port[1], sns)) {
731a2fdc13dSmcbride 			DPFPRINTF(LOG_NOTICE,
732a2fdc13dSmcbride 			    "pf: NAT proxy port allocation (%u-%u) failed",
7330ef3d4feShenning 			    r->nat.proxy_port[0],
734a2fdc13dSmcbride 			    r->nat.proxy_port[1]);
7350ef3d4feShenning 			return (-1);
73654f930a6Spyr 		}
737cbdc262eSmcbride 		*nr = r;
738492cf661Skn 		pf_addrcpy(&pd->nsaddr, &naddr, pd->af);
739ccf63ac6Shenning 		pd->nsport = nport;
74054f930a6Spyr 	}
74136754172Smcbride 	if (r->rdr.addr.type != PF_ADDR_NONE) {
742ccf63ac6Shenning 		if (pf_map_addr(pd->af, r, &pd->nsaddr, &naddr, NULL, sns,
743ccf63ac6Shenning 		    &r->rdr, PF_SN_RDR))
7440ef3d4feShenning 			return (-1);
7450ef3d4feShenning 		if ((r->rdr.opts & PF_POOL_TYPEMASK) == PF_POOL_BITMASK)
746492cf661Skn 			pf_poolmask(&naddr, &naddr,  &r->rdr.addr.v.a.mask,
747ccf63ac6Shenning 			    &pd->ndaddr, pd->af);
74854f930a6Spyr 
7496d03c693Sbluhm 		nport = 0;
7500ef3d4feShenning 		if (r->rdr.proxy_port[1]) {
75154f930a6Spyr 			u_int32_t	tmp_nport;
75238bfd041Ssashan 			u_int16_t	div;
75354f930a6Spyr 
75438bfd041Ssashan 			div = r->rdr.proxy_port[1] - r->rdr.proxy_port[0] + 1;
75538bfd041Ssashan 			div = (div == 0) ? 1 : div;
75638bfd041Ssashan 
75738bfd041Ssashan 			tmp_nport = ((ntohs(pd->ndport) - ntohs(r->dst.port[0])) % div) +
7580ef3d4feShenning 			    r->rdr.proxy_port[0];
75954f930a6Spyr 
76054f930a6Spyr 			/* wrap around if necessary */
76154f930a6Spyr 			if (tmp_nport > 65535)
76254f930a6Spyr 				tmp_nport -= 65535;
7630ef3d4feShenning 			nport = htons((u_int16_t)tmp_nport);
7640ef3d4feShenning 		} else if (r->rdr.proxy_port[0])
7650ef3d4feShenning 			nport = htons(r->rdr.proxy_port[0]);
766cbdc262eSmcbride 		*nr = r;
767492cf661Skn 		pf_addrcpy(&pd->ndaddr, &naddr, pd->af);
7680ef3d4feShenning 		if (nport)
769ccf63ac6Shenning 			pd->ndport = nport;
77054f930a6Spyr 	}
77154f930a6Spyr 
7720ef3d4feShenning 	return (0);
77354f930a6Spyr }
774bcb11948Szinke 
775f1029a73Sbluhm #ifdef INET6
776bcb11948Szinke int
pf_get_transaddr_af(struct pf_rule * r,struct pf_pdesc * pd,struct pf_src_node ** sns)77797326e01Sclaudio pf_get_transaddr_af(struct pf_rule *r, struct pf_pdesc *pd,
77897326e01Sclaudio     struct pf_src_node **sns)
77997326e01Sclaudio {
78097326e01Sclaudio 	struct pf_addr	ndaddr, nsaddr, naddr;
7816d03c693Sbluhm 	u_int16_t	nport;
78297326e01Sclaudio 	int		prefixlen = 96;
78397326e01Sclaudio 
784c50ff513Ssthen 	if (pf_status.debug >= LOG_INFO) {
785c50ff513Ssthen 		log(LOG_INFO, "pf: af-to %s %s, ",
78697326e01Sclaudio 		    pd->naf == AF_INET ? "inet" : "inet6",
78797326e01Sclaudio 		    r->rdr.addr.type == PF_ADDR_NONE ? "nat" : "rdr");
78897326e01Sclaudio 		pf_print_host(&pd->nsaddr, pd->nsport, pd->af);
78997326e01Sclaudio 		addlog(" -> ");
79097326e01Sclaudio 		pf_print_host(&pd->ndaddr, pd->ndport, pd->af);
79197326e01Sclaudio 		addlog("\n");
79297326e01Sclaudio 	}
79397326e01Sclaudio 
79497326e01Sclaudio 	if (r->nat.addr.type == PF_ADDR_NONE)
79597326e01Sclaudio 		panic("pf_get_transaddr_af: no nat pool for source address");
79697326e01Sclaudio 
79797326e01Sclaudio 	/* get source address and port */
7986d03c693Sbluhm 	nport = 0;
79997326e01Sclaudio 	if (pf_get_sport(pd, r, &nsaddr, &nport,
80097326e01Sclaudio 	    r->nat.proxy_port[0], r->nat.proxy_port[1], sns)) {
80197326e01Sclaudio 		DPFPRINTF(LOG_NOTICE,
80297326e01Sclaudio 		    "pf: af-to NAT proxy port allocation (%u-%u) failed",
80397326e01Sclaudio 		    r->nat.proxy_port[0],
80497326e01Sclaudio 		    r->nat.proxy_port[1]);
80597326e01Sclaudio 		return (-1);
80697326e01Sclaudio 	}
80797326e01Sclaudio 	pd->nsport = nport;
80897326e01Sclaudio 
80997326e01Sclaudio 	if (pd->proto == IPPROTO_ICMPV6 && pd->naf == AF_INET) {
81097326e01Sclaudio 		if (pd->dir == PF_IN) {
811faf7e06fSmpi 			pd->ndport = ntohs(pd->ndport);
81297326e01Sclaudio 			if (pd->ndport == ICMP6_ECHO_REQUEST)
81397326e01Sclaudio 				pd->ndport = ICMP_ECHO;
81497326e01Sclaudio 			else if (pd->ndport == ICMP6_ECHO_REPLY)
81597326e01Sclaudio 				pd->ndport = ICMP_ECHOREPLY;
816faf7e06fSmpi 			pd->ndport = htons(pd->ndport);
81797326e01Sclaudio 		} else {
818faf7e06fSmpi 			pd->nsport = ntohs(pd->nsport);
81997326e01Sclaudio 			if (pd->nsport == ICMP6_ECHO_REQUEST)
82097326e01Sclaudio 				pd->nsport = ICMP_ECHO;
82197326e01Sclaudio 			else if (pd->nsport == ICMP6_ECHO_REPLY)
82297326e01Sclaudio 				pd->nsport = ICMP_ECHOREPLY;
823faf7e06fSmpi 			pd->nsport = htons(pd->nsport);
82497326e01Sclaudio 		}
82597326e01Sclaudio 	} else if (pd->proto == IPPROTO_ICMP && pd->naf == AF_INET6) {
82697326e01Sclaudio 		if (pd->dir == PF_IN) {
827faf7e06fSmpi 			pd->ndport = ntohs(pd->ndport);
82897326e01Sclaudio 			if (pd->ndport == ICMP_ECHO)
82997326e01Sclaudio 				pd->ndport = ICMP6_ECHO_REQUEST;
83097326e01Sclaudio 			else if (pd->ndport == ICMP_ECHOREPLY)
83197326e01Sclaudio 				pd->ndport = ICMP6_ECHO_REPLY;
832faf7e06fSmpi 			pd->ndport = htons(pd->ndport);
83397326e01Sclaudio 		} else {
834faf7e06fSmpi 			pd->nsport = ntohs(pd->nsport);
83597326e01Sclaudio 			if (pd->nsport == ICMP_ECHO)
83697326e01Sclaudio 				pd->nsport = ICMP6_ECHO_REQUEST;
83797326e01Sclaudio 			else if (pd->nsport == ICMP_ECHOREPLY)
83897326e01Sclaudio 				pd->nsport = ICMP6_ECHO_REPLY;
839faf7e06fSmpi 			pd->nsport = htons(pd->nsport);
84097326e01Sclaudio 		}
84197326e01Sclaudio 	}
84297326e01Sclaudio 
84397326e01Sclaudio 	/* get the destination address and port */
84497326e01Sclaudio 	if (r->rdr.addr.type != PF_ADDR_NONE) {
84597326e01Sclaudio 		if (pf_map_addr(pd->naf, r, &nsaddr, &naddr, NULL, sns,
84697326e01Sclaudio 		    &r->rdr, PF_SN_RDR))
84797326e01Sclaudio 			return (-1);
84897326e01Sclaudio 		if (r->rdr.proxy_port[0])
84997326e01Sclaudio 			pd->ndport = htons(r->rdr.proxy_port[0]);
85097326e01Sclaudio 
85197326e01Sclaudio 		if (pd->naf == AF_INET) {
85297326e01Sclaudio 			/* The prefix is the IPv4 rdr address */
85397326e01Sclaudio 			prefixlen = in_mask2len((struct in_addr *)
85497326e01Sclaudio 			    &r->rdr.addr.v.a.mask);
85597326e01Sclaudio 			inet_nat46(pd->naf, &pd->ndaddr,
85697326e01Sclaudio 			    &ndaddr, &naddr, prefixlen);
85797326e01Sclaudio 		} else {
85897326e01Sclaudio 			/* The prefix is the IPv6 rdr address */
85997326e01Sclaudio 			prefixlen =
86097326e01Sclaudio 			    in6_mask2len((struct in6_addr *)
86197326e01Sclaudio 			    &r->rdr.addr.v.a.mask, NULL);
86297326e01Sclaudio 			inet_nat64(pd->naf, &pd->ndaddr,
86397326e01Sclaudio 			    &ndaddr, &naddr, prefixlen);
86497326e01Sclaudio 		}
86597326e01Sclaudio 	} else {
86697326e01Sclaudio 		if (pd->naf == AF_INET) {
86797326e01Sclaudio 			/* The prefix is the IPv6 dst address */
86897326e01Sclaudio 			prefixlen =
86997326e01Sclaudio 			    in6_mask2len((struct in6_addr *)
87097326e01Sclaudio 			    &r->dst.addr.v.a.mask, NULL);
87197326e01Sclaudio 			if (prefixlen < 32)
87297326e01Sclaudio 				prefixlen = 96;
87397326e01Sclaudio 			inet_nat64(pd->naf, &pd->ndaddr,
87497326e01Sclaudio 			    &ndaddr, &pd->ndaddr, prefixlen);
87597326e01Sclaudio 		} else {
87697326e01Sclaudio 			/*
87797326e01Sclaudio 			 * The prefix is the IPv6 nat address
87897326e01Sclaudio 			 * (that was stored in pd->nsaddr)
87997326e01Sclaudio 			 */
88097326e01Sclaudio 			prefixlen = in6_mask2len((struct in6_addr *)
88197326e01Sclaudio 			    &r->nat.addr.v.a.mask, NULL);
88297326e01Sclaudio 			if (prefixlen > 96)
88397326e01Sclaudio 				prefixlen = 96;
88497326e01Sclaudio 			inet_nat64(pd->naf, &pd->ndaddr,
88597326e01Sclaudio 			    &ndaddr, &nsaddr, prefixlen);
88697326e01Sclaudio 		}
88797326e01Sclaudio 	}
88897326e01Sclaudio 
889492cf661Skn 	pf_addrcpy(&pd->nsaddr, &nsaddr, pd->naf);
890492cf661Skn 	pf_addrcpy(&pd->ndaddr, &ndaddr, pd->naf);
89197326e01Sclaudio 
892c50ff513Ssthen 	if (pf_status.debug >= LOG_INFO) {
893c50ff513Ssthen 		log(LOG_INFO, "pf: af-to %s %s done, prefixlen %d, ",
89497326e01Sclaudio 		    pd->naf == AF_INET ? "inet" : "inet6",
89597326e01Sclaudio 		    r->rdr.addr.type == PF_ADDR_NONE ? "nat" : "rdr",
89697326e01Sclaudio 		    prefixlen);
89797326e01Sclaudio 		pf_print_host(&pd->nsaddr, pd->nsport, pd->naf);
89897326e01Sclaudio 		addlog(" -> ");
89997326e01Sclaudio 		pf_print_host(&pd->ndaddr, pd->ndport, pd->naf);
90097326e01Sclaudio 		addlog("\n");
90197326e01Sclaudio 	}
90297326e01Sclaudio 
90397326e01Sclaudio 	return (0);
90497326e01Sclaudio }
905f1029a73Sbluhm #endif /* INET6 */
90697326e01Sclaudio 
90797326e01Sclaudio int
pf_postprocess_addr(struct pf_state * cur)908ff513d8eSmikeb pf_postprocess_addr(struct pf_state *cur)
909ff513d8eSmikeb {
910bcb11948Szinke 	struct pf_rule		*nr;
911ff513d8eSmikeb 	struct pf_state_key	*sks;
912ff513d8eSmikeb 	struct pf_pool		 rpool;
913ff513d8eSmikeb 	struct pf_addr		 lookup_addr;
914d9e07e8aSjsg 	int			 slbcount = -1;
915bcb11948Szinke 
916bcb11948Szinke 	nr = cur->natrule.ptr;
917bcb11948Szinke 
918ff513d8eSmikeb 	if (nr == NULL)
919ff513d8eSmikeb 		return (0);
920ff513d8eSmikeb 
921bcb11948Szinke 	/* decrease counter */
922bcb11948Szinke 
92356a5bba3Sjsg 	sks = cur->key[PF_SK_STACK];
924bcb11948Szinke 
925bcb11948Szinke 	/* check for outgoing or ingoing balancing */
926bcb11948Szinke 	if (nr->rt == PF_ROUTETO)
927bcb11948Szinke 		lookup_addr = cur->rt_addr;
928bcb11948Szinke 	else if (sks != NULL)
929bcb11948Szinke 		lookup_addr = sks->addr[1];
930bcb11948Szinke 	else {
931bcb11948Szinke 		if (pf_status.debug >= LOG_DEBUG) {
932df4df068Sblambert 			log(LOG_DEBUG, "pf: %s: unable to obtain address",
933df4df068Sblambert 			    __func__);
934bcb11948Szinke 		}
935bcb11948Szinke 		return (1);
936bcb11948Szinke 	}
937bcb11948Szinke 
938bcb11948Szinke 	/* check for appropriate pool */
939bcb11948Szinke 	if (nr->rdr.addr.type != PF_ADDR_NONE)
940bcb11948Szinke 		rpool = nr->rdr;
941bcb11948Szinke 	else if (nr->nat.addr.type != PF_ADDR_NONE)
942bcb11948Szinke 		rpool = nr->nat;
943bcb11948Szinke 	else if (nr->route.addr.type != PF_ADDR_NONE)
944bcb11948Szinke 		rpool = nr->route;
945443353daSjsg 	else
9468972f59eSjsg 		return (0);
947bcb11948Szinke 
948bcb11948Szinke 	if (((rpool.opts & PF_POOL_TYPEMASK) != PF_POOL_LEASTSTATES))
949bcb11948Szinke 		return (0);
950bcb11948Szinke 
951bcb11948Szinke 	if (rpool.addr.type == PF_ADDR_TABLE) {
952bcb11948Szinke 		if ((slbcount = pfr_states_decrease(
953bcb11948Szinke 		    rpool.addr.p.tbl,
954bcb11948Szinke 		    &lookup_addr, sks->af)) == -1) {
955bcb11948Szinke 			if (pf_status.debug >= LOG_DEBUG) {
956df4df068Sblambert 				log(LOG_DEBUG, "pf: %s: selected address ",
957df4df068Sblambert 				    __func__);
958bcb11948Szinke 				pf_print_host(&lookup_addr,
959bcb11948Szinke 				    sks->port[0], sks->af);
960bcb11948Szinke 				addlog(". Failed to "
961bcb11948Szinke 				    "decrease count!\n");
962bcb11948Szinke 			}
963bcb11948Szinke 			return (1);
964bcb11948Szinke 		}
965bcb11948Szinke 	} else if (rpool.addr.type == PF_ADDR_DYNIFTL) {
966bcb11948Szinke 		if ((slbcount = pfr_states_decrease(
967bcb11948Szinke 		    rpool.addr.p.dyn->pfid_kt,
968bcb11948Szinke 		    &lookup_addr, sks->af)) == -1) {
969bcb11948Szinke 			if (pf_status.debug >= LOG_DEBUG) {
970df4df068Sblambert 				log(LOG_DEBUG, "pf: %s: selected address ",
971df4df068Sblambert 				    __func__);
972bcb11948Szinke 				pf_print_host(&lookup_addr,
973bcb11948Szinke 				    sks->port[0], sks->af);
974bcb11948Szinke 				addlog(". Failed to "
975bcb11948Szinke 				    "decrease count!\n");
976bcb11948Szinke 			}
977bcb11948Szinke 			return (1);
978bcb11948Szinke 		}
979bcb11948Szinke 	}
980bcb11948Szinke 	if (slbcount > -1) {
981c50ff513Ssthen 		if (pf_status.debug >= LOG_INFO) {
982c50ff513Ssthen 			log(LOG_INFO, "pf: %s: selected address ", __func__);
983bcb11948Szinke 			pf_print_host(&lookup_addr, sks->port[0],
984bcb11948Szinke 			    sks->af);
985bcb11948Szinke 			addlog(" decreased state count to %u\n",
986bcb11948Szinke 			    slbcount);
987bcb11948Szinke 		}
988bcb11948Szinke 	}
989bcb11948Szinke 	return (0);
990bcb11948Szinke }
991