xref: /freebsd/sys/netpfil/pf/pf_lb.c (revision daea703963f53b5530c2957f72bed57b10151810)
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 (udp_mapping) {
242390dc369STom Jones 		MPASS(*udp_mapping == NULL);
243fcdb520cSKristof Provost 	}
244390dc369STom Jones 
245390dc369STom Jones 	/*
246390dc369STom Jones 	 * If we are UDP and have an existing mapping we can get source port
247390dc369STom Jones 	 * from the mapping. In this case we have to look up the src_node as
248390dc369STom Jones 	 * pf_map_addr would.
249390dc369STom Jones 	 */
250*daea7039SKajetan Staszkiewicz 	if (pd->proto == IPPROTO_UDP && (rpool->opts & PF_POOL_ENDPI)) {
251390dc369STom Jones 		struct pf_udp_endpoint_cmp udp_source;
252390dc369STom Jones 
253390dc369STom Jones 		bzero(&udp_source, sizeof(udp_source));
2542d7e68d5SKristof Provost 		udp_source.af = pd->af;
2552d7e68d5SKristof Provost 		PF_ACPY(&udp_source.addr, &pd->nsaddr, pd->af);
2562d7e68d5SKristof Provost 		udp_source.port = pd->nsport;
257fcdb520cSKristof Provost 		if (udp_mapping) {
258390dc369STom Jones 			*udp_mapping = pf_udp_mapping_find(&udp_source);
259390dc369STom Jones 			if (*udp_mapping) {
2602d7e68d5SKristof Provost 				PF_ACPY(naddr, &(*udp_mapping)->endpoints[1].addr, pd->af);
261390dc369STom Jones 				*nport = (*udp_mapping)->endpoints[1].port;
262390dc369STom Jones 				/* Try to find a src_node as per pf_map_addr(). */
263*daea7039SKajetan Staszkiewicz 				if (*sn == NULL && rpool->opts & PF_POOL_STICKYADDR &&
264*daea7039SKajetan Staszkiewicz 				    (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE)
2652d7e68d5SKristof Provost 					*sn = pf_find_src_node(&pd->nsaddr, r, pd->af, sh, false);
266b9c0321dSKajetan Staszkiewicz 				if (*sn != NULL)
267b9c0321dSKajetan Staszkiewicz 					PF_SRC_NODE_UNLOCK(*sn);
268390dc369STom Jones 				return (0);
269390dc369STom Jones 			} else {
2702d7e68d5SKristof Provost 				*udp_mapping = pf_udp_mapping_create(pd->af, &pd->nsaddr,
2712d7e68d5SKristof Provost 				    pd->nsport, &init_addr, 0);
272390dc369STom Jones 				if (*udp_mapping == NULL)
2733b3a8eb9SGleb Smirnoff 					return (1);
274390dc369STom Jones 			}
275390dc369STom Jones 		}
276fcdb520cSKristof Provost 	}
277390dc369STom Jones 
278*daea7039SKajetan Staszkiewicz 	if (pf_map_addr_sn(pd->naf, r, &pd->nsaddr, naddr, NULL, &init_addr,
279fcdb520cSKristof Provost 	    sn, sh, rpool))
280390dc369STom Jones 		goto failed;
2813b3a8eb9SGleb Smirnoff 
2822d7e68d5SKristof Provost 	if (pd->proto == IPPROTO_ICMP) {
283534ee17eSKristof Provost 		if (*nport == htons(ICMP_ECHO)) {
284534ee17eSKristof Provost 			low = 1;
285534ee17eSKristof Provost 			high = 65535;
286534ee17eSKristof Provost 		} else
287534ee17eSKristof Provost 			return (0);	/* Don't try to modify non-echo ICMP */
288534ee17eSKristof Provost 	}
289534ee17eSKristof Provost #ifdef INET6
2902d7e68d5SKristof Provost 	if (pd->proto == IPPROTO_ICMPV6) {
291534ee17eSKristof Provost 		if (*nport == htons(ICMP6_ECHO_REQUEST)) {
292534ee17eSKristof Provost 			low = 1;
293534ee17eSKristof Provost 			high = 65535;
294534ee17eSKristof Provost 		} else
295534ee17eSKristof Provost 			return (0);	/* Don't try to modify non-echo ICMP */
296534ee17eSKristof Provost 	}
297534ee17eSKristof Provost #endif /* INET6 */
298534ee17eSKristof Provost 
2998fc6e19cSGleb Smirnoff 	bzero(&key, sizeof(key));
300fcdb520cSKristof Provost 	key.af = pd->naf;
3012d7e68d5SKristof Provost 	key.proto = pd->proto;
3022d7e68d5SKristof Provost 	key.port[0] = pd->ndport;
3032d7e68d5SKristof Provost 	PF_ACPY(&key.addr[0], &pd->ndaddr, key.af);
3048fc6e19cSGleb Smirnoff 
3058fc6e19cSGleb Smirnoff 	do {
3068fc6e19cSGleb Smirnoff 		PF_ACPY(&key.addr[1], naddr, key.af);
307fcdb520cSKristof Provost 		if (udp_mapping && *udp_mapping)
3082d7e68d5SKristof Provost 			PF_ACPY(&(*udp_mapping)->endpoints[1].addr, naddr, pd->af);
3093b3a8eb9SGleb Smirnoff 
3103b3a8eb9SGleb Smirnoff 		/*
3113b3a8eb9SGleb Smirnoff 		 * port search; start random, step;
3123b3a8eb9SGleb Smirnoff 		 * similar 2 portloop in in_pcbbind
3133b3a8eb9SGleb Smirnoff 		 */
3142d7e68d5SKristof Provost 		if (pd->proto == IPPROTO_SCTP) {
3152d7e68d5SKristof Provost 			key.port[1] = pd->nsport;
3166053adafSKristof Provost 			if (!pf_find_state_all_exists(&key, PF_IN)) {
3172d7e68d5SKristof Provost 				*nport = pd->nsport;
3186053adafSKristof Provost 				return (0);
3196053adafSKristof Provost 			} else {
3206053adafSKristof Provost 				return (1); /* Fail mapping. */
3216053adafSKristof Provost 			}
3222d7e68d5SKristof Provost 		} else if (!(pd->proto == IPPROTO_TCP || pd->proto == IPPROTO_UDP ||
3232d7e68d5SKristof Provost 		    pd->proto == IPPROTO_ICMP) || (low == 0 && high == 0)) {
3248fc6e19cSGleb Smirnoff 			/*
3258fc6e19cSGleb Smirnoff 			 * XXX bug: icmp states don't use the id on both sides.
3268fc6e19cSGleb Smirnoff 			 * (traceroute -I through nat)
3278fc6e19cSGleb Smirnoff 			 */
3282d7e68d5SKristof Provost 			key.port[1] = pd->nsport;
32919d6e29bSMateusz Guzik 			if (!pf_find_state_all_exists(&key, PF_IN)) {
3302d7e68d5SKristof Provost 				*nport = pd->nsport;
3313b3a8eb9SGleb Smirnoff 				return (0);
3328fc6e19cSGleb Smirnoff 			}
3333b3a8eb9SGleb Smirnoff 		} else if (low == high) {
3348fc6e19cSGleb Smirnoff 			key.port[1] = htons(low);
33519d6e29bSMateusz Guzik 			if (!pf_find_state_all_exists(&key, PF_IN)) {
336fcdb520cSKristof Provost 				if (udp_mapping && *udp_mapping != NULL) {
337390dc369STom Jones 					(*udp_mapping)->endpoints[1].port = htons(low);
338390dc369STom Jones 					if (pf_udp_mapping_insert(*udp_mapping) == 0) {
3393b3a8eb9SGleb Smirnoff 						*nport = htons(low);
3403b3a8eb9SGleb Smirnoff 						return (0);
3413b3a8eb9SGleb Smirnoff 					}
3423b3a8eb9SGleb Smirnoff 				} else {
343390dc369STom Jones 					*nport = htons(low);
344390dc369STom Jones 					return (0);
345390dc369STom Jones 				}
346390dc369STom Jones 			}
347390dc369STom Jones 		} else {
3487f3ad018SKristof Provost 			uint32_t tmp;
3497f3ad018SKristof Provost 			uint16_t cut;
3503b3a8eb9SGleb Smirnoff 
3513b3a8eb9SGleb Smirnoff 			if (low > high) {
3523b3a8eb9SGleb Smirnoff 				tmp = low;
3533b3a8eb9SGleb Smirnoff 				low = high;
3543b3a8eb9SGleb Smirnoff 				high = tmp;
3553b3a8eb9SGleb Smirnoff 			}
3563b3a8eb9SGleb Smirnoff 			/* low < high */
357e4e01d9cSGleb Smirnoff 			cut = arc4random() % (1 + high - low) + low;
3583b3a8eb9SGleb Smirnoff 			/* low <= cut <= high */
3597f3ad018SKristof Provost 			for (tmp = cut; tmp <= high && tmp <= 0xffff; ++tmp) {
360fcdb520cSKristof Provost 				if (udp_mapping && *udp_mapping != NULL) {
361390dc369STom Jones 					(*udp_mapping)->endpoints[1].port = htons(tmp);
362390dc369STom Jones 					if (pf_udp_mapping_insert(*udp_mapping) == 0) {
363390dc369STom Jones 						*nport = htons(tmp);
364390dc369STom Jones 						return (0);
365390dc369STom Jones 					}
366390dc369STom Jones 				} else {
3678fc6e19cSGleb Smirnoff 					key.port[1] = htons(tmp);
36819d6e29bSMateusz Guzik 					if (!pf_find_state_all_exists(&key, PF_IN)) {
3693b3a8eb9SGleb Smirnoff 						*nport = htons(tmp);
3703b3a8eb9SGleb Smirnoff 						return (0);
3713b3a8eb9SGleb Smirnoff 					}
3723b3a8eb9SGleb Smirnoff 				}
373390dc369STom Jones 			}
3747f3ad018SKristof Provost 			tmp = cut;
3757f3ad018SKristof Provost 			for (tmp -= 1; tmp >= low && tmp <= 0xffff; --tmp) {
3762d7e68d5SKristof Provost 				if (pd->proto == IPPROTO_UDP &&
377*daea7039SKajetan Staszkiewicz 				    (rpool->opts & PF_POOL_ENDPI &&
378fcdb520cSKristof Provost 				    udp_mapping != NULL)) {
379390dc369STom Jones 					(*udp_mapping)->endpoints[1].port = htons(tmp);
380390dc369STom Jones 					if (pf_udp_mapping_insert(*udp_mapping) == 0) {
381390dc369STom Jones 						*nport = htons(tmp);
382390dc369STom Jones 						return (0);
383390dc369STom Jones 					}
384390dc369STom Jones 				} else {
3858fc6e19cSGleb Smirnoff 					key.port[1] = htons(tmp);
38619d6e29bSMateusz Guzik 					if (!pf_find_state_all_exists(&key, PF_IN)) {
3873b3a8eb9SGleb Smirnoff 						*nport = htons(tmp);
3883b3a8eb9SGleb Smirnoff 						return (0);
3893b3a8eb9SGleb Smirnoff 					}
3903b3a8eb9SGleb Smirnoff 				}
3913b3a8eb9SGleb Smirnoff 			}
392390dc369STom Jones 		}
3933b3a8eb9SGleb Smirnoff 
394*daea7039SKajetan Staszkiewicz 		switch (rpool->opts & PF_POOL_TYPEMASK) {
3953b3a8eb9SGleb Smirnoff 		case PF_POOL_RANDOM:
3963b3a8eb9SGleb Smirnoff 		case PF_POOL_ROUNDROBIN:
3972b0a4ffaSKristof Provost 			/*
3982b0a4ffaSKristof Provost 			 * pick a different source address since we're out
3992b0a4ffaSKristof Provost 			 * of free port choices for the current one.
4002b0a4ffaSKristof Provost 			 */
401b9c0321dSKajetan Staszkiewicz 			(*sn) = NULL;
402*daea7039SKajetan Staszkiewicz 			if (pf_map_addr_sn(pd->naf, r, &pd->nsaddr, naddr, NULL,
403*daea7039SKajetan Staszkiewicz 			    &init_addr, sn, sh, rpool))
4043b3a8eb9SGleb Smirnoff 				return (1);
4053b3a8eb9SGleb Smirnoff 			break;
4063b3a8eb9SGleb Smirnoff 		case PF_POOL_NONE:
4073b3a8eb9SGleb Smirnoff 		case PF_POOL_SRCHASH:
4083b3a8eb9SGleb Smirnoff 		case PF_POOL_BITMASK:
4093b3a8eb9SGleb Smirnoff 		default:
4103b3a8eb9SGleb Smirnoff 			return (1);
4113b3a8eb9SGleb Smirnoff 		}
412fcdb520cSKristof Provost 	} while (! PF_AEQ(&init_addr, naddr, pd->naf) );
413390dc369STom Jones 
414390dc369STom Jones failed:
415fcdb520cSKristof Provost 	if (udp_mapping) {
416390dc369STom Jones 		uma_zfree(V_pf_udp_mapping_z, *udp_mapping);
417390dc369STom Jones 		*udp_mapping = NULL;
418fcdb520cSKristof Provost 	}
419fcdb520cSKristof Provost 
4203b3a8eb9SGleb Smirnoff 	return (1);					/* none available */
4213b3a8eb9SGleb Smirnoff }
4223b3a8eb9SGleb Smirnoff 
4237d381d0aSKristof Provost static bool
4247d381d0aSKristof Provost pf_islinklocal(const sa_family_t af, const struct pf_addr *addr)
4257d381d0aSKristof Provost {
4267d381d0aSKristof Provost 	if (af == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&addr->v6))
4277d381d0aSKristof Provost 		return (true);
4287d381d0aSKristof Provost 	return (false);
4297d381d0aSKristof Provost }
4307d381d0aSKristof Provost 
4312aa21096SKurosawa Takahiro static int
4322d7e68d5SKristof Provost pf_get_mape_sport(struct pf_pdesc *pd, struct pf_krule *r,
4332d7e68d5SKristof Provost     struct pf_addr *naddr, uint16_t *nport,
434b9c0321dSKajetan Staszkiewicz     struct pf_ksrc_node **sn, struct pf_srchash **sh,
435b9c0321dSKajetan Staszkiewicz     struct pf_udp_mapping **udp_mapping)
4362aa21096SKurosawa Takahiro {
4372aa21096SKurosawa Takahiro 	uint16_t psmask, low, highmask;
4382aa21096SKurosawa Takahiro 	uint16_t i, ahigh, cut;
4392aa21096SKurosawa Takahiro 	int ashift, psidshift;
4402aa21096SKurosawa Takahiro 
441e11dacbfSKristof Provost 	ashift = 16 - r->rdr.mape.offset;
442e11dacbfSKristof Provost 	psidshift = ashift - r->rdr.mape.psidlen;
443e11dacbfSKristof Provost 	psmask = r->rdr.mape.psid & ((1U << r->rdr.mape.psidlen) - 1);
4442aa21096SKurosawa Takahiro 	psmask = psmask << psidshift;
4452aa21096SKurosawa Takahiro 	highmask = (1U << psidshift) - 1;
4462aa21096SKurosawa Takahiro 
447e11dacbfSKristof Provost 	ahigh = (1U << r->rdr.mape.offset) - 1;
4482aa21096SKurosawa Takahiro 	cut = arc4random() & ahigh;
4492aa21096SKurosawa Takahiro 	if (cut == 0)
4502aa21096SKurosawa Takahiro 		cut = 1;
4512aa21096SKurosawa Takahiro 
4522aa21096SKurosawa Takahiro 	for (i = cut; i <= ahigh; i++) {
4532aa21096SKurosawa Takahiro 		low = (i << ashift) | psmask;
4542d7e68d5SKristof Provost 		if (!pf_get_sport(pd, r,
455fcdb520cSKristof Provost 		    naddr, nport, low, low | highmask, sn, sh, &r->rdr,
456fcdb520cSKristof Provost 		    udp_mapping))
4572aa21096SKurosawa Takahiro 			return (0);
4582aa21096SKurosawa Takahiro 	}
4592aa21096SKurosawa Takahiro 	for (i = cut - 1; i > 0; i--) {
4602aa21096SKurosawa Takahiro 		low = (i << ashift) | psmask;
4612d7e68d5SKristof Provost 		if (!pf_get_sport(pd, r,
462fcdb520cSKristof Provost 		    naddr, nport, low, low | highmask, sn, sh, &r->rdr,
463fcdb520cSKristof Provost 		    udp_mapping))
4642aa21096SKurosawa Takahiro 			return (0);
4652aa21096SKurosawa Takahiro 	}
4662aa21096SKurosawa Takahiro 	return (1);
4672aa21096SKurosawa Takahiro }
4682aa21096SKurosawa Takahiro 
46916303d2bSKajetan Staszkiewicz u_short
470e86bddeaSKristof Provost pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
471fcdb520cSKristof Provost     struct pf_addr *naddr, struct pfi_kkif **nkif, struct pf_addr *init_addr,
472fcdb520cSKristof Provost     struct pf_kpool *rpool)
4733b3a8eb9SGleb Smirnoff {
4749569fdddSMark Johnston 	u_short			 reason = PFRES_MATCH;
4753b3a8eb9SGleb Smirnoff 	struct pf_addr		*raddr = NULL, *rmask = NULL;
4763b3a8eb9SGleb Smirnoff 
4775f5e32f1SKristof Provost 	mtx_lock(&rpool->mtx);
478e85343b1SGleb Smirnoff 	/* Find the route using chosen algorithm. Store the found route
479e85343b1SGleb Smirnoff 	   in src_node if it was given or found. */
4805f5e32f1SKristof Provost 	if (rpool->cur->addr.type == PF_ADDR_NOROUTE) {
48116303d2bSKajetan Staszkiewicz 		reason = PFRES_MAPFAILED;
48216303d2bSKajetan Staszkiewicz 		goto done_pool_mtx;
4835f5e32f1SKristof Provost 	}
4843b3a8eb9SGleb Smirnoff 	if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) {
4853b3a8eb9SGleb Smirnoff 		switch (af) {
4863b3a8eb9SGleb Smirnoff #ifdef INET
4873b3a8eb9SGleb Smirnoff 		case AF_INET:
4883b3a8eb9SGleb Smirnoff 			if (rpool->cur->addr.p.dyn->pfid_acnt4 < 1 &&
4893b3a8eb9SGleb Smirnoff 			    (rpool->opts & PF_POOL_TYPEMASK) !=
4905f5e32f1SKristof Provost 			    PF_POOL_ROUNDROBIN) {
49116303d2bSKajetan Staszkiewicz 				reason = PFRES_MAPFAILED;
49216303d2bSKajetan Staszkiewicz 				goto done_pool_mtx;
4935f5e32f1SKristof Provost 			}
4943b3a8eb9SGleb Smirnoff 			raddr = &rpool->cur->addr.p.dyn->pfid_addr4;
4953b3a8eb9SGleb Smirnoff 			rmask = &rpool->cur->addr.p.dyn->pfid_mask4;
4963b3a8eb9SGleb Smirnoff 			break;
4973b3a8eb9SGleb Smirnoff #endif /* INET */
4983b3a8eb9SGleb Smirnoff #ifdef INET6
4993b3a8eb9SGleb Smirnoff 		case AF_INET6:
5003b3a8eb9SGleb Smirnoff 			if (rpool->cur->addr.p.dyn->pfid_acnt6 < 1 &&
5013b3a8eb9SGleb Smirnoff 			    (rpool->opts & PF_POOL_TYPEMASK) !=
5025f5e32f1SKristof Provost 			    PF_POOL_ROUNDROBIN) {
50316303d2bSKajetan Staszkiewicz 				reason = PFRES_MAPFAILED;
50416303d2bSKajetan Staszkiewicz 				goto done_pool_mtx;
5055f5e32f1SKristof Provost 			}
5063b3a8eb9SGleb Smirnoff 			raddr = &rpool->cur->addr.p.dyn->pfid_addr6;
5073b3a8eb9SGleb Smirnoff 			rmask = &rpool->cur->addr.p.dyn->pfid_mask6;
5083b3a8eb9SGleb Smirnoff 			break;
5093b3a8eb9SGleb Smirnoff #endif /* INET6 */
5103b3a8eb9SGleb Smirnoff 		}
5113b3a8eb9SGleb Smirnoff 	} else if (rpool->cur->addr.type == PF_ADDR_TABLE) {
5125f5e32f1SKristof Provost 		if ((rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_ROUNDROBIN) {
51316303d2bSKajetan Staszkiewicz 			reason = PFRES_MAPFAILED;
51416303d2bSKajetan Staszkiewicz 			goto done_pool_mtx; /* unsupported */
5155f5e32f1SKristof Provost 		}
5163b3a8eb9SGleb Smirnoff 	} else {
5173b3a8eb9SGleb Smirnoff 		raddr = &rpool->cur->addr.v.a.addr;
5183b3a8eb9SGleb Smirnoff 		rmask = &rpool->cur->addr.v.a.mask;
5193b3a8eb9SGleb Smirnoff 	}
5203b3a8eb9SGleb Smirnoff 
5213b3a8eb9SGleb Smirnoff 	switch (rpool->opts & PF_POOL_TYPEMASK) {
5223b3a8eb9SGleb Smirnoff 	case PF_POOL_NONE:
5233b3a8eb9SGleb Smirnoff 		PF_ACPY(naddr, raddr, af);
5243b3a8eb9SGleb Smirnoff 		break;
5253b3a8eb9SGleb Smirnoff 	case PF_POOL_BITMASK:
5263b3a8eb9SGleb Smirnoff 		PF_POOLMASK(naddr, raddr, rmask, saddr, af);
5273b3a8eb9SGleb Smirnoff 		break;
5283b3a8eb9SGleb Smirnoff 	case PF_POOL_RANDOM:
5293b3a8eb9SGleb Smirnoff 		if (init_addr != NULL && PF_AZERO(init_addr, af)) {
5303b3a8eb9SGleb Smirnoff 			switch (af) {
5313b3a8eb9SGleb Smirnoff #ifdef INET
5323b3a8eb9SGleb Smirnoff 			case AF_INET:
5333b3a8eb9SGleb Smirnoff 				rpool->counter.addr32[0] = htonl(arc4random());
5343b3a8eb9SGleb Smirnoff 				break;
5353b3a8eb9SGleb Smirnoff #endif /* INET */
5363b3a8eb9SGleb Smirnoff #ifdef INET6
5373b3a8eb9SGleb Smirnoff 			case AF_INET6:
5383b3a8eb9SGleb Smirnoff 				if (rmask->addr32[3] != 0xffffffff)
5393b3a8eb9SGleb Smirnoff 					rpool->counter.addr32[3] =
5403b3a8eb9SGleb Smirnoff 					    htonl(arc4random());
5413b3a8eb9SGleb Smirnoff 				else
5423b3a8eb9SGleb Smirnoff 					break;
5433b3a8eb9SGleb Smirnoff 				if (rmask->addr32[2] != 0xffffffff)
5443b3a8eb9SGleb Smirnoff 					rpool->counter.addr32[2] =
5453b3a8eb9SGleb Smirnoff 					    htonl(arc4random());
5463b3a8eb9SGleb Smirnoff 				else
5473b3a8eb9SGleb Smirnoff 					break;
5483b3a8eb9SGleb Smirnoff 				if (rmask->addr32[1] != 0xffffffff)
5493b3a8eb9SGleb Smirnoff 					rpool->counter.addr32[1] =
5503b3a8eb9SGleb Smirnoff 					    htonl(arc4random());
5513b3a8eb9SGleb Smirnoff 				else
5523b3a8eb9SGleb Smirnoff 					break;
5533b3a8eb9SGleb Smirnoff 				if (rmask->addr32[0] != 0xffffffff)
5543b3a8eb9SGleb Smirnoff 					rpool->counter.addr32[0] =
5553b3a8eb9SGleb Smirnoff 					    htonl(arc4random());
5563b3a8eb9SGleb Smirnoff 				break;
5573b3a8eb9SGleb Smirnoff #endif /* INET6 */
5583b3a8eb9SGleb Smirnoff 			}
5593b3a8eb9SGleb Smirnoff 			PF_POOLMASK(naddr, raddr, rmask, &rpool->counter, af);
5603b3a8eb9SGleb Smirnoff 			PF_ACPY(init_addr, naddr, af);
5613b3a8eb9SGleb Smirnoff 
5623b3a8eb9SGleb Smirnoff 		} else {
5633b3a8eb9SGleb Smirnoff 			PF_AINC(&rpool->counter, af);
5643b3a8eb9SGleb Smirnoff 			PF_POOLMASK(naddr, raddr, rmask, &rpool->counter, af);
5653b3a8eb9SGleb Smirnoff 		}
5663b3a8eb9SGleb Smirnoff 		break;
5673b3a8eb9SGleb Smirnoff 	case PF_POOL_SRCHASH:
5683b3a8eb9SGleb Smirnoff 	    {
5693b3a8eb9SGleb Smirnoff 		unsigned char hash[16];
5703b3a8eb9SGleb Smirnoff 
5713b3a8eb9SGleb Smirnoff 		pf_hash(saddr, (struct pf_addr *)&hash, &rpool->key, af);
5723b3a8eb9SGleb Smirnoff 		PF_POOLMASK(naddr, raddr, rmask, (struct pf_addr *)&hash, af);
5733b3a8eb9SGleb Smirnoff 		break;
5743b3a8eb9SGleb Smirnoff 	    }
5753b3a8eb9SGleb Smirnoff 	case PF_POOL_ROUNDROBIN:
5763b3a8eb9SGleb Smirnoff 	    {
577320c1116SKristof Provost 		struct pf_kpooladdr *acur = rpool->cur;
5783b3a8eb9SGleb Smirnoff 
5793b3a8eb9SGleb Smirnoff 		if (rpool->cur->addr.type == PF_ADDR_TABLE) {
5803b3a8eb9SGleb Smirnoff 			if (!pfr_pool_get(rpool->cur->addr.p.tbl,
5817d381d0aSKristof Provost 			    &rpool->tblidx, &rpool->counter, af, NULL))
5823b3a8eb9SGleb Smirnoff 				goto get_addr;
5833b3a8eb9SGleb Smirnoff 		} else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) {
5843b3a8eb9SGleb Smirnoff 			if (!pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt,
5857d381d0aSKristof Provost 			    &rpool->tblidx, &rpool->counter, af, pf_islinklocal))
5863b3a8eb9SGleb Smirnoff 				goto get_addr;
5873b3a8eb9SGleb Smirnoff 		} else if (pf_match_addr(0, raddr, rmask, &rpool->counter, af))
5883b3a8eb9SGleb Smirnoff 			goto get_addr;
5893b3a8eb9SGleb Smirnoff 
5903b3a8eb9SGleb Smirnoff 	try_next:
5913b3a8eb9SGleb Smirnoff 		if (TAILQ_NEXT(rpool->cur, entries) == NULL)
5923b3a8eb9SGleb Smirnoff 			rpool->cur = TAILQ_FIRST(&rpool->list);
5933b3a8eb9SGleb Smirnoff 		else
5943b3a8eb9SGleb Smirnoff 			rpool->cur = TAILQ_NEXT(rpool->cur, entries);
5953b3a8eb9SGleb Smirnoff 		if (rpool->cur->addr.type == PF_ADDR_TABLE) {
5963b3a8eb9SGleb Smirnoff 			if (pfr_pool_get(rpool->cur->addr.p.tbl,
5977d381d0aSKristof Provost 			    &rpool->tblidx, &rpool->counter, af, NULL)) {
5983b3a8eb9SGleb Smirnoff 				/* table contains no address of type 'af' */
5993b3a8eb9SGleb Smirnoff 				if (rpool->cur != acur)
6003b3a8eb9SGleb Smirnoff 					goto try_next;
60116303d2bSKajetan Staszkiewicz 				reason = PFRES_MAPFAILED;
60216303d2bSKajetan Staszkiewicz 				goto done_pool_mtx;
6033b3a8eb9SGleb Smirnoff 			}
6043b3a8eb9SGleb Smirnoff 		} else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) {
6053b3a8eb9SGleb Smirnoff 			rpool->tblidx = -1;
6063b3a8eb9SGleb Smirnoff 			if (pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt,
6077d381d0aSKristof Provost 			    &rpool->tblidx, &rpool->counter, af, pf_islinklocal)) {
6083b3a8eb9SGleb Smirnoff 				/* table contains no address of type 'af' */
6093b3a8eb9SGleb Smirnoff 				if (rpool->cur != acur)
6103b3a8eb9SGleb Smirnoff 					goto try_next;
61116303d2bSKajetan Staszkiewicz 				reason = PFRES_MAPFAILED;
61216303d2bSKajetan Staszkiewicz 				goto done_pool_mtx;
6133b3a8eb9SGleb Smirnoff 			}
6143b3a8eb9SGleb Smirnoff 		} else {
6153b3a8eb9SGleb Smirnoff 			raddr = &rpool->cur->addr.v.a.addr;
6163b3a8eb9SGleb Smirnoff 			rmask = &rpool->cur->addr.v.a.mask;
6173b3a8eb9SGleb Smirnoff 			PF_ACPY(&rpool->counter, raddr, af);
6183b3a8eb9SGleb Smirnoff 		}
6193b3a8eb9SGleb Smirnoff 
6203b3a8eb9SGleb Smirnoff 	get_addr:
6213b3a8eb9SGleb Smirnoff 		PF_ACPY(naddr, &rpool->counter, af);
6223b3a8eb9SGleb Smirnoff 		if (init_addr != NULL && PF_AZERO(init_addr, af))
6233b3a8eb9SGleb Smirnoff 			PF_ACPY(init_addr, naddr, af);
6243b3a8eb9SGleb Smirnoff 		PF_AINC(&rpool->counter, af);
6253b3a8eb9SGleb Smirnoff 		break;
6263b3a8eb9SGleb Smirnoff 	    }
6273b3a8eb9SGleb Smirnoff 	}
628d10de21fSKajetan Staszkiewicz 
629d10de21fSKajetan Staszkiewicz 	if (nkif)
630d10de21fSKajetan Staszkiewicz 		*nkif = rpool->cur->kif;
631d10de21fSKajetan Staszkiewicz 
6328e3d2529SKajetan Staszkiewicz done_pool_mtx:
6338e3d2529SKajetan Staszkiewicz 	mtx_unlock(&rpool->mtx);
6348e3d2529SKajetan Staszkiewicz 
6358e3d2529SKajetan Staszkiewicz 	if (reason) {
6368e3d2529SKajetan Staszkiewicz 		counter_u64_add(V_pf_status.counters[reason], 1);
6378e3d2529SKajetan Staszkiewicz 	}
6388e3d2529SKajetan Staszkiewicz 
6398e3d2529SKajetan Staszkiewicz 	return (reason);
6408e3d2529SKajetan Staszkiewicz }
6418e3d2529SKajetan Staszkiewicz 
6428e3d2529SKajetan Staszkiewicz u_short
6438e3d2529SKajetan Staszkiewicz pf_map_addr_sn(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
6448e3d2529SKajetan Staszkiewicz     struct pf_addr *naddr, struct pfi_kkif **nkif, struct pf_addr *init_addr,
645fcdb520cSKristof Provost     struct pf_ksrc_node **sn, struct pf_srchash **sh, struct pf_kpool *rpool)
6468e3d2529SKajetan Staszkiewicz {
6478e3d2529SKajetan Staszkiewicz 	u_short			 reason = 0;
6488e3d2529SKajetan Staszkiewicz 
649c49c9da2SKajetan Staszkiewicz 	KASSERT(*sn == NULL, ("*sn not NULL"));
650c49c9da2SKajetan Staszkiewicz 
651b9c0321dSKajetan Staszkiewicz 	/*
652c49c9da2SKajetan Staszkiewicz 	 * If this is a sticky-address rule, try to find an existing src_node.
653c49c9da2SKajetan Staszkiewicz 	 * Request the sh to be unlocked if sn was not found, as we never
654c49c9da2SKajetan Staszkiewicz 	 * insert a new sn when parsing the ruleset.
655b9c0321dSKajetan Staszkiewicz 	 */
656*daea7039SKajetan Staszkiewicz 	if (rpool->opts & PF_POOL_STICKYADDR &&
657*daea7039SKajetan Staszkiewicz 	    (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE)
658b9c0321dSKajetan Staszkiewicz 		*sn = pf_find_src_node(saddr, r, af, sh, false);
6598e3d2529SKajetan Staszkiewicz 
660c49c9da2SKajetan Staszkiewicz 	if (*sn != NULL) {
661b9c0321dSKajetan Staszkiewicz 		PF_SRC_NODE_LOCK_ASSERT(*sn);
662b9c0321dSKajetan Staszkiewicz 
6638e3d2529SKajetan Staszkiewicz 		/* If the supplied address is the same as the current one we've
6648e3d2529SKajetan Staszkiewicz 		 * been asked before, so tell the caller that there's no other
6658e3d2529SKajetan Staszkiewicz 		 * address to be had. */
6668e3d2529SKajetan Staszkiewicz 		if (PF_AEQ(naddr, &(*sn)->raddr, af)) {
6678e3d2529SKajetan Staszkiewicz 			reason = PFRES_MAPFAILED;
6688e3d2529SKajetan Staszkiewicz 			goto done;
6698e3d2529SKajetan Staszkiewicz 		}
6708e3d2529SKajetan Staszkiewicz 
6718e3d2529SKajetan Staszkiewicz 		PF_ACPY(naddr, &(*sn)->raddr, af);
6728e3d2529SKajetan Staszkiewicz 		if (nkif)
6738e3d2529SKajetan Staszkiewicz 			*nkif = (*sn)->rkif;
6748e3d2529SKajetan Staszkiewicz 		if (V_pf_status.debug >= PF_DEBUG_NOISY) {
6758e3d2529SKajetan Staszkiewicz 			printf("pf_map_addr: src tracking maps ");
6768e3d2529SKajetan Staszkiewicz 			pf_print_host(saddr, 0, af);
6778e3d2529SKajetan Staszkiewicz 			printf(" to ");
6788e3d2529SKajetan Staszkiewicz 			pf_print_host(naddr, 0, af);
6798e3d2529SKajetan Staszkiewicz 			if (nkif)
6808e3d2529SKajetan Staszkiewicz 				printf("@%s", (*nkif)->pfik_name);
6818e3d2529SKajetan Staszkiewicz 			printf("\n");
6828e3d2529SKajetan Staszkiewicz 		}
6838e3d2529SKajetan Staszkiewicz 		goto done;
6848e3d2529SKajetan Staszkiewicz 	}
6858e3d2529SKajetan Staszkiewicz 
6868e3d2529SKajetan Staszkiewicz 	/*
6878e3d2529SKajetan Staszkiewicz 	 * Source node has not been found. Find a new address and store it
6888e3d2529SKajetan Staszkiewicz 	 * in variables given by the caller.
6898e3d2529SKajetan Staszkiewicz 	 */
690fcdb520cSKristof Provost 	if (pf_map_addr(af, r, saddr, naddr, nkif, init_addr, rpool) != 0) {
6918e3d2529SKajetan Staszkiewicz 		/* pf_map_addr() sets reason counters on its own */
6928e3d2529SKajetan Staszkiewicz 		goto done;
6938e3d2529SKajetan Staszkiewicz 	}
6948e3d2529SKajetan Staszkiewicz 
695498cca14SKristof Provost 	if (V_pf_status.debug >= PF_DEBUG_NOISY &&
6963b3a8eb9SGleb Smirnoff 	    (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) {
6973b3a8eb9SGleb Smirnoff 		printf("pf_map_addr: selected address ");
6983b3a8eb9SGleb Smirnoff 		pf_print_host(naddr, 0, af);
699d10de21fSKajetan Staszkiewicz 		if (nkif)
700d10de21fSKajetan Staszkiewicz 			printf("@%s", (*nkif)->pfik_name);
7013b3a8eb9SGleb Smirnoff 		printf("\n");
7023b3a8eb9SGleb Smirnoff 	}
7033b3a8eb9SGleb Smirnoff 
70416303d2bSKajetan Staszkiewicz done:
705b9c0321dSKajetan Staszkiewicz 	if ((*sn) != NULL)
706b9c0321dSKajetan Staszkiewicz 		PF_SRC_NODE_UNLOCK(*sn);
707b9c0321dSKajetan Staszkiewicz 
70816303d2bSKajetan Staszkiewicz 	if (reason) {
70916303d2bSKajetan Staszkiewicz 		counter_u64_add(V_pf_status.counters[reason], 1);
71016303d2bSKajetan Staszkiewicz 	}
71116303d2bSKajetan Staszkiewicz 
71216303d2bSKajetan Staszkiewicz 	return (reason);
7133b3a8eb9SGleb Smirnoff }
7143b3a8eb9SGleb Smirnoff 
7157e65cfc9SMark Johnston u_short
7169a405864SKristof Provost pf_get_translation(struct pf_pdesc *pd, int off,
7172d7e68d5SKristof Provost     struct pf_state_key **skp, struct pf_state_key **nkp,
718b9c0321dSKajetan Staszkiewicz     struct pf_kanchor_stackframe *anchor_stack, struct pf_krule **rp,
719390dc369STom Jones     struct pf_udp_mapping **udp_mapping)
7203b3a8eb9SGleb Smirnoff {
721e86bddeaSKristof Provost 	struct pf_krule	*r = NULL;
7223b3a8eb9SGleb Smirnoff 	struct pf_addr	*naddr;
723b9c0321dSKajetan Staszkiewicz 	struct pf_ksrc_node	*sn = NULL;
724b9c0321dSKajetan Staszkiewicz 	struct pf_srchash	*sh = NULL;
7259897a669SMark Johnston 	uint16_t	*nportp;
7262aa21096SKurosawa Takahiro 	uint16_t	 low, high;
7277e65cfc9SMark Johnston 	u_short		 reason;
7283b3a8eb9SGleb Smirnoff 
7293b3a8eb9SGleb Smirnoff 	PF_RULES_RASSERT();
7303b3a8eb9SGleb Smirnoff 	KASSERT(*skp == NULL, ("*skp not NULL"));
7313b3a8eb9SGleb Smirnoff 	KASSERT(*nkp == NULL, ("*nkp not NULL"));
7323b3a8eb9SGleb Smirnoff 
7337e65cfc9SMark Johnston 	*rp = NULL;
7347e65cfc9SMark Johnston 
735f2064dd1SKajetan Staszkiewicz 	if (pd->dir == PF_OUT) {
7362d7e68d5SKristof Provost 		r = pf_match_translation(pd, PF_RULESET_BINAT, anchor_stack);
7373b3a8eb9SGleb Smirnoff 		if (r == NULL)
7382d7e68d5SKristof Provost 			r = pf_match_translation(pd, PF_RULESET_NAT, anchor_stack);
7393b3a8eb9SGleb Smirnoff 	} else {
7402d7e68d5SKristof Provost 		r = pf_match_translation(pd, PF_RULESET_RDR, anchor_stack);
7413b3a8eb9SGleb Smirnoff 		if (r == NULL)
7422d7e68d5SKristof Provost 			r = pf_match_translation(pd, PF_RULESET_BINAT, anchor_stack);
7433b3a8eb9SGleb Smirnoff 	}
7443b3a8eb9SGleb Smirnoff 
7453b3a8eb9SGleb Smirnoff 	if (r == NULL)
7467e65cfc9SMark Johnston 		return (PFRES_MAX);
7473b3a8eb9SGleb Smirnoff 
7483b3a8eb9SGleb Smirnoff 	switch (r->action) {
7493b3a8eb9SGleb Smirnoff 	case PF_NONAT:
7503b3a8eb9SGleb Smirnoff 	case PF_NOBINAT:
7513b3a8eb9SGleb Smirnoff 	case PF_NORDR:
7527e65cfc9SMark Johnston 		return (PFRES_MAX);
7533b3a8eb9SGleb Smirnoff 	}
7543b3a8eb9SGleb Smirnoff 
755fcdb520cSKristof Provost 	if (pf_state_key_setup(pd, pd->nsport, pd->ndport, skp, nkp))
7567e65cfc9SMark Johnston 		return (PFRES_MEMORY);
7573b3a8eb9SGleb Smirnoff 
7583b3a8eb9SGleb Smirnoff 	naddr = &(*nkp)->addr[1];
7599897a669SMark Johnston 	nportp = &(*nkp)->port[1];
7603b3a8eb9SGleb Smirnoff 
7613b3a8eb9SGleb Smirnoff 	switch (r->action) {
7623b3a8eb9SGleb Smirnoff 	case PF_NAT:
7632aa21096SKurosawa Takahiro 		if (pd->proto == IPPROTO_ICMP) {
7642aa21096SKurosawa Takahiro 			low = 1;
7652aa21096SKurosawa Takahiro 			high = 65535;
7662aa21096SKurosawa Takahiro 		} else {
767e11dacbfSKristof Provost 			low  = r->rdr.proxy_port[0];
768e11dacbfSKristof Provost 			high = r->rdr.proxy_port[1];
7692aa21096SKurosawa Takahiro 		}
770e11dacbfSKristof Provost 		if (r->rdr.mape.offset > 0) {
7712d7e68d5SKristof Provost 			if (pf_get_mape_sport(pd, r, naddr, nportp, &sn,
7722d7e68d5SKristof Provost 			    &sh, udp_mapping)) {
7732aa21096SKurosawa Takahiro 				DPFPRINTF(PF_DEBUG_MISC,
7742aa21096SKurosawa Takahiro 				    ("pf: MAP-E port allocation (%u/%u/%u)"
7752aa21096SKurosawa Takahiro 				    " failed\n",
776e11dacbfSKristof Provost 				    r->rdr.mape.offset,
777e11dacbfSKristof Provost 				    r->rdr.mape.psidlen,
778e11dacbfSKristof Provost 				    r->rdr.mape.psid));
7797e65cfc9SMark Johnston 				reason = PFRES_MAPFAILED;
7802aa21096SKurosawa Takahiro 				goto notrans;
7812aa21096SKurosawa Takahiro 			}
7822d7e68d5SKristof Provost 		} else if (pf_get_sport(pd, r, naddr, nportp, low, high, &sn,
783fcdb520cSKristof Provost 		    &sh, &r->rdr, udp_mapping)) {
7843b3a8eb9SGleb Smirnoff 			DPFPRINTF(PF_DEBUG_MISC,
7853b3a8eb9SGleb Smirnoff 			    ("pf: NAT proxy port allocation (%u-%u) failed\n",
786e11dacbfSKristof Provost 			    r->rdr.proxy_port[0], r->rdr.proxy_port[1]));
7877e65cfc9SMark Johnston 			reason = PFRES_MAPFAILED;
7883b3a8eb9SGleb Smirnoff 			goto notrans;
7893b3a8eb9SGleb Smirnoff 		}
7903b3a8eb9SGleb Smirnoff 		break;
7913b3a8eb9SGleb Smirnoff 	case PF_BINAT:
792f2064dd1SKajetan Staszkiewicz 		switch (pd->dir) {
7933b3a8eb9SGleb Smirnoff 		case PF_OUT:
794e11dacbfSKristof Provost 			if (r->rdr.cur->addr.type == PF_ADDR_DYNIFTL){
7953b3a8eb9SGleb Smirnoff 				switch (pd->af) {
7963b3a8eb9SGleb Smirnoff #ifdef INET
7973b3a8eb9SGleb Smirnoff 				case AF_INET:
798e11dacbfSKristof Provost 					if (r->rdr.cur->addr.p.dyn->
7997e65cfc9SMark Johnston 					    pfid_acnt4 < 1) {
8007e65cfc9SMark Johnston 						reason = PFRES_MAPFAILED;
8013b3a8eb9SGleb Smirnoff 						goto notrans;
8027e65cfc9SMark Johnston 					}
8033b3a8eb9SGleb Smirnoff 					PF_POOLMASK(naddr,
804e11dacbfSKristof Provost 					    &r->rdr.cur->addr.p.dyn->
8053b3a8eb9SGleb Smirnoff 					    pfid_addr4,
806e11dacbfSKristof Provost 					    &r->rdr.cur->addr.p.dyn->
8072d7e68d5SKristof Provost 					    pfid_mask4, &pd->nsaddr, AF_INET);
8083b3a8eb9SGleb Smirnoff 					break;
8093b3a8eb9SGleb Smirnoff #endif /* INET */
8103b3a8eb9SGleb Smirnoff #ifdef INET6
8113b3a8eb9SGleb Smirnoff 				case AF_INET6:
812e11dacbfSKristof Provost 					if (r->rdr.cur->addr.p.dyn->
8137e65cfc9SMark Johnston 					    pfid_acnt6 < 1) {
8147e65cfc9SMark Johnston 						reason = PFRES_MAPFAILED;
8153b3a8eb9SGleb Smirnoff 						goto notrans;
8167e65cfc9SMark Johnston 					}
8173b3a8eb9SGleb Smirnoff 					PF_POOLMASK(naddr,
818e11dacbfSKristof Provost 					    &r->rdr.cur->addr.p.dyn->
8193b3a8eb9SGleb Smirnoff 					    pfid_addr6,
820e11dacbfSKristof Provost 					    &r->rdr.cur->addr.p.dyn->
8212d7e68d5SKristof Provost 					    pfid_mask6, &pd->nsaddr, AF_INET6);
8223b3a8eb9SGleb Smirnoff 					break;
8233b3a8eb9SGleb Smirnoff #endif /* INET6 */
8243b3a8eb9SGleb Smirnoff 				}
8253b3a8eb9SGleb Smirnoff 			} else
8263b3a8eb9SGleb Smirnoff 				PF_POOLMASK(naddr,
827e11dacbfSKristof Provost 				    &r->rdr.cur->addr.v.a.addr,
8282d7e68d5SKristof Provost 				    &r->rdr.cur->addr.v.a.mask, &pd->nsaddr,
8293b3a8eb9SGleb Smirnoff 				    pd->af);
8303b3a8eb9SGleb Smirnoff 			break;
8313b3a8eb9SGleb Smirnoff 		case PF_IN:
8323b3a8eb9SGleb Smirnoff 			if (r->src.addr.type == PF_ADDR_DYNIFTL) {
8333b3a8eb9SGleb Smirnoff 				switch (pd->af) {
8343b3a8eb9SGleb Smirnoff #ifdef INET
8353b3a8eb9SGleb Smirnoff 				case AF_INET:
8367e65cfc9SMark Johnston 					if (r->src.addr.p.dyn->pfid_acnt4 < 1) {
8377e65cfc9SMark Johnston 						reason = PFRES_MAPFAILED;
8383b3a8eb9SGleb Smirnoff 						goto notrans;
8397e65cfc9SMark Johnston 					}
8403b3a8eb9SGleb Smirnoff 					PF_POOLMASK(naddr,
8413b3a8eb9SGleb Smirnoff 					    &r->src.addr.p.dyn->pfid_addr4,
8423b3a8eb9SGleb Smirnoff 					    &r->src.addr.p.dyn->pfid_mask4,
8432d7e68d5SKristof Provost 					    &pd->ndaddr, AF_INET);
8443b3a8eb9SGleb Smirnoff 					break;
8453b3a8eb9SGleb Smirnoff #endif /* INET */
8463b3a8eb9SGleb Smirnoff #ifdef INET6
8473b3a8eb9SGleb Smirnoff 				case AF_INET6:
8487e65cfc9SMark Johnston 					if (r->src.addr.p.dyn->pfid_acnt6 < 1) {
8497e65cfc9SMark Johnston 						reason = PFRES_MAPFAILED;
8503b3a8eb9SGleb Smirnoff 						goto notrans;
8517e65cfc9SMark Johnston 					}
8523b3a8eb9SGleb Smirnoff 					PF_POOLMASK(naddr,
8533b3a8eb9SGleb Smirnoff 					    &r->src.addr.p.dyn->pfid_addr6,
8543b3a8eb9SGleb Smirnoff 					    &r->src.addr.p.dyn->pfid_mask6,
8552d7e68d5SKristof Provost 					    &pd->ndaddr, AF_INET6);
8563b3a8eb9SGleb Smirnoff 					break;
8573b3a8eb9SGleb Smirnoff #endif /* INET6 */
8583b3a8eb9SGleb Smirnoff 				}
8593b3a8eb9SGleb Smirnoff 			} else
8603b3a8eb9SGleb Smirnoff 				PF_POOLMASK(naddr, &r->src.addr.v.a.addr,
8612d7e68d5SKristof Provost 				    &r->src.addr.v.a.mask, &pd->ndaddr, pd->af);
8623b3a8eb9SGleb Smirnoff 			break;
8633b3a8eb9SGleb Smirnoff 		}
8643b3a8eb9SGleb Smirnoff 		break;
8653b3a8eb9SGleb Smirnoff 	case PF_RDR: {
8669897a669SMark Johnston 		struct pf_state_key_cmp key;
867339a1977SMark Johnston 		int tries;
8689897a669SMark Johnston 		uint16_t cut, low, high, nport;
8699897a669SMark Johnston 
8702d7e68d5SKristof Provost 		reason = pf_map_addr_sn(pd->af, r, &pd->nsaddr, naddr, NULL,
871fcdb520cSKristof Provost 		    NULL, &sn, &sh, &r->rdr);
8727e65cfc9SMark Johnston 		if (reason != 0)
8733b3a8eb9SGleb Smirnoff 			goto notrans;
874e11dacbfSKristof Provost 		if ((r->rdr.opts & PF_POOL_TYPEMASK) == PF_POOL_BITMASK)
875e11dacbfSKristof Provost 			PF_POOLMASK(naddr, naddr, &r->rdr.cur->addr.v.a.mask,
8762d7e68d5SKristof Provost 			    &pd->ndaddr, pd->af);
8773b3a8eb9SGleb Smirnoff 
8786053adafSKristof Provost 		/* Do not change SCTP ports. */
8796053adafSKristof Provost 		if (pd->proto == IPPROTO_SCTP)
8806053adafSKristof Provost 			break;
8816053adafSKristof Provost 
882e11dacbfSKristof Provost 		if (r->rdr.proxy_port[1]) {
8833b3a8eb9SGleb Smirnoff 			uint32_t	tmp_nport;
8843b3a8eb9SGleb Smirnoff 
8852d7e68d5SKristof Provost 			tmp_nport = ((ntohs(pd->ndport) - ntohs(r->dst.port[0])) %
886e11dacbfSKristof Provost 			    (r->rdr.proxy_port[1] - r->rdr.proxy_port[0] +
887e11dacbfSKristof Provost 			    1)) + r->rdr.proxy_port[0];
8883b3a8eb9SGleb Smirnoff 
8893b3a8eb9SGleb Smirnoff 			/* Wrap around if necessary. */
8903b3a8eb9SGleb Smirnoff 			if (tmp_nport > 65535)
8913b3a8eb9SGleb Smirnoff 				tmp_nport -= 65535;
8929897a669SMark Johnston 			nport = htons((uint16_t)tmp_nport);
893e11dacbfSKristof Provost 		} else if (r->rdr.proxy_port[0])
894e11dacbfSKristof Provost 			nport = htons(r->rdr.proxy_port[0]);
8959897a669SMark Johnston 		else
8962d7e68d5SKristof Provost 			nport = pd->ndport;
8979897a669SMark Johnston 
8989897a669SMark Johnston 		/*
8999897a669SMark Johnston 		 * Update the destination port.
9009897a669SMark Johnston 		 */
9019897a669SMark Johnston 		*nportp = nport;
9029897a669SMark Johnston 
9039897a669SMark Johnston 		/*
9049897a669SMark Johnston 		 * Do we have a source port conflict in the stack state?  Try to
9059897a669SMark Johnston 		 * modulate the source port if so.  Note that this is racy since
9069897a669SMark Johnston 		 * the state lookup may not find any matches here but will once
9079897a669SMark Johnston 		 * pf_create_state() actually instantiates the state.
9089897a669SMark Johnston 		 */
9099897a669SMark Johnston 		bzero(&key, sizeof(key));
9109897a669SMark Johnston 		key.af = pd->af;
9119897a669SMark Johnston 		key.proto = pd->proto;
9122d7e68d5SKristof Provost 		key.port[0] = pd->nsport;
9132d7e68d5SKristof Provost 		PF_ACPY(&key.addr[0], &pd->nsaddr, key.af);
9149897a669SMark Johnston 		key.port[1] = nport;
9159897a669SMark Johnston 		PF_ACPY(&key.addr[1], naddr, key.af);
9169897a669SMark Johnston 
9179897a669SMark Johnston 		if (!pf_find_state_all_exists(&key, PF_OUT))
9189897a669SMark Johnston 			break;
9199897a669SMark Johnston 
920339a1977SMark Johnston 		tries = 0;
921339a1977SMark Johnston 
9229897a669SMark Johnston 		low = 50001;	/* XXX-MJ PF_NAT_PROXY_PORT_LOW/HIGH */
9239897a669SMark Johnston 		high = 65535;
9249897a669SMark Johnston 		cut = arc4random() % (1 + high - low) + low;
9259897a669SMark Johnston 		for (uint32_t tmp = cut;
926339a1977SMark Johnston 		    tmp <= high && tmp <= UINT16_MAX &&
927339a1977SMark Johnston 		    tries < V_pf_rdr_srcport_rewrite_tries;
928339a1977SMark Johnston 		    tmp++, tries++) {
9299897a669SMark Johnston 			key.port[0] = htons(tmp);
9309897a669SMark Johnston 			if (!pf_find_state_all_exists(&key, PF_OUT)) {
9319897a669SMark Johnston 				/* Update the source port. */
9329897a669SMark Johnston 				(*nkp)->port[0] = htons(tmp);
9339897a669SMark Johnston 				goto out;
9349897a669SMark Johnston 			}
9359897a669SMark Johnston 		}
936339a1977SMark Johnston 		for (uint32_t tmp = cut - 1;
937339a1977SMark Johnston 		    tmp >= low && tries < V_pf_rdr_srcport_rewrite_tries;
938339a1977SMark Johnston 		    tmp--, tries++) {
9399897a669SMark Johnston 			key.port[0] = htons(tmp);
9409897a669SMark Johnston 			if (!pf_find_state_all_exists(&key, PF_OUT)) {
9419897a669SMark Johnston 				/* Update the source port. */
9429897a669SMark Johnston 				(*nkp)->port[0] = htons(tmp);
9439897a669SMark Johnston 				goto out;
9449897a669SMark Johnston 			}
9459897a669SMark Johnston 		}
9469897a669SMark Johnston 
9479569fdddSMark Johnston 		/*
9489569fdddSMark Johnston 		 * We failed to find a match.  Push on ahead anyway, let
9499569fdddSMark Johnston 		 * pf_state_insert() be the arbiter of whether the state
9509569fdddSMark Johnston 		 * conflict is tolerable.  In particular, with TCP connections
9519569fdddSMark Johnston 		 * the state may be reused if the TCP state is terminal.
9529569fdddSMark Johnston 		 */
9539897a669SMark Johnston 		DPFPRINTF(PF_DEBUG_MISC,
9549897a669SMark Johnston 		    ("pf: RDR source port allocation failed\n"));
9559569fdddSMark Johnston 		break;
9567e65cfc9SMark Johnston 
9579897a669SMark Johnston out:
9589897a669SMark Johnston 		DPFPRINTF(PF_DEBUG_MISC,
9599897a669SMark Johnston 		    ("pf: RDR source port allocation %u->%u\n",
9602d7e68d5SKristof Provost 		    ntohs(pd->nsport), ntohs((*nkp)->port[0])));
9613b3a8eb9SGleb Smirnoff 		break;
9623b3a8eb9SGleb Smirnoff 	}
9633b3a8eb9SGleb Smirnoff 	default:
9643b3a8eb9SGleb Smirnoff 		panic("%s: unknown action %u", __func__, r->action);
9653b3a8eb9SGleb Smirnoff 	}
9663b3a8eb9SGleb Smirnoff 
9673b3a8eb9SGleb Smirnoff 	/* Return success only if translation really happened. */
9687e65cfc9SMark Johnston 	if (bcmp(*skp, *nkp, sizeof(struct pf_state_key_cmp))) {
9697e65cfc9SMark Johnston 		*rp = r;
9707e65cfc9SMark Johnston 		return (PFRES_MATCH);
9717e65cfc9SMark Johnston 	}
9723b3a8eb9SGleb Smirnoff 
9737e65cfc9SMark Johnston 	reason = PFRES_MAX;
9743b3a8eb9SGleb Smirnoff notrans:
9753b3a8eb9SGleb Smirnoff 	uma_zfree(V_pf_state_key_z, *nkp);
9763b3a8eb9SGleb Smirnoff 	uma_zfree(V_pf_state_key_z, *skp);
9773b3a8eb9SGleb Smirnoff 	*skp = *nkp = NULL;
9783b3a8eb9SGleb Smirnoff 
9797e65cfc9SMark Johnston 	return (reason);
9803b3a8eb9SGleb Smirnoff }
981fcdb520cSKristof Provost 
982fcdb520cSKristof Provost int
983fcdb520cSKristof Provost pf_get_transaddr_af(struct pf_krule *r, struct pf_pdesc *pd)
984fcdb520cSKristof Provost {
985fcdb520cSKristof Provost #if defined(INET) && defined(INET6)
986fcdb520cSKristof Provost 	struct pf_addr	 ndaddr, nsaddr, naddr;
987fcdb520cSKristof Provost 	u_int16_t	 nport = 0;
988fcdb520cSKristof Provost 	int		 prefixlen = 96;
989fcdb520cSKristof Provost 	struct pf_srchash	*sh = NULL;
990fcdb520cSKristof Provost 	struct pf_ksrc_node	*sns = NULL;
991fcdb520cSKristof Provost 
9924be8e29eSKristof Provost 	bzero(&nsaddr, sizeof(nsaddr));
9934be8e29eSKristof Provost 	bzero(&ndaddr, sizeof(ndaddr));
9944be8e29eSKristof Provost 
995fcdb520cSKristof Provost 	if (V_pf_status.debug >= PF_DEBUG_MISC) {
996fcdb520cSKristof Provost 		printf("pf: af-to %s %s, ",
997fcdb520cSKristof Provost 		    pd->naf == AF_INET ? "inet" : "inet6",
998fcdb520cSKristof Provost 		    TAILQ_EMPTY(&r->rdr.list) ? "nat" : "rdr");
999fcdb520cSKristof Provost 		pf_print_host(&pd->nsaddr, pd->nsport, pd->af);
1000fcdb520cSKristof Provost 		printf(" -> ");
1001fcdb520cSKristof Provost 		pf_print_host(&pd->ndaddr, pd->ndport, pd->af);
1002fcdb520cSKristof Provost 		printf("\n");
1003fcdb520cSKristof Provost 	}
1004fcdb520cSKristof Provost 
1005fcdb520cSKristof Provost 	if (TAILQ_EMPTY(&r->nat.list))
1006fcdb520cSKristof Provost 		panic("pf_get_transaddr_af: no nat pool for source address");
1007fcdb520cSKristof Provost 
1008fcdb520cSKristof Provost 	/* get source address and port */
1009fcdb520cSKristof Provost 	if (pf_get_sport(pd, r, &nsaddr, &nport,
1010fcdb520cSKristof Provost 	    r->nat.proxy_port[0], r->nat.proxy_port[1], &sns, &sh, &r->nat, NULL)) {
1011fcdb520cSKristof Provost 		DPFPRINTF(PF_DEBUG_MISC,
1012fcdb520cSKristof Provost 		    ("pf: af-to NAT proxy port allocation (%u-%u) failed",
1013fcdb520cSKristof Provost 		    r->nat.proxy_port[0], r->nat.proxy_port[1]));
1014fcdb520cSKristof Provost 		return (-1);
1015fcdb520cSKristof Provost 	}
1016fcdb520cSKristof Provost 
1017fcdb520cSKristof Provost 	if (pd->proto == IPPROTO_ICMPV6 && pd->naf == AF_INET) {
1018fcdb520cSKristof Provost 		if (pd->dir == PF_IN) {
1019fcdb520cSKristof Provost 			NTOHS(pd->ndport);
1020fcdb520cSKristof Provost 			if (pd->ndport == ICMP6_ECHO_REQUEST)
1021fcdb520cSKristof Provost 				pd->ndport = ICMP_ECHO;
1022fcdb520cSKristof Provost 			else if (pd->ndport == ICMP6_ECHO_REPLY)
1023fcdb520cSKristof Provost 				pd->ndport = ICMP_ECHOREPLY;
1024fcdb520cSKristof Provost 			HTONS(pd->ndport);
1025fcdb520cSKristof Provost 		} else {
1026fcdb520cSKristof Provost 			NTOHS(pd->nsport);
1027fcdb520cSKristof Provost 			if (pd->nsport == ICMP6_ECHO_REQUEST)
1028fcdb520cSKristof Provost 				pd->nsport = ICMP_ECHO;
1029fcdb520cSKristof Provost 			else if (pd->nsport == ICMP6_ECHO_REPLY)
1030fcdb520cSKristof Provost 				pd->nsport = ICMP_ECHOREPLY;
1031fcdb520cSKristof Provost 			HTONS(pd->nsport);
1032fcdb520cSKristof Provost 		}
1033fcdb520cSKristof Provost 	} else if (pd->proto == IPPROTO_ICMP && pd->naf == AF_INET6) {
1034fcdb520cSKristof Provost 		if (pd->dir == PF_IN) {
1035fcdb520cSKristof Provost 			NTOHS(pd->ndport);
1036fcdb520cSKristof Provost 			if (pd->ndport == ICMP_ECHO)
1037fcdb520cSKristof Provost 				pd->ndport = ICMP6_ECHO_REQUEST;
1038fcdb520cSKristof Provost 			else if (pd->ndport == ICMP_ECHOREPLY)
1039fcdb520cSKristof Provost 				pd->ndport = ICMP6_ECHO_REPLY;
1040fcdb520cSKristof Provost 			HTONS(pd->ndport);
1041fcdb520cSKristof Provost 		} else {
1042fcdb520cSKristof Provost 			NTOHS(pd->nsport);
1043fcdb520cSKristof Provost 			if (pd->nsport == ICMP_ECHO)
1044fcdb520cSKristof Provost 				pd->nsport = ICMP6_ECHO_REQUEST;
1045fcdb520cSKristof Provost 			else if (pd->nsport == ICMP_ECHOREPLY)
1046fcdb520cSKristof Provost 				pd->nsport = ICMP6_ECHO_REPLY;
1047fcdb520cSKristof Provost 			HTONS(pd->nsport);
1048fcdb520cSKristof Provost 		}
1049fcdb520cSKristof Provost 	}
1050fcdb520cSKristof Provost 
1051fcdb520cSKristof Provost 	/* get the destination address and port */
1052fcdb520cSKristof Provost 	if (! TAILQ_EMPTY(&r->rdr.list)) {
1053fcdb520cSKristof Provost 		if (pf_map_addr_sn(pd->naf, r, &nsaddr, &naddr, NULL, NULL,
1054fcdb520cSKristof Provost 		    &sns, NULL, &r->rdr))
1055fcdb520cSKristof Provost 			return (-1);
1056fcdb520cSKristof Provost 		if (r->rdr.proxy_port[0])
1057fcdb520cSKristof Provost 			pd->ndport = htons(r->rdr.proxy_port[0]);
1058fcdb520cSKristof Provost 
1059fcdb520cSKristof Provost 		if (pd->naf == AF_INET) {
1060fcdb520cSKristof Provost 			/* The prefix is the IPv4 rdr address */
1061fcdb520cSKristof Provost 			prefixlen = in_mask2len(
1062fcdb520cSKristof Provost 			    (struct in_addr *)&r->rdr.cur->addr.v.a.mask);
1063fcdb520cSKristof Provost 			inet_nat46(pd->naf, &pd->ndaddr, &ndaddr, &naddr,
1064fcdb520cSKristof Provost 			    prefixlen);
1065fcdb520cSKristof Provost 		} else {
1066fcdb520cSKristof Provost 			/* The prefix is the IPv6 rdr address */
1067fcdb520cSKristof Provost 			prefixlen = in6_mask2len(
1068fcdb520cSKristof Provost 			    (struct in6_addr *)&r->rdr.cur->addr.v.a.mask, NULL);
1069fcdb520cSKristof Provost 			inet_nat64(pd->naf, &pd->ndaddr, &ndaddr, &naddr,
1070fcdb520cSKristof Provost 			    prefixlen);
1071fcdb520cSKristof Provost 		}
1072fcdb520cSKristof Provost 	} else {
1073fcdb520cSKristof Provost 		if (pd->naf == AF_INET) {
1074fcdb520cSKristof Provost 			/* The prefix is the IPv6 dst address */
1075fcdb520cSKristof Provost 			prefixlen = in6_mask2len(
1076fcdb520cSKristof Provost 			    (struct in6_addr *)&r->dst.addr.v.a.mask, NULL);
1077fcdb520cSKristof Provost 			if (prefixlen < 32)
1078fcdb520cSKristof Provost 				prefixlen = 96;
1079fcdb520cSKristof Provost 			inet_nat64(pd->naf, &pd->ndaddr, &ndaddr, &pd->ndaddr,
1080fcdb520cSKristof Provost 			    prefixlen);
1081fcdb520cSKristof Provost 		} else {
1082fcdb520cSKristof Provost 			/*
1083fcdb520cSKristof Provost 			 * The prefix is the IPv6 nat address
1084fcdb520cSKristof Provost 			 * (that was stored in pd->nsaddr)
1085fcdb520cSKristof Provost 			 */
1086fcdb520cSKristof Provost 			prefixlen = in6_mask2len(
1087fcdb520cSKristof Provost 			    (struct in6_addr *)&r->nat.cur->addr.v.a.mask, NULL);
1088fcdb520cSKristof Provost 			if (prefixlen > 96)
1089fcdb520cSKristof Provost 				prefixlen = 96;
1090fcdb520cSKristof Provost 			inet_nat64(pd->naf, &pd->ndaddr, &ndaddr, &nsaddr,
1091fcdb520cSKristof Provost 			    prefixlen);
1092fcdb520cSKristof Provost 		}
1093fcdb520cSKristof Provost 	}
1094fcdb520cSKristof Provost 
1095fcdb520cSKristof Provost 	PF_ACPY(&pd->nsaddr, &nsaddr, pd->naf);
1096fcdb520cSKristof Provost 	PF_ACPY(&pd->ndaddr, &ndaddr, pd->naf);
1097fcdb520cSKristof Provost 
1098fcdb520cSKristof Provost 	if (V_pf_status.debug >= PF_DEBUG_MISC) {
1099fcdb520cSKristof Provost 		printf("pf: af-to %s done, prefixlen %d, ",
1100fcdb520cSKristof Provost 		    pd->naf == AF_INET ? "inet" : "inet6",
1101fcdb520cSKristof Provost 		    prefixlen);
1102fcdb520cSKristof Provost 		pf_print_host(&pd->nsaddr, pd->nsport, pd->naf);
1103fcdb520cSKristof Provost 		printf(" -> ");
1104fcdb520cSKristof Provost 		pf_print_host(&pd->ndaddr, pd->ndport, pd->naf);
1105fcdb520cSKristof Provost 		printf("\n");
1106fcdb520cSKristof Provost 	}
1107fcdb520cSKristof Provost 
1108fcdb520cSKristof Provost 	return (0);
1109fcdb520cSKristof Provost #else
1110fcdb520cSKristof Provost 	return (-1);
1111fcdb520cSKristof Provost #endif
1112fcdb520cSKristof Provost }
1113