xref: /freebsd-src/contrib/pf/ftp-proxy/filter.c (revision 096efeb658b5a6d63068bd90f3c6508f74767bba)
1e0bfbfceSBjoern A. Zeeb /*	$OpenBSD: filter.c,v 1.8 2008/06/13 07:25:26 claudio Exp $ */
267ecd4f3SMax Laier 
367ecd4f3SMax Laier /*
467ecd4f3SMax Laier  * Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
567ecd4f3SMax Laier  *
667ecd4f3SMax Laier  * Permission to use, copy, modify, and distribute this software for any
767ecd4f3SMax Laier  * purpose with or without fee is hereby granted, provided that the above
867ecd4f3SMax Laier  * copyright notice and this permission notice appear in all copies.
967ecd4f3SMax Laier  *
1067ecd4f3SMax Laier  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1167ecd4f3SMax Laier  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1267ecd4f3SMax Laier  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1367ecd4f3SMax Laier  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1467ecd4f3SMax Laier  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1567ecd4f3SMax Laier  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1667ecd4f3SMax Laier  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1767ecd4f3SMax Laier  */
1867ecd4f3SMax Laier 
1967ecd4f3SMax Laier #include <sys/ioctl.h>
2067ecd4f3SMax Laier #include <sys/types.h>
2167ecd4f3SMax Laier #include <sys/socket.h>
2267ecd4f3SMax Laier 
2367ecd4f3SMax Laier #include <net/if.h>
2467ecd4f3SMax Laier #include <net/pfvar.h>
2567ecd4f3SMax Laier #include <netinet/in.h>
2667ecd4f3SMax Laier #include <netinet/tcp.h>
2767ecd4f3SMax Laier #include <arpa/inet.h>
2867ecd4f3SMax Laier 
2967ecd4f3SMax Laier #include <err.h>
3067ecd4f3SMax Laier #include <errno.h>
3195be9288SKristof Provost #include <libpfctl.h>
3267ecd4f3SMax Laier #include <fcntl.h>
3367ecd4f3SMax Laier #include <stdio.h>
3467ecd4f3SMax Laier #include <string.h>
3567ecd4f3SMax Laier #include <unistd.h>
3667ecd4f3SMax Laier 
3767ecd4f3SMax Laier #include "filter.h"
3867ecd4f3SMax Laier 
3967ecd4f3SMax Laier /* From netinet/in.h, but only _KERNEL_ gets them. */
4067ecd4f3SMax Laier #define satosin(sa)	((struct sockaddr_in *)(sa))
4167ecd4f3SMax Laier #define satosin6(sa)	((struct sockaddr_in6 *)(sa))
4267ecd4f3SMax Laier 
4367ecd4f3SMax Laier enum { TRANS_FILTER = 0, TRANS_NAT, TRANS_RDR, TRANS_SIZE };
4467ecd4f3SMax Laier 
4567ecd4f3SMax Laier int prepare_rule(u_int32_t, int, struct sockaddr *, struct sockaddr *,
4667ecd4f3SMax Laier     u_int16_t);
4767ecd4f3SMax Laier int server_lookup4(struct sockaddr_in *, struct sockaddr_in *,
4867ecd4f3SMax Laier     struct sockaddr_in *);
4967ecd4f3SMax Laier int server_lookup6(struct sockaddr_in6 *, struct sockaddr_in6 *,
5067ecd4f3SMax Laier     struct sockaddr_in6 *);
5167ecd4f3SMax Laier 
5267ecd4f3SMax Laier static struct pfioc_pooladdr	pfp;
53e9eb0941SKristof Provost static struct pfctl_rule	pfrule;
54e9eb0941SKristof Provost static char			pfanchor[PF_ANCHOR_NAME_SIZE];
55e9eb0941SKristof Provost static char			pfanchor_call[PF_ANCHOR_NAME_SIZE];
56e9eb0941SKristof Provost static uint32_t			pfticket;
57e9eb0941SKristof Provost static uint32_t			pfpool_ticket;
5867ecd4f3SMax Laier static struct pfioc_trans	pft;
5967ecd4f3SMax Laier static struct pfioc_trans_e	pfte[TRANS_SIZE];
60044243fcSKristof Provost static int rule_log;
61324fd7ecSKristof Provost static struct pfctl_handle *pfh = NULL;
62e0bfbfceSBjoern A. Zeeb static const char *qname, *tagname;
6367ecd4f3SMax Laier 
6467ecd4f3SMax Laier int
6567ecd4f3SMax Laier add_filter(u_int32_t id, u_int8_t dir, struct sockaddr *src,
6667ecd4f3SMax Laier     struct sockaddr *dst, u_int16_t d_port)
6767ecd4f3SMax Laier {
6867ecd4f3SMax Laier 	if (!src || !dst || !d_port) {
6967ecd4f3SMax Laier 		errno = EINVAL;
7067ecd4f3SMax Laier 		return (-1);
7167ecd4f3SMax Laier 	}
7267ecd4f3SMax Laier 
7367ecd4f3SMax Laier 	if (prepare_rule(id, PF_RULESET_FILTER, src, dst, d_port) == -1)
7467ecd4f3SMax Laier 		return (-1);
7567ecd4f3SMax Laier 
76e9eb0941SKristof Provost 	pfrule.direction = dir;
77324fd7ecSKristof Provost 	if (pfctl_add_rule_h(pfh, &pfrule, pfanchor, pfanchor_call,
78e9eb0941SKristof Provost 	    pfticket, pfpool_ticket))
7967ecd4f3SMax Laier 		return (-1);
8067ecd4f3SMax Laier 
8167ecd4f3SMax Laier 	return (0);
8267ecd4f3SMax Laier }
8367ecd4f3SMax Laier 
8467ecd4f3SMax Laier int
8567ecd4f3SMax Laier add_nat(u_int32_t id, struct sockaddr *src, struct sockaddr *dst,
8667ecd4f3SMax Laier     u_int16_t d_port, struct sockaddr *nat, u_int16_t nat_range_low,
8767ecd4f3SMax Laier     u_int16_t nat_range_high)
8867ecd4f3SMax Laier {
8967ecd4f3SMax Laier 	if (!src || !dst || !d_port || !nat || !nat_range_low ||
9067ecd4f3SMax Laier 	    (src->sa_family != nat->sa_family)) {
9167ecd4f3SMax Laier 		errno = EINVAL;
9267ecd4f3SMax Laier 		return (-1);
9367ecd4f3SMax Laier 	}
9467ecd4f3SMax Laier 
9567ecd4f3SMax Laier 	if (prepare_rule(id, PF_RULESET_NAT, src, dst, d_port) == -1)
9667ecd4f3SMax Laier 		return (-1);
9767ecd4f3SMax Laier 
9867ecd4f3SMax Laier 	if (nat->sa_family == AF_INET) {
9967ecd4f3SMax Laier 		memcpy(&pfp.addr.addr.v.a.addr.v4,
10067ecd4f3SMax Laier 		    &satosin(nat)->sin_addr.s_addr, 4);
10167ecd4f3SMax Laier 		memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4);
10267ecd4f3SMax Laier 	} else {
10367ecd4f3SMax Laier 		memcpy(&pfp.addr.addr.v.a.addr.v6,
10467ecd4f3SMax Laier 		    &satosin6(nat)->sin6_addr.s6_addr, 16);
10567ecd4f3SMax Laier 		memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16);
10667ecd4f3SMax Laier 	}
107044243fcSKristof Provost 	if (ioctl(pfctl_fd(pfh), DIOCADDADDR, &pfp) == -1)
10867ecd4f3SMax Laier 		return (-1);
10967ecd4f3SMax Laier 
110*096efeb6SKristof Provost 	pfrule.rdr.proxy_port[0] = nat_range_low;
111*096efeb6SKristof Provost 	pfrule.rdr.proxy_port[1] = nat_range_high;
112324fd7ecSKristof Provost 	if (pfctl_add_rule_h(pfh, &pfrule, pfanchor, pfanchor_call,
113e9eb0941SKristof Provost 	    pfticket, pfpool_ticket))
11467ecd4f3SMax Laier 		return (-1);
11567ecd4f3SMax Laier 
11667ecd4f3SMax Laier 	return (0);
11767ecd4f3SMax Laier }
11867ecd4f3SMax Laier 
11967ecd4f3SMax Laier int
12067ecd4f3SMax Laier add_rdr(u_int32_t id, struct sockaddr *src, struct sockaddr *dst,
12167ecd4f3SMax Laier     u_int16_t d_port, struct sockaddr *rdr, u_int16_t rdr_port)
12267ecd4f3SMax Laier {
12367ecd4f3SMax Laier 	if (!src || !dst || !d_port || !rdr || !rdr_port ||
12467ecd4f3SMax Laier 	    (src->sa_family != rdr->sa_family)) {
12567ecd4f3SMax Laier 		errno = EINVAL;
12667ecd4f3SMax Laier 		return (-1);
12767ecd4f3SMax Laier 	}
12867ecd4f3SMax Laier 
12967ecd4f3SMax Laier 	if (prepare_rule(id, PF_RULESET_RDR, src, dst, d_port) == -1)
13067ecd4f3SMax Laier 		return (-1);
13167ecd4f3SMax Laier 
13267ecd4f3SMax Laier 	if (rdr->sa_family == AF_INET) {
13367ecd4f3SMax Laier 		memcpy(&pfp.addr.addr.v.a.addr.v4,
13467ecd4f3SMax Laier 		    &satosin(rdr)->sin_addr.s_addr, 4);
13567ecd4f3SMax Laier 		memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4);
13667ecd4f3SMax Laier 	} else {
13767ecd4f3SMax Laier 		memcpy(&pfp.addr.addr.v.a.addr.v6,
13867ecd4f3SMax Laier 		    &satosin6(rdr)->sin6_addr.s6_addr, 16);
13967ecd4f3SMax Laier 		memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16);
14067ecd4f3SMax Laier 	}
141044243fcSKristof Provost 	if (ioctl(pfctl_fd(pfh), DIOCADDADDR, &pfp) == -1)
14267ecd4f3SMax Laier 		return (-1);
14367ecd4f3SMax Laier 
144*096efeb6SKristof Provost 	pfrule.rdr.proxy_port[0] = rdr_port;
145324fd7ecSKristof Provost 	if (pfctl_add_rule_h(pfh, &pfrule, pfanchor, pfanchor_call,
146e9eb0941SKristof Provost 	    pfticket, pfpool_ticket))
14767ecd4f3SMax Laier 		return (-1);
14867ecd4f3SMax Laier 
14967ecd4f3SMax Laier 	return (0);
15067ecd4f3SMax Laier }
15167ecd4f3SMax Laier 
15267ecd4f3SMax Laier int
15367ecd4f3SMax Laier do_commit(void)
15467ecd4f3SMax Laier {
155044243fcSKristof Provost 	if (ioctl(pfctl_fd(pfh), DIOCXCOMMIT, &pft) == -1)
15667ecd4f3SMax Laier 		return (-1);
15767ecd4f3SMax Laier 
15867ecd4f3SMax Laier 	return (0);
15967ecd4f3SMax Laier }
16067ecd4f3SMax Laier 
16167ecd4f3SMax Laier int
16267ecd4f3SMax Laier do_rollback(void)
16367ecd4f3SMax Laier {
164044243fcSKristof Provost 	if (ioctl(pfctl_fd(pfh), DIOCXROLLBACK, &pft) == -1)
16567ecd4f3SMax Laier 		return (-1);
16667ecd4f3SMax Laier 
16767ecd4f3SMax Laier 	return (0);
16867ecd4f3SMax Laier }
16967ecd4f3SMax Laier 
17067ecd4f3SMax Laier void
171e0bfbfceSBjoern A. Zeeb init_filter(const char *opt_qname, const char *opt_tagname, int opt_verbose)
17267ecd4f3SMax Laier {
173ddd08375SKristof Provost 	struct pfctl_status *status;
17467ecd4f3SMax Laier 
17567ecd4f3SMax Laier 	qname = opt_qname;
176e0bfbfceSBjoern A. Zeeb 	tagname = opt_tagname;
17767ecd4f3SMax Laier 
17867ecd4f3SMax Laier 	if (opt_verbose == 1)
17967ecd4f3SMax Laier 		rule_log = PF_LOG;
18067ecd4f3SMax Laier 	else if (opt_verbose == 2)
18167ecd4f3SMax Laier 		rule_log = PF_LOG_ALL;
18267ecd4f3SMax Laier 
183324fd7ecSKristof Provost 	pfh = pfctl_open(PF_DEVICE);
184324fd7ecSKristof Provost 	if (pfh == NULL)
185324fd7ecSKristof Provost 		err(1, "pfctl_open");
1865824df8dSKristof Provost 	status = pfctl_get_status_h(pfh);
187ddd08375SKristof Provost 	if (status == NULL)
18867ecd4f3SMax Laier 		err(1, "DIOCGETSTATUS");
189ddd08375SKristof Provost 	if (!status->running)
19067ecd4f3SMax Laier 		errx(1, "pf is disabled");
191ddd08375SKristof Provost 
192ddd08375SKristof Provost 	pfctl_free_status(status);
19367ecd4f3SMax Laier }
19467ecd4f3SMax Laier 
19567ecd4f3SMax Laier int
19667ecd4f3SMax Laier prepare_commit(u_int32_t id)
19767ecd4f3SMax Laier {
19867ecd4f3SMax Laier 	char an[PF_ANCHOR_NAME_SIZE];
19967ecd4f3SMax Laier 	int i;
20067ecd4f3SMax Laier 
20167ecd4f3SMax Laier 	memset(&pft, 0, sizeof pft);
20267ecd4f3SMax Laier 	pft.size = TRANS_SIZE;
20367ecd4f3SMax Laier 	pft.esize = sizeof pfte[0];
20467ecd4f3SMax Laier 	pft.array = pfte;
20567ecd4f3SMax Laier 
20667ecd4f3SMax Laier 	snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR,
20767ecd4f3SMax Laier 	    getpid(), id);
20867ecd4f3SMax Laier 	for (i = 0; i < TRANS_SIZE; i++) {
20967ecd4f3SMax Laier 		memset(&pfte[i], 0, sizeof pfte[0]);
21067ecd4f3SMax Laier 		strlcpy(pfte[i].anchor, an, PF_ANCHOR_NAME_SIZE);
21167ecd4f3SMax Laier 		switch (i) {
21267ecd4f3SMax Laier 		case TRANS_FILTER:
21367ecd4f3SMax Laier 			pfte[i].rs_num = PF_RULESET_FILTER;
21467ecd4f3SMax Laier 			break;
21567ecd4f3SMax Laier 		case TRANS_NAT:
21667ecd4f3SMax Laier 			pfte[i].rs_num = PF_RULESET_NAT;
21767ecd4f3SMax Laier 			break;
21867ecd4f3SMax Laier 		case TRANS_RDR:
21967ecd4f3SMax Laier 			pfte[i].rs_num = PF_RULESET_RDR;
22067ecd4f3SMax Laier 			break;
22167ecd4f3SMax Laier 		default:
22267ecd4f3SMax Laier 			errno = EINVAL;
22367ecd4f3SMax Laier 			return (-1);
22467ecd4f3SMax Laier 		}
22567ecd4f3SMax Laier 	}
22667ecd4f3SMax Laier 
227044243fcSKristof Provost 	if (ioctl(pfctl_fd(pfh), DIOCXBEGIN, &pft) == -1)
22867ecd4f3SMax Laier 		return (-1);
22967ecd4f3SMax Laier 
23067ecd4f3SMax Laier 	return (0);
23167ecd4f3SMax Laier }
23267ecd4f3SMax Laier 
23367ecd4f3SMax Laier int
23467ecd4f3SMax Laier prepare_rule(u_int32_t id, int rs_num, struct sockaddr *src,
23567ecd4f3SMax Laier     struct sockaddr *dst, u_int16_t d_port)
23667ecd4f3SMax Laier {
23767ecd4f3SMax Laier 	char an[PF_ANCHOR_NAME_SIZE];
23867ecd4f3SMax Laier 
23967ecd4f3SMax Laier 	if ((src->sa_family != AF_INET && src->sa_family != AF_INET6) ||
24067ecd4f3SMax Laier 	    (src->sa_family != dst->sa_family)) {
24167ecd4f3SMax Laier 	    	errno = EPROTONOSUPPORT;
24267ecd4f3SMax Laier 		return (-1);
24367ecd4f3SMax Laier 	}
24467ecd4f3SMax Laier 
24567ecd4f3SMax Laier 	memset(&pfp, 0, sizeof pfp);
246e9eb0941SKristof Provost 	memset(&pfrule, 0, sizeof pfrule);
24767ecd4f3SMax Laier 	snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR,
24867ecd4f3SMax Laier 	    getpid(), id);
24967ecd4f3SMax Laier 	strlcpy(pfp.anchor, an, PF_ANCHOR_NAME_SIZE);
250e9eb0941SKristof Provost 	strlcpy(pfanchor, an, PF_ANCHOR_NAME_SIZE);
25167ecd4f3SMax Laier 
25267ecd4f3SMax Laier 	switch (rs_num) {
25367ecd4f3SMax Laier 	case PF_RULESET_FILTER:
254e9eb0941SKristof Provost 		pfticket = pfte[TRANS_FILTER].ticket;
25567ecd4f3SMax Laier 		break;
25667ecd4f3SMax Laier 	case PF_RULESET_NAT:
257e9eb0941SKristof Provost 		pfticket = pfte[TRANS_NAT].ticket;
25867ecd4f3SMax Laier 		break;
25967ecd4f3SMax Laier 	case PF_RULESET_RDR:
260e9eb0941SKristof Provost 		pfticket = pfte[TRANS_RDR].ticket;
26167ecd4f3SMax Laier 		break;
26267ecd4f3SMax Laier 	default:
26367ecd4f3SMax Laier 		errno = EINVAL;
26467ecd4f3SMax Laier 		return (-1);
26567ecd4f3SMax Laier 	}
266ba2a9207SKristof Provost 	if (pfctl_begin_addrs(pfh, &pfp.ticket))
26767ecd4f3SMax Laier 		return (-1);
268e9eb0941SKristof Provost 	pfpool_ticket = pfp.ticket;
26967ecd4f3SMax Laier 
27067ecd4f3SMax Laier 	/* Generic for all rule types. */
271e9eb0941SKristof Provost 	pfrule.af = src->sa_family;
272e9eb0941SKristof Provost 	pfrule.proto = IPPROTO_TCP;
273e9eb0941SKristof Provost 	pfrule.src.addr.type = PF_ADDR_ADDRMASK;
274e9eb0941SKristof Provost 	pfrule.dst.addr.type = PF_ADDR_ADDRMASK;
27567ecd4f3SMax Laier 	if (src->sa_family == AF_INET) {
276e9eb0941SKristof Provost 		memcpy(&pfrule.src.addr.v.a.addr.v4,
27767ecd4f3SMax Laier 		    &satosin(src)->sin_addr.s_addr, 4);
278e9eb0941SKristof Provost 		memset(&pfrule.src.addr.v.a.mask.addr8, 255, 4);
279e9eb0941SKristof Provost 		memcpy(&pfrule.dst.addr.v.a.addr.v4,
28067ecd4f3SMax Laier 		    &satosin(dst)->sin_addr.s_addr, 4);
281e9eb0941SKristof Provost 		memset(&pfrule.dst.addr.v.a.mask.addr8, 255, 4);
28267ecd4f3SMax Laier 	} else {
283e9eb0941SKristof Provost 		memcpy(&pfrule.src.addr.v.a.addr.v6,
28467ecd4f3SMax Laier 		    &satosin6(src)->sin6_addr.s6_addr, 16);
285e9eb0941SKristof Provost 		memset(&pfrule.src.addr.v.a.mask.addr8, 255, 16);
286e9eb0941SKristof Provost 		memcpy(&pfrule.dst.addr.v.a.addr.v6,
28767ecd4f3SMax Laier 		    &satosin6(dst)->sin6_addr.s6_addr, 16);
288e9eb0941SKristof Provost 		memset(&pfrule.dst.addr.v.a.mask.addr8, 255, 16);
28967ecd4f3SMax Laier 	}
290e9eb0941SKristof Provost 	pfrule.dst.port_op = PF_OP_EQ;
291e9eb0941SKristof Provost 	pfrule.dst.port[0] = htons(d_port);
29267ecd4f3SMax Laier 
29367ecd4f3SMax Laier 	switch (rs_num) {
29467ecd4f3SMax Laier 	case PF_RULESET_FILTER:
29567ecd4f3SMax Laier 		/*
296e0bfbfceSBjoern A. Zeeb 		 * pass [quick] [log] inet[6] proto tcp \
29767ecd4f3SMax Laier 		 *     from $src to $dst port = $d_port flags S/SA keep state
298e0bfbfceSBjoern A. Zeeb 		 *     (max 1) [queue qname] [tag tagname]
29967ecd4f3SMax Laier 		 */
300e9eb0941SKristof Provost 		pfrule.action = PF_PASS;
301e9eb0941SKristof Provost 		pfrule.quick = 1;
302e9eb0941SKristof Provost 		pfrule.log = rule_log;
303e9eb0941SKristof Provost 		pfrule.keep_state = 1;
304e9eb0941SKristof Provost 		pfrule.flags = TH_SYN;
305e9eb0941SKristof Provost 		pfrule.flagset = (TH_SYN|TH_ACK);
306e9eb0941SKristof Provost 		pfrule.max_states = 1;
30767ecd4f3SMax Laier 		if (qname != NULL)
308e9eb0941SKristof Provost 			strlcpy(pfrule.qname, qname, sizeof pfrule.qname);
309e0bfbfceSBjoern A. Zeeb 		if (tagname != NULL) {
310e9eb0941SKristof Provost 			pfrule.quick = 0;
311e9eb0941SKristof Provost 			strlcpy(pfrule.tagname, tagname,
312e9eb0941SKristof Provost                                 sizeof pfrule.tagname);
313e0bfbfceSBjoern A. Zeeb 		}
31467ecd4f3SMax Laier 		break;
31567ecd4f3SMax Laier 	case PF_RULESET_NAT:
31667ecd4f3SMax Laier 		/*
31767ecd4f3SMax Laier 		 * nat inet[6] proto tcp from $src to $dst port $d_port -> $nat
31867ecd4f3SMax Laier 		 */
319e9eb0941SKristof Provost 		pfrule.action = PF_NAT;
32067ecd4f3SMax Laier 		break;
32167ecd4f3SMax Laier 	case PF_RULESET_RDR:
32267ecd4f3SMax Laier 		/*
32367ecd4f3SMax Laier 		 * rdr inet[6] proto tcp from $src to $dst port $d_port -> $rdr
32467ecd4f3SMax Laier 		 */
325e9eb0941SKristof Provost 		pfrule.action = PF_RDR;
32667ecd4f3SMax Laier 		break;
32767ecd4f3SMax Laier 	default:
32867ecd4f3SMax Laier 		errno = EINVAL;
32967ecd4f3SMax Laier 		return (-1);
33067ecd4f3SMax Laier 	}
33167ecd4f3SMax Laier 
33267ecd4f3SMax Laier 	return (0);
33367ecd4f3SMax Laier }
33467ecd4f3SMax Laier 
33567ecd4f3SMax Laier int
33667ecd4f3SMax Laier server_lookup(struct sockaddr *client, struct sockaddr *proxy,
33767ecd4f3SMax Laier     struct sockaddr *server)
33867ecd4f3SMax Laier {
33967ecd4f3SMax Laier 	if (client->sa_family == AF_INET)
34067ecd4f3SMax Laier 		return (server_lookup4(satosin(client), satosin(proxy),
34167ecd4f3SMax Laier 		    satosin(server)));
34267ecd4f3SMax Laier 
34367ecd4f3SMax Laier 	if (client->sa_family == AF_INET6)
34467ecd4f3SMax Laier 		return (server_lookup6(satosin6(client), satosin6(proxy),
34567ecd4f3SMax Laier 		    satosin6(server)));
34667ecd4f3SMax Laier 
34767ecd4f3SMax Laier 	errno = EPROTONOSUPPORT;
34867ecd4f3SMax Laier 	return (-1);
34967ecd4f3SMax Laier }
35067ecd4f3SMax Laier 
35167ecd4f3SMax Laier int
35267ecd4f3SMax Laier server_lookup4(struct sockaddr_in *client, struct sockaddr_in *proxy,
35367ecd4f3SMax Laier     struct sockaddr_in *server)
35467ecd4f3SMax Laier {
35571d3c704SKristof Provost 	struct pfctl_natlook_key k = {};
35671d3c704SKristof Provost 	struct pfctl_natlook r = {};
35767ecd4f3SMax Laier 
35871d3c704SKristof Provost 	k.direction = PF_OUT;
35971d3c704SKristof Provost 	k.af = AF_INET;
36071d3c704SKristof Provost 	k.proto = IPPROTO_TCP;
36171d3c704SKristof Provost 	memcpy(&k.saddr.v4, &client->sin_addr.s_addr, sizeof(k.saddr.v4));
36271d3c704SKristof Provost 	memcpy(&k.daddr.v4, &proxy->sin_addr.s_addr, sizeof(k.daddr.v4));
36371d3c704SKristof Provost 	k.sport = client->sin_port;
36471d3c704SKristof Provost 	k.dport = proxy->sin_port;
36567ecd4f3SMax Laier 
36671d3c704SKristof Provost 	if (pfctl_natlook(pfh, &k, &r))
36767ecd4f3SMax Laier 		return (-1);
36867ecd4f3SMax Laier 
36967ecd4f3SMax Laier 	memset(server, 0, sizeof(struct sockaddr_in));
37067ecd4f3SMax Laier 	server->sin_len = sizeof(struct sockaddr_in);
37167ecd4f3SMax Laier 	server->sin_family = AF_INET;
37271d3c704SKristof Provost 	memcpy(&server->sin_addr.s_addr, &r.daddr.v4,
37371d3c704SKristof Provost 	    sizeof(server->sin_addr.s_addr));
37471d3c704SKristof Provost 	server->sin_port = r.dport;
37567ecd4f3SMax Laier 
37667ecd4f3SMax Laier 	return (0);
37767ecd4f3SMax Laier }
37867ecd4f3SMax Laier 
37967ecd4f3SMax Laier int
38067ecd4f3SMax Laier server_lookup6(struct sockaddr_in6 *client, struct sockaddr_in6 *proxy,
38167ecd4f3SMax Laier     struct sockaddr_in6 *server)
38267ecd4f3SMax Laier {
38371d3c704SKristof Provost 	struct pfctl_natlook_key k = {};
38471d3c704SKristof Provost 	struct pfctl_natlook r = {};
38567ecd4f3SMax Laier 
38671d3c704SKristof Provost 	k.direction = PF_OUT;
38771d3c704SKristof Provost 	k.af = AF_INET6;
38871d3c704SKristof Provost 	k.proto = IPPROTO_TCP;
38971d3c704SKristof Provost 	memcpy(&k.saddr.v6, &client->sin6_addr.s6_addr, sizeof(k.saddr.v6));
39071d3c704SKristof Provost 	memcpy(&k.daddr.v6, &proxy->sin6_addr.s6_addr, sizeof(k.daddr.v6));
39171d3c704SKristof Provost 	k.sport = client->sin6_port;
39271d3c704SKristof Provost 	k.dport = proxy->sin6_port;
39367ecd4f3SMax Laier 
39471d3c704SKristof Provost 	if (pfctl_natlook(pfh, &k, &r))
39567ecd4f3SMax Laier 		return (-1);
39667ecd4f3SMax Laier 
39767ecd4f3SMax Laier 	memset(server, 0, sizeof(struct sockaddr_in6));
39867ecd4f3SMax Laier 	server->sin6_len = sizeof(struct sockaddr_in6);
39967ecd4f3SMax Laier 	server->sin6_family = AF_INET6;
40071d3c704SKristof Provost 	memcpy(&server->sin6_addr.s6_addr, &r.daddr.v6,
40171d3c704SKristof Provost 	    sizeof(server->sin6_addr));
40271d3c704SKristof Provost 	server->sin6_port = r.dport;
40367ecd4f3SMax Laier 
40467ecd4f3SMax Laier 	return (0);
40567ecd4f3SMax Laier }
406