1/*	$OpenBSD: filter.c,v 1.1 2005/12/28 19:07:07 jcs Exp $ */
2/*	$FreeBSD: releng/10.3/contrib/pf/tftp-proxy/filter.c 171172 2007-07-03 12:30:03Z mlaier $ */
3
4/*
5 * Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include <syslog.h>
21
22#include <sys/ioctl.h>
23#include <sys/types.h>
24#include <sys/socket.h>
25
26#include <net/if.h>
27#include <net/pfvar.h>
28#include <netinet/in.h>
29#include <netinet/tcp.h>
30#include <arpa/inet.h>
31
32#include <err.h>
33#include <errno.h>
34#include <fcntl.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <unistd.h>
39
40#include "filter.h"
41
42/* From netinet/in.h, but only _KERNEL_ gets them. */
43#define satosin(sa)	((struct sockaddr_in *)(sa))
44#define satosin6(sa)	((struct sockaddr_in6 *)(sa))
45
46enum { TRANS_FILTER = 0, TRANS_NAT, TRANS_RDR, TRANS_SIZE };
47
48int prepare_rule(u_int32_t, int, struct sockaddr *, struct sockaddr *,
49    u_int16_t, u_int8_t);
50int server_lookup4(struct sockaddr_in *, struct sockaddr_in *,
51    struct sockaddr_in *, u_int8_t);
52int server_lookup6(struct sockaddr_in6 *, struct sockaddr_in6 *,
53    struct sockaddr_in6 *, u_int8_t);
54
55static struct pfioc_pooladdr	pfp;
56static struct pfioc_rule	pfr;
57static struct pfioc_trans	pft;
58static struct pfioc_trans_e	pfte[TRANS_SIZE];
59static int dev, rule_log;
60static char *qname;
61
62int
63add_filter(u_int32_t id, u_int8_t dir, struct sockaddr *src,
64    struct sockaddr *dst, u_int16_t d_port, u_int8_t proto)
65{
66	if (!src || !dst || !d_port || !proto) {
67		errno = EINVAL;
68		return (-1);
69	}
70
71	if (prepare_rule(id, PF_RULESET_FILTER, src, dst, d_port, proto) == -1)
72		return (-1);
73
74	pfr.rule.direction = dir;
75	if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
76		return (-1);
77
78	return (0);
79}
80
81int
82add_nat(u_int32_t id, struct sockaddr *src, struct sockaddr *dst,
83    u_int16_t d_port, struct sockaddr *nat, u_int16_t nat_range_low,
84    u_int16_t nat_range_high, u_int8_t proto)
85{
86	if (!src || !dst || !d_port || !nat || !nat_range_low || !proto ||
87	    (src->sa_family != nat->sa_family)) {
88		errno = EINVAL;
89		return (-1);
90	}
91
92	if (prepare_rule(id, PF_RULESET_NAT, src, dst, d_port, proto) == -1)
93		return (-1);
94
95	if (nat->sa_family == AF_INET) {
96		memcpy(&pfp.addr.addr.v.a.addr.v4,
97		    &satosin(nat)->sin_addr.s_addr, 4);
98		memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4);
99	} else {
100		memcpy(&pfp.addr.addr.v.a.addr.v6,
101		    &satosin6(nat)->sin6_addr.s6_addr, 16);
102		memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16);
103	}
104	if (ioctl(dev, DIOCADDADDR, &pfp) == -1)
105		return (-1);
106
107	pfr.rule.rpool.proxy_port[0] = nat_range_low;
108	pfr.rule.rpool.proxy_port[1] = nat_range_high;
109	if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
110		return (-1);
111
112	return (0);
113}
114
115int
116add_rdr(u_int32_t id, struct sockaddr *src, struct sockaddr *dst,
117    u_int16_t d_port, struct sockaddr *rdr, u_int16_t rdr_port, u_int8_t proto)
118{
119	if (!src || !dst || !d_port || !rdr || !rdr_port || !proto ||
120	    (src->sa_family != rdr->sa_family)) {
121		errno = EINVAL;
122		return (-1);
123	}
124
125	if (prepare_rule(id, PF_RULESET_RDR, src, dst, d_port, proto) == -1)
126		return (-1);
127
128	if (rdr->sa_family == AF_INET) {
129		memcpy(&pfp.addr.addr.v.a.addr.v4,
130		    &satosin(rdr)->sin_addr.s_addr, 4);
131		memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4);
132	} else {
133		memcpy(&pfp.addr.addr.v.a.addr.v6,
134		    &satosin6(rdr)->sin6_addr.s6_addr, 16);
135		memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16);
136	}
137	if (ioctl(dev, DIOCADDADDR, &pfp) == -1)
138		return (-1);
139
140	pfr.rule.rpool.proxy_port[0] = rdr_port;
141	if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
142		return (-1);
143
144	return (0);
145}
146
147int
148do_commit(void)
149{
150	if (ioctl(dev, DIOCXCOMMIT, &pft) == -1)
151		return (-1);
152
153	return (0);
154}
155
156int
157do_rollback(void)
158{
159	if (ioctl(dev, DIOCXROLLBACK, &pft) == -1)
160		return (-1);
161
162	return (0);
163}
164
165void
166init_filter(char *opt_qname, int opt_verbose)
167{
168	struct pf_status status;
169
170	qname = opt_qname;
171
172	if (opt_verbose == 1)
173		rule_log = PF_LOG;
174	else if (opt_verbose == 2)
175		rule_log = PF_LOG_ALL;
176
177	dev = open("/dev/pf", O_RDWR);
178	if (dev == -1) {
179		syslog(LOG_ERR, "can't open /dev/pf");
180		exit(1);
181	}
182	if (ioctl(dev, DIOCGETSTATUS, &status) == -1) {
183		syslog(LOG_ERR, "DIOCGETSTATUS");
184		exit(1);
185	}
186	if (!status.running) {
187		syslog(LOG_ERR, "pf is disabled");
188		exit(1);
189	}
190}
191
192int
193prepare_commit(u_int32_t id)
194{
195	char an[PF_ANCHOR_NAME_SIZE];
196	int i;
197
198	memset(&pft, 0, sizeof pft);
199	pft.size = TRANS_SIZE;
200	pft.esize = sizeof pfte[0];
201	pft.array = pfte;
202
203	snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR,
204	    getpid(), id);
205	for (i = 0; i < TRANS_SIZE; i++) {
206		memset(&pfte[i], 0, sizeof pfte[0]);
207		strlcpy(pfte[i].anchor, an, PF_ANCHOR_NAME_SIZE);
208		switch (i) {
209		case TRANS_FILTER:
210			pfte[i].rs_num = PF_RULESET_FILTER;
211			break;
212		case TRANS_NAT:
213			pfte[i].rs_num = PF_RULESET_NAT;
214			break;
215		case TRANS_RDR:
216			pfte[i].rs_num = PF_RULESET_RDR;
217			break;
218		default:
219			errno = EINVAL;
220			return (-1);
221		}
222	}
223
224	if (ioctl(dev, DIOCXBEGIN, &pft) == -1)
225		return (-1);
226
227	return (0);
228}
229
230int
231prepare_rule(u_int32_t id, int rs_num, struct sockaddr *src,
232    struct sockaddr *dst, u_int16_t d_port, u_int8_t proto)
233{
234	char an[PF_ANCHOR_NAME_SIZE];
235
236	if ((src->sa_family != AF_INET && src->sa_family != AF_INET6) ||
237	    (src->sa_family != dst->sa_family)) {
238	    	errno = EPROTONOSUPPORT;
239		return (-1);
240	}
241
242	memset(&pfp, 0, sizeof pfp);
243	memset(&pfr, 0, sizeof pfr);
244	snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR,
245	    getpid(), id);
246	strlcpy(pfp.anchor, an, PF_ANCHOR_NAME_SIZE);
247	strlcpy(pfr.anchor, an, PF_ANCHOR_NAME_SIZE);
248
249	switch (rs_num) {
250	case PF_RULESET_FILTER:
251		pfr.ticket = pfte[TRANS_FILTER].ticket;
252		break;
253	case PF_RULESET_NAT:
254		pfr.ticket = pfte[TRANS_NAT].ticket;
255		break;
256	case PF_RULESET_RDR:
257		pfr.ticket = pfte[TRANS_RDR].ticket;
258		break;
259	default:
260		errno = EINVAL;
261		return (-1);
262	}
263	if (ioctl(dev, DIOCBEGINADDRS, &pfp) == -1)
264		return (-1);
265	pfr.pool_ticket = pfp.ticket;
266
267	/* Generic for all rule types. */
268	pfr.rule.af = src->sa_family;
269	pfr.rule.proto = proto;
270	pfr.rule.src.addr.type = PF_ADDR_ADDRMASK;
271	pfr.rule.dst.addr.type = PF_ADDR_ADDRMASK;
272	if (src->sa_family == AF_INET) {
273		memcpy(&pfr.rule.src.addr.v.a.addr.v4,
274		    &satosin(src)->sin_addr.s_addr, 4);
275		memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 4);
276		memcpy(&pfr.rule.dst.addr.v.a.addr.v4,
277		    &satosin(dst)->sin_addr.s_addr, 4);
278		memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 4);
279	} else {
280		memcpy(&pfr.rule.src.addr.v.a.addr.v6,
281		    &satosin6(src)->sin6_addr.s6_addr, 16);
282		memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 16);
283		memcpy(&pfr.rule.dst.addr.v.a.addr.v6,
284		    &satosin6(dst)->sin6_addr.s6_addr, 16);
285		memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 16);
286	}
287	pfr.rule.dst.port_op = PF_OP_EQ;
288	pfr.rule.dst.port[0] = htons(d_port);
289
290	switch (rs_num) {
291	case PF_RULESET_FILTER:
292		/*
293		 * pass quick [log] inet[6] proto tcp \
294		 *     from $src to $dst port = $d_port flags S/SAFR keep state
295		 *     (max 1) [queue qname]
296		 */
297		pfr.rule.action = PF_PASS;
298		pfr.rule.quick = 1;
299		pfr.rule.log = rule_log;
300		pfr.rule.keep_state = 1;
301#ifdef __FreeBSD__
302		pfr.rule.flags = (proto == IPPROTO_TCP ? TH_SYN : 0);
303		pfr.rule.flagset = (proto == IPPROTO_TCP ?
304		    (TH_SYN|TH_ACK|TH_FIN|TH_RST) : 0);
305#else
306		pfr.rule.flags = (proto == IPPROTO_TCP ? TH_SYN : NULL);
307		pfr.rule.flagset = (proto == IPPROTO_TCP ?
308		    (TH_SYN|TH_ACK|TH_FIN|TH_RST) : NULL);
309#endif
310		pfr.rule.max_states = 1;
311		if (qname != NULL)
312			strlcpy(pfr.rule.qname, qname, sizeof pfr.rule.qname);
313		break;
314	case PF_RULESET_NAT:
315		/*
316		 * nat inet[6] proto tcp from $src to $dst port $d_port -> $nat
317		 */
318		pfr.rule.action = PF_NAT;
319		break;
320	case PF_RULESET_RDR:
321		/*
322		 * rdr inet[6] proto tcp from $src to $dst port $d_port -> $rdr
323		 */
324		pfr.rule.action = PF_RDR;
325		break;
326	default:
327		errno = EINVAL;
328		return (-1);
329	}
330
331	return (0);
332}
333
334int
335server_lookup(struct sockaddr *client, struct sockaddr *proxy,
336    struct sockaddr *server, u_int8_t proto)
337{
338	if (client->sa_family == AF_INET)
339		return (server_lookup4(satosin(client), satosin(proxy),
340		    satosin(server), proto));
341
342	if (client->sa_family == AF_INET6)
343		return (server_lookup6(satosin6(client), satosin6(proxy),
344		    satosin6(server), proto));
345
346	errno = EPROTONOSUPPORT;
347	return (-1);
348}
349
350int
351server_lookup4(struct sockaddr_in *client, struct sockaddr_in *proxy,
352    struct sockaddr_in *server, u_int8_t proto)
353{
354	struct pfioc_natlook pnl;
355
356	memset(&pnl, 0, sizeof pnl);
357	pnl.direction = PF_OUT;
358	pnl.af = AF_INET;
359	pnl.proto = proto;
360	memcpy(&pnl.saddr.v4, &client->sin_addr.s_addr, sizeof pnl.saddr.v4);
361	memcpy(&pnl.daddr.v4, &proxy->sin_addr.s_addr, sizeof pnl.daddr.v4);
362	pnl.sport = client->sin_port;
363	pnl.dport = proxy->sin_port;
364
365	if (ioctl(dev, DIOCNATLOOK, &pnl) == -1)
366		return (-1);
367
368	memset(server, 0, sizeof(struct sockaddr_in));
369	server->sin_len = sizeof(struct sockaddr_in);
370	server->sin_family = AF_INET;
371	memcpy(&server->sin_addr.s_addr, &pnl.rdaddr.v4,
372	    sizeof server->sin_addr.s_addr);
373	server->sin_port = pnl.rdport;
374
375	return (0);
376}
377
378int
379server_lookup6(struct sockaddr_in6 *client, struct sockaddr_in6 *proxy,
380    struct sockaddr_in6 *server, u_int8_t proto)
381{
382	struct pfioc_natlook pnl;
383
384	memset(&pnl, 0, sizeof pnl);
385	pnl.direction = PF_OUT;
386	pnl.af = AF_INET6;
387	pnl.proto = proto;
388	memcpy(&pnl.saddr.v6, &client->sin6_addr.s6_addr, sizeof pnl.saddr.v6);
389	memcpy(&pnl.daddr.v6, &proxy->sin6_addr.s6_addr, sizeof pnl.daddr.v6);
390	pnl.sport = client->sin6_port;
391	pnl.dport = proxy->sin6_port;
392
393	if (ioctl(dev, DIOCNATLOOK, &pnl) == -1)
394		return (-1);
395
396	memset(server, 0, sizeof(struct sockaddr_in6));
397	server->sin6_len = sizeof(struct sockaddr_in6);
398	server->sin6_family = AF_INET6;
399	memcpy(&server->sin6_addr.s6_addr, &pnl.rdaddr.v6,
400	    sizeof server->sin6_addr);
401	server->sin6_port = pnl.rdport;
402
403	return (0);
404}
405