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