1248590Smm/*	$OpenBSD: filter.c,v 1.8 2008/06/13 07:25:26 claudio Exp $ */
2248590Smm
3248590Smm/*
4248590Smm * Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
5248590Smm *
6248590Smm * Permission to use, copy, modify, and distribute this software for any
7248590Smm * purpose with or without fee is hereby granted, provided that the above
8248590Smm * copyright notice and this permission notice appear in all copies.
9248590Smm *
10248590Smm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11248590Smm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12248590Smm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13248590Smm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14248590Smm * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15248590Smm * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16248590Smm * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17248590Smm */
18248590Smm
19248590Smm#include <sys/ioctl.h>
20248590Smm#include <sys/types.h>
21248590Smm#include <sys/socket.h>
22248590Smm
23248590Smm#include <net/if.h>
24248590Smm#include <net/pfvar.h>
25248590Smm#include <netinet/in.h>
26248590Smm#include <netinet/tcp.h>
27248590Smm#include <arpa/inet.h>
28248590Smm
29248590Smm#include <err.h>
30248590Smm#include <errno.h>
31248590Smm#include <fcntl.h>
32248590Smm#include <stdio.h>
33248590Smm#include <string.h>
34248590Smm#include <unistd.h>
35248590Smm
36248590Smm#include "filter.h"
37248590Smm
38248590Smm/* From netinet/in.h, but only _KERNEL_ gets them. */
39248590Smm#define satosin(sa)	((struct sockaddr_in *)(sa))
40248590Smm#define satosin6(sa)	((struct sockaddr_in6 *)(sa))
41248590Smm
42248590Smmenum { TRANS_FILTER = 0, TRANS_NAT, TRANS_RDR, TRANS_SIZE };
43248590Smm
44248590Smmint prepare_rule(u_int32_t, int, struct sockaddr *, struct sockaddr *,
45248590Smm    u_int16_t);
46248590Smmint server_lookup4(struct sockaddr_in *, struct sockaddr_in *,
47248590Smm    struct sockaddr_in *);
48248590Smmint server_lookup6(struct sockaddr_in6 *, struct sockaddr_in6 *,
49248590Smm    struct sockaddr_in6 *);
50248590Smm
51248590Smmstatic struct pfioc_pooladdr	pfp;
52248590Smmstatic struct pfioc_rule	pfr;
53248590Smmstatic struct pfioc_trans	pft;
54248590Smmstatic struct pfioc_trans_e	pfte[TRANS_SIZE];
55248590Smmstatic int dev, rule_log;
56248590Smmstatic const char *qname, *tagname;
57248590Smm
58248590Smmint
59248590Smmadd_filter(u_int32_t id, u_int8_t dir, struct sockaddr *src,
60248590Smm    struct sockaddr *dst, u_int16_t d_port)
61248590Smm{
62248590Smm	if (!src || !dst || !d_port) {
63248590Smm		errno = EINVAL;
64248590Smm		return (-1);
65248590Smm	}
66248590Smm
67248590Smm	if (prepare_rule(id, PF_RULESET_FILTER, src, dst, d_port) == -1)
68248590Smm		return (-1);
69248590Smm
70248590Smm	pfr.rule.direction = dir;
71248590Smm	if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
72248590Smm		return (-1);
73248590Smm
74248590Smm	return (0);
75248590Smm}
76248590Smm
77248590Smmint
78248590Smmadd_nat(u_int32_t id, struct sockaddr *src, struct sockaddr *dst,
79248590Smm    u_int16_t d_port, struct sockaddr *nat, u_int16_t nat_range_low,
80248590Smm    u_int16_t nat_range_high)
81248590Smm{
82248590Smm	if (!src || !dst || !d_port || !nat || !nat_range_low ||
83248590Smm	    (src->sa_family != nat->sa_family)) {
84248590Smm		errno = EINVAL;
85248590Smm		return (-1);
86248590Smm	}
87248590Smm
88302001Smm	if (prepare_rule(id, PF_RULESET_NAT, src, dst, d_port) == -1)
89248590Smm		return (-1);
90248590Smm
91248590Smm	if (nat->sa_family == AF_INET) {
92248590Smm		memcpy(&pfp.addr.addr.v.a.addr.v4,
93248590Smm		    &satosin(nat)->sin_addr.s_addr, 4);
94248590Smm		memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4);
95248590Smm	} else {
96248590Smm		memcpy(&pfp.addr.addr.v.a.addr.v6,
97248590Smm		    &satosin6(nat)->sin6_addr.s6_addr, 16);
98248590Smm		memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16);
99248590Smm	}
100248590Smm	if (ioctl(dev, DIOCADDADDR, &pfp) == -1)
101248590Smm		return (-1);
102248590Smm
103248590Smm	pfr.rule.rpool.proxy_port[0] = nat_range_low;
104248590Smm	pfr.rule.rpool.proxy_port[1] = nat_range_high;
105248590Smm	if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
106248590Smm		return (-1);
107248590Smm
108248590Smm	return (0);
109248590Smm}
110248590Smm
111248590Smmint
112248590Smmadd_rdr(u_int32_t id, struct sockaddr *src, struct sockaddr *dst,
113248590Smm    u_int16_t d_port, struct sockaddr *rdr, u_int16_t rdr_port)
114248590Smm{
115248590Smm	if (!src || !dst || !d_port || !rdr || !rdr_port ||
116248590Smm	    (src->sa_family != rdr->sa_family)) {
117248590Smm		errno = EINVAL;
118248590Smm		return (-1);
119248590Smm	}
120248590Smm
121248590Smm	if (prepare_rule(id, PF_RULESET_RDR, src, dst, d_port) == -1)
122248590Smm		return (-1);
123248590Smm
124248590Smm	if (rdr->sa_family == AF_INET) {
125248590Smm		memcpy(&pfp.addr.addr.v.a.addr.v4,
126248590Smm		    &satosin(rdr)->sin_addr.s_addr, 4);
127248590Smm		memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4);
128248590Smm	} else {
129248590Smm		memcpy(&pfp.addr.addr.v.a.addr.v6,
130248590Smm		    &satosin6(rdr)->sin6_addr.s6_addr, 16);
131248590Smm		memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16);
132248590Smm	}
133248590Smm	if (ioctl(dev, DIOCADDADDR, &pfp) == -1)
134248590Smm		return (-1);
135248590Smm
136248590Smm	pfr.rule.rpool.proxy_port[0] = rdr_port;
137248590Smm	if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
138248590Smm		return (-1);
139248590Smm
140248590Smm	return (0);
141248590Smm}
142248590Smm
143248590Smmint
144248590Smmdo_commit(void)
145248590Smm{
146248590Smm	if (ioctl(dev, DIOCXCOMMIT, &pft) == -1)
147248590Smm		return (-1);
148248590Smm
149248590Smm	return (0);
150248590Smm}
151248590Smm
152248590Smmint
153248590Smmdo_rollback(void)
154248590Smm{
155248590Smm	if (ioctl(dev, DIOCXROLLBACK, &pft) == -1)
156248590Smm		return (-1);
157248590Smm
158248590Smm	return (0);
159248590Smm}
160248590Smm
161248590Smmvoid
162248590Smminit_filter(const char *opt_qname, const char *opt_tagname, int opt_verbose)
163248590Smm{
164248590Smm	struct pf_status status;
165248590Smm
166248590Smm	qname = opt_qname;
167248590Smm	tagname = opt_tagname;
168248590Smm
169248590Smm	if (opt_verbose == 1)
170248590Smm		rule_log = PF_LOG;
171248590Smm	else if (opt_verbose == 2)
172248590Smm		rule_log = PF_LOG_ALL;
173248590Smm
174248590Smm	dev = open("/dev/pf", O_RDWR);
175248590Smm	if (dev == -1)
176302001Smm		err(1, "open /dev/pf");
177248590Smm	if (ioctl(dev, DIOCGETSTATUS, &status) == -1)
178248590Smm		err(1, "DIOCGETSTATUS");
179248590Smm	if (!status.running)
180248590Smm		errx(1, "pf is disabled");
181248590Smm}
182248590Smm
183248590Smmint
184248590Smmprepare_commit(u_int32_t id)
185248590Smm{
186248590Smm	char an[PF_ANCHOR_NAME_SIZE];
187248590Smm	int i;
188248590Smm
189248590Smm	memset(&pft, 0, sizeof pft);
190248590Smm	pft.size = TRANS_SIZE;
191248590Smm	pft.esize = sizeof pfte[0];
192248590Smm	pft.array = pfte;
193248590Smm
194248590Smm	snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR,
195248590Smm	    getpid(), id);
196248590Smm	for (i = 0; i < TRANS_SIZE; i++) {
197248590Smm		memset(&pfte[i], 0, sizeof pfte[0]);
198248590Smm		strlcpy(pfte[i].anchor, an, PF_ANCHOR_NAME_SIZE);
199248590Smm		switch (i) {
200248590Smm		case TRANS_FILTER:
201248590Smm			pfte[i].rs_num = PF_RULESET_FILTER;
202248590Smm			break;
203248590Smm		case TRANS_NAT:
204248590Smm			pfte[i].rs_num = PF_RULESET_NAT;
205248590Smm			break;
206248590Smm		case TRANS_RDR:
207248590Smm			pfte[i].rs_num = PF_RULESET_RDR;
208248590Smm			break;
209248590Smm		default:
210248590Smm			errno = EINVAL;
211248590Smm			return (-1);
212248590Smm		}
213248590Smm	}
214248590Smm
215248590Smm	if (ioctl(dev, DIOCXBEGIN, &pft) == -1)
216248590Smm		return (-1);
217248590Smm
218248590Smm	return (0);
219248590Smm}
220248590Smm
221248590Smmint
222248590Smmprepare_rule(u_int32_t id, int rs_num, struct sockaddr *src,
223248590Smm    struct sockaddr *dst, u_int16_t d_port)
224248590Smm{
225248590Smm	char an[PF_ANCHOR_NAME_SIZE];
226248590Smm
227248590Smm	if ((src->sa_family != AF_INET && src->sa_family != AF_INET6) ||
228248590Smm	    (src->sa_family != dst->sa_family)) {
229248590Smm	    	errno = EPROTONOSUPPORT;
230248590Smm		return (-1);
231248590Smm	}
232248590Smm
233248590Smm	memset(&pfp, 0, sizeof pfp);
234248590Smm	memset(&pfr, 0, sizeof pfr);
235248590Smm	snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR,
236248590Smm	    getpid(), id);
237248590Smm	strlcpy(pfp.anchor, an, PF_ANCHOR_NAME_SIZE);
238248590Smm	strlcpy(pfr.anchor, an, PF_ANCHOR_NAME_SIZE);
239248590Smm
240248590Smm	switch (rs_num) {
241248590Smm	case PF_RULESET_FILTER:
242248590Smm		pfr.ticket = pfte[TRANS_FILTER].ticket;
243248590Smm		break;
244248590Smm	case PF_RULESET_NAT:
245248590Smm		pfr.ticket = pfte[TRANS_NAT].ticket;
246248590Smm		break;
247248590Smm	case PF_RULESET_RDR:
248248590Smm		pfr.ticket = pfte[TRANS_RDR].ticket;
249248590Smm		break;
250248590Smm	default:
251248590Smm		errno = EINVAL;
252248590Smm		return (-1);
253248590Smm	}
254248590Smm	if (ioctl(dev, DIOCBEGINADDRS, &pfp) == -1)
255248590Smm		return (-1);
256248590Smm	pfr.pool_ticket = pfp.ticket;
257248590Smm
258248590Smm	/* Generic for all rule types. */
259248590Smm	pfr.rule.af = src->sa_family;
260248590Smm	pfr.rule.proto = IPPROTO_TCP;
261248590Smm	pfr.rule.src.addr.type = PF_ADDR_ADDRMASK;
262248590Smm	pfr.rule.dst.addr.type = PF_ADDR_ADDRMASK;
263248590Smm	if (src->sa_family == AF_INET) {
264248590Smm		memcpy(&pfr.rule.src.addr.v.a.addr.v4,
265248590Smm		    &satosin(src)->sin_addr.s_addr, 4);
266248590Smm		memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 4);
267248590Smm		memcpy(&pfr.rule.dst.addr.v.a.addr.v4,
268248590Smm		    &satosin(dst)->sin_addr.s_addr, 4);
269248590Smm		memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 4);
270248590Smm	} else {
271248590Smm		memcpy(&pfr.rule.src.addr.v.a.addr.v6,
272248590Smm		    &satosin6(src)->sin6_addr.s6_addr, 16);
273248590Smm		memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 16);
274248590Smm		memcpy(&pfr.rule.dst.addr.v.a.addr.v6,
275248590Smm		    &satosin6(dst)->sin6_addr.s6_addr, 16);
276248590Smm		memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 16);
277248590Smm	}
278248590Smm	pfr.rule.dst.port_op = PF_OP_EQ;
279248590Smm	pfr.rule.dst.port[0] = htons(d_port);
280248590Smm
281248590Smm	switch (rs_num) {
282248590Smm	case PF_RULESET_FILTER:
283248590Smm		/*
284248590Smm		 * pass [quick] [log] inet[6] proto tcp \
285248590Smm		 *     from $src to $dst port = $d_port flags S/SA keep state
286248590Smm		 *     (max 1) [queue qname] [tag tagname]
287248590Smm		 */
288248590Smm		pfr.rule.action = PF_PASS;
289248590Smm		pfr.rule.quick = 1;
290248590Smm		pfr.rule.log = rule_log;
291248590Smm		pfr.rule.keep_state = 1;
292248590Smm		pfr.rule.flags = TH_SYN;
293248590Smm		pfr.rule.flagset = (TH_SYN|TH_ACK);
294248590Smm		pfr.rule.max_states = 1;
295248590Smm		if (qname != NULL)
296248590Smm			strlcpy(pfr.rule.qname, qname, sizeof pfr.rule.qname);
297248590Smm		if (tagname != NULL) {
298248590Smm			pfr.rule.quick = 0;
299248590Smm			strlcpy(pfr.rule.tagname, tagname,
300248590Smm                                sizeof pfr.rule.tagname);
301248590Smm		}
302248590Smm		break;
303248590Smm	case PF_RULESET_NAT:
304248590Smm		/*
305248590Smm		 * nat inet[6] proto tcp from $src to $dst port $d_port -> $nat
306248590Smm		 */
307248590Smm		pfr.rule.action = PF_NAT;
308248590Smm		break;
309248590Smm	case PF_RULESET_RDR:
310248590Smm		/*
311248590Smm		 * rdr inet[6] proto tcp from $src to $dst port $d_port -> $rdr
312248590Smm		 */
313248590Smm		pfr.rule.action = PF_RDR;
314248590Smm		break;
315248590Smm	default:
316248590Smm		errno = EINVAL;
317248590Smm		return (-1);
318248590Smm	}
319248590Smm
320248590Smm	return (0);
321248590Smm}
322248590Smm
323248590Smmint
324248590Smmserver_lookup(struct sockaddr *client, struct sockaddr *proxy,
325248590Smm    struct sockaddr *server)
326248590Smm{
327248590Smm	if (client->sa_family == AF_INET)
328248590Smm		return (server_lookup4(satosin(client), satosin(proxy),
329248590Smm		    satosin(server)));
330248590Smm
331248590Smm	if (client->sa_family == AF_INET6)
332248590Smm		return (server_lookup6(satosin6(client), satosin6(proxy),
333248590Smm		    satosin6(server)));
334248590Smm
335248590Smm	errno = EPROTONOSUPPORT;
336248590Smm	return (-1);
337248590Smm}
338248590Smm
339248590Smmint
340248590Smmserver_lookup4(struct sockaddr_in *client, struct sockaddr_in *proxy,
341248590Smm    struct sockaddr_in *server)
342248590Smm{
343248590Smm	struct pfioc_natlook pnl;
344248590Smm
345248590Smm	memset(&pnl, 0, sizeof pnl);
346248590Smm	pnl.direction = PF_OUT;
347248590Smm	pnl.af = AF_INET;
348248590Smm	pnl.proto = IPPROTO_TCP;
349248590Smm	memcpy(&pnl.saddr.v4, &client->sin_addr.s_addr, sizeof pnl.saddr.v4);
350248590Smm	memcpy(&pnl.daddr.v4, &proxy->sin_addr.s_addr, sizeof pnl.daddr.v4);
351248590Smm	pnl.sport = client->sin_port;
352248590Smm	pnl.dport = proxy->sin_port;
353248590Smm
354248590Smm	if (ioctl(dev, DIOCNATLOOK, &pnl) == -1)
355248590Smm		return (-1);
356248590Smm
357248590Smm	memset(server, 0, sizeof(struct sockaddr_in));
358248590Smm	server->sin_len = sizeof(struct sockaddr_in);
359248590Smm	server->sin_family = AF_INET;
360248590Smm	memcpy(&server->sin_addr.s_addr, &pnl.rdaddr.v4,
361248590Smm	    sizeof server->sin_addr.s_addr);
362248590Smm	server->sin_port = pnl.rdport;
363248590Smm
364248590Smm	return (0);
365248590Smm}
366248590Smm
367248590Smmint
368248590Smmserver_lookup6(struct sockaddr_in6 *client, struct sockaddr_in6 *proxy,
369248590Smm    struct sockaddr_in6 *server)
370248590Smm{
371248590Smm	struct pfioc_natlook pnl;
372248590Smm
373248590Smm	memset(&pnl, 0, sizeof pnl);
374248590Smm	pnl.direction = PF_OUT;
375248590Smm	pnl.af = AF_INET6;
376248590Smm	pnl.proto = IPPROTO_TCP;
377248590Smm	memcpy(&pnl.saddr.v6, &client->sin6_addr.s6_addr, sizeof pnl.saddr.v6);
378248590Smm	memcpy(&pnl.daddr.v6, &proxy->sin6_addr.s6_addr, sizeof pnl.daddr.v6);
379248590Smm	pnl.sport = client->sin6_port;
380248590Smm	pnl.dport = proxy->sin6_port;
381248590Smm
382248590Smm	if (ioctl(dev, DIOCNATLOOK, &pnl) == -1)
383248590Smm		return (-1);
384248590Smm
385248590Smm	memset(server, 0, sizeof(struct sockaddr_in6));
386248590Smm	server->sin6_len = sizeof(struct sockaddr_in6);
387248590Smm	server->sin6_family = AF_INET6;
388248590Smm	memcpy(&server->sin6_addr.s6_addr, &pnl.rdaddr.v6,
389248590Smm	    sizeof server->sin6_addr);
390248590Smm	server->sin6_port = pnl.rdport;
391248590Smm
392248590Smm	return (0);
393248590Smm}
394248590Smm