xref: /openbsd-src/usr.sbin/relayd/pfe_filter.c (revision 7c726e761b00f31b5ae94bae8bc5eee71c7cebd0)
1*7c726e76Ssashan /*	$OpenBSD: pfe_filter.c,v 1.66 2024/06/17 08:02:57 sashan Exp $	*/
2feb9ff76Sreyk 
3feb9ff76Sreyk /*
436f5dc5eSpyr  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
5feb9ff76Sreyk  *
6feb9ff76Sreyk  * Permission to use, copy, modify, and distribute this software for any
7feb9ff76Sreyk  * purpose with or without fee is hereby granted, provided that the above
8feb9ff76Sreyk  * copyright notice and this permission notice appear in all copies.
9feb9ff76Sreyk  *
10feb9ff76Sreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11feb9ff76Sreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12feb9ff76Sreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13feb9ff76Sreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14feb9ff76Sreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15feb9ff76Sreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16feb9ff76Sreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17feb9ff76Sreyk  */
18feb9ff76Sreyk 
19feb9ff76Sreyk #include <sys/types.h>
20feb9ff76Sreyk #include <sys/queue.h>
21feb9ff76Sreyk #include <sys/socket.h>
22f04ff968Sreyk #include <sys/time.h>
23feb9ff76Sreyk #include <sys/ioctl.h>
24feb9ff76Sreyk 
25f04ff968Sreyk #include <net/if.h>
26feb9ff76Sreyk #include <netinet/in.h>
27d006fa44Sreyk #include <netinet/tcp.h>
28feb9ff76Sreyk #include <arpa/inet.h>
2968928c43Sderaadt #include <net/pfvar.h>
30feb9ff76Sreyk 
31feb9ff76Sreyk #include <limits.h>
32feb9ff76Sreyk #include <string.h>
33feb9ff76Sreyk #include <stdio.h>
34feb9ff76Sreyk #include <stdlib.h>
35a2195becSreyk #include <unistd.h>
36feb9ff76Sreyk #include <errno.h>
37feb9ff76Sreyk 
38e2318a52Sderaadt #define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
39e2318a52Sderaadt 
40748ceb64Sreyk #include "relayd.h"
41feb9ff76Sreyk 
42748ceb64Sreyk int	 transaction_init(struct relayd *, const char *);
43748ceb64Sreyk int	 transaction_commit(struct relayd *);
44748ceb64Sreyk void	 kill_tables(struct relayd *);
45ff37295aSreyk int	 kill_srcnodes(struct relayd *, struct table *);
46feb9ff76Sreyk 
47feb9ff76Sreyk void
init_tables(struct relayd * env)48748ceb64Sreyk init_tables(struct relayd *env)
49feb9ff76Sreyk {
50feb9ff76Sreyk 	int			 i;
519591a9f7Spyr 	struct rdr		*rdr;
52feb9ff76Sreyk 	struct pfr_table	*tables;
53feb9ff76Sreyk 	struct pfioc_table	 io;
54feb9ff76Sreyk 
55586b5f8aSreyk 	if (!(env->sc_conf.flags & F_NEEDPF))
56f3485f87Spyr 		return;
57f3485f87Spyr 
5835d10c30Sreyk 	if ((tables = calloc(env->sc_rdrcount, sizeof(*tables))) == NULL)
59feb9ff76Sreyk 		fatal("calloc");
60feb9ff76Sreyk 	i = 0;
61feb9ff76Sreyk 
6235d10c30Sreyk 	TAILQ_FOREACH(rdr, env->sc_rdrs, entry) {
63748ceb64Sreyk 		if (strlcpy(tables[i].pfrt_anchor, RELAYD_ANCHOR "/",
64a82f8bfbSreyk 		    sizeof(tables[i].pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE)
65a82f8bfbSreyk 			goto toolong;
669591a9f7Spyr 		if (strlcat(tables[i].pfrt_anchor, rdr->conf.name,
67a82f8bfbSreyk 		    sizeof(tables[i].pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE)
68a82f8bfbSreyk 			goto toolong;
699591a9f7Spyr 		if (strlcpy(tables[i].pfrt_name, rdr->conf.name,
70a82f8bfbSreyk 		    sizeof(tables[i].pfrt_name)) >=
71a82f8bfbSreyk 		    sizeof(tables[i].pfrt_name))
72a82f8bfbSreyk 			goto toolong;
73feb9ff76Sreyk 		tables[i].pfrt_flags |= PFR_TFLAG_PERSIST;
74feb9ff76Sreyk 		i++;
75feb9ff76Sreyk 	}
7635d10c30Sreyk 	if (i != env->sc_rdrcount)
77efc39811Sbenno 		fatalx("%s: table count modified", __func__);
78feb9ff76Sreyk 
79feb9ff76Sreyk 	memset(&io, 0, sizeof(io));
8035d10c30Sreyk 	io.pfrio_size = env->sc_rdrcount;
81feb9ff76Sreyk 	io.pfrio_esize = sizeof(*tables);
82feb9ff76Sreyk 	io.pfrio_buffer = tables;
83feb9ff76Sreyk 
8435d10c30Sreyk 	if (ioctl(env->sc_pf->dev, DIOCRADDTABLES, &io) == -1)
85efc39811Sbenno 		fatal("%s: cannot create tables", __func__);
8685a8c65fSreyk 	log_debug("%s: created %d tables", __func__, io.pfrio_nadd);
87feb9ff76Sreyk 
880d33279aSreyk 	free(tables);
890d33279aSreyk 
9035d10c30Sreyk 	if (io.pfrio_nadd == env->sc_rdrcount)
91feb9ff76Sreyk 		return;
92feb9ff76Sreyk 
93feb9ff76Sreyk 	/*
94feb9ff76Sreyk 	 * clear all tables, since some already existed
95feb9ff76Sreyk 	 */
9635d10c30Sreyk 	TAILQ_FOREACH(rdr, env->sc_rdrs, entry)
979591a9f7Spyr 		flush_table(env, rdr);
98a82f8bfbSreyk 
99a82f8bfbSreyk 	return;
100a82f8bfbSreyk 
101a82f8bfbSreyk  toolong:
102efc39811Sbenno 	fatal("%s: name too long", __func__);
103feb9ff76Sreyk }
104feb9ff76Sreyk 
105feb9ff76Sreyk void
kill_tables(struct relayd * env)106fb5ec8acSreyk kill_tables(struct relayd *env)
107fb5ec8acSreyk {
108feb9ff76Sreyk 	struct pfioc_table	 io;
1099591a9f7Spyr 	struct rdr		*rdr;
110fb5ec8acSreyk 	int			 cnt = 0;
111feb9ff76Sreyk 
112586b5f8aSreyk 	if (!(env->sc_conf.flags & F_NEEDPF))
113f3485f87Spyr 		return;
114f3485f87Spyr 
11535d10c30Sreyk 	TAILQ_FOREACH(rdr, env->sc_rdrs, entry) {
116fb5ec8acSreyk 		memset(&io, 0, sizeof(io));
117748ceb64Sreyk 		if (strlcpy(io.pfrio_table.pfrt_anchor, RELAYD_ANCHOR "/",
118a82f8bfbSreyk 		    sizeof(io.pfrio_table.pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE)
119a82f8bfbSreyk 			goto toolong;
1209591a9f7Spyr 		if (strlcat(io.pfrio_table.pfrt_anchor, rdr->conf.name,
121a82f8bfbSreyk 		    sizeof(io.pfrio_table.pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE)
122a82f8bfbSreyk 			goto toolong;
12335d10c30Sreyk 		if (ioctl(env->sc_pf->dev, DIOCRCLRTABLES, &io) == -1)
124efc39811Sbenno 			fatal("%s: ioctl failed", __func__);
125fb5ec8acSreyk 		cnt += io.pfrio_ndel;
126feb9ff76Sreyk 	}
12785a8c65fSreyk 	log_debug("%s: deleted %d tables", __func__, cnt);
128a82f8bfbSreyk 	return;
129a82f8bfbSreyk 
130a82f8bfbSreyk  toolong:
131efc39811Sbenno 	fatal("%s: name too long", __func__);
132feb9ff76Sreyk }
133feb9ff76Sreyk 
134feb9ff76Sreyk void
sync_table(struct relayd * env,struct rdr * rdr,struct table * table)1359591a9f7Spyr sync_table(struct relayd *env, struct rdr *rdr, struct table *table)
136feb9ff76Sreyk {
137ff37295aSreyk 	int			 i, cnt = 0;
138feb9ff76Sreyk 	struct pfioc_table	 io;
139feb9ff76Sreyk 	struct pfr_addr		*addlist;
140feb9ff76Sreyk 	struct sockaddr_in	*sain;
141feb9ff76Sreyk 	struct sockaddr_in6	*sain6;
142feb9ff76Sreyk 	struct host		*host;
143feb9ff76Sreyk 
144586b5f8aSreyk 	if (!(env->sc_conf.flags & F_NEEDPF))
145f3485f87Spyr 		return;
146f3485f87Spyr 
147feb9ff76Sreyk 	if (table == NULL)
148feb9ff76Sreyk 		return;
149feb9ff76Sreyk 
150feb9ff76Sreyk 	if (table->up == 0) {
1519591a9f7Spyr 		flush_table(env, rdr);
152feb9ff76Sreyk 		return;
153feb9ff76Sreyk 	}
154feb9ff76Sreyk 
155feb9ff76Sreyk 	if ((addlist = calloc(table->up, sizeof(*addlist))) == NULL)
156feb9ff76Sreyk 		fatal("calloc");
157feb9ff76Sreyk 
158feb9ff76Sreyk 	memset(&io, 0, sizeof(io));
159feb9ff76Sreyk 	io.pfrio_esize = sizeof(struct pfr_addr);
160feb9ff76Sreyk 	io.pfrio_size = table->up;
161feb9ff76Sreyk 	io.pfrio_size2 = 0;
162feb9ff76Sreyk 	io.pfrio_buffer = addlist;
163748ceb64Sreyk 	if (strlcpy(io.pfrio_table.pfrt_anchor, RELAYD_ANCHOR "/",
164a82f8bfbSreyk 	    sizeof(io.pfrio_table.pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE)
165a82f8bfbSreyk 		goto toolong;
1669591a9f7Spyr 	if (strlcat(io.pfrio_table.pfrt_anchor, rdr->conf.name,
167a82f8bfbSreyk 	    sizeof(io.pfrio_table.pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE)
168a82f8bfbSreyk 		goto toolong;
1699591a9f7Spyr 	if (strlcpy(io.pfrio_table.pfrt_name, rdr->conf.name,
170a82f8bfbSreyk 	    sizeof(io.pfrio_table.pfrt_name)) >=
171a82f8bfbSreyk 	    sizeof(io.pfrio_table.pfrt_name))
172a82f8bfbSreyk 		goto toolong;
173feb9ff76Sreyk 
174feb9ff76Sreyk 	i = 0;
175feb9ff76Sreyk 	TAILQ_FOREACH(host, &table->hosts, entry) {
176d57a63abSreyk 		if (host->up != HOST_UP)
177feb9ff76Sreyk 			continue;
178feb9ff76Sreyk 		memset(&(addlist[i]), 0, sizeof(addlist[i]));
17968b79041Spyr 		switch (host->conf.ss.ss_family) {
180feb9ff76Sreyk 		case AF_INET:
18168b79041Spyr 			sain = (struct sockaddr_in *)&host->conf.ss;
182feb9ff76Sreyk 			addlist[i].pfra_af = AF_INET;
183feb9ff76Sreyk 			memcpy(&(addlist[i].pfra_ip4addr), &sain->sin_addr,
184feb9ff76Sreyk 			    sizeof(sain->sin_addr));
185feb9ff76Sreyk 			addlist[i].pfra_net = 32;
186feb9ff76Sreyk 			break;
187feb9ff76Sreyk 		case AF_INET6:
18868b79041Spyr 			sain6 = (struct sockaddr_in6 *)&host->conf.ss;
189feb9ff76Sreyk 			addlist[i].pfra_af = AF_INET6;
190feb9ff76Sreyk 			memcpy(&(addlist[i].pfra_ip6addr), &sain6->sin6_addr,
191feb9ff76Sreyk 			    sizeof(sain6->sin6_addr));
192feb9ff76Sreyk 			addlist[i].pfra_net = 128;
193feb9ff76Sreyk 			break;
194feb9ff76Sreyk 		default:
195efc39811Sbenno 			fatalx("%s: unknown address family", __func__);
196feb9ff76Sreyk 			break;
197feb9ff76Sreyk 		}
198feb9ff76Sreyk 		i++;
199feb9ff76Sreyk 	}
200feb9ff76Sreyk 	if (i != table->up)
201efc39811Sbenno 		fatalx("%s: desynchronized", __func__);
202feb9ff76Sreyk 
20335d10c30Sreyk 	if (ioctl(env->sc_pf->dev, DIOCRSETADDRS, &io) == -1)
204efc39811Sbenno 		fatal("%s: cannot set address list", __func__);
205ff37295aSreyk 	if (rdr->conf.flags & F_STICKY)
206ff37295aSreyk 		cnt = kill_srcnodes(env, table);
2070d33279aSreyk 	free(addlist);
2080d33279aSreyk 
209586b5f8aSreyk 	if (env->sc_conf.opts & RELAYD_OPT_LOGUPDATE)
210fb43c767Sreyk 		log_info("table %s: %d added, %d deleted, "
211ff37295aSreyk 		    "%d changed, %d killed", io.pfrio_table.pfrt_name,
212ff37295aSreyk 		    io.pfrio_nadd, io.pfrio_ndel, io.pfrio_nchange, cnt);
213a82f8bfbSreyk 	return;
214a82f8bfbSreyk 
215a82f8bfbSreyk  toolong:
216efc39811Sbenno 	fatal("%s: name too long", __func__);
217feb9ff76Sreyk }
218feb9ff76Sreyk 
219ff37295aSreyk int
kill_srcnodes(struct relayd * env,struct table * table)220ff37295aSreyk kill_srcnodes(struct relayd *env, struct table *table)
221ff37295aSreyk {
222ff37295aSreyk 	struct host			*host;
223ff37295aSreyk 	struct pfioc_src_node_kill	 psnk;
224ff37295aSreyk 	int				 cnt = 0;
225ff37295aSreyk 	struct sockaddr_in		*sain;
226ff37295aSreyk 	struct sockaddr_in6		*sain6;
227ff37295aSreyk 
228ff37295aSreyk 	bzero(&psnk, sizeof(psnk));
229ff37295aSreyk 
230ff37295aSreyk 	/* Only match the destination address, source mask will be zero */
231ff37295aSreyk 	memset(&psnk.psnk_dst.addr.v.a.mask, 0xff,
232ff37295aSreyk 	    sizeof(psnk.psnk_dst.addr.v.a.mask));
233ff37295aSreyk 
234ff37295aSreyk 	TAILQ_FOREACH(host, &table->hosts, entry) {
235ff37295aSreyk 		if (host->up != HOST_DOWN)
236ff37295aSreyk 			continue;
237ff37295aSreyk 
238ff37295aSreyk 		switch (host->conf.ss.ss_family) {
239ff37295aSreyk 		case AF_INET:
240ff37295aSreyk 		sain = (struct sockaddr_in *)&host->conf.ss;
241ff37295aSreyk 			bcopy(&sain->sin_addr,
242ff37295aSreyk 			    &psnk.psnk_dst.addr.v.a.addr.v4,
243ff37295aSreyk 			    sizeof(psnk.psnk_dst.addr.v.a.addr.v4));
244ff37295aSreyk 			break;
245ff37295aSreyk 		case AF_INET6:
246ff37295aSreyk 			sain6 = (struct sockaddr_in6 *)&host->conf.ss;
247ff37295aSreyk 			bcopy(&sain6->sin6_addr,
248ff37295aSreyk 			    &psnk.psnk_dst.addr.v.a.addr.v6,
249ff37295aSreyk 			    sizeof(psnk.psnk_dst.addr.v.a.addr.v6));
250ff37295aSreyk 			break;
251ff37295aSreyk 		default:
252efc39811Sbenno 			fatalx("%s: unknown address family", __func__);
253ff37295aSreyk 			break;
254ff37295aSreyk 		}
255ff37295aSreyk 
256ff37295aSreyk 		psnk.psnk_af = host->conf.ss.ss_family;
257ff37295aSreyk 		psnk.psnk_killed = 0;
258ff37295aSreyk 
259ff37295aSreyk 		if (ioctl(env->sc_pf->dev,
260ff37295aSreyk 		    DIOCKILLSRCNODES, &psnk) == -1)
261efc39811Sbenno 			fatal("%s: cannot kill src nodes", __func__);
262ff37295aSreyk 		cnt += psnk.psnk_killed;
263ff37295aSreyk 	}
264ff37295aSreyk 
265ff37295aSreyk 	return (cnt);
266ff37295aSreyk }
267ff37295aSreyk 
268feb9ff76Sreyk void
flush_table(struct relayd * env,struct rdr * rdr)2699591a9f7Spyr flush_table(struct relayd *env, struct rdr *rdr)
270feb9ff76Sreyk {
271feb9ff76Sreyk 	struct pfioc_table	io;
272feb9ff76Sreyk 
273586b5f8aSreyk 	if (!(env->sc_conf.flags & F_NEEDPF))
274f3485f87Spyr 		return;
275f3485f87Spyr 
276feb9ff76Sreyk 	memset(&io, 0, sizeof(io));
277748ceb64Sreyk 	if (strlcpy(io.pfrio_table.pfrt_anchor, RELAYD_ANCHOR "/",
278a82f8bfbSreyk 	    sizeof(io.pfrio_table.pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE)
279a82f8bfbSreyk 		goto toolong;
2809591a9f7Spyr 	if (strlcat(io.pfrio_table.pfrt_anchor, rdr->conf.name,
281a82f8bfbSreyk 	    sizeof(io.pfrio_table.pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE)
282a82f8bfbSreyk 		goto toolong;
2839591a9f7Spyr 	if (strlcpy(io.pfrio_table.pfrt_name, rdr->conf.name,
284a82f8bfbSreyk 	    sizeof(io.pfrio_table.pfrt_name)) >=
285a82f8bfbSreyk 	    sizeof(io.pfrio_table.pfrt_name))
286a82f8bfbSreyk 		goto toolong;
28735d10c30Sreyk 	if (ioctl(env->sc_pf->dev, DIOCRCLRADDRS, &io) == -1)
288efc39811Sbenno 		fatal("%s: cannot flush table addresses", __func__);
289e0fca9f4Sreyk 
290e0fca9f4Sreyk 	io.pfrio_esize = sizeof(io.pfrio_table);
291e0fca9f4Sreyk 	io.pfrio_size = 1;
292e0fca9f4Sreyk 	io.pfrio_buffer = &io.pfrio_table;
29335d10c30Sreyk 	if (ioctl(env->sc_pf->dev, DIOCRCLRTSTATS, &io) == -1)
294efc39811Sbenno 		fatal("%s: cannot flush table stats", __func__);
295e0fca9f4Sreyk 
29685a8c65fSreyk 	log_debug("%s: flushed table %s", __func__, rdr->conf.name);
297feb9ff76Sreyk 	return;
298a82f8bfbSreyk 
299a82f8bfbSreyk  toolong:
300efc39811Sbenno 	fatal("%s: name too long", __func__);
301feb9ff76Sreyk }
302feb9ff76Sreyk 
303feb9ff76Sreyk int
transaction_init(struct relayd * env,const char * anchor)304748ceb64Sreyk transaction_init(struct relayd *env, const char *anchor)
305feb9ff76Sreyk {
306d006fa44Sreyk 	env->sc_pf->pft.size = 1;
307d006fa44Sreyk 	env->sc_pf->pft.esize = sizeof(env->sc_pf->pfte);
308d006fa44Sreyk 	env->sc_pf->pft.array = &env->sc_pf->pfte;
309feb9ff76Sreyk 
310d006fa44Sreyk 	bzero(&env->sc_pf->pfte, sizeof(env->sc_pf->pfte));
311d006fa44Sreyk 	(void)strlcpy(env->sc_pf->pfte.anchor,
3120eb8c740Sreyk 	    anchor, PF_ANCHOR_NAME_SIZE);
3130adc8d55Sclaudio 	env->sc_pf->pfte.type = PF_TRANS_RULESET;
3140eb8c740Sreyk 
3150eb8c740Sreyk 	if (ioctl(env->sc_pf->dev, DIOCXBEGIN,
316d006fa44Sreyk 	    &env->sc_pf->pft) == -1)
317feb9ff76Sreyk 		return (-1);
318d006fa44Sreyk 
319feb9ff76Sreyk 	return (0);
320feb9ff76Sreyk }
321feb9ff76Sreyk 
322feb9ff76Sreyk int
transaction_commit(struct relayd * env)323748ceb64Sreyk transaction_commit(struct relayd *env)
324feb9ff76Sreyk {
3250eb8c740Sreyk 	if (ioctl(env->sc_pf->dev, DIOCXCOMMIT,
326d006fa44Sreyk 	    &env->sc_pf->pft) == -1)
327feb9ff76Sreyk 		return (-1);
328d006fa44Sreyk 
329feb9ff76Sreyk 	return (0);
330feb9ff76Sreyk }
331feb9ff76Sreyk 
332feb9ff76Sreyk void
sync_ruleset(struct relayd * env,struct rdr * rdr,int enable)3339591a9f7Spyr sync_ruleset(struct relayd *env, struct rdr *rdr, int enable)
334feb9ff76Sreyk {
335feb9ff76Sreyk 	struct pfioc_rule	 rio;
336feb9ff76Sreyk 	struct sockaddr_in	*sain;
337feb9ff76Sreyk 	struct sockaddr_in6	*sain6;
338feb9ff76Sreyk 	struct address		*address;
339feb9ff76Sreyk 	char			 anchor[PF_ANCHOR_NAME_SIZE];
3400eb8c740Sreyk 	struct table		*t = rdr->table;
341feb9ff76Sreyk 
342586b5f8aSreyk 	if ((env->sc_conf.flags & F_NEEDPF) == 0)
343f3485f87Spyr 		return;
344f3485f87Spyr 
345feb9ff76Sreyk 	bzero(anchor, sizeof(anchor));
346748ceb64Sreyk 	if (strlcpy(anchor, RELAYD_ANCHOR "/", sizeof(anchor)) >=
347a82f8bfbSreyk 	    PF_ANCHOR_NAME_SIZE)
348a82f8bfbSreyk 		goto toolong;
3499591a9f7Spyr 	if (strlcat(anchor, rdr->conf.name, sizeof(anchor)) >=
350a82f8bfbSreyk 	    PF_ANCHOR_NAME_SIZE)
351a82f8bfbSreyk 		goto toolong;
352a82f8bfbSreyk 	if (transaction_init(env, anchor) == -1) {
35385a8c65fSreyk 		log_warn("%s: transaction init failed", __func__);
354a82f8bfbSreyk 		return;
355a82f8bfbSreyk 	}
356feb9ff76Sreyk 
357feb9ff76Sreyk 	if (!enable) {
358a82f8bfbSreyk 		if (transaction_commit(env) == -1)
35985a8c65fSreyk 			log_warn("%s: remove rules transaction failed",
36085a8c65fSreyk 			    __func__);
361a82f8bfbSreyk 		else
36285a8c65fSreyk 			log_debug("%s: rules removed", __func__);
363feb9ff76Sreyk 		return;
364feb9ff76Sreyk 	}
365feb9ff76Sreyk 
3669591a9f7Spyr 	TAILQ_FOREACH(address, &rdr->virts, entry) {
367feb9ff76Sreyk 		memset(&rio, 0, sizeof(rio));
368feb9ff76Sreyk 		(void)strlcpy(rio.anchor, anchor, sizeof(rio.anchor));
369feb9ff76Sreyk 
370e48c97edSreyk 		if (rdr->conf.flags & F_MATCH) {
371e48c97edSreyk 			rio.rule.action = PF_MATCH;
372e48c97edSreyk 			rio.rule.quick = 0;
373e48c97edSreyk 		} else {
374d006fa44Sreyk 			rio.rule.action = PF_PASS;
375d006fa44Sreyk 			rio.rule.quick = 1; /* force first match */
376e48c97edSreyk 		}
377e48c97edSreyk 		rio.rule.direction = PF_IN;
378d006fa44Sreyk 		rio.rule.keep_state = PF_STATE_NORMAL;
379d006fa44Sreyk 
380*7c726e76Ssashan 		if (rdr->conf.flags & F_PFLOG)
381*7c726e76Ssashan 			rio.rule.log = 1;
382*7c726e76Ssashan 		else
383*7c726e76Ssashan 			rio.rule.log = 0; /* allow change via reload */
384*7c726e76Ssashan 
3853c03a838Sreyk 		switch (t->conf.fwdmode) {
3863c03a838Sreyk 		case FWD_NORMAL:
387d006fa44Sreyk 			/* traditional redirection */
388d006fa44Sreyk 			if (address->ipproto == IPPROTO_TCP) {
389d006fa44Sreyk 				rio.rule.flags = TH_SYN;
390d006fa44Sreyk 				rio.rule.flagset = (TH_SYN|TH_ACK);
391d006fa44Sreyk 			}
3923c03a838Sreyk 			break;
3933c03a838Sreyk 		case FWD_ROUTE:
3940eb8c740Sreyk 			/* re-route with pf for DSR (direct server return) */
3950eb8c740Sreyk 			rio.rule.rt = PF_ROUTETO;
3960eb8c740Sreyk 
3979efbe2d4Sreyk 			/* Use sloppy state handling for half connections */
3989efbe2d4Sreyk 			rio.rule.rule_flag = PFRULE_STATESLOPPY;
3993c03a838Sreyk 			break;
4003c03a838Sreyk 		default:
401efc39811Sbenno 			fatalx("%s: invalid forward mode", __func__);
4023c03a838Sreyk 			/* NOTREACHED */
4030eb8c740Sreyk 		}
4040eb8c740Sreyk 
405d006fa44Sreyk 		rio.ticket = env->sc_pf->pfte.ticket;
406feb9ff76Sreyk 
407feb9ff76Sreyk 		rio.rule.af = address->ss.ss_family;
4084ac0ad23Sreyk 		rio.rule.proto = address->ipproto;
409feb9ff76Sreyk 		rio.rule.src.addr.type = PF_ADDR_ADDRMASK;
410feb9ff76Sreyk 		rio.rule.dst.addr.type = PF_ADDR_ADDRMASK;
411232f7df1Sreyk 		rio.rule.dst.port_op = address->port.op;
412232f7df1Sreyk 		rio.rule.dst.port[0] = address->port.val[0];
413232f7df1Sreyk 		rio.rule.dst.port[1] = address->port.val[1];
414feb9ff76Sreyk 		rio.rule.rtableid = -1; /* stay in the main routing table */
415005c709bSbenno 		rio.rule.onrdomain = env->sc_rtable;
416005c709bSbenno 		DPRINTF("%s rtable %d",__func__,env->sc_rtable);
4170eb8c740Sreyk 
4184ac0ad23Sreyk 		if (rio.rule.proto == IPPROTO_TCP)
4194ac0ad23Sreyk 			rio.rule.timeout[PFTM_TCP_ESTABLISHED] =
4201ab70d21Sreyk 			    (u_int32_t)MINIMUM(rdr->conf.timeout.tv_sec,
4211ab70d21Sreyk 			    INT_MAX);
4224ac0ad23Sreyk 
4239591a9f7Spyr 		if (strlen(rdr->conf.tag))
4249591a9f7Spyr 			(void)strlcpy(rio.rule.tagname, rdr->conf.tag,
425feb9ff76Sreyk 			    sizeof(rio.rule.tagname));
426feb9ff76Sreyk 		if (strlen(address->ifname))
427feb9ff76Sreyk 			(void)strlcpy(rio.rule.ifname, address->ifname,
428feb9ff76Sreyk 			    sizeof(rio.rule.ifname));
429feb9ff76Sreyk 
430feb9ff76Sreyk 		if (address->ss.ss_family == AF_INET) {
431feb9ff76Sreyk 			sain = (struct sockaddr_in *)&address->ss;
432feb9ff76Sreyk 
433feb9ff76Sreyk 			rio.rule.dst.addr.v.a.addr.addr32[0] =
434feb9ff76Sreyk 			    sain->sin_addr.s_addr;
435feb9ff76Sreyk 			rio.rule.dst.addr.v.a.mask.addr32[0] = 0xffffffff;
436feb9ff76Sreyk 		} else {
437feb9ff76Sreyk 			sain6 = (struct sockaddr_in6 *)&address->ss;
438feb9ff76Sreyk 
439feb9ff76Sreyk 			memcpy(&rio.rule.dst.addr.v.a.addr.v6,
440feb9ff76Sreyk 			    &sain6->sin6_addr.s6_addr,
441feb9ff76Sreyk 			    sizeof(sain6->sin6_addr.s6_addr));
442feb9ff76Sreyk 			memset(&rio.rule.dst.addr.v.a.mask.addr8, 0xff, 16);
443feb9ff76Sreyk 		}
444feb9ff76Sreyk 
4456f79e280Sdlg 		rio.rule.nat.addr.type = PF_ADDR_NONE;
44636754172Smcbride 		rio.rule.rdr.addr.type = PF_ADDR_TABLE;
4470eb8c740Sreyk 		if (strlen(t->conf.ifname))
44836754172Smcbride 			(void)strlcpy(rio.rule.rdr.ifname, t->conf.ifname,
44936754172Smcbride 			    sizeof(rio.rule.rdr.ifname));
45036754172Smcbride 		if (strlcpy(rio.rule.rdr.addr.v.tblname, rdr->conf.name,
45136754172Smcbride 		    sizeof(rio.rule.rdr.addr.v.tblname)) >=
45236754172Smcbride 		    sizeof(rio.rule.rdr.addr.v.tblname))
453efc39811Sbenno 			fatal("%s: table name too long", __func__);
454feb9ff76Sreyk 
455232f7df1Sreyk 		if (address->port.op == PF_OP_EQ ||
456232f7df1Sreyk 		    rdr->table->conf.flags & F_PORT) {
457d006fa44Sreyk 			rio.rule.rdr.proxy_port[0] =
458232f7df1Sreyk 			    ntohs(rdr->table->conf.port);
459d006fa44Sreyk 			rio.rule.rdr.port_op = PF_OP_EQ;
460232f7df1Sreyk 		}
46159e187d0Sreyk 
46259e187d0Sreyk 		switch (rdr->conf.mode) {
4636933853bSreyk 		case RELAY_DSTMODE_RANDOM:
4646933853bSreyk 			rio.rule.rdr.opts = PF_POOL_RANDOM;
4656933853bSreyk 			break;
46659e187d0Sreyk 		case RELAY_DSTMODE_ROUNDROBIN:
467d006fa44Sreyk 			rio.rule.rdr.opts = PF_POOL_ROUNDROBIN;
46859e187d0Sreyk 			break;
4696933853bSreyk 		case RELAY_DSTMODE_SRCHASH:
4706933853bSreyk 			rio.rule.rdr.opts = PF_POOL_SRCHASH;
4716933853bSreyk 			break;
47259e187d0Sreyk 		case RELAY_DSTMODE_LEASTSTATES:
47359e187d0Sreyk 			rio.rule.rdr.opts = PF_POOL_LEASTSTATES;
47459e187d0Sreyk 			break;
47559e187d0Sreyk 		default:
476efc39811Sbenno 			fatalx("%s: unsupported mode", __func__);
47759e187d0Sreyk 			/* NOTREACHED */
47859e187d0Sreyk 		}
4799591a9f7Spyr 		if (rdr->conf.flags & F_STICKY)
480d006fa44Sreyk 			rio.rule.rdr.opts |= PF_POOL_STICKYADDR;
4816933853bSreyk 		if (rdr->conf.flags & F_HASHKEY)
4826933853bSreyk 			memcpy(rio.rule.rdr.key.key32, rdr->conf.key.data,
4836933853bSreyk 			    sizeof(rio.rule.rdr.key.key32));
484feb9ff76Sreyk 
48595a1f8c2Spyr 		if (rio.rule.rt == PF_ROUTETO) {
48695a1f8c2Spyr 			memcpy(&rio.rule.route, &rio.rule.rdr,
48795a1f8c2Spyr 			    sizeof(rio.rule.route));
48895a1f8c2Spyr 			rio.rule.rdr.addr.type = PF_ADDR_NONE;
48995a1f8c2Spyr 		}
49095a1f8c2Spyr 
49135d10c30Sreyk 		if (ioctl(env->sc_pf->dev, DIOCADDRULE, &rio) == -1)
492feb9ff76Sreyk 			fatal("cannot add rule");
49385a8c65fSreyk 		log_debug("%s: rule added to anchor \"%s\"", __func__, anchor);
494feb9ff76Sreyk 	}
495a82f8bfbSreyk 	if (transaction_commit(env) == -1)
49685a8c65fSreyk 		log_warn("%s: add rules transaction failed", __func__);
497a82f8bfbSreyk 	return;
498a82f8bfbSreyk 
499a82f8bfbSreyk  toolong:
500efc39811Sbenno 	fatal("%s: name too long", __func__);
501feb9ff76Sreyk }
502feb9ff76Sreyk 
503feb9ff76Sreyk void
flush_rulesets(struct relayd * env)504748ceb64Sreyk flush_rulesets(struct relayd *env)
505feb9ff76Sreyk {
5069591a9f7Spyr 	struct rdr	*rdr;
507feb9ff76Sreyk 	char		 anchor[PF_ANCHOR_NAME_SIZE];
508feb9ff76Sreyk 
509586b5f8aSreyk 	if (!(env->sc_conf.flags & F_NEEDPF))
510f3485f87Spyr 		return;
511f3485f87Spyr 
512feb9ff76Sreyk 	kill_tables(env);
51335d10c30Sreyk 	TAILQ_FOREACH(rdr, env->sc_rdrs, entry) {
514748ceb64Sreyk 		if (strlcpy(anchor, RELAYD_ANCHOR "/", sizeof(anchor)) >=
515a82f8bfbSreyk 		    PF_ANCHOR_NAME_SIZE)
516a82f8bfbSreyk 			goto toolong;
5179591a9f7Spyr 		if (strlcat(anchor, rdr->conf.name, sizeof(anchor)) >=
518a82f8bfbSreyk 		    PF_ANCHOR_NAME_SIZE)
519a82f8bfbSreyk 			goto toolong;
520a82f8bfbSreyk 		if (transaction_init(env, anchor) == -1 ||
521a82f8bfbSreyk 		    transaction_commit(env) == -1)
52285a8c65fSreyk 			log_warn("%s: transaction for %s/ failed", __func__,
523748ceb64Sreyk 			    RELAYD_ANCHOR);
524feb9ff76Sreyk 	}
525748ceb64Sreyk 	if (strlcpy(anchor, RELAYD_ANCHOR, sizeof(anchor)) >=
526a82f8bfbSreyk 	    PF_ANCHOR_NAME_SIZE)
527a82f8bfbSreyk 		goto toolong;
528a82f8bfbSreyk 	if (transaction_init(env, anchor) == -1 ||
529a82f8bfbSreyk 	    transaction_commit(env) == -1)
53085a8c65fSreyk 		log_warn("%s: transaction for %s failed", __func__,
531748ceb64Sreyk 		    RELAYD_ANCHOR);
53285a8c65fSreyk 	log_debug("%s: flushed rules", __func__);
533a82f8bfbSreyk 	return;
534a82f8bfbSreyk 
535a82f8bfbSreyk  toolong:
536efc39811Sbenno 	fatal("%s: name too long", __func__);
537feb9ff76Sreyk }
5382edd718bSreyk 
5392edd718bSreyk int
natlook(struct relayd * env,struct ctl_natlook * cnl)540748ceb64Sreyk natlook(struct relayd *env, struct ctl_natlook *cnl)
5412edd718bSreyk {
5422edd718bSreyk 	struct pfioc_natlook	 pnl;
5432edd718bSreyk 	struct sockaddr_in	*in, *out;
5442edd718bSreyk 	struct sockaddr_in6	*in6, *out6;
5452edd718bSreyk 	char			 ibuf[BUFSIZ], obuf[BUFSIZ];
5462edd718bSreyk 
547586b5f8aSreyk 	if (!(env->sc_conf.flags & F_NEEDPF))
548f3485f87Spyr 		return (0);
549f3485f87Spyr 
5502edd718bSreyk 	bzero(&pnl, sizeof(pnl));
5512edd718bSreyk 
5522edd718bSreyk 	if ((pnl.af = cnl->src.ss_family) != cnl->dst.ss_family)
553efc39811Sbenno 		fatalx("%s: illegal address families", __func__);
5542edd718bSreyk 	switch (pnl.af) {
5552edd718bSreyk 	case AF_INET:
5562edd718bSreyk 		in = (struct sockaddr_in *)&cnl->src;
5572edd718bSreyk 		out = (struct sockaddr_in *)&cnl->dst;
5589d07e7a6Sreyk 		bcopy(&in->sin_addr, &pnl.saddr.v4, sizeof(pnl.saddr.v4));
5592edd718bSreyk 		pnl.sport = in->sin_port;
5609d07e7a6Sreyk 		bcopy(&out->sin_addr, &pnl.daddr.v4, sizeof(pnl.daddr.v4));
5612edd718bSreyk 		pnl.dport = out->sin_port;
5622edd718bSreyk 		break;
5632edd718bSreyk 	case AF_INET6:
5642edd718bSreyk 		in6 = (struct sockaddr_in6 *)&cnl->src;
5652edd718bSreyk 		out6 = (struct sockaddr_in6 *)&cnl->dst;
5669d07e7a6Sreyk 		bcopy(&in6->sin6_addr, &pnl.saddr.v6, sizeof(pnl.saddr.v6));
5672edd718bSreyk 		pnl.sport = in6->sin6_port;
5689d07e7a6Sreyk 		bcopy(&out6->sin6_addr, &pnl.daddr.v6, sizeof(pnl.daddr.v6));
5692edd718bSreyk 		pnl.dport = out6->sin6_port;
5702edd718bSreyk 	}
571aeda6e0eSreyk 	pnl.proto = cnl->proto;
5722edd718bSreyk 	pnl.direction = PF_IN;
5732edd718bSreyk 	cnl->in = 1;
5742edd718bSreyk 
57535d10c30Sreyk 	if (ioctl(env->sc_pf->dev, DIOCNATLOOK, &pnl) == -1) {
5762edd718bSreyk 		pnl.direction = PF_OUT;
5772edd718bSreyk 		cnl->in = 0;
57835d10c30Sreyk 		if (ioctl(env->sc_pf->dev, DIOCNATLOOK, &pnl) == -1) {
57985a8c65fSreyk 			log_debug("%s: ioctl: %s", __func__, strerror(errno));
5802edd718bSreyk 			return (-1);
5812edd718bSreyk 		}
5822edd718bSreyk 	}
5832edd718bSreyk 
5842edd718bSreyk 	inet_ntop(pnl.af, &pnl.rsaddr, ibuf, sizeof(ibuf));
5852edd718bSreyk 	inet_ntop(pnl.af, &pnl.rdaddr, obuf, sizeof(obuf));
58685a8c65fSreyk 	log_debug("%s: %s %s:%d -> %s:%d", __func__,
5872edd718bSreyk 	    pnl.direction == PF_IN ? "in" : "out",
5882edd718bSreyk 	    ibuf, ntohs(pnl.rsport), obuf, ntohs(pnl.rdport));
5892edd718bSreyk 
5902edd718bSreyk 	switch (pnl.af) {
5912edd718bSreyk 	case AF_INET:
5922edd718bSreyk 		in = (struct sockaddr_in *)&cnl->rsrc;
5932edd718bSreyk 		out = (struct sockaddr_in *)&cnl->rdst;
5949d07e7a6Sreyk 		bcopy(&pnl.rsaddr.v4, &in->sin_addr, sizeof(in->sin_addr));
5952edd718bSreyk 		in->sin_port = pnl.rsport;
5969d07e7a6Sreyk 		bcopy(&pnl.rdaddr.v4, &out->sin_addr, sizeof(out->sin_addr));
5972edd718bSreyk 		out->sin_port = pnl.rdport;
5982edd718bSreyk 		break;
5992edd718bSreyk 	case AF_INET6:
6002edd718bSreyk 		in6 = (struct sockaddr_in6 *)&cnl->rsrc;
6012edd718bSreyk 		out6 = (struct sockaddr_in6 *)&cnl->rdst;
6029d07e7a6Sreyk 		bcopy(&pnl.rsaddr.v6, &in6->sin6_addr, sizeof(in6->sin6_addr));
6039d07e7a6Sreyk 		bcopy(&pnl.rdaddr.v6, &out6->sin6_addr,
6043b507e48Sreyk 		    sizeof(out6->sin6_addr));
6052edd718bSreyk 		break;
6062edd718bSreyk 	}
6072edd718bSreyk 	cnl->rsrc.ss_family = pnl.af;
6082edd718bSreyk 	cnl->rdst.ss_family = pnl.af;
6092edd718bSreyk 	cnl->rsport = pnl.rsport;
6102edd718bSreyk 	cnl->rdport = pnl.rdport;
6112edd718bSreyk 
6122edd718bSreyk 	return (0);
6132edd718bSreyk }
614dec6607bSreyk 
615dec6607bSreyk u_int64_t
check_table(struct relayd * env,struct rdr * rdr,struct table * table)616dec6607bSreyk check_table(struct relayd *env, struct rdr *rdr, struct table *table)
617dec6607bSreyk {
618dec6607bSreyk 	struct pfioc_table	 io;
619dec6607bSreyk 	struct pfr_tstats	 tstats;
620dec6607bSreyk 
621dec6607bSreyk 	if (table == NULL)
622dec6607bSreyk 		return (0);
623dec6607bSreyk 
624dec6607bSreyk 	bzero(&io, sizeof(io));
625dec6607bSreyk 	io.pfrio_esize = sizeof(struct pfr_tstats);
626dec6607bSreyk 	io.pfrio_size = 1;
627dec6607bSreyk 	io.pfrio_buffer = &tstats;
628dec6607bSreyk 	if (strlcpy(io.pfrio_table.pfrt_anchor, RELAYD_ANCHOR "/",
629dec6607bSreyk 	    sizeof(io.pfrio_table.pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE)
630dec6607bSreyk 		goto toolong;
631dec6607bSreyk 	if (strlcat(io.pfrio_table.pfrt_anchor, rdr->conf.name,
632dec6607bSreyk 	    sizeof(io.pfrio_table.pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE)
633dec6607bSreyk 		goto toolong;
634dec6607bSreyk 	if (strlcpy(io.pfrio_table.pfrt_name, rdr->conf.name,
635dec6607bSreyk 	    sizeof(io.pfrio_table.pfrt_name)) >=
636dec6607bSreyk 	    sizeof(io.pfrio_table.pfrt_name))
637dec6607bSreyk 		goto toolong;
638dec6607bSreyk 
63935d10c30Sreyk 	if (ioctl(env->sc_pf->dev, DIOCRGETTSTATS, &io) == -1)
640e32ac0fbSsashan 		fatal("%s: cannot get table stats for %s@%s", __func__,
641e32ac0fbSsashan 		    io.pfrio_table.pfrt_name, io.pfrio_table.pfrt_anchor);
642dec6607bSreyk 
643dec6607bSreyk 	return (tstats.pfrts_match);
644dec6607bSreyk 
645dec6607bSreyk  toolong:
646efc39811Sbenno 	fatal("%s: name too long", __func__);
647dec6607bSreyk 	return (0);
648dec6607bSreyk }
649