xref: /freebsd/sys/netpfil/pf/pf_lb.c (revision 4be8e29e776b078b43e95c04259ecebdf407481f)
1d8aa10ccSGleb Smirnoff /*-
2fe267a55SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause
3fe267a55SPedro F. Giffuni  *
43b3a8eb9SGleb Smirnoff  * Copyright (c) 2001 Daniel Hartmeier
53b3a8eb9SGleb Smirnoff  * Copyright (c) 2002 - 2008 Henning Brauer
63b3a8eb9SGleb Smirnoff  * All rights reserved.
73b3a8eb9SGleb Smirnoff  *
83b3a8eb9SGleb Smirnoff  * Redistribution and use in source and binary forms, with or without
93b3a8eb9SGleb Smirnoff  * modification, are permitted provided that the following conditions
103b3a8eb9SGleb Smirnoff  * are met:
113b3a8eb9SGleb Smirnoff  *
123b3a8eb9SGleb Smirnoff  *    - Redistributions of source code must retain the above copyright
133b3a8eb9SGleb Smirnoff  *      notice, this list of conditions and the following disclaimer.
143b3a8eb9SGleb Smirnoff  *    - Redistributions in binary form must reproduce the above
153b3a8eb9SGleb Smirnoff  *      copyright notice, this list of conditions and the following
163b3a8eb9SGleb Smirnoff  *      disclaimer in the documentation and/or other materials provided
173b3a8eb9SGleb Smirnoff  *      with the distribution.
183b3a8eb9SGleb Smirnoff  *
193b3a8eb9SGleb Smirnoff  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
203b3a8eb9SGleb Smirnoff  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
213b3a8eb9SGleb Smirnoff  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
223b3a8eb9SGleb Smirnoff  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
233b3a8eb9SGleb Smirnoff  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
243b3a8eb9SGleb Smirnoff  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
253b3a8eb9SGleb Smirnoff  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
263b3a8eb9SGleb Smirnoff  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
273b3a8eb9SGleb Smirnoff  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
283b3a8eb9SGleb Smirnoff  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
293b3a8eb9SGleb Smirnoff  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
303b3a8eb9SGleb Smirnoff  * POSSIBILITY OF SUCH DAMAGE.
313b3a8eb9SGleb Smirnoff  *
323b3a8eb9SGleb Smirnoff  * Effort sponsored in part by the Defense Advanced Research Projects
333b3a8eb9SGleb Smirnoff  * Agency (DARPA) and Air Force Research Laboratory, Air Force
343b3a8eb9SGleb Smirnoff  * Materiel Command, USAF, under agreement number F30602-01-2-0537.
353b3a8eb9SGleb Smirnoff  *
36d8aa10ccSGleb Smirnoff  *	$OpenBSD: pf_lb.c,v 1.2 2009/02/12 02:13:15 sthen Exp $
373b3a8eb9SGleb Smirnoff  */
383b3a8eb9SGleb Smirnoff 
393b3a8eb9SGleb Smirnoff #include <sys/cdefs.h>
403b3a8eb9SGleb Smirnoff #include "opt_pf.h"
413b3a8eb9SGleb Smirnoff #include "opt_inet.h"
423b3a8eb9SGleb Smirnoff #include "opt_inet6.h"
433b3a8eb9SGleb Smirnoff 
443b3a8eb9SGleb Smirnoff #include <sys/param.h>
4576039bc8SGleb Smirnoff #include <sys/lock.h>
4676039bc8SGleb Smirnoff #include <sys/mbuf.h>
473b3a8eb9SGleb Smirnoff #include <sys/socket.h>
483b3a8eb9SGleb Smirnoff #include <sys/sysctl.h>
493b3a8eb9SGleb Smirnoff 
503b3a8eb9SGleb Smirnoff #include <net/if.h>
51fcdb520cSKristof Provost #include <net/if_var.h>
5276039bc8SGleb Smirnoff #include <net/vnet.h>
533b3a8eb9SGleb Smirnoff #include <net/pfvar.h>
543b3a8eb9SGleb Smirnoff #include <net/if_pflog.h>
553b3a8eb9SGleb Smirnoff 
56fcdb520cSKristof Provost #ifdef INET
57fcdb520cSKristof Provost #include <netinet/in_var.h>
58fcdb520cSKristof Provost #endif
59fcdb520cSKristof Provost 
60fcdb520cSKristof Provost #ifdef INET6
61fcdb520cSKristof Provost #include <netinet6/in6_var.h>
62fcdb520cSKristof Provost #endif
63fcdb520cSKristof Provost 
64fcdb520cSKristof Provost 
65339a1977SMark Johnston /*
66339a1977SMark Johnston  * Limit the amount of work we do to find a free source port for redirects that
67339a1977SMark Johnston  * introduce a state conflict.
68339a1977SMark Johnston  */
69339a1977SMark Johnston #define	V_pf_rdr_srcport_rewrite_tries	VNET(pf_rdr_srcport_rewrite_tries)
70339a1977SMark Johnston VNET_DEFINE_STATIC(int, pf_rdr_srcport_rewrite_tries) = 16;
71339a1977SMark Johnston 
723b3a8eb9SGleb Smirnoff #define DPFPRINTF(n, x)	if (V_pf_status.debug >= (n)) printf x
733b3a8eb9SGleb Smirnoff 
743b3a8eb9SGleb Smirnoff static void		 pf_hash(struct pf_addr *, struct pf_addr *,
753b3a8eb9SGleb Smirnoff 			    struct pf_poolhashkey *, sa_family_t);
769a405864SKristof Provost static struct pf_krule	*pf_match_translation(struct pf_pdesc *,
772d7e68d5SKristof Provost 			    int, struct pf_kanchor_stackframe *);
782d7e68d5SKristof Provost static int pf_get_sport(struct pf_pdesc *, struct pf_krule *,
792d7e68d5SKristof Provost     struct pf_addr *, uint16_t *, uint16_t, uint16_t, struct pf_ksrc_node **,
80fcdb520cSKristof Provost     struct pf_srchash **, struct pf_kpool *, struct pf_udp_mapping **);
817d381d0aSKristof Provost static bool		 pf_islinklocal(const sa_family_t, const struct pf_addr *);
823b3a8eb9SGleb Smirnoff 
833b3a8eb9SGleb Smirnoff #define mix(a,b,c) \
843b3a8eb9SGleb Smirnoff 	do {					\
853b3a8eb9SGleb Smirnoff 		a -= b; a -= c; a ^= (c >> 13);	\
863b3a8eb9SGleb Smirnoff 		b -= c; b -= a; b ^= (a << 8);	\
873b3a8eb9SGleb Smirnoff 		c -= a; c -= b; c ^= (b >> 13);	\
883b3a8eb9SGleb Smirnoff 		a -= b; a -= c; a ^= (c >> 12);	\
893b3a8eb9SGleb Smirnoff 		b -= c; b -= a; b ^= (a << 16);	\
903b3a8eb9SGleb Smirnoff 		c -= a; c -= b; c ^= (b >> 5);	\
913b3a8eb9SGleb Smirnoff 		a -= b; a -= c; a ^= (c >> 3);	\
923b3a8eb9SGleb Smirnoff 		b -= c; b -= a; b ^= (a << 10);	\
933b3a8eb9SGleb Smirnoff 		c -= a; c -= b; c ^= (b >> 15);	\
943b3a8eb9SGleb Smirnoff 	} while (0)
953b3a8eb9SGleb Smirnoff 
963b3a8eb9SGleb Smirnoff /*
973b3a8eb9SGleb Smirnoff  * hash function based on bridge_hash in if_bridge.c
983b3a8eb9SGleb Smirnoff  */
993b3a8eb9SGleb Smirnoff static void
1003b3a8eb9SGleb Smirnoff pf_hash(struct pf_addr *inaddr, struct pf_addr *hash,
1013b3a8eb9SGleb Smirnoff     struct pf_poolhashkey *key, sa_family_t af)
1023b3a8eb9SGleb Smirnoff {
1033b3a8eb9SGleb Smirnoff 	u_int32_t	a = 0x9e3779b9, b = 0x9e3779b9, c = key->key32[0];
1043b3a8eb9SGleb Smirnoff 
1053b3a8eb9SGleb Smirnoff 	switch (af) {
1063b3a8eb9SGleb Smirnoff #ifdef INET
1073b3a8eb9SGleb Smirnoff 	case AF_INET:
1083b3a8eb9SGleb Smirnoff 		a += inaddr->addr32[0];
1093b3a8eb9SGleb Smirnoff 		b += key->key32[1];
1103b3a8eb9SGleb Smirnoff 		mix(a, b, c);
1113b3a8eb9SGleb Smirnoff 		hash->addr32[0] = c + key->key32[2];
1123b3a8eb9SGleb Smirnoff 		break;
1133b3a8eb9SGleb Smirnoff #endif /* INET */
1143b3a8eb9SGleb Smirnoff #ifdef INET6
1153b3a8eb9SGleb Smirnoff 	case AF_INET6:
1163b3a8eb9SGleb Smirnoff 		a += inaddr->addr32[0];
1173b3a8eb9SGleb Smirnoff 		b += inaddr->addr32[2];
1183b3a8eb9SGleb Smirnoff 		mix(a, b, c);
1193b3a8eb9SGleb Smirnoff 		hash->addr32[0] = c;
1203b3a8eb9SGleb Smirnoff 		a += inaddr->addr32[1];
1213b3a8eb9SGleb Smirnoff 		b += inaddr->addr32[3];
1223b3a8eb9SGleb Smirnoff 		c += key->key32[1];
1233b3a8eb9SGleb Smirnoff 		mix(a, b, c);
1243b3a8eb9SGleb Smirnoff 		hash->addr32[1] = c;
1253b3a8eb9SGleb Smirnoff 		a += inaddr->addr32[2];
1263b3a8eb9SGleb Smirnoff 		b += inaddr->addr32[1];
1273b3a8eb9SGleb Smirnoff 		c += key->key32[2];
1283b3a8eb9SGleb Smirnoff 		mix(a, b, c);
1293b3a8eb9SGleb Smirnoff 		hash->addr32[2] = c;
1303b3a8eb9SGleb Smirnoff 		a += inaddr->addr32[3];
1313b3a8eb9SGleb Smirnoff 		b += inaddr->addr32[0];
1323b3a8eb9SGleb Smirnoff 		c += key->key32[3];
1333b3a8eb9SGleb Smirnoff 		mix(a, b, c);
1343b3a8eb9SGleb Smirnoff 		hash->addr32[3] = c;
1353b3a8eb9SGleb Smirnoff 		break;
1363b3a8eb9SGleb Smirnoff #endif /* INET6 */
1373b3a8eb9SGleb Smirnoff 	}
1383b3a8eb9SGleb Smirnoff }
1393b3a8eb9SGleb Smirnoff 
140e86bddeaSKristof Provost static struct pf_krule *
1419a405864SKristof Provost pf_match_translation(struct pf_pdesc *pd,
1422d7e68d5SKristof Provost     int rs_num, struct pf_kanchor_stackframe *anchor_stack)
1433b3a8eb9SGleb Smirnoff {
144e86bddeaSKristof Provost 	struct pf_krule		*r, *rm = NULL;
145e86bddeaSKristof Provost 	struct pf_kruleset	*ruleset = NULL;
1463b3a8eb9SGleb Smirnoff 	int			 tag = -1;
1473b3a8eb9SGleb Smirnoff 	int			 rtableid = -1;
1483b3a8eb9SGleb Smirnoff 	int			 asd = 0;
1493b3a8eb9SGleb Smirnoff 
1503b3a8eb9SGleb Smirnoff 	r = TAILQ_FIRST(pf_main_ruleset.rules[rs_num].active.ptr);
1516b94546aSMateusz Guzik 	while (r != NULL) {
1523b3a8eb9SGleb Smirnoff 		struct pf_rule_addr	*src = NULL, *dst = NULL;
1533b3a8eb9SGleb Smirnoff 		struct pf_addr_wrap	*xdst = NULL;
1543b3a8eb9SGleb Smirnoff 
155f2064dd1SKajetan Staszkiewicz 		if (r->action == PF_BINAT && pd->dir == PF_IN) {
1563b3a8eb9SGleb Smirnoff 			src = &r->dst;
157e11dacbfSKristof Provost 			if (r->rdr.cur != NULL)
158e11dacbfSKristof Provost 				xdst = &r->rdr.cur->addr;
1593b3a8eb9SGleb Smirnoff 		} else {
1603b3a8eb9SGleb Smirnoff 			src = &r->src;
1613b3a8eb9SGleb Smirnoff 			dst = &r->dst;
1623b3a8eb9SGleb Smirnoff 		}
1633b3a8eb9SGleb Smirnoff 
16402cf67ccSMateusz Guzik 		pf_counter_u64_add(&r->evaluations, 1);
165b4a42589SKristof Provost 		if (pfi_kkif_match(r->kif, pd->kif) == r->ifnot)
166e5c64b26SKajetan Staszkiewicz 			r = r->skip[PF_SKIP_IFP];
167f2064dd1SKajetan Staszkiewicz 		else if (r->direction && r->direction != pd->dir)
168e5c64b26SKajetan Staszkiewicz 			r = r->skip[PF_SKIP_DIR];
1693b3a8eb9SGleb Smirnoff 		else if (r->af && r->af != pd->af)
170e5c64b26SKajetan Staszkiewicz 			r = r->skip[PF_SKIP_AF];
1713b3a8eb9SGleb Smirnoff 		else if (r->proto && r->proto != pd->proto)
172e5c64b26SKajetan Staszkiewicz 			r = r->skip[PF_SKIP_PROTO];
1732d7e68d5SKristof Provost 		else if (PF_MISMATCHAW(&src->addr, &pd->nsaddr, pd->af,
1749a405864SKristof Provost 		    src->neg, pd->kif, M_GETFIB(pd->m)))
1753b3a8eb9SGleb Smirnoff 			r = r->skip[src == &r->src ? PF_SKIP_SRC_ADDR :
176e5c64b26SKajetan Staszkiewicz 			    PF_SKIP_DST_ADDR];
1773b3a8eb9SGleb Smirnoff 		else if (src->port_op && !pf_match_port(src->port_op,
1782d7e68d5SKristof Provost 		    src->port[0], src->port[1], pd->nsport))
1793b3a8eb9SGleb Smirnoff 			r = r->skip[src == &r->src ? PF_SKIP_SRC_PORT :
180e5c64b26SKajetan Staszkiewicz 			    PF_SKIP_DST_PORT];
1813b3a8eb9SGleb Smirnoff 		else if (dst != NULL &&
1822d7e68d5SKristof Provost 		    PF_MISMATCHAW(&dst->addr, &pd->ndaddr, pd->af, dst->neg, NULL,
1839a405864SKristof Provost 		    M_GETFIB(pd->m)))
184e5c64b26SKajetan Staszkiewicz 			r = r->skip[PF_SKIP_DST_ADDR];
1852d7e68d5SKristof Provost 		else if (xdst != NULL && PF_MISMATCHAW(xdst, &pd->ndaddr, pd->af,
1869a405864SKristof Provost 		    0, NULL, M_GETFIB(pd->m)))
1873b3a8eb9SGleb Smirnoff 			r = TAILQ_NEXT(r, entries);
1883b3a8eb9SGleb Smirnoff 		else if (dst != NULL && dst->port_op &&
1893b3a8eb9SGleb Smirnoff 		    !pf_match_port(dst->port_op, dst->port[0],
1902d7e68d5SKristof Provost 		    dst->port[1], pd->ndport))
191e5c64b26SKajetan Staszkiewicz 			r = r->skip[PF_SKIP_DST_PORT];
1929a405864SKristof Provost 		else if (r->match_tag && !pf_match_tag(pd->m, r, &tag,
1933b3a8eb9SGleb Smirnoff 		    pd->pf_mtag ? pd->pf_mtag->tag : 0))
1943b3a8eb9SGleb Smirnoff 			r = TAILQ_NEXT(r, entries);
1953b3a8eb9SGleb Smirnoff 		else if (r->os_fingerprint != PF_OSFP_ANY && (pd->proto !=
1969a405864SKristof Provost 		    IPPROTO_TCP || !pf_osfp_match(pf_osfp_fingerprint(pd,
197739731b8SKristof Provost 		    &pd->hdr.tcp), r->os_fingerprint)))
1983b3a8eb9SGleb Smirnoff 			r = TAILQ_NEXT(r, entries);
1993b3a8eb9SGleb Smirnoff 		else {
2003b3a8eb9SGleb Smirnoff 			if (r->tag)
2013b3a8eb9SGleb Smirnoff 				tag = r->tag;
2023b3a8eb9SGleb Smirnoff 			if (r->rtableid >= 0)
2033b3a8eb9SGleb Smirnoff 				rtableid = r->rtableid;
2043b3a8eb9SGleb Smirnoff 			if (r->anchor == NULL) {
2053b3a8eb9SGleb Smirnoff 				rm = r;
2066b94546aSMateusz Guzik 				if (rm->action == PF_NONAT ||
2076b94546aSMateusz Guzik 				    rm->action == PF_NORDR ||
2086b94546aSMateusz Guzik 				    rm->action == PF_NOBINAT) {
2096b94546aSMateusz Guzik 					rm = NULL;
2106b94546aSMateusz Guzik 				}
2116b94546aSMateusz Guzik 				break;
2123b3a8eb9SGleb Smirnoff 			} else
2131d6139c0SGleb Smirnoff 				pf_step_into_anchor(anchor_stack, &asd,
2141d6139c0SGleb Smirnoff 				    &ruleset, rs_num, &r, NULL, NULL);
2153b3a8eb9SGleb Smirnoff 		}
2163b3a8eb9SGleb Smirnoff 		if (r == NULL)
2171d6139c0SGleb Smirnoff 			pf_step_out_of_anchor(anchor_stack, &asd, &ruleset,
2181d6139c0SGleb Smirnoff 			    rs_num, &r, NULL, NULL);
2193b3a8eb9SGleb Smirnoff 	}
2203b3a8eb9SGleb Smirnoff 
2219a405864SKristof Provost 	if (tag > 0 && pf_tag_packet(pd, tag))
2223b3a8eb9SGleb Smirnoff 		return (NULL);
2233b3a8eb9SGleb Smirnoff 	if (rtableid >= 0)
2249a405864SKristof Provost 		M_SETFIB(pd->m, rtableid);
2253b3a8eb9SGleb Smirnoff 
2263b3a8eb9SGleb Smirnoff 	return (rm);
2273b3a8eb9SGleb Smirnoff }
2283b3a8eb9SGleb Smirnoff 
2293b3a8eb9SGleb Smirnoff static int
2302d7e68d5SKristof Provost pf_get_sport(struct pf_pdesc *pd, struct pf_krule *r,
2312d7e68d5SKristof Provost     struct pf_addr *naddr, uint16_t *nport, uint16_t low,
2322d7e68d5SKristof Provost     uint16_t high, struct pf_ksrc_node **sn,
233fcdb520cSKristof Provost     struct pf_srchash **sh, struct pf_kpool *rpool,
234fcdb520cSKristof Provost     struct pf_udp_mapping **udp_mapping)
2353b3a8eb9SGleb Smirnoff {
2363b3a8eb9SGleb Smirnoff 	struct pf_state_key_cmp	key;
2373b3a8eb9SGleb Smirnoff 	struct pf_addr		init_addr;
2383b3a8eb9SGleb Smirnoff 
2393b3a8eb9SGleb Smirnoff 	bzero(&init_addr, sizeof(init_addr));
240390dc369STom Jones 
241fcdb520cSKristof Provost 	if (! TAILQ_EMPTY(&r->nat.list) &&
242fcdb520cSKristof Provost 	    pf_map_addr_sn(pd->naf, r, &pd->nsaddr, naddr, NULL, &init_addr,
243fcdb520cSKristof Provost 	    sn, sh, &r->nat))
244fcdb520cSKristof Provost 		return (1);
245fcdb520cSKristof Provost 
246fcdb520cSKristof Provost 	if (udp_mapping) {
247390dc369STom Jones 		MPASS(*udp_mapping == NULL);
248fcdb520cSKristof Provost 	}
249390dc369STom Jones 
250390dc369STom Jones 	/*
251390dc369STom Jones 	 * If we are UDP and have an existing mapping we can get source port
252390dc369STom Jones 	 * from the mapping. In this case we have to look up the src_node as
253390dc369STom Jones 	 * pf_map_addr would.
254390dc369STom Jones 	 */
2552d7e68d5SKristof Provost 	if (pd->proto == IPPROTO_UDP && (r->rdr.opts & PF_POOL_ENDPI)) {
256390dc369STom Jones 		struct pf_udp_endpoint_cmp udp_source;
257390dc369STom Jones 
258390dc369STom Jones 		bzero(&udp_source, sizeof(udp_source));
2592d7e68d5SKristof Provost 		udp_source.af = pd->af;
2602d7e68d5SKristof Provost 		PF_ACPY(&udp_source.addr, &pd->nsaddr, pd->af);
2612d7e68d5SKristof Provost 		udp_source.port = pd->nsport;
262fcdb520cSKristof Provost 		if (udp_mapping) {
263390dc369STom Jones 			*udp_mapping = pf_udp_mapping_find(&udp_source);
264390dc369STom Jones 			if (*udp_mapping) {
2652d7e68d5SKristof Provost 				PF_ACPY(naddr, &(*udp_mapping)->endpoints[1].addr, pd->af);
266390dc369STom Jones 				*nport = (*udp_mapping)->endpoints[1].port;
267390dc369STom Jones 				/* Try to find a src_node as per pf_map_addr(). */
268e11dacbfSKristof Provost 				if (*sn == NULL && r->rdr.opts & PF_POOL_STICKYADDR &&
269e11dacbfSKristof Provost 				    (r->rdr.opts & PF_POOL_TYPEMASK) != PF_POOL_NONE)
2702d7e68d5SKristof Provost 					*sn = pf_find_src_node(&pd->nsaddr, r, pd->af, sh, false);
271b9c0321dSKajetan Staszkiewicz 				if (*sn != NULL)
272b9c0321dSKajetan Staszkiewicz 					PF_SRC_NODE_UNLOCK(*sn);
273390dc369STom Jones 				return (0);
274390dc369STom Jones 			} else {
2752d7e68d5SKristof Provost 				*udp_mapping = pf_udp_mapping_create(pd->af, &pd->nsaddr,
2762d7e68d5SKristof Provost 				    pd->nsport, &init_addr, 0);
277390dc369STom Jones 				if (*udp_mapping == NULL)
2783b3a8eb9SGleb Smirnoff 					return (1);
279390dc369STom Jones 			}
280390dc369STom Jones 		}
281fcdb520cSKristof Provost 	}
282390dc369STom Jones 
283fcdb520cSKristof Provost 	if (pf_map_addr_sn(pd->af, r, &pd->nsaddr, naddr, NULL, &init_addr,
284fcdb520cSKristof Provost 	    sn, sh, rpool))
285390dc369STom Jones 		goto failed;
2863b3a8eb9SGleb Smirnoff 
2872d7e68d5SKristof Provost 	if (pd->proto == IPPROTO_ICMP) {
288534ee17eSKristof Provost 		if (*nport == htons(ICMP_ECHO)) {
289534ee17eSKristof Provost 			low = 1;
290534ee17eSKristof Provost 			high = 65535;
291534ee17eSKristof Provost 		} else
292534ee17eSKristof Provost 			return (0);	/* Don't try to modify non-echo ICMP */
293534ee17eSKristof Provost 	}
294534ee17eSKristof Provost #ifdef INET6
2952d7e68d5SKristof Provost 	if (pd->proto == IPPROTO_ICMPV6) {
296534ee17eSKristof Provost 		if (*nport == htons(ICMP6_ECHO_REQUEST)) {
297534ee17eSKristof Provost 			low = 1;
298534ee17eSKristof Provost 			high = 65535;
299534ee17eSKristof Provost 		} else
300534ee17eSKristof Provost 			return (0);	/* Don't try to modify non-echo ICMP */
301534ee17eSKristof Provost 	}
302534ee17eSKristof Provost #endif /* INET6 */
303534ee17eSKristof Provost 
3048fc6e19cSGleb Smirnoff 	bzero(&key, sizeof(key));
305fcdb520cSKristof Provost 	key.af = pd->naf;
3062d7e68d5SKristof Provost 	key.proto = pd->proto;
3072d7e68d5SKristof Provost 	key.port[0] = pd->ndport;
3082d7e68d5SKristof Provost 	PF_ACPY(&key.addr[0], &pd->ndaddr, key.af);
3098fc6e19cSGleb Smirnoff 
3108fc6e19cSGleb Smirnoff 	do {
3118fc6e19cSGleb Smirnoff 		PF_ACPY(&key.addr[1], naddr, key.af);
312fcdb520cSKristof Provost 		if (udp_mapping && *udp_mapping)
3132d7e68d5SKristof Provost 			PF_ACPY(&(*udp_mapping)->endpoints[1].addr, naddr, pd->af);
3143b3a8eb9SGleb Smirnoff 
3153b3a8eb9SGleb Smirnoff 		/*
3163b3a8eb9SGleb Smirnoff 		 * port search; start random, step;
3173b3a8eb9SGleb Smirnoff 		 * similar 2 portloop in in_pcbbind
3183b3a8eb9SGleb Smirnoff 		 */
3192d7e68d5SKristof Provost 		if (pd->proto == IPPROTO_SCTP) {
3202d7e68d5SKristof Provost 			key.port[1] = pd->nsport;
3216053adafSKristof Provost 			if (!pf_find_state_all_exists(&key, PF_IN)) {
3222d7e68d5SKristof Provost 				*nport = pd->nsport;
3236053adafSKristof Provost 				return (0);
3246053adafSKristof Provost 			} else {
3256053adafSKristof Provost 				return (1); /* Fail mapping. */
3266053adafSKristof Provost 			}
3272d7e68d5SKristof Provost 		} else if (!(pd->proto == IPPROTO_TCP || pd->proto == IPPROTO_UDP ||
3282d7e68d5SKristof Provost 		    pd->proto == IPPROTO_ICMP) || (low == 0 && high == 0)) {
3298fc6e19cSGleb Smirnoff 			/*
3308fc6e19cSGleb Smirnoff 			 * XXX bug: icmp states don't use the id on both sides.
3318fc6e19cSGleb Smirnoff 			 * (traceroute -I through nat)
3328fc6e19cSGleb Smirnoff 			 */
3332d7e68d5SKristof Provost 			key.port[1] = pd->nsport;
33419d6e29bSMateusz Guzik 			if (!pf_find_state_all_exists(&key, PF_IN)) {
3352d7e68d5SKristof Provost 				*nport = pd->nsport;
3363b3a8eb9SGleb Smirnoff 				return (0);
3378fc6e19cSGleb Smirnoff 			}
3383b3a8eb9SGleb Smirnoff 		} else if (low == high) {
3398fc6e19cSGleb Smirnoff 			key.port[1] = htons(low);
34019d6e29bSMateusz Guzik 			if (!pf_find_state_all_exists(&key, PF_IN)) {
341fcdb520cSKristof Provost 				if (udp_mapping && *udp_mapping != NULL) {
342390dc369STom Jones 					(*udp_mapping)->endpoints[1].port = htons(low);
343390dc369STom Jones 					if (pf_udp_mapping_insert(*udp_mapping) == 0) {
3443b3a8eb9SGleb Smirnoff 						*nport = htons(low);
3453b3a8eb9SGleb Smirnoff 						return (0);
3463b3a8eb9SGleb Smirnoff 					}
3473b3a8eb9SGleb Smirnoff 				} else {
348390dc369STom Jones 					*nport = htons(low);
349390dc369STom Jones 					return (0);
350390dc369STom Jones 				}
351390dc369STom Jones 			}
352390dc369STom Jones 		} else {
3537f3ad018SKristof Provost 			uint32_t tmp;
3547f3ad018SKristof Provost 			uint16_t cut;
3553b3a8eb9SGleb Smirnoff 
3563b3a8eb9SGleb Smirnoff 			if (low > high) {
3573b3a8eb9SGleb Smirnoff 				tmp = low;
3583b3a8eb9SGleb Smirnoff 				low = high;
3593b3a8eb9SGleb Smirnoff 				high = tmp;
3603b3a8eb9SGleb Smirnoff 			}
3613b3a8eb9SGleb Smirnoff 			/* low < high */
362e4e01d9cSGleb Smirnoff 			cut = arc4random() % (1 + high - low) + low;
3633b3a8eb9SGleb Smirnoff 			/* low <= cut <= high */
3647f3ad018SKristof Provost 			for (tmp = cut; tmp <= high && tmp <= 0xffff; ++tmp) {
365fcdb520cSKristof Provost 				if (udp_mapping && *udp_mapping != NULL) {
366390dc369STom Jones 					(*udp_mapping)->endpoints[1].port = htons(tmp);
367390dc369STom Jones 					if (pf_udp_mapping_insert(*udp_mapping) == 0) {
368390dc369STom Jones 						*nport = htons(tmp);
369390dc369STom Jones 						return (0);
370390dc369STom Jones 					}
371390dc369STom Jones 				} else {
3728fc6e19cSGleb Smirnoff 					key.port[1] = htons(tmp);
37319d6e29bSMateusz Guzik 					if (!pf_find_state_all_exists(&key, PF_IN)) {
3743b3a8eb9SGleb Smirnoff 						*nport = htons(tmp);
3753b3a8eb9SGleb Smirnoff 						return (0);
3763b3a8eb9SGleb Smirnoff 					}
3773b3a8eb9SGleb Smirnoff 				}
378390dc369STom Jones 			}
3797f3ad018SKristof Provost 			tmp = cut;
3807f3ad018SKristof Provost 			for (tmp -= 1; tmp >= low && tmp <= 0xffff; --tmp) {
3812d7e68d5SKristof Provost 				if (pd->proto == IPPROTO_UDP &&
382fcdb520cSKristof Provost 				    (r->rdr.opts & PF_POOL_ENDPI &&
383fcdb520cSKristof Provost 				    udp_mapping != NULL)) {
384390dc369STom Jones 					(*udp_mapping)->endpoints[1].port = htons(tmp);
385390dc369STom Jones 					if (pf_udp_mapping_insert(*udp_mapping) == 0) {
386390dc369STom Jones 						*nport = htons(tmp);
387390dc369STom Jones 						return (0);
388390dc369STom Jones 					}
389390dc369STom Jones 				} else {
3908fc6e19cSGleb Smirnoff 					key.port[1] = htons(tmp);
39119d6e29bSMateusz Guzik 					if (!pf_find_state_all_exists(&key, PF_IN)) {
3923b3a8eb9SGleb Smirnoff 						*nport = htons(tmp);
3933b3a8eb9SGleb Smirnoff 						return (0);
3943b3a8eb9SGleb Smirnoff 					}
3953b3a8eb9SGleb Smirnoff 				}
3963b3a8eb9SGleb Smirnoff 			}
397390dc369STom Jones 		}
3983b3a8eb9SGleb Smirnoff 
399e11dacbfSKristof Provost 		switch (r->rdr.opts & PF_POOL_TYPEMASK) {
4003b3a8eb9SGleb Smirnoff 		case PF_POOL_RANDOM:
4013b3a8eb9SGleb Smirnoff 		case PF_POOL_ROUNDROBIN:
4022b0a4ffaSKristof Provost 			/*
4032b0a4ffaSKristof Provost 			 * pick a different source address since we're out
4042b0a4ffaSKristof Provost 			 * of free port choices for the current one.
4052b0a4ffaSKristof Provost 			 */
406b9c0321dSKajetan Staszkiewicz 			(*sn) = NULL;
4072d7e68d5SKristof Provost 			if (pf_map_addr_sn(pd->af, r, &pd->nsaddr, naddr, NULL,
408fcdb520cSKristof Provost 			    &init_addr, sn, sh, &r->rdr))
4093b3a8eb9SGleb Smirnoff 				return (1);
4103b3a8eb9SGleb Smirnoff 			break;
4113b3a8eb9SGleb Smirnoff 		case PF_POOL_NONE:
4123b3a8eb9SGleb Smirnoff 		case PF_POOL_SRCHASH:
4133b3a8eb9SGleb Smirnoff 		case PF_POOL_BITMASK:
4143b3a8eb9SGleb Smirnoff 		default:
4153b3a8eb9SGleb Smirnoff 			return (1);
4163b3a8eb9SGleb Smirnoff 		}
417fcdb520cSKristof Provost 	} while (! PF_AEQ(&init_addr, naddr, pd->naf) );
418390dc369STom Jones 
419390dc369STom Jones failed:
420fcdb520cSKristof Provost 	if (udp_mapping) {
421390dc369STom Jones 		uma_zfree(V_pf_udp_mapping_z, *udp_mapping);
422390dc369STom Jones 		*udp_mapping = NULL;
423fcdb520cSKristof Provost 	}
424fcdb520cSKristof Provost 
4253b3a8eb9SGleb Smirnoff 	return (1);					/* none available */
4263b3a8eb9SGleb Smirnoff }
4273b3a8eb9SGleb Smirnoff 
4287d381d0aSKristof Provost static bool
4297d381d0aSKristof Provost pf_islinklocal(const sa_family_t af, const struct pf_addr *addr)
4307d381d0aSKristof Provost {
4317d381d0aSKristof Provost 	if (af == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&addr->v6))
4327d381d0aSKristof Provost 		return (true);
4337d381d0aSKristof Provost 	return (false);
4347d381d0aSKristof Provost }
4357d381d0aSKristof Provost 
4362aa21096SKurosawa Takahiro static int
4372d7e68d5SKristof Provost pf_get_mape_sport(struct pf_pdesc *pd, struct pf_krule *r,
4382d7e68d5SKristof Provost     struct pf_addr *naddr, uint16_t *nport,
439b9c0321dSKajetan Staszkiewicz     struct pf_ksrc_node **sn, struct pf_srchash **sh,
440b9c0321dSKajetan Staszkiewicz     struct pf_udp_mapping **udp_mapping)
4412aa21096SKurosawa Takahiro {
4422aa21096SKurosawa Takahiro 	uint16_t psmask, low, highmask;
4432aa21096SKurosawa Takahiro 	uint16_t i, ahigh, cut;
4442aa21096SKurosawa Takahiro 	int ashift, psidshift;
4452aa21096SKurosawa Takahiro 
446e11dacbfSKristof Provost 	ashift = 16 - r->rdr.mape.offset;
447e11dacbfSKristof Provost 	psidshift = ashift - r->rdr.mape.psidlen;
448e11dacbfSKristof Provost 	psmask = r->rdr.mape.psid & ((1U << r->rdr.mape.psidlen) - 1);
4492aa21096SKurosawa Takahiro 	psmask = psmask << psidshift;
4502aa21096SKurosawa Takahiro 	highmask = (1U << psidshift) - 1;
4512aa21096SKurosawa Takahiro 
452e11dacbfSKristof Provost 	ahigh = (1U << r->rdr.mape.offset) - 1;
4532aa21096SKurosawa Takahiro 	cut = arc4random() & ahigh;
4542aa21096SKurosawa Takahiro 	if (cut == 0)
4552aa21096SKurosawa Takahiro 		cut = 1;
4562aa21096SKurosawa Takahiro 
4572aa21096SKurosawa Takahiro 	for (i = cut; i <= ahigh; i++) {
4582aa21096SKurosawa Takahiro 		low = (i << ashift) | psmask;
4592d7e68d5SKristof Provost 		if (!pf_get_sport(pd, r,
460fcdb520cSKristof Provost 		    naddr, nport, low, low | highmask, sn, sh, &r->rdr,
461fcdb520cSKristof Provost 		    udp_mapping))
4622aa21096SKurosawa Takahiro 			return (0);
4632aa21096SKurosawa Takahiro 	}
4642aa21096SKurosawa Takahiro 	for (i = cut - 1; i > 0; i--) {
4652aa21096SKurosawa Takahiro 		low = (i << ashift) | psmask;
4662d7e68d5SKristof Provost 		if (!pf_get_sport(pd, r,
467fcdb520cSKristof Provost 		    naddr, nport, low, low | highmask, sn, sh, &r->rdr,
468fcdb520cSKristof Provost 		    udp_mapping))
4692aa21096SKurosawa Takahiro 			return (0);
4702aa21096SKurosawa Takahiro 	}
4712aa21096SKurosawa Takahiro 	return (1);
4722aa21096SKurosawa Takahiro }
4732aa21096SKurosawa Takahiro 
47416303d2bSKajetan Staszkiewicz u_short
475e86bddeaSKristof Provost pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
476fcdb520cSKristof Provost     struct pf_addr *naddr, struct pfi_kkif **nkif, struct pf_addr *init_addr,
477fcdb520cSKristof Provost     struct pf_kpool *rpool)
4783b3a8eb9SGleb Smirnoff {
4799569fdddSMark Johnston 	u_short			 reason = PFRES_MATCH;
4803b3a8eb9SGleb Smirnoff 	struct pf_addr		*raddr = NULL, *rmask = NULL;
4813b3a8eb9SGleb Smirnoff 
4825f5e32f1SKristof Provost 	mtx_lock(&rpool->mtx);
483e85343b1SGleb Smirnoff 	/* Find the route using chosen algorithm. Store the found route
484e85343b1SGleb Smirnoff 	   in src_node if it was given or found. */
4855f5e32f1SKristof Provost 	if (rpool->cur->addr.type == PF_ADDR_NOROUTE) {
48616303d2bSKajetan Staszkiewicz 		reason = PFRES_MAPFAILED;
48716303d2bSKajetan Staszkiewicz 		goto done_pool_mtx;
4885f5e32f1SKristof Provost 	}
4893b3a8eb9SGleb Smirnoff 	if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) {
4903b3a8eb9SGleb Smirnoff 		switch (af) {
4913b3a8eb9SGleb Smirnoff #ifdef INET
4923b3a8eb9SGleb Smirnoff 		case AF_INET:
4933b3a8eb9SGleb Smirnoff 			if (rpool->cur->addr.p.dyn->pfid_acnt4 < 1 &&
4943b3a8eb9SGleb Smirnoff 			    (rpool->opts & PF_POOL_TYPEMASK) !=
4955f5e32f1SKristof Provost 			    PF_POOL_ROUNDROBIN) {
49616303d2bSKajetan Staszkiewicz 				reason = PFRES_MAPFAILED;
49716303d2bSKajetan Staszkiewicz 				goto done_pool_mtx;
4985f5e32f1SKristof Provost 			}
4993b3a8eb9SGleb Smirnoff 			raddr = &rpool->cur->addr.p.dyn->pfid_addr4;
5003b3a8eb9SGleb Smirnoff 			rmask = &rpool->cur->addr.p.dyn->pfid_mask4;
5013b3a8eb9SGleb Smirnoff 			break;
5023b3a8eb9SGleb Smirnoff #endif /* INET */
5033b3a8eb9SGleb Smirnoff #ifdef INET6
5043b3a8eb9SGleb Smirnoff 		case AF_INET6:
5053b3a8eb9SGleb Smirnoff 			if (rpool->cur->addr.p.dyn->pfid_acnt6 < 1 &&
5063b3a8eb9SGleb Smirnoff 			    (rpool->opts & PF_POOL_TYPEMASK) !=
5075f5e32f1SKristof Provost 			    PF_POOL_ROUNDROBIN) {
50816303d2bSKajetan Staszkiewicz 				reason = PFRES_MAPFAILED;
50916303d2bSKajetan Staszkiewicz 				goto done_pool_mtx;
5105f5e32f1SKristof Provost 			}
5113b3a8eb9SGleb Smirnoff 			raddr = &rpool->cur->addr.p.dyn->pfid_addr6;
5123b3a8eb9SGleb Smirnoff 			rmask = &rpool->cur->addr.p.dyn->pfid_mask6;
5133b3a8eb9SGleb Smirnoff 			break;
5143b3a8eb9SGleb Smirnoff #endif /* INET6 */
5153b3a8eb9SGleb Smirnoff 		}
5163b3a8eb9SGleb Smirnoff 	} else if (rpool->cur->addr.type == PF_ADDR_TABLE) {
5175f5e32f1SKristof Provost 		if ((rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_ROUNDROBIN) {
51816303d2bSKajetan Staszkiewicz 			reason = PFRES_MAPFAILED;
51916303d2bSKajetan Staszkiewicz 			goto done_pool_mtx; /* unsupported */
5205f5e32f1SKristof Provost 		}
5213b3a8eb9SGleb Smirnoff 	} else {
5223b3a8eb9SGleb Smirnoff 		raddr = &rpool->cur->addr.v.a.addr;
5233b3a8eb9SGleb Smirnoff 		rmask = &rpool->cur->addr.v.a.mask;
5243b3a8eb9SGleb Smirnoff 	}
5253b3a8eb9SGleb Smirnoff 
5263b3a8eb9SGleb Smirnoff 	switch (rpool->opts & PF_POOL_TYPEMASK) {
5273b3a8eb9SGleb Smirnoff 	case PF_POOL_NONE:
5283b3a8eb9SGleb Smirnoff 		PF_ACPY(naddr, raddr, af);
5293b3a8eb9SGleb Smirnoff 		break;
5303b3a8eb9SGleb Smirnoff 	case PF_POOL_BITMASK:
5313b3a8eb9SGleb Smirnoff 		PF_POOLMASK(naddr, raddr, rmask, saddr, af);
5323b3a8eb9SGleb Smirnoff 		break;
5333b3a8eb9SGleb Smirnoff 	case PF_POOL_RANDOM:
5343b3a8eb9SGleb Smirnoff 		if (init_addr != NULL && PF_AZERO(init_addr, af)) {
5353b3a8eb9SGleb Smirnoff 			switch (af) {
5363b3a8eb9SGleb Smirnoff #ifdef INET
5373b3a8eb9SGleb Smirnoff 			case AF_INET:
5383b3a8eb9SGleb Smirnoff 				rpool->counter.addr32[0] = htonl(arc4random());
5393b3a8eb9SGleb Smirnoff 				break;
5403b3a8eb9SGleb Smirnoff #endif /* INET */
5413b3a8eb9SGleb Smirnoff #ifdef INET6
5423b3a8eb9SGleb Smirnoff 			case AF_INET6:
5433b3a8eb9SGleb Smirnoff 				if (rmask->addr32[3] != 0xffffffff)
5443b3a8eb9SGleb Smirnoff 					rpool->counter.addr32[3] =
5453b3a8eb9SGleb Smirnoff 					    htonl(arc4random());
5463b3a8eb9SGleb Smirnoff 				else
5473b3a8eb9SGleb Smirnoff 					break;
5483b3a8eb9SGleb Smirnoff 				if (rmask->addr32[2] != 0xffffffff)
5493b3a8eb9SGleb Smirnoff 					rpool->counter.addr32[2] =
5503b3a8eb9SGleb Smirnoff 					    htonl(arc4random());
5513b3a8eb9SGleb Smirnoff 				else
5523b3a8eb9SGleb Smirnoff 					break;
5533b3a8eb9SGleb Smirnoff 				if (rmask->addr32[1] != 0xffffffff)
5543b3a8eb9SGleb Smirnoff 					rpool->counter.addr32[1] =
5553b3a8eb9SGleb Smirnoff 					    htonl(arc4random());
5563b3a8eb9SGleb Smirnoff 				else
5573b3a8eb9SGleb Smirnoff 					break;
5583b3a8eb9SGleb Smirnoff 				if (rmask->addr32[0] != 0xffffffff)
5593b3a8eb9SGleb Smirnoff 					rpool->counter.addr32[0] =
5603b3a8eb9SGleb Smirnoff 					    htonl(arc4random());
5613b3a8eb9SGleb Smirnoff 				break;
5623b3a8eb9SGleb Smirnoff #endif /* INET6 */
5633b3a8eb9SGleb Smirnoff 			}
5643b3a8eb9SGleb Smirnoff 			PF_POOLMASK(naddr, raddr, rmask, &rpool->counter, af);
5653b3a8eb9SGleb Smirnoff 			PF_ACPY(init_addr, naddr, af);
5663b3a8eb9SGleb Smirnoff 
5673b3a8eb9SGleb Smirnoff 		} else {
5683b3a8eb9SGleb Smirnoff 			PF_AINC(&rpool->counter, af);
5693b3a8eb9SGleb Smirnoff 			PF_POOLMASK(naddr, raddr, rmask, &rpool->counter, af);
5703b3a8eb9SGleb Smirnoff 		}
5713b3a8eb9SGleb Smirnoff 		break;
5723b3a8eb9SGleb Smirnoff 	case PF_POOL_SRCHASH:
5733b3a8eb9SGleb Smirnoff 	    {
5743b3a8eb9SGleb Smirnoff 		unsigned char hash[16];
5753b3a8eb9SGleb Smirnoff 
5763b3a8eb9SGleb Smirnoff 		pf_hash(saddr, (struct pf_addr *)&hash, &rpool->key, af);
5773b3a8eb9SGleb Smirnoff 		PF_POOLMASK(naddr, raddr, rmask, (struct pf_addr *)&hash, af);
5783b3a8eb9SGleb Smirnoff 		break;
5793b3a8eb9SGleb Smirnoff 	    }
5803b3a8eb9SGleb Smirnoff 	case PF_POOL_ROUNDROBIN:
5813b3a8eb9SGleb Smirnoff 	    {
582320c1116SKristof Provost 		struct pf_kpooladdr *acur = rpool->cur;
5833b3a8eb9SGleb Smirnoff 
5843b3a8eb9SGleb Smirnoff 		if (rpool->cur->addr.type == PF_ADDR_TABLE) {
5853b3a8eb9SGleb Smirnoff 			if (!pfr_pool_get(rpool->cur->addr.p.tbl,
5867d381d0aSKristof Provost 			    &rpool->tblidx, &rpool->counter, af, NULL))
5873b3a8eb9SGleb Smirnoff 				goto get_addr;
5883b3a8eb9SGleb Smirnoff 		} else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) {
5893b3a8eb9SGleb Smirnoff 			if (!pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt,
5907d381d0aSKristof Provost 			    &rpool->tblidx, &rpool->counter, af, pf_islinklocal))
5913b3a8eb9SGleb Smirnoff 				goto get_addr;
5923b3a8eb9SGleb Smirnoff 		} else if (pf_match_addr(0, raddr, rmask, &rpool->counter, af))
5933b3a8eb9SGleb Smirnoff 			goto get_addr;
5943b3a8eb9SGleb Smirnoff 
5953b3a8eb9SGleb Smirnoff 	try_next:
5963b3a8eb9SGleb Smirnoff 		if (TAILQ_NEXT(rpool->cur, entries) == NULL)
5973b3a8eb9SGleb Smirnoff 			rpool->cur = TAILQ_FIRST(&rpool->list);
5983b3a8eb9SGleb Smirnoff 		else
5993b3a8eb9SGleb Smirnoff 			rpool->cur = TAILQ_NEXT(rpool->cur, entries);
6003b3a8eb9SGleb Smirnoff 		if (rpool->cur->addr.type == PF_ADDR_TABLE) {
6013b3a8eb9SGleb Smirnoff 			if (pfr_pool_get(rpool->cur->addr.p.tbl,
6027d381d0aSKristof Provost 			    &rpool->tblidx, &rpool->counter, af, NULL)) {
6033b3a8eb9SGleb Smirnoff 				/* table contains no address of type 'af' */
6043b3a8eb9SGleb Smirnoff 				if (rpool->cur != acur)
6053b3a8eb9SGleb Smirnoff 					goto try_next;
60616303d2bSKajetan Staszkiewicz 				reason = PFRES_MAPFAILED;
60716303d2bSKajetan Staszkiewicz 				goto done_pool_mtx;
6083b3a8eb9SGleb Smirnoff 			}
6093b3a8eb9SGleb Smirnoff 		} else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) {
6103b3a8eb9SGleb Smirnoff 			rpool->tblidx = -1;
6113b3a8eb9SGleb Smirnoff 			if (pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt,
6127d381d0aSKristof Provost 			    &rpool->tblidx, &rpool->counter, af, pf_islinklocal)) {
6133b3a8eb9SGleb Smirnoff 				/* table contains no address of type 'af' */
6143b3a8eb9SGleb Smirnoff 				if (rpool->cur != acur)
6153b3a8eb9SGleb Smirnoff 					goto try_next;
61616303d2bSKajetan Staszkiewicz 				reason = PFRES_MAPFAILED;
61716303d2bSKajetan Staszkiewicz 				goto done_pool_mtx;
6183b3a8eb9SGleb Smirnoff 			}
6193b3a8eb9SGleb Smirnoff 		} else {
6203b3a8eb9SGleb Smirnoff 			raddr = &rpool->cur->addr.v.a.addr;
6213b3a8eb9SGleb Smirnoff 			rmask = &rpool->cur->addr.v.a.mask;
6223b3a8eb9SGleb Smirnoff 			PF_ACPY(&rpool->counter, raddr, af);
6233b3a8eb9SGleb Smirnoff 		}
6243b3a8eb9SGleb Smirnoff 
6253b3a8eb9SGleb Smirnoff 	get_addr:
6263b3a8eb9SGleb Smirnoff 		PF_ACPY(naddr, &rpool->counter, af);
6273b3a8eb9SGleb Smirnoff 		if (init_addr != NULL && PF_AZERO(init_addr, af))
6283b3a8eb9SGleb Smirnoff 			PF_ACPY(init_addr, naddr, af);
6293b3a8eb9SGleb Smirnoff 		PF_AINC(&rpool->counter, af);
6303b3a8eb9SGleb Smirnoff 		break;
6313b3a8eb9SGleb Smirnoff 	    }
6323b3a8eb9SGleb Smirnoff 	}
633d10de21fSKajetan Staszkiewicz 
634d10de21fSKajetan Staszkiewicz 	if (nkif)
635d10de21fSKajetan Staszkiewicz 		*nkif = rpool->cur->kif;
636d10de21fSKajetan Staszkiewicz 
6378e3d2529SKajetan Staszkiewicz done_pool_mtx:
6388e3d2529SKajetan Staszkiewicz 	mtx_unlock(&rpool->mtx);
6398e3d2529SKajetan Staszkiewicz 
6408e3d2529SKajetan Staszkiewicz 	if (reason) {
6418e3d2529SKajetan Staszkiewicz 		counter_u64_add(V_pf_status.counters[reason], 1);
6428e3d2529SKajetan Staszkiewicz 	}
6438e3d2529SKajetan Staszkiewicz 
6448e3d2529SKajetan Staszkiewicz 	return (reason);
6458e3d2529SKajetan Staszkiewicz }
6468e3d2529SKajetan Staszkiewicz 
6478e3d2529SKajetan Staszkiewicz u_short
6488e3d2529SKajetan Staszkiewicz pf_map_addr_sn(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
6498e3d2529SKajetan Staszkiewicz     struct pf_addr *naddr, struct pfi_kkif **nkif, struct pf_addr *init_addr,
650fcdb520cSKristof Provost     struct pf_ksrc_node **sn, struct pf_srchash **sh, struct pf_kpool *rpool)
6518e3d2529SKajetan Staszkiewicz {
6528e3d2529SKajetan Staszkiewicz 	u_short			 reason = 0;
6538e3d2529SKajetan Staszkiewicz 
654c49c9da2SKajetan Staszkiewicz 	KASSERT(*sn == NULL, ("*sn not NULL"));
655c49c9da2SKajetan Staszkiewicz 
656b9c0321dSKajetan Staszkiewicz 	/*
657c49c9da2SKajetan Staszkiewicz 	 * If this is a sticky-address rule, try to find an existing src_node.
658c49c9da2SKajetan Staszkiewicz 	 * Request the sh to be unlocked if sn was not found, as we never
659c49c9da2SKajetan Staszkiewicz 	 * insert a new sn when parsing the ruleset.
660b9c0321dSKajetan Staszkiewicz 	 */
661e11dacbfSKristof Provost 	if (r->rdr.opts & PF_POOL_STICKYADDR &&
662e11dacbfSKristof Provost 	    (r->rdr.opts & PF_POOL_TYPEMASK) != PF_POOL_NONE)
663b9c0321dSKajetan Staszkiewicz 		*sn = pf_find_src_node(saddr, r, af, sh, false);
6648e3d2529SKajetan Staszkiewicz 
665c49c9da2SKajetan Staszkiewicz 	if (*sn != NULL) {
666b9c0321dSKajetan Staszkiewicz 		PF_SRC_NODE_LOCK_ASSERT(*sn);
667b9c0321dSKajetan Staszkiewicz 
6688e3d2529SKajetan Staszkiewicz 		/* If the supplied address is the same as the current one we've
6698e3d2529SKajetan Staszkiewicz 		 * been asked before, so tell the caller that there's no other
6708e3d2529SKajetan Staszkiewicz 		 * address to be had. */
6718e3d2529SKajetan Staszkiewicz 		if (PF_AEQ(naddr, &(*sn)->raddr, af)) {
6728e3d2529SKajetan Staszkiewicz 			reason = PFRES_MAPFAILED;
6738e3d2529SKajetan Staszkiewicz 			goto done;
6748e3d2529SKajetan Staszkiewicz 		}
6758e3d2529SKajetan Staszkiewicz 
6768e3d2529SKajetan Staszkiewicz 		PF_ACPY(naddr, &(*sn)->raddr, af);
6778e3d2529SKajetan Staszkiewicz 		if (nkif)
6788e3d2529SKajetan Staszkiewicz 			*nkif = (*sn)->rkif;
6798e3d2529SKajetan Staszkiewicz 		if (V_pf_status.debug >= PF_DEBUG_NOISY) {
6808e3d2529SKajetan Staszkiewicz 			printf("pf_map_addr: src tracking maps ");
6818e3d2529SKajetan Staszkiewicz 			pf_print_host(saddr, 0, af);
6828e3d2529SKajetan Staszkiewicz 			printf(" to ");
6838e3d2529SKajetan Staszkiewicz 			pf_print_host(naddr, 0, af);
6848e3d2529SKajetan Staszkiewicz 			if (nkif)
6858e3d2529SKajetan Staszkiewicz 				printf("@%s", (*nkif)->pfik_name);
6868e3d2529SKajetan Staszkiewicz 			printf("\n");
6878e3d2529SKajetan Staszkiewicz 		}
6888e3d2529SKajetan Staszkiewicz 		goto done;
6898e3d2529SKajetan Staszkiewicz 	}
6908e3d2529SKajetan Staszkiewicz 
6918e3d2529SKajetan Staszkiewicz 	/*
6928e3d2529SKajetan Staszkiewicz 	 * Source node has not been found. Find a new address and store it
6938e3d2529SKajetan Staszkiewicz 	 * in variables given by the caller.
6948e3d2529SKajetan Staszkiewicz 	 */
695fcdb520cSKristof Provost 	if (pf_map_addr(af, r, saddr, naddr, nkif, init_addr, rpool) != 0) {
6968e3d2529SKajetan Staszkiewicz 		/* pf_map_addr() sets reason counters on its own */
6978e3d2529SKajetan Staszkiewicz 		goto done;
6988e3d2529SKajetan Staszkiewicz 	}
6998e3d2529SKajetan Staszkiewicz 
700498cca14SKristof Provost 	if (V_pf_status.debug >= PF_DEBUG_NOISY &&
7013b3a8eb9SGleb Smirnoff 	    (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) {
7023b3a8eb9SGleb Smirnoff 		printf("pf_map_addr: selected address ");
7033b3a8eb9SGleb Smirnoff 		pf_print_host(naddr, 0, af);
704d10de21fSKajetan Staszkiewicz 		if (nkif)
705d10de21fSKajetan Staszkiewicz 			printf("@%s", (*nkif)->pfik_name);
7063b3a8eb9SGleb Smirnoff 		printf("\n");
7073b3a8eb9SGleb Smirnoff 	}
7083b3a8eb9SGleb Smirnoff 
70916303d2bSKajetan Staszkiewicz done:
710b9c0321dSKajetan Staszkiewicz 	if ((*sn) != NULL)
711b9c0321dSKajetan Staszkiewicz 		PF_SRC_NODE_UNLOCK(*sn);
712b9c0321dSKajetan Staszkiewicz 
71316303d2bSKajetan Staszkiewicz 	if (reason) {
71416303d2bSKajetan Staszkiewicz 		counter_u64_add(V_pf_status.counters[reason], 1);
71516303d2bSKajetan Staszkiewicz 	}
71616303d2bSKajetan Staszkiewicz 
71716303d2bSKajetan Staszkiewicz 	return (reason);
7183b3a8eb9SGleb Smirnoff }
7193b3a8eb9SGleb Smirnoff 
7207e65cfc9SMark Johnston u_short
7219a405864SKristof Provost pf_get_translation(struct pf_pdesc *pd, int off,
7222d7e68d5SKristof Provost     struct pf_state_key **skp, struct pf_state_key **nkp,
723b9c0321dSKajetan Staszkiewicz     struct pf_kanchor_stackframe *anchor_stack, struct pf_krule **rp,
724390dc369STom Jones     struct pf_udp_mapping **udp_mapping)
7253b3a8eb9SGleb Smirnoff {
726e86bddeaSKristof Provost 	struct pf_krule	*r = NULL;
7273b3a8eb9SGleb Smirnoff 	struct pf_addr	*naddr;
728b9c0321dSKajetan Staszkiewicz 	struct pf_ksrc_node	*sn = NULL;
729b9c0321dSKajetan Staszkiewicz 	struct pf_srchash	*sh = NULL;
7309897a669SMark Johnston 	uint16_t	*nportp;
7312aa21096SKurosawa Takahiro 	uint16_t	 low, high;
7327e65cfc9SMark Johnston 	u_short		 reason;
7333b3a8eb9SGleb Smirnoff 
7343b3a8eb9SGleb Smirnoff 	PF_RULES_RASSERT();
7353b3a8eb9SGleb Smirnoff 	KASSERT(*skp == NULL, ("*skp not NULL"));
7363b3a8eb9SGleb Smirnoff 	KASSERT(*nkp == NULL, ("*nkp not NULL"));
7373b3a8eb9SGleb Smirnoff 
7387e65cfc9SMark Johnston 	*rp = NULL;
7397e65cfc9SMark Johnston 
740f2064dd1SKajetan Staszkiewicz 	if (pd->dir == PF_OUT) {
7412d7e68d5SKristof Provost 		r = pf_match_translation(pd, PF_RULESET_BINAT, anchor_stack);
7423b3a8eb9SGleb Smirnoff 		if (r == NULL)
7432d7e68d5SKristof Provost 			r = pf_match_translation(pd, PF_RULESET_NAT, anchor_stack);
7443b3a8eb9SGleb Smirnoff 	} else {
7452d7e68d5SKristof Provost 		r = pf_match_translation(pd, PF_RULESET_RDR, anchor_stack);
7463b3a8eb9SGleb Smirnoff 		if (r == NULL)
7472d7e68d5SKristof Provost 			r = pf_match_translation(pd, PF_RULESET_BINAT, anchor_stack);
7483b3a8eb9SGleb Smirnoff 	}
7493b3a8eb9SGleb Smirnoff 
7503b3a8eb9SGleb Smirnoff 	if (r == NULL)
7517e65cfc9SMark Johnston 		return (PFRES_MAX);
7523b3a8eb9SGleb Smirnoff 
7533b3a8eb9SGleb Smirnoff 	switch (r->action) {
7543b3a8eb9SGleb Smirnoff 	case PF_NONAT:
7553b3a8eb9SGleb Smirnoff 	case PF_NOBINAT:
7563b3a8eb9SGleb Smirnoff 	case PF_NORDR:
7577e65cfc9SMark Johnston 		return (PFRES_MAX);
7583b3a8eb9SGleb Smirnoff 	}
7593b3a8eb9SGleb Smirnoff 
760fcdb520cSKristof Provost 	if (pf_state_key_setup(pd, pd->nsport, pd->ndport, skp, nkp))
7617e65cfc9SMark Johnston 		return (PFRES_MEMORY);
7623b3a8eb9SGleb Smirnoff 
7633b3a8eb9SGleb Smirnoff 	naddr = &(*nkp)->addr[1];
7649897a669SMark Johnston 	nportp = &(*nkp)->port[1];
7653b3a8eb9SGleb Smirnoff 
7663b3a8eb9SGleb Smirnoff 	switch (r->action) {
7673b3a8eb9SGleb Smirnoff 	case PF_NAT:
7682aa21096SKurosawa Takahiro 		if (pd->proto == IPPROTO_ICMP) {
7692aa21096SKurosawa Takahiro 			low = 1;
7702aa21096SKurosawa Takahiro 			high = 65535;
7712aa21096SKurosawa Takahiro 		} else {
772e11dacbfSKristof Provost 			low  = r->rdr.proxy_port[0];
773e11dacbfSKristof Provost 			high = r->rdr.proxy_port[1];
7742aa21096SKurosawa Takahiro 		}
775e11dacbfSKristof Provost 		if (r->rdr.mape.offset > 0) {
7762d7e68d5SKristof Provost 			if (pf_get_mape_sport(pd, r, naddr, nportp, &sn,
7772d7e68d5SKristof Provost 			    &sh, udp_mapping)) {
7782aa21096SKurosawa Takahiro 				DPFPRINTF(PF_DEBUG_MISC,
7792aa21096SKurosawa Takahiro 				    ("pf: MAP-E port allocation (%u/%u/%u)"
7802aa21096SKurosawa Takahiro 				    " failed\n",
781e11dacbfSKristof Provost 				    r->rdr.mape.offset,
782e11dacbfSKristof Provost 				    r->rdr.mape.psidlen,
783e11dacbfSKristof Provost 				    r->rdr.mape.psid));
7847e65cfc9SMark Johnston 				reason = PFRES_MAPFAILED;
7852aa21096SKurosawa Takahiro 				goto notrans;
7862aa21096SKurosawa Takahiro 			}
7872d7e68d5SKristof Provost 		} else if (pf_get_sport(pd, r, naddr, nportp, low, high, &sn,
788fcdb520cSKristof Provost 		    &sh, &r->rdr, udp_mapping)) {
7893b3a8eb9SGleb Smirnoff 			DPFPRINTF(PF_DEBUG_MISC,
7903b3a8eb9SGleb Smirnoff 			    ("pf: NAT proxy port allocation (%u-%u) failed\n",
791e11dacbfSKristof Provost 			    r->rdr.proxy_port[0], r->rdr.proxy_port[1]));
7927e65cfc9SMark Johnston 			reason = PFRES_MAPFAILED;
7933b3a8eb9SGleb Smirnoff 			goto notrans;
7943b3a8eb9SGleb Smirnoff 		}
7953b3a8eb9SGleb Smirnoff 		break;
7963b3a8eb9SGleb Smirnoff 	case PF_BINAT:
797f2064dd1SKajetan Staszkiewicz 		switch (pd->dir) {
7983b3a8eb9SGleb Smirnoff 		case PF_OUT:
799e11dacbfSKristof Provost 			if (r->rdr.cur->addr.type == PF_ADDR_DYNIFTL){
8003b3a8eb9SGleb Smirnoff 				switch (pd->af) {
8013b3a8eb9SGleb Smirnoff #ifdef INET
8023b3a8eb9SGleb Smirnoff 				case AF_INET:
803e11dacbfSKristof Provost 					if (r->rdr.cur->addr.p.dyn->
8047e65cfc9SMark Johnston 					    pfid_acnt4 < 1) {
8057e65cfc9SMark Johnston 						reason = PFRES_MAPFAILED;
8063b3a8eb9SGleb Smirnoff 						goto notrans;
8077e65cfc9SMark Johnston 					}
8083b3a8eb9SGleb Smirnoff 					PF_POOLMASK(naddr,
809e11dacbfSKristof Provost 					    &r->rdr.cur->addr.p.dyn->
8103b3a8eb9SGleb Smirnoff 					    pfid_addr4,
811e11dacbfSKristof Provost 					    &r->rdr.cur->addr.p.dyn->
8122d7e68d5SKristof Provost 					    pfid_mask4, &pd->nsaddr, AF_INET);
8133b3a8eb9SGleb Smirnoff 					break;
8143b3a8eb9SGleb Smirnoff #endif /* INET */
8153b3a8eb9SGleb Smirnoff #ifdef INET6
8163b3a8eb9SGleb Smirnoff 				case AF_INET6:
817e11dacbfSKristof Provost 					if (r->rdr.cur->addr.p.dyn->
8187e65cfc9SMark Johnston 					    pfid_acnt6 < 1) {
8197e65cfc9SMark Johnston 						reason = PFRES_MAPFAILED;
8203b3a8eb9SGleb Smirnoff 						goto notrans;
8217e65cfc9SMark Johnston 					}
8223b3a8eb9SGleb Smirnoff 					PF_POOLMASK(naddr,
823e11dacbfSKristof Provost 					    &r->rdr.cur->addr.p.dyn->
8243b3a8eb9SGleb Smirnoff 					    pfid_addr6,
825e11dacbfSKristof Provost 					    &r->rdr.cur->addr.p.dyn->
8262d7e68d5SKristof Provost 					    pfid_mask6, &pd->nsaddr, AF_INET6);
8273b3a8eb9SGleb Smirnoff 					break;
8283b3a8eb9SGleb Smirnoff #endif /* INET6 */
8293b3a8eb9SGleb Smirnoff 				}
8303b3a8eb9SGleb Smirnoff 			} else
8313b3a8eb9SGleb Smirnoff 				PF_POOLMASK(naddr,
832e11dacbfSKristof Provost 				    &r->rdr.cur->addr.v.a.addr,
8332d7e68d5SKristof Provost 				    &r->rdr.cur->addr.v.a.mask, &pd->nsaddr,
8343b3a8eb9SGleb Smirnoff 				    pd->af);
8353b3a8eb9SGleb Smirnoff 			break;
8363b3a8eb9SGleb Smirnoff 		case PF_IN:
8373b3a8eb9SGleb Smirnoff 			if (r->src.addr.type == PF_ADDR_DYNIFTL) {
8383b3a8eb9SGleb Smirnoff 				switch (pd->af) {
8393b3a8eb9SGleb Smirnoff #ifdef INET
8403b3a8eb9SGleb Smirnoff 				case AF_INET:
8417e65cfc9SMark Johnston 					if (r->src.addr.p.dyn->pfid_acnt4 < 1) {
8427e65cfc9SMark Johnston 						reason = PFRES_MAPFAILED;
8433b3a8eb9SGleb Smirnoff 						goto notrans;
8447e65cfc9SMark Johnston 					}
8453b3a8eb9SGleb Smirnoff 					PF_POOLMASK(naddr,
8463b3a8eb9SGleb Smirnoff 					    &r->src.addr.p.dyn->pfid_addr4,
8473b3a8eb9SGleb Smirnoff 					    &r->src.addr.p.dyn->pfid_mask4,
8482d7e68d5SKristof Provost 					    &pd->ndaddr, AF_INET);
8493b3a8eb9SGleb Smirnoff 					break;
8503b3a8eb9SGleb Smirnoff #endif /* INET */
8513b3a8eb9SGleb Smirnoff #ifdef INET6
8523b3a8eb9SGleb Smirnoff 				case AF_INET6:
8537e65cfc9SMark Johnston 					if (r->src.addr.p.dyn->pfid_acnt6 < 1) {
8547e65cfc9SMark Johnston 						reason = PFRES_MAPFAILED;
8553b3a8eb9SGleb Smirnoff 						goto notrans;
8567e65cfc9SMark Johnston 					}
8573b3a8eb9SGleb Smirnoff 					PF_POOLMASK(naddr,
8583b3a8eb9SGleb Smirnoff 					    &r->src.addr.p.dyn->pfid_addr6,
8593b3a8eb9SGleb Smirnoff 					    &r->src.addr.p.dyn->pfid_mask6,
8602d7e68d5SKristof Provost 					    &pd->ndaddr, AF_INET6);
8613b3a8eb9SGleb Smirnoff 					break;
8623b3a8eb9SGleb Smirnoff #endif /* INET6 */
8633b3a8eb9SGleb Smirnoff 				}
8643b3a8eb9SGleb Smirnoff 			} else
8653b3a8eb9SGleb Smirnoff 				PF_POOLMASK(naddr, &r->src.addr.v.a.addr,
8662d7e68d5SKristof Provost 				    &r->src.addr.v.a.mask, &pd->ndaddr, pd->af);
8673b3a8eb9SGleb Smirnoff 			break;
8683b3a8eb9SGleb Smirnoff 		}
8693b3a8eb9SGleb Smirnoff 		break;
8703b3a8eb9SGleb Smirnoff 	case PF_RDR: {
8719897a669SMark Johnston 		struct pf_state_key_cmp key;
872339a1977SMark Johnston 		int tries;
8739897a669SMark Johnston 		uint16_t cut, low, high, nport;
8749897a669SMark Johnston 
8752d7e68d5SKristof Provost 		reason = pf_map_addr_sn(pd->af, r, &pd->nsaddr, naddr, NULL,
876fcdb520cSKristof Provost 		    NULL, &sn, &sh, &r->rdr);
8777e65cfc9SMark Johnston 		if (reason != 0)
8783b3a8eb9SGleb Smirnoff 			goto notrans;
879e11dacbfSKristof Provost 		if ((r->rdr.opts & PF_POOL_TYPEMASK) == PF_POOL_BITMASK)
880e11dacbfSKristof Provost 			PF_POOLMASK(naddr, naddr, &r->rdr.cur->addr.v.a.mask,
8812d7e68d5SKristof Provost 			    &pd->ndaddr, pd->af);
8823b3a8eb9SGleb Smirnoff 
8836053adafSKristof Provost 		/* Do not change SCTP ports. */
8846053adafSKristof Provost 		if (pd->proto == IPPROTO_SCTP)
8856053adafSKristof Provost 			break;
8866053adafSKristof Provost 
887e11dacbfSKristof Provost 		if (r->rdr.proxy_port[1]) {
8883b3a8eb9SGleb Smirnoff 			uint32_t	tmp_nport;
8893b3a8eb9SGleb Smirnoff 
8902d7e68d5SKristof Provost 			tmp_nport = ((ntohs(pd->ndport) - ntohs(r->dst.port[0])) %
891e11dacbfSKristof Provost 			    (r->rdr.proxy_port[1] - r->rdr.proxy_port[0] +
892e11dacbfSKristof Provost 			    1)) + r->rdr.proxy_port[0];
8933b3a8eb9SGleb Smirnoff 
8943b3a8eb9SGleb Smirnoff 			/* Wrap around if necessary. */
8953b3a8eb9SGleb Smirnoff 			if (tmp_nport > 65535)
8963b3a8eb9SGleb Smirnoff 				tmp_nport -= 65535;
8979897a669SMark Johnston 			nport = htons((uint16_t)tmp_nport);
898e11dacbfSKristof Provost 		} else if (r->rdr.proxy_port[0])
899e11dacbfSKristof Provost 			nport = htons(r->rdr.proxy_port[0]);
9009897a669SMark Johnston 		else
9012d7e68d5SKristof Provost 			nport = pd->ndport;
9029897a669SMark Johnston 
9039897a669SMark Johnston 		/*
9049897a669SMark Johnston 		 * Update the destination port.
9059897a669SMark Johnston 		 */
9069897a669SMark Johnston 		*nportp = nport;
9079897a669SMark Johnston 
9089897a669SMark Johnston 		/*
9099897a669SMark Johnston 		 * Do we have a source port conflict in the stack state?  Try to
9109897a669SMark Johnston 		 * modulate the source port if so.  Note that this is racy since
9119897a669SMark Johnston 		 * the state lookup may not find any matches here but will once
9129897a669SMark Johnston 		 * pf_create_state() actually instantiates the state.
9139897a669SMark Johnston 		 */
9149897a669SMark Johnston 		bzero(&key, sizeof(key));
9159897a669SMark Johnston 		key.af = pd->af;
9169897a669SMark Johnston 		key.proto = pd->proto;
9172d7e68d5SKristof Provost 		key.port[0] = pd->nsport;
9182d7e68d5SKristof Provost 		PF_ACPY(&key.addr[0], &pd->nsaddr, key.af);
9199897a669SMark Johnston 		key.port[1] = nport;
9209897a669SMark Johnston 		PF_ACPY(&key.addr[1], naddr, key.af);
9219897a669SMark Johnston 
9229897a669SMark Johnston 		if (!pf_find_state_all_exists(&key, PF_OUT))
9239897a669SMark Johnston 			break;
9249897a669SMark Johnston 
925339a1977SMark Johnston 		tries = 0;
926339a1977SMark Johnston 
9279897a669SMark Johnston 		low = 50001;	/* XXX-MJ PF_NAT_PROXY_PORT_LOW/HIGH */
9289897a669SMark Johnston 		high = 65535;
9299897a669SMark Johnston 		cut = arc4random() % (1 + high - low) + low;
9309897a669SMark Johnston 		for (uint32_t tmp = cut;
931339a1977SMark Johnston 		    tmp <= high && tmp <= UINT16_MAX &&
932339a1977SMark Johnston 		    tries < V_pf_rdr_srcport_rewrite_tries;
933339a1977SMark Johnston 		    tmp++, tries++) {
9349897a669SMark Johnston 			key.port[0] = htons(tmp);
9359897a669SMark Johnston 			if (!pf_find_state_all_exists(&key, PF_OUT)) {
9369897a669SMark Johnston 				/* Update the source port. */
9379897a669SMark Johnston 				(*nkp)->port[0] = htons(tmp);
9389897a669SMark Johnston 				goto out;
9399897a669SMark Johnston 			}
9409897a669SMark Johnston 		}
941339a1977SMark Johnston 		for (uint32_t tmp = cut - 1;
942339a1977SMark Johnston 		    tmp >= low && tries < V_pf_rdr_srcport_rewrite_tries;
943339a1977SMark Johnston 		    tmp--, tries++) {
9449897a669SMark Johnston 			key.port[0] = htons(tmp);
9459897a669SMark Johnston 			if (!pf_find_state_all_exists(&key, PF_OUT)) {
9469897a669SMark Johnston 				/* Update the source port. */
9479897a669SMark Johnston 				(*nkp)->port[0] = htons(tmp);
9489897a669SMark Johnston 				goto out;
9499897a669SMark Johnston 			}
9509897a669SMark Johnston 		}
9519897a669SMark Johnston 
9529569fdddSMark Johnston 		/*
9539569fdddSMark Johnston 		 * We failed to find a match.  Push on ahead anyway, let
9549569fdddSMark Johnston 		 * pf_state_insert() be the arbiter of whether the state
9559569fdddSMark Johnston 		 * conflict is tolerable.  In particular, with TCP connections
9569569fdddSMark Johnston 		 * the state may be reused if the TCP state is terminal.
9579569fdddSMark Johnston 		 */
9589897a669SMark Johnston 		DPFPRINTF(PF_DEBUG_MISC,
9599897a669SMark Johnston 		    ("pf: RDR source port allocation failed\n"));
9609569fdddSMark Johnston 		break;
9617e65cfc9SMark Johnston 
9629897a669SMark Johnston out:
9639897a669SMark Johnston 		DPFPRINTF(PF_DEBUG_MISC,
9649897a669SMark Johnston 		    ("pf: RDR source port allocation %u->%u\n",
9652d7e68d5SKristof Provost 		    ntohs(pd->nsport), ntohs((*nkp)->port[0])));
9663b3a8eb9SGleb Smirnoff 		break;
9673b3a8eb9SGleb Smirnoff 	}
9683b3a8eb9SGleb Smirnoff 	default:
9693b3a8eb9SGleb Smirnoff 		panic("%s: unknown action %u", __func__, r->action);
9703b3a8eb9SGleb Smirnoff 	}
9713b3a8eb9SGleb Smirnoff 
9723b3a8eb9SGleb Smirnoff 	/* Return success only if translation really happened. */
9737e65cfc9SMark Johnston 	if (bcmp(*skp, *nkp, sizeof(struct pf_state_key_cmp))) {
9747e65cfc9SMark Johnston 		*rp = r;
9757e65cfc9SMark Johnston 		return (PFRES_MATCH);
9767e65cfc9SMark Johnston 	}
9773b3a8eb9SGleb Smirnoff 
9787e65cfc9SMark Johnston 	reason = PFRES_MAX;
9793b3a8eb9SGleb Smirnoff notrans:
9803b3a8eb9SGleb Smirnoff 	uma_zfree(V_pf_state_key_z, *nkp);
9813b3a8eb9SGleb Smirnoff 	uma_zfree(V_pf_state_key_z, *skp);
9823b3a8eb9SGleb Smirnoff 	*skp = *nkp = NULL;
9833b3a8eb9SGleb Smirnoff 
9847e65cfc9SMark Johnston 	return (reason);
9853b3a8eb9SGleb Smirnoff }
986fcdb520cSKristof Provost 
987fcdb520cSKristof Provost int
988fcdb520cSKristof Provost pf_get_transaddr_af(struct pf_krule *r, struct pf_pdesc *pd)
989fcdb520cSKristof Provost {
990fcdb520cSKristof Provost #if defined(INET) && defined(INET6)
991fcdb520cSKristof Provost 	struct pf_addr	 ndaddr, nsaddr, naddr;
992fcdb520cSKristof Provost 	u_int16_t	 nport = 0;
993fcdb520cSKristof Provost 	int		 prefixlen = 96;
994fcdb520cSKristof Provost 	struct pf_srchash	*sh = NULL;
995fcdb520cSKristof Provost 	struct pf_ksrc_node	*sns = NULL;
996fcdb520cSKristof Provost 
997*4be8e29eSKristof Provost 	bzero(&nsaddr, sizeof(nsaddr));
998*4be8e29eSKristof Provost 	bzero(&ndaddr, sizeof(ndaddr));
999*4be8e29eSKristof Provost 
1000fcdb520cSKristof Provost 	if (V_pf_status.debug >= PF_DEBUG_MISC) {
1001fcdb520cSKristof Provost 		printf("pf: af-to %s %s, ",
1002fcdb520cSKristof Provost 		    pd->naf == AF_INET ? "inet" : "inet6",
1003fcdb520cSKristof Provost 		    TAILQ_EMPTY(&r->rdr.list) ? "nat" : "rdr");
1004fcdb520cSKristof Provost 		pf_print_host(&pd->nsaddr, pd->nsport, pd->af);
1005fcdb520cSKristof Provost 		printf(" -> ");
1006fcdb520cSKristof Provost 		pf_print_host(&pd->ndaddr, pd->ndport, pd->af);
1007fcdb520cSKristof Provost 		printf("\n");
1008fcdb520cSKristof Provost 	}
1009fcdb520cSKristof Provost 
1010fcdb520cSKristof Provost 	if (TAILQ_EMPTY(&r->nat.list))
1011fcdb520cSKristof Provost 		panic("pf_get_transaddr_af: no nat pool for source address");
1012fcdb520cSKristof Provost 
1013fcdb520cSKristof Provost 	/* get source address and port */
1014fcdb520cSKristof Provost 	if (pf_get_sport(pd, r, &nsaddr, &nport,
1015fcdb520cSKristof Provost 	    r->nat.proxy_port[0], r->nat.proxy_port[1], &sns, &sh, &r->nat, NULL)) {
1016fcdb520cSKristof Provost 		DPFPRINTF(PF_DEBUG_MISC,
1017fcdb520cSKristof Provost 		    ("pf: af-to NAT proxy port allocation (%u-%u) failed",
1018fcdb520cSKristof Provost 		    r->nat.proxy_port[0], r->nat.proxy_port[1]));
1019fcdb520cSKristof Provost 		return (-1);
1020fcdb520cSKristof Provost 	}
1021fcdb520cSKristof Provost 
1022fcdb520cSKristof Provost 	if (pd->proto == IPPROTO_ICMPV6 && pd->naf == AF_INET) {
1023fcdb520cSKristof Provost 		if (pd->dir == PF_IN) {
1024fcdb520cSKristof Provost 			NTOHS(pd->ndport);
1025fcdb520cSKristof Provost 			if (pd->ndport == ICMP6_ECHO_REQUEST)
1026fcdb520cSKristof Provost 				pd->ndport = ICMP_ECHO;
1027fcdb520cSKristof Provost 			else if (pd->ndport == ICMP6_ECHO_REPLY)
1028fcdb520cSKristof Provost 				pd->ndport = ICMP_ECHOREPLY;
1029fcdb520cSKristof Provost 			HTONS(pd->ndport);
1030fcdb520cSKristof Provost 		} else {
1031fcdb520cSKristof Provost 			NTOHS(pd->nsport);
1032fcdb520cSKristof Provost 			if (pd->nsport == ICMP6_ECHO_REQUEST)
1033fcdb520cSKristof Provost 				pd->nsport = ICMP_ECHO;
1034fcdb520cSKristof Provost 			else if (pd->nsport == ICMP6_ECHO_REPLY)
1035fcdb520cSKristof Provost 				pd->nsport = ICMP_ECHOREPLY;
1036fcdb520cSKristof Provost 			HTONS(pd->nsport);
1037fcdb520cSKristof Provost 		}
1038fcdb520cSKristof Provost 	} else if (pd->proto == IPPROTO_ICMP && pd->naf == AF_INET6) {
1039fcdb520cSKristof Provost 		if (pd->dir == PF_IN) {
1040fcdb520cSKristof Provost 			NTOHS(pd->ndport);
1041fcdb520cSKristof Provost 			if (pd->ndport == ICMP_ECHO)
1042fcdb520cSKristof Provost 				pd->ndport = ICMP6_ECHO_REQUEST;
1043fcdb520cSKristof Provost 			else if (pd->ndport == ICMP_ECHOREPLY)
1044fcdb520cSKristof Provost 				pd->ndport = ICMP6_ECHO_REPLY;
1045fcdb520cSKristof Provost 			HTONS(pd->ndport);
1046fcdb520cSKristof Provost 		} else {
1047fcdb520cSKristof Provost 			NTOHS(pd->nsport);
1048fcdb520cSKristof Provost 			if (pd->nsport == ICMP_ECHO)
1049fcdb520cSKristof Provost 				pd->nsport = ICMP6_ECHO_REQUEST;
1050fcdb520cSKristof Provost 			else if (pd->nsport == ICMP_ECHOREPLY)
1051fcdb520cSKristof Provost 				pd->nsport = ICMP6_ECHO_REPLY;
1052fcdb520cSKristof Provost 			HTONS(pd->nsport);
1053fcdb520cSKristof Provost 		}
1054fcdb520cSKristof Provost 	}
1055fcdb520cSKristof Provost 
1056fcdb520cSKristof Provost 	/* get the destination address and port */
1057fcdb520cSKristof Provost 	if (! TAILQ_EMPTY(&r->rdr.list)) {
1058fcdb520cSKristof Provost 		if (pf_map_addr_sn(pd->naf, r, &nsaddr, &naddr, NULL, NULL,
1059fcdb520cSKristof Provost 		    &sns, NULL, &r->rdr))
1060fcdb520cSKristof Provost 			return (-1);
1061fcdb520cSKristof Provost 		if (r->rdr.proxy_port[0])
1062fcdb520cSKristof Provost 			pd->ndport = htons(r->rdr.proxy_port[0]);
1063fcdb520cSKristof Provost 
1064fcdb520cSKristof Provost 		if (pd->naf == AF_INET) {
1065fcdb520cSKristof Provost 			/* The prefix is the IPv4 rdr address */
1066fcdb520cSKristof Provost 			prefixlen = in_mask2len(
1067fcdb520cSKristof Provost 			    (struct in_addr *)&r->rdr.cur->addr.v.a.mask);
1068fcdb520cSKristof Provost 			inet_nat46(pd->naf, &pd->ndaddr, &ndaddr, &naddr,
1069fcdb520cSKristof Provost 			    prefixlen);
1070fcdb520cSKristof Provost 		} else {
1071fcdb520cSKristof Provost 			/* The prefix is the IPv6 rdr address */
1072fcdb520cSKristof Provost 			prefixlen = in6_mask2len(
1073fcdb520cSKristof Provost 			    (struct in6_addr *)&r->rdr.cur->addr.v.a.mask, NULL);
1074fcdb520cSKristof Provost 			inet_nat64(pd->naf, &pd->ndaddr, &ndaddr, &naddr,
1075fcdb520cSKristof Provost 			    prefixlen);
1076fcdb520cSKristof Provost 		}
1077fcdb520cSKristof Provost 	} else {
1078fcdb520cSKristof Provost 		if (pd->naf == AF_INET) {
1079fcdb520cSKristof Provost 			/* The prefix is the IPv6 dst address */
1080fcdb520cSKristof Provost 			prefixlen = in6_mask2len(
1081fcdb520cSKristof Provost 			    (struct in6_addr *)&r->dst.addr.v.a.mask, NULL);
1082fcdb520cSKristof Provost 			if (prefixlen < 32)
1083fcdb520cSKristof Provost 				prefixlen = 96;
1084fcdb520cSKristof Provost 			inet_nat64(pd->naf, &pd->ndaddr, &ndaddr, &pd->ndaddr,
1085fcdb520cSKristof Provost 			    prefixlen);
1086fcdb520cSKristof Provost 		} else {
1087fcdb520cSKristof Provost 			/*
1088fcdb520cSKristof Provost 			 * The prefix is the IPv6 nat address
1089fcdb520cSKristof Provost 			 * (that was stored in pd->nsaddr)
1090fcdb520cSKristof Provost 			 */
1091fcdb520cSKristof Provost 			prefixlen = in6_mask2len(
1092fcdb520cSKristof Provost 			    (struct in6_addr *)&r->nat.cur->addr.v.a.mask, NULL);
1093fcdb520cSKristof Provost 			if (prefixlen > 96)
1094fcdb520cSKristof Provost 				prefixlen = 96;
1095fcdb520cSKristof Provost 			inet_nat64(pd->naf, &pd->ndaddr, &ndaddr, &nsaddr,
1096fcdb520cSKristof Provost 			    prefixlen);
1097fcdb520cSKristof Provost 		}
1098fcdb520cSKristof Provost 	}
1099fcdb520cSKristof Provost 
1100fcdb520cSKristof Provost 	PF_ACPY(&pd->nsaddr, &nsaddr, pd->naf);
1101fcdb520cSKristof Provost 	PF_ACPY(&pd->ndaddr, &ndaddr, pd->naf);
1102fcdb520cSKristof Provost 
1103fcdb520cSKristof Provost 	if (V_pf_status.debug >= PF_DEBUG_MISC) {
1104fcdb520cSKristof Provost 		printf("pf: af-to %s done, prefixlen %d, ",
1105fcdb520cSKristof Provost 		    pd->naf == AF_INET ? "inet" : "inet6",
1106fcdb520cSKristof Provost 		    prefixlen);
1107fcdb520cSKristof Provost 		pf_print_host(&pd->nsaddr, pd->nsport, pd->naf);
1108fcdb520cSKristof Provost 		printf(" -> ");
1109fcdb520cSKristof Provost 		pf_print_host(&pd->ndaddr, pd->ndport, pd->naf);
1110fcdb520cSKristof Provost 		printf("\n");
1111fcdb520cSKristof Provost 	}
1112fcdb520cSKristof Provost 
1113fcdb520cSKristof Provost 	return (0);
1114fcdb520cSKristof Provost #else
1115fcdb520cSKristof Provost 	return (-1);
1116fcdb520cSKristof Provost #endif
1117fcdb520cSKristof Provost }
1118