xref: /freebsd-src/contrib/pf/tftp-proxy/filter.c (revision 096efeb658b5a6d63068bd90f3c6508f74767bba)
167ecd4f3SMax Laier /*	$OpenBSD: filter.c,v 1.1 2005/12/28 19:07:07 jcs Exp $ */
25ee7cd21SMax Laier /*	$FreeBSD$ */
367ecd4f3SMax Laier 
467ecd4f3SMax Laier /*
567ecd4f3SMax Laier  * Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
667ecd4f3SMax Laier  *
767ecd4f3SMax Laier  * Permission to use, copy, modify, and distribute this software for any
867ecd4f3SMax Laier  * purpose with or without fee is hereby granted, provided that the above
967ecd4f3SMax Laier  * copyright notice and this permission notice appear in all copies.
1067ecd4f3SMax Laier  *
1167ecd4f3SMax Laier  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1267ecd4f3SMax Laier  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1367ecd4f3SMax Laier  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1467ecd4f3SMax Laier  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1567ecd4f3SMax Laier  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1667ecd4f3SMax Laier  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1767ecd4f3SMax Laier  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1867ecd4f3SMax Laier  */
1967ecd4f3SMax Laier 
2067ecd4f3SMax Laier #include <syslog.h>
2167ecd4f3SMax Laier 
2267ecd4f3SMax Laier #include <sys/ioctl.h>
2367ecd4f3SMax Laier #include <sys/types.h>
2467ecd4f3SMax Laier #include <sys/socket.h>
2567ecd4f3SMax Laier 
2667ecd4f3SMax Laier #include <net/if.h>
2767ecd4f3SMax Laier #include <net/pfvar.h>
2867ecd4f3SMax Laier #include <netinet/in.h>
2967ecd4f3SMax Laier #include <netinet/tcp.h>
3067ecd4f3SMax Laier #include <arpa/inet.h>
3167ecd4f3SMax Laier 
3267ecd4f3SMax Laier #include <err.h>
3367ecd4f3SMax Laier #include <errno.h>
3467ecd4f3SMax Laier #include <fcntl.h>
3595be9288SKristof Provost #include <libpfctl.h>
3667ecd4f3SMax Laier #include <stdio.h>
3767ecd4f3SMax Laier #include <stdlib.h>
3867ecd4f3SMax Laier #include <string.h>
3967ecd4f3SMax Laier #include <unistd.h>
4067ecd4f3SMax Laier 
4167ecd4f3SMax Laier #include "filter.h"
4267ecd4f3SMax Laier 
4367ecd4f3SMax Laier /* From netinet/in.h, but only _KERNEL_ gets them. */
4467ecd4f3SMax Laier #define satosin(sa)	((struct sockaddr_in *)(sa))
4567ecd4f3SMax Laier #define satosin6(sa)	((struct sockaddr_in6 *)(sa))
4667ecd4f3SMax Laier 
4767ecd4f3SMax Laier enum { TRANS_FILTER = 0, TRANS_NAT, TRANS_RDR, TRANS_SIZE };
4867ecd4f3SMax Laier 
4967ecd4f3SMax Laier int prepare_rule(u_int32_t, int, struct sockaddr *, struct sockaddr *,
5067ecd4f3SMax Laier     u_int16_t, u_int8_t);
5167ecd4f3SMax Laier int server_lookup4(struct sockaddr_in *, struct sockaddr_in *,
5267ecd4f3SMax Laier     struct sockaddr_in *, u_int8_t);
5367ecd4f3SMax Laier int server_lookup6(struct sockaddr_in6 *, struct sockaddr_in6 *,
5467ecd4f3SMax Laier     struct sockaddr_in6 *, u_int8_t);
5567ecd4f3SMax Laier 
5667ecd4f3SMax Laier static struct pfioc_pooladdr	pfp;
57e9eb0941SKristof Provost static struct pfctl_rule	pfrule;
58e9eb0941SKristof Provost static uint32_t			pfticket;
59e9eb0941SKristof Provost static uint32_t			pfpool_ticket;
60e9eb0941SKristof Provost static char			pfanchor[PF_ANCHOR_NAME_SIZE];
61e9eb0941SKristof Provost static char			pfanchor_call[PF_ANCHOR_NAME_SIZE];
6267ecd4f3SMax Laier static struct pfioc_trans	pft;
6367ecd4f3SMax Laier static struct pfioc_trans_e	pfte[TRANS_SIZE];
64044243fcSKristof Provost static int rule_log;
65324fd7ecSKristof Provost static struct pfctl_handle *pfh = NULL;
6667ecd4f3SMax Laier static char *qname;
6767ecd4f3SMax Laier 
6867ecd4f3SMax Laier int
6967ecd4f3SMax Laier add_filter(u_int32_t id, u_int8_t dir, struct sockaddr *src,
7067ecd4f3SMax Laier     struct sockaddr *dst, u_int16_t d_port, u_int8_t proto)
7167ecd4f3SMax Laier {
7267ecd4f3SMax Laier 	if (!src || !dst || !d_port || !proto) {
7367ecd4f3SMax Laier 		errno = EINVAL;
7467ecd4f3SMax Laier 		return (-1);
7567ecd4f3SMax Laier 	}
7667ecd4f3SMax Laier 
7767ecd4f3SMax Laier 	if (prepare_rule(id, PF_RULESET_FILTER, src, dst, d_port, proto) == -1)
7867ecd4f3SMax Laier 		return (-1);
7967ecd4f3SMax Laier 
80e9eb0941SKristof Provost 	pfrule.direction = dir;
81324fd7ecSKristof Provost 	if (pfctl_add_rule_h(pfh, &pfrule, pfanchor, pfanchor_call,
82e9eb0941SKristof Provost 	    pfticket, pfpool_ticket))
8367ecd4f3SMax Laier 		return (-1);
8467ecd4f3SMax Laier 
8567ecd4f3SMax Laier 	return (0);
8667ecd4f3SMax Laier }
8767ecd4f3SMax Laier 
8867ecd4f3SMax Laier int
8967ecd4f3SMax Laier add_nat(u_int32_t id, struct sockaddr *src, struct sockaddr *dst,
9067ecd4f3SMax Laier     u_int16_t d_port, struct sockaddr *nat, u_int16_t nat_range_low,
9167ecd4f3SMax Laier     u_int16_t nat_range_high, u_int8_t proto)
9267ecd4f3SMax Laier {
9367ecd4f3SMax Laier 	if (!src || !dst || !d_port || !nat || !nat_range_low || !proto ||
9467ecd4f3SMax Laier 	    (src->sa_family != nat->sa_family)) {
9567ecd4f3SMax Laier 		errno = EINVAL;
9667ecd4f3SMax Laier 		return (-1);
9767ecd4f3SMax Laier 	}
9867ecd4f3SMax Laier 
9967ecd4f3SMax Laier 	if (prepare_rule(id, PF_RULESET_NAT, src, dst, d_port, proto) == -1)
10067ecd4f3SMax Laier 		return (-1);
10167ecd4f3SMax Laier 
10267ecd4f3SMax Laier 	if (nat->sa_family == AF_INET) {
10367ecd4f3SMax Laier 		memcpy(&pfp.addr.addr.v.a.addr.v4,
10467ecd4f3SMax Laier 		    &satosin(nat)->sin_addr.s_addr, 4);
10567ecd4f3SMax Laier 		memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4);
10667ecd4f3SMax Laier 	} else {
10767ecd4f3SMax Laier 		memcpy(&pfp.addr.addr.v.a.addr.v6,
10867ecd4f3SMax Laier 		    &satosin6(nat)->sin6_addr.s6_addr, 16);
10967ecd4f3SMax Laier 		memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16);
11067ecd4f3SMax Laier 	}
111044243fcSKristof Provost 	if (ioctl(pfctl_fd(pfh), DIOCADDADDR, &pfp) == -1)
11267ecd4f3SMax Laier 		return (-1);
11367ecd4f3SMax Laier 
114*096efeb6SKristof Provost 	pfrule.rdr.proxy_port[0] = nat_range_low;
115*096efeb6SKristof Provost 	pfrule.rdr.proxy_port[1] = nat_range_high;
116324fd7ecSKristof Provost 	if (pfctl_add_rule_h(pfh, &pfrule, pfanchor, pfanchor_call,
117e9eb0941SKristof Provost 	    pfticket, pfpool_ticket))
11867ecd4f3SMax Laier 		return (-1);
11967ecd4f3SMax Laier 
12067ecd4f3SMax Laier 	return (0);
12167ecd4f3SMax Laier }
12267ecd4f3SMax Laier 
12367ecd4f3SMax Laier int
12467ecd4f3SMax Laier add_rdr(u_int32_t id, struct sockaddr *src, struct sockaddr *dst,
12567ecd4f3SMax Laier     u_int16_t d_port, struct sockaddr *rdr, u_int16_t rdr_port, u_int8_t proto)
12667ecd4f3SMax Laier {
12767ecd4f3SMax Laier 	if (!src || !dst || !d_port || !rdr || !rdr_port || !proto ||
12867ecd4f3SMax Laier 	    (src->sa_family != rdr->sa_family)) {
12967ecd4f3SMax Laier 		errno = EINVAL;
13067ecd4f3SMax Laier 		return (-1);
13167ecd4f3SMax Laier 	}
13267ecd4f3SMax Laier 
13367ecd4f3SMax Laier 	if (prepare_rule(id, PF_RULESET_RDR, src, dst, d_port, proto) == -1)
13467ecd4f3SMax Laier 		return (-1);
13567ecd4f3SMax Laier 
13667ecd4f3SMax Laier 	if (rdr->sa_family == AF_INET) {
13767ecd4f3SMax Laier 		memcpy(&pfp.addr.addr.v.a.addr.v4,
13867ecd4f3SMax Laier 		    &satosin(rdr)->sin_addr.s_addr, 4);
13967ecd4f3SMax Laier 		memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4);
14067ecd4f3SMax Laier 	} else {
14167ecd4f3SMax Laier 		memcpy(&pfp.addr.addr.v.a.addr.v6,
14267ecd4f3SMax Laier 		    &satosin6(rdr)->sin6_addr.s6_addr, 16);
14367ecd4f3SMax Laier 		memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16);
14467ecd4f3SMax Laier 	}
145044243fcSKristof Provost 	if (ioctl(pfctl_fd(pfh), DIOCADDADDR, &pfp) == -1)
14667ecd4f3SMax Laier 		return (-1);
14767ecd4f3SMax Laier 
148*096efeb6SKristof Provost 	pfrule.rdr.proxy_port[0] = rdr_port;
149324fd7ecSKristof Provost 	if (pfctl_add_rule_h(pfh, &pfrule, pfanchor, pfanchor_call,
150e9eb0941SKristof Provost 	    pfticket, pfpool_ticket))
15167ecd4f3SMax Laier 		return (-1);
15267ecd4f3SMax Laier 
15367ecd4f3SMax Laier 	return (0);
15467ecd4f3SMax Laier }
15567ecd4f3SMax Laier 
15667ecd4f3SMax Laier int
15767ecd4f3SMax Laier do_commit(void)
15867ecd4f3SMax Laier {
159044243fcSKristof Provost 	if (ioctl(pfctl_fd(pfh), DIOCXCOMMIT, &pft) == -1)
16067ecd4f3SMax Laier 		return (-1);
16167ecd4f3SMax Laier 
16267ecd4f3SMax Laier 	return (0);
16367ecd4f3SMax Laier }
16467ecd4f3SMax Laier 
16567ecd4f3SMax Laier int
16667ecd4f3SMax Laier do_rollback(void)
16767ecd4f3SMax Laier {
168044243fcSKristof Provost 	if (ioctl(pfctl_fd(pfh), DIOCXROLLBACK, &pft) == -1)
16967ecd4f3SMax Laier 		return (-1);
17067ecd4f3SMax Laier 
17167ecd4f3SMax Laier 	return (0);
17267ecd4f3SMax Laier }
17367ecd4f3SMax Laier 
17467ecd4f3SMax Laier void
17567ecd4f3SMax Laier init_filter(char *opt_qname, int opt_verbose)
17667ecd4f3SMax Laier {
177ddd08375SKristof Provost 	struct pfctl_status *status;
17867ecd4f3SMax Laier 
17967ecd4f3SMax Laier 	qname = opt_qname;
18067ecd4f3SMax Laier 
18167ecd4f3SMax Laier 	if (opt_verbose == 1)
18267ecd4f3SMax Laier 		rule_log = PF_LOG;
18367ecd4f3SMax Laier 	else if (opt_verbose == 2)
18467ecd4f3SMax Laier 		rule_log = PF_LOG_ALL;
18567ecd4f3SMax Laier 
186324fd7ecSKristof Provost 	pfh = pfctl_open(PF_DEVICE);
187324fd7ecSKristof Provost 	if (pfh == NULL) {
188324fd7ecSKristof Provost 		syslog(LOG_ERR, "can't pfctl_open()");
189324fd7ecSKristof Provost 		exit(1);
190324fd7ecSKristof Provost 	}
1915824df8dSKristof Provost 	status = pfctl_get_status_h(pfh);
192ddd08375SKristof Provost 	if (status == NULL) {
19367ecd4f3SMax Laier 		syslog(LOG_ERR, "DIOCGETSTATUS");
19467ecd4f3SMax Laier 		exit(1);
19567ecd4f3SMax Laier 	}
196ddd08375SKristof Provost 	if (!status->running) {
19767ecd4f3SMax Laier 		syslog(LOG_ERR, "pf is disabled");
19867ecd4f3SMax Laier 		exit(1);
19967ecd4f3SMax Laier 	}
200ddd08375SKristof Provost 
201ddd08375SKristof Provost 	pfctl_free_status(status);
20267ecd4f3SMax Laier }
20367ecd4f3SMax Laier 
20467ecd4f3SMax Laier int
20567ecd4f3SMax Laier prepare_commit(u_int32_t id)
20667ecd4f3SMax Laier {
20767ecd4f3SMax Laier 	char an[PF_ANCHOR_NAME_SIZE];
20867ecd4f3SMax Laier 	int i;
20967ecd4f3SMax Laier 
21067ecd4f3SMax Laier 	memset(&pft, 0, sizeof pft);
21167ecd4f3SMax Laier 	pft.size = TRANS_SIZE;
21267ecd4f3SMax Laier 	pft.esize = sizeof pfte[0];
21367ecd4f3SMax Laier 	pft.array = pfte;
21467ecd4f3SMax Laier 
21567ecd4f3SMax Laier 	snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR,
21667ecd4f3SMax Laier 	    getpid(), id);
21767ecd4f3SMax Laier 	for (i = 0; i < TRANS_SIZE; i++) {
21867ecd4f3SMax Laier 		memset(&pfte[i], 0, sizeof pfte[0]);
21967ecd4f3SMax Laier 		strlcpy(pfte[i].anchor, an, PF_ANCHOR_NAME_SIZE);
22067ecd4f3SMax Laier 		switch (i) {
22167ecd4f3SMax Laier 		case TRANS_FILTER:
22267ecd4f3SMax Laier 			pfte[i].rs_num = PF_RULESET_FILTER;
22367ecd4f3SMax Laier 			break;
22467ecd4f3SMax Laier 		case TRANS_NAT:
22567ecd4f3SMax Laier 			pfte[i].rs_num = PF_RULESET_NAT;
22667ecd4f3SMax Laier 			break;
22767ecd4f3SMax Laier 		case TRANS_RDR:
22867ecd4f3SMax Laier 			pfte[i].rs_num = PF_RULESET_RDR;
22967ecd4f3SMax Laier 			break;
23067ecd4f3SMax Laier 		default:
23167ecd4f3SMax Laier 			errno = EINVAL;
23267ecd4f3SMax Laier 			return (-1);
23367ecd4f3SMax Laier 		}
23467ecd4f3SMax Laier 	}
23567ecd4f3SMax Laier 
236044243fcSKristof Provost 	if (ioctl(pfctl_fd(pfh), DIOCXBEGIN, &pft) == -1)
23767ecd4f3SMax Laier 		return (-1);
23867ecd4f3SMax Laier 
23967ecd4f3SMax Laier 	return (0);
24067ecd4f3SMax Laier }
24167ecd4f3SMax Laier 
24267ecd4f3SMax Laier int
24367ecd4f3SMax Laier prepare_rule(u_int32_t id, int rs_num, struct sockaddr *src,
24467ecd4f3SMax Laier     struct sockaddr *dst, u_int16_t d_port, u_int8_t proto)
24567ecd4f3SMax Laier {
24667ecd4f3SMax Laier 	char an[PF_ANCHOR_NAME_SIZE];
24767ecd4f3SMax Laier 
24867ecd4f3SMax Laier 	if ((src->sa_family != AF_INET && src->sa_family != AF_INET6) ||
24967ecd4f3SMax Laier 	    (src->sa_family != dst->sa_family)) {
25067ecd4f3SMax Laier 	    	errno = EPROTONOSUPPORT;
25167ecd4f3SMax Laier 		return (-1);
25267ecd4f3SMax Laier 	}
25367ecd4f3SMax Laier 
25467ecd4f3SMax Laier 	memset(&pfp, 0, sizeof pfp);
255e9eb0941SKristof Provost 	memset(&pfrule, 0, sizeof pfrule);
25667ecd4f3SMax Laier 	snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR,
25767ecd4f3SMax Laier 	    getpid(), id);
25867ecd4f3SMax Laier 	strlcpy(pfp.anchor, an, PF_ANCHOR_NAME_SIZE);
259e9eb0941SKristof Provost 	strlcpy(pfanchor, an, PF_ANCHOR_NAME_SIZE);
26067ecd4f3SMax Laier 
26167ecd4f3SMax Laier 	switch (rs_num) {
26267ecd4f3SMax Laier 	case PF_RULESET_FILTER:
263e9eb0941SKristof Provost 		pfticket = pfte[TRANS_FILTER].ticket;
26467ecd4f3SMax Laier 		break;
26567ecd4f3SMax Laier 	case PF_RULESET_NAT:
266e9eb0941SKristof Provost 		pfticket = pfte[TRANS_NAT].ticket;
26767ecd4f3SMax Laier 		break;
26867ecd4f3SMax Laier 	case PF_RULESET_RDR:
269e9eb0941SKristof Provost 		pfticket = pfte[TRANS_RDR].ticket;
27067ecd4f3SMax Laier 		break;
27167ecd4f3SMax Laier 	default:
27267ecd4f3SMax Laier 		errno = EINVAL;
27367ecd4f3SMax Laier 		return (-1);
27467ecd4f3SMax Laier 	}
275ba2a9207SKristof Provost 	if (pfctl_begin_addrs(pfh, &pfp.ticket))
27667ecd4f3SMax Laier 		return (-1);
277e9eb0941SKristof Provost 	pfpool_ticket = pfp.ticket;
27867ecd4f3SMax Laier 
27967ecd4f3SMax Laier 	/* Generic for all rule types. */
280e9eb0941SKristof Provost 	pfrule.af = src->sa_family;
281e9eb0941SKristof Provost 	pfrule.proto = proto;
282e9eb0941SKristof Provost 	pfrule.src.addr.type = PF_ADDR_ADDRMASK;
283e9eb0941SKristof Provost 	pfrule.dst.addr.type = PF_ADDR_ADDRMASK;
28467ecd4f3SMax Laier 	if (src->sa_family == AF_INET) {
285e9eb0941SKristof Provost 		memcpy(&pfrule.src.addr.v.a.addr.v4,
28667ecd4f3SMax Laier 		    &satosin(src)->sin_addr.s_addr, 4);
287e9eb0941SKristof Provost 		memset(&pfrule.src.addr.v.a.mask.addr8, 255, 4);
288e9eb0941SKristof Provost 		memcpy(&pfrule.dst.addr.v.a.addr.v4,
28967ecd4f3SMax Laier 		    &satosin(dst)->sin_addr.s_addr, 4);
290e9eb0941SKristof Provost 		memset(&pfrule.dst.addr.v.a.mask.addr8, 255, 4);
29167ecd4f3SMax Laier 	} else {
292e9eb0941SKristof Provost 		memcpy(&pfrule.src.addr.v.a.addr.v6,
29367ecd4f3SMax Laier 		    &satosin6(src)->sin6_addr.s6_addr, 16);
294e9eb0941SKristof Provost 		memset(&pfrule.src.addr.v.a.mask.addr8, 255, 16);
295e9eb0941SKristof Provost 		memcpy(&pfrule.dst.addr.v.a.addr.v6,
29667ecd4f3SMax Laier 		    &satosin6(dst)->sin6_addr.s6_addr, 16);
297e9eb0941SKristof Provost 		memset(&pfrule.dst.addr.v.a.mask.addr8, 255, 16);
29867ecd4f3SMax Laier 	}
299e9eb0941SKristof Provost 	pfrule.dst.port_op = PF_OP_EQ;
300e9eb0941SKristof Provost 	pfrule.dst.port[0] = htons(d_port);
30167ecd4f3SMax Laier 
30267ecd4f3SMax Laier 	switch (rs_num) {
30367ecd4f3SMax Laier 	case PF_RULESET_FILTER:
30467ecd4f3SMax Laier 		/*
30567ecd4f3SMax Laier 		 * pass quick [log] inet[6] proto tcp \
30667ecd4f3SMax Laier 		 *     from $src to $dst port = $d_port flags S/SAFR keep state
30767ecd4f3SMax Laier 		 *     (max 1) [queue qname]
30867ecd4f3SMax Laier 		 */
309e9eb0941SKristof Provost 		pfrule.action = PF_PASS;
310e9eb0941SKristof Provost 		pfrule.quick = 1;
311e9eb0941SKristof Provost 		pfrule.log = rule_log;
312e9eb0941SKristof Provost 		pfrule.keep_state = 1;
3135ee7cd21SMax Laier #ifdef __FreeBSD__
314e9eb0941SKristof Provost 		pfrule.flags = (proto == IPPROTO_TCP ? TH_SYN : 0);
315e9eb0941SKristof Provost 		pfrule.flagset = (proto == IPPROTO_TCP ?
3165ee7cd21SMax Laier 		    (TH_SYN|TH_ACK|TH_FIN|TH_RST) : 0);
3175ee7cd21SMax Laier #else
318e9eb0941SKristof Provost 		pfrule.flags = (proto == IPPROTO_TCP ? TH_SYN : NULL);
319e9eb0941SKristof Provost 		pfrule.flagset = (proto == IPPROTO_TCP ?
32067ecd4f3SMax Laier 		    (TH_SYN|TH_ACK|TH_FIN|TH_RST) : NULL);
3215ee7cd21SMax Laier #endif
322e9eb0941SKristof Provost 		pfrule.max_states = 1;
32367ecd4f3SMax Laier 		if (qname != NULL)
324e9eb0941SKristof Provost 			strlcpy(pfrule.qname, qname, sizeof pfrule.qname);
32567ecd4f3SMax Laier 		break;
32667ecd4f3SMax Laier 	case PF_RULESET_NAT:
32767ecd4f3SMax Laier 		/*
32867ecd4f3SMax Laier 		 * nat inet[6] proto tcp from $src to $dst port $d_port -> $nat
32967ecd4f3SMax Laier 		 */
330e9eb0941SKristof Provost 		pfrule.action = PF_NAT;
33167ecd4f3SMax Laier 		break;
33267ecd4f3SMax Laier 	case PF_RULESET_RDR:
33367ecd4f3SMax Laier 		/*
33467ecd4f3SMax Laier 		 * rdr inet[6] proto tcp from $src to $dst port $d_port -> $rdr
33567ecd4f3SMax Laier 		 */
336e9eb0941SKristof Provost 		pfrule.action = PF_RDR;
33767ecd4f3SMax Laier 		break;
33867ecd4f3SMax Laier 	default:
33967ecd4f3SMax Laier 		errno = EINVAL;
34067ecd4f3SMax Laier 		return (-1);
34167ecd4f3SMax Laier 	}
34267ecd4f3SMax Laier 
34367ecd4f3SMax Laier 	return (0);
34467ecd4f3SMax Laier }
34567ecd4f3SMax Laier 
34667ecd4f3SMax Laier int
34767ecd4f3SMax Laier server_lookup(struct sockaddr *client, struct sockaddr *proxy,
34867ecd4f3SMax Laier     struct sockaddr *server, u_int8_t proto)
34967ecd4f3SMax Laier {
35067ecd4f3SMax Laier 	if (client->sa_family == AF_INET)
35167ecd4f3SMax Laier 		return (server_lookup4(satosin(client), satosin(proxy),
35267ecd4f3SMax Laier 		    satosin(server), proto));
35367ecd4f3SMax Laier 
35467ecd4f3SMax Laier 	if (client->sa_family == AF_INET6)
35567ecd4f3SMax Laier 		return (server_lookup6(satosin6(client), satosin6(proxy),
35667ecd4f3SMax Laier 		    satosin6(server), proto));
35767ecd4f3SMax Laier 
35867ecd4f3SMax Laier 	errno = EPROTONOSUPPORT;
35967ecd4f3SMax Laier 	return (-1);
36067ecd4f3SMax Laier }
36167ecd4f3SMax Laier 
36267ecd4f3SMax Laier int
36367ecd4f3SMax Laier server_lookup4(struct sockaddr_in *client, struct sockaddr_in *proxy,
36467ecd4f3SMax Laier     struct sockaddr_in *server, u_int8_t proto)
36567ecd4f3SMax Laier {
36671d3c704SKristof Provost 	struct pfctl_natlook_key k = {};
36771d3c704SKristof Provost 	struct pfctl_natlook r = {};
36867ecd4f3SMax Laier 
36971d3c704SKristof Provost 	k.direction = PF_OUT;
37071d3c704SKristof Provost 	k.af = AF_INET;
37171d3c704SKristof Provost 	k.proto = proto;
37271d3c704SKristof Provost 	memcpy(&k.saddr.v4, &client->sin_addr.s_addr, sizeof(k.saddr.v4));
37371d3c704SKristof Provost 	memcpy(&k.daddr.v4, &proxy->sin_addr.s_addr, sizeof(k.daddr.v4));
37471d3c704SKristof Provost 	k.sport = client->sin_port;
37571d3c704SKristof Provost 	k.dport = proxy->sin_port;
37667ecd4f3SMax Laier 
37771d3c704SKristof Provost 	if (pfctl_natlook(pfh, &k, &r))
37867ecd4f3SMax Laier 		return (-1);
37967ecd4f3SMax Laier 
38067ecd4f3SMax Laier 	memset(server, 0, sizeof(struct sockaddr_in));
38167ecd4f3SMax Laier 	server->sin_len = sizeof(struct sockaddr_in);
38267ecd4f3SMax Laier 	server->sin_family = AF_INET;
38371d3c704SKristof Provost 	memcpy(&server->sin_addr.s_addr, &r.daddr.v4,
38467ecd4f3SMax Laier 	    sizeof server->sin_addr.s_addr);
38571d3c704SKristof Provost 	server->sin_port = r.dport;
38667ecd4f3SMax Laier 
38767ecd4f3SMax Laier 	return (0);
38867ecd4f3SMax Laier }
38967ecd4f3SMax Laier 
39067ecd4f3SMax Laier int
39167ecd4f3SMax Laier server_lookup6(struct sockaddr_in6 *client, struct sockaddr_in6 *proxy,
39267ecd4f3SMax Laier     struct sockaddr_in6 *server, u_int8_t proto)
39367ecd4f3SMax Laier {
39471d3c704SKristof Provost 	struct pfctl_natlook_key k = {};
39571d3c704SKristof Provost 	struct pfctl_natlook r = {};
39667ecd4f3SMax Laier 
39771d3c704SKristof Provost 	k.direction = PF_OUT;
39871d3c704SKristof Provost 	k.af = AF_INET6;
39971d3c704SKristof Provost 	k.proto = proto;
40071d3c704SKristof Provost 	memcpy(&k.saddr.v6, &client->sin6_addr.s6_addr, sizeof k.saddr.v6);
40171d3c704SKristof Provost 	memcpy(&k.daddr.v6, &proxy->sin6_addr.s6_addr, sizeof k.daddr.v6);
40271d3c704SKristof Provost 	k.sport = client->sin6_port;
40371d3c704SKristof Provost 	k.dport = proxy->sin6_port;
40467ecd4f3SMax Laier 
40571d3c704SKristof Provost 	if (pfctl_natlook(pfh, &k, &r))
40667ecd4f3SMax Laier 		return (-1);
40767ecd4f3SMax Laier 
40867ecd4f3SMax Laier 	memset(server, 0, sizeof(struct sockaddr_in6));
40967ecd4f3SMax Laier 	server->sin6_len = sizeof(struct sockaddr_in6);
41067ecd4f3SMax Laier 	server->sin6_family = AF_INET6;
41171d3c704SKristof Provost 	memcpy(&server->sin6_addr.s6_addr, &r.daddr.v6,
41267ecd4f3SMax Laier 	    sizeof server->sin6_addr);
41371d3c704SKristof Provost 	server->sin6_port = r.dport;
41467ecd4f3SMax Laier 
41567ecd4f3SMax Laier 	return (0);
41667ecd4f3SMax Laier }
417