1 /* $OpenBSD: pfe_filter.c,v 1.67 2026/03/02 19:28:01 rsadowski Exp $ */
4 * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/types.h>
20 #include <sys/queue.h>
21 #include <sys/socket.h>
23 #include <sys/ioctl.h>
26 #include <netinet/in.h>
27 #include <netinet/tcp.h>
28 #include <arpa/inet.h>
29 #include <net/pfvar.h>
38 #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
43 int transaction_init(struct relayd *, const char *);
44 int transaction_commit(struct relayd *);
45 void kill_tables(struct relayd *);
46 int kill_srcnodes(struct relayd *, struct table *);
49 init_tables(struct relayd *env)
53 struct pfr_table *tables;
54 struct pfioc_table io;
56 if (!(env->sc_conf.flags & F_NEEDPF))
59 if ((tables = calloc(env->sc_rdrcount, sizeof(*tables))) == NULL)
63 TAILQ_FOREACH(rdr, env->sc_rdrs, entry) {
64 if (strlcpy(tables[i].pfrt_anchor, RELAYD_ANCHOR "/",
65 sizeof(tables[i].pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE)
67 if (strlcat(tables[i].pfrt_anchor, rdr->conf.name,
68 sizeof(tables[i].pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE)
70 if (strlcpy(tables[i].pfrt_name, rdr->conf.name,
71 sizeof(tables[i].pfrt_name)) >=
72 sizeof(tables[i].pfrt_name))
74 tables[i].pfrt_flags |= PFR_TFLAG_PERSIST;
77 if (i != env->sc_rdrcount)
78 fatalx("%s: table count modified", __func__);
80 memset(&io, 0, sizeof(io));
81 io.pfrio_size = env->sc_rdrcount;
82 io.pfrio_esize = sizeof(*tables);
83 io.pfrio_buffer = tables;
85 if (ioctl(env->sc_pf->dev, DIOCRADDTABLES, &io) == -1)
86 fatal("%s: cannot create tables", __func__);
87 log_debug("%s: created %d tables", __func__, io.pfrio_nadd);
91 if (io.pfrio_nadd == env->sc_rdrcount)
95 * clear all tables, since some already existed
97 TAILQ_FOREACH(rdr, env->sc_rdrs, entry)
98 flush_table(env, rdr);
103 fatal("%s: name too long", __func__);
107 kill_tables(struct relayd *env)
109 struct pfioc_table io;
113 if (!(env->sc_conf.flags & F_NEEDPF))
116 TAILQ_FOREACH(rdr, env->sc_rdrs, entry) {
117 memset(&io, 0, sizeof(io));
118 if (strlcpy(io.pfrio_table.pfrt_anchor, RELAYD_ANCHOR "/",
119 sizeof(io.pfrio_table.pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE)
121 if (strlcat(io.pfrio_table.pfrt_anchor, rdr->conf.name,
122 sizeof(io.pfrio_table.pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE)
124 if (ioctl(env->sc_pf->dev, DIOCRCLRTABLES, &io) == -1)
125 fatal("%s: ioctl failed", __func__);
126 cnt += io.pfrio_ndel;
128 log_debug("%s: deleted %d tables", __func__, cnt);
132 fatal("%s: name too long", __func__);
136 sync_table(struct relayd *env, struct rdr *rdr, struct table *table)
139 struct pfioc_table io;
140 struct pfr_addr *addlist;
141 struct sockaddr_in *sain;
142 struct sockaddr_in6 *sain6;
145 if (!(env->sc_conf.flags & F_NEEDPF))
151 if (table->up == 0) {
152 flush_table(env, rdr);
156 if ((addlist = calloc(table->up, sizeof(*addlist))) == NULL)
159 memset(&io, 0, sizeof(io));
160 io.pfrio_esize = sizeof(struct pfr_addr);
161 io.pfrio_size = table->up;
163 io.pfrio_buffer = addlist;
164 if (strlcpy(io.pfrio_table.pfrt_anchor, RELAYD_ANCHOR "/",
165 sizeof(io.pfrio_table.pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE)
167 if (strlcat(io.pfrio_table.pfrt_anchor, rdr->conf.name,
168 sizeof(io.pfrio_table.pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE)
170 if (strlcpy(io.pfrio_table.pfrt_name, rdr->conf.name,
171 sizeof(io.pfrio_table.pfrt_name)) >=
172 sizeof(io.pfrio_table.pfrt_name))
176 TAILQ_FOREACH(host, &table->hosts, entry) {
177 if (host->up != HOST_UP)
179 memset(&(addlist[i]), 0, sizeof(addlist[i]));
180 switch (host->conf.ss.ss_family) {
182 sain = (struct sockaddr_in *)&host->conf.ss;
183 addlist[i].pfra_af = AF_INET;
184 memcpy(&(addlist[i].pfra_ip4addr), &sain->sin_addr,
185 sizeof(sain->sin_addr));
186 addlist[i].pfra_net = 32;
189 sain6 = (struct sockaddr_in6 *)&host->conf.ss;
190 addlist[i].pfra_af = AF_INET6;
191 memcpy(&(addlist[i].pfra_ip6addr), &sain6->sin6_addr,
192 sizeof(sain6->sin6_addr));
193 addlist[i].pfra_net = 128;
196 fatalx("%s: unknown address family", __func__);
202 fatalx("%s: desynchronized", __func__);
204 if (ioctl(env->sc_pf->dev, DIOCRSETADDRS, &io) == -1)
205 fatal("%s: cannot set address list", __func__);
206 if (rdr->conf.flags & F_STICKY)
207 cnt = kill_srcnodes(env, table);
210 if (env->sc_conf.opts & RELAYD_OPT_LOGUPDATE)
211 log_info("table %s: %d added, %d deleted, "
212 "%d changed, %d killed", io.pfrio_table.pfrt_name,
213 io.pfrio_nadd, io.pfrio_ndel, io.pfrio_nchange, cnt);
217 fatal("%s: name too long", __func__);
221 kill_srcnodes(struct relayd *env, struct table *table)
224 struct pfioc_src_node_kill psnk;
226 struct sockaddr_in *sain;
227 struct sockaddr_in6 *sain6;
229 bzero(&psnk, sizeof(psnk));
231 /* Only match the destination address, source mask will be zero */
232 memset(&psnk.psnk_dst.addr.v.a.mask, 0xff,
233 sizeof(psnk.psnk_dst.addr.v.a.mask));
235 TAILQ_FOREACH(host, &table->hosts, entry) {
236 if (host->up != HOST_DOWN)
239 switch (host->conf.ss.ss_family) {
241 sain = (struct sockaddr_in *)&host->conf.ss;
242 bcopy(&sain->sin_addr,
243 &psnk.psnk_dst.addr.v.a.addr.v4,
244 sizeof(psnk.psnk_dst.addr.v.a.addr.v4));
247 sain6 = (struct sockaddr_in6 *)&host->conf.ss;
248 bcopy(&sain6->sin6_addr,
249 &psnk.psnk_dst.addr.v.a.addr.v6,
250 sizeof(psnk.psnk_dst.addr.v.a.addr.v6));
253 fatalx("%s: unknown address family", __func__);
257 psnk.psnk_af = host->conf.ss.ss_family;
258 psnk.psnk_killed = 0;
260 if (ioctl(env->sc_pf->dev,
261 DIOCKILLSRCNODES, &psnk) == -1)
262 fatal("%s: cannot kill src nodes", __func__);
263 cnt += psnk.psnk_killed;
270 flush_table(struct relayd *env, struct rdr *rdr)
272 struct pfioc_table io;
274 if (!(env->sc_conf.flags & F_NEEDPF))
277 memset(&io, 0, sizeof(io));
278 if (strlcpy(io.pfrio_table.pfrt_anchor, RELAYD_ANCHOR "/",
279 sizeof(io.pfrio_table.pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE)
281 if (strlcat(io.pfrio_table.pfrt_anchor, rdr->conf.name,
282 sizeof(io.pfrio_table.pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE)
284 if (strlcpy(io.pfrio_table.pfrt_name, rdr->conf.name,
285 sizeof(io.pfrio_table.pfrt_name)) >=
286 sizeof(io.pfrio_table.pfrt_name))
288 if (ioctl(env->sc_pf->dev, DIOCRCLRADDRS, &io) == -1)
289 fatal("%s: cannot flush table addresses", __func__);
291 io.pfrio_esize = sizeof(io.pfrio_table);
293 io.pfrio_buffer = &io.pfrio_table;
294 if (ioctl(env->sc_pf->dev, DIOCRCLRTSTATS, &io) == -1)
295 fatal("%s: cannot flush table stats", __func__);
297 log_debug("%s: flushed table %s", __func__, rdr->conf.name);
301 fatal("%s: name too long", __func__);
305 transaction_init(struct relayd *env, const char *anchor)
307 env->sc_pf->pft.size = 1;
308 env->sc_pf->pft.esize = sizeof(env->sc_pf->pfte);
309 env->sc_pf->pft.array = &env->sc_pf->pfte;
311 bzero(&env->sc_pf->pfte, sizeof(env->sc_pf->pfte));
312 (void)strlcpy(env->sc_pf->pfte.anchor,
313 anchor, PF_ANCHOR_NAME_SIZE);
314 env->sc_pf->pfte.type = PF_TRANS_RULESET;
316 if (ioctl(env->sc_pf->dev, DIOCXBEGIN,
317 &env->sc_pf->pft) == -1)
324 transaction_commit(struct relayd *env)
326 if (ioctl(env->sc_pf->dev, DIOCXCOMMIT,
327 &env->sc_pf->pft) == -1)
334 sync_ruleset(struct relayd *env, struct rdr *rdr, int enable)
336 struct pfioc_rule rio;
337 struct sockaddr_in *sain;
338 struct sockaddr_in6 *sain6;
339 struct address *address;
340 char anchor[PF_ANCHOR_NAME_SIZE];
341 struct table *t = rdr->table;
343 if ((env->sc_conf.flags & F_NEEDPF) == 0)
346 bzero(anchor, sizeof(anchor));
347 if (strlcpy(anchor, RELAYD_ANCHOR "/", sizeof(anchor)) >=
350 if (strlcat(anchor, rdr->conf.name, sizeof(anchor)) >=
353 if (transaction_init(env, anchor) == -1) {
354 log_warn("%s: transaction init failed", __func__);
359 if (transaction_commit(env) == -1)
360 log_warn("%s: remove rules transaction failed",
363 log_debug("%s: rules removed", __func__);
367 TAILQ_FOREACH(address, &rdr->virts, entry) {
368 memset(&rio, 0, sizeof(rio));
369 (void)strlcpy(rio.anchor, anchor, sizeof(rio.anchor));
371 if (rdr->conf.flags & F_MATCH) {
372 rio.rule.action = PF_MATCH;
375 rio.rule.action = PF_PASS;
376 rio.rule.quick = 1; /* force first match */
378 rio.rule.direction = PF_IN;
379 rio.rule.keep_state = PF_STATE_NORMAL;
381 if (rdr->conf.flags & F_PFLOG)
384 rio.rule.log = 0; /* allow change via reload */
386 switch (t->conf.fwdmode) {
388 /* traditional redirection */
389 if (address->ipproto == IPPROTO_TCP) {
390 rio.rule.flags = TH_SYN;
391 rio.rule.flagset = (TH_SYN|TH_ACK);
395 /* re-route with pf for DSR (direct server return) */
396 rio.rule.rt = PF_ROUTETO;
398 /* Use sloppy state handling for half connections */
399 rio.rule.rule_flag = PFRULE_STATESLOPPY;
402 fatalx("%s: invalid forward mode", __func__);
406 rio.ticket = env->sc_pf->pfte.ticket;
408 rio.rule.af = address->ss.ss_family;
409 rio.rule.proto = address->ipproto;
410 rio.rule.src.addr.type = PF_ADDR_ADDRMASK;
411 rio.rule.dst.addr.type = PF_ADDR_ADDRMASK;
412 rio.rule.dst.port_op = address->port.op;
413 rio.rule.dst.port[0] = address->port.val[0];
414 rio.rule.dst.port[1] = address->port.val[1];
415 rio.rule.rtableid = -1; /* stay in the main routing table */
416 rio.rule.onrdomain = env->sc_rtable;
417 DPRINTF("%s rtable %d",__func__,env->sc_rtable);
419 if (rio.rule.proto == IPPROTO_TCP)
420 rio.rule.timeout[PFTM_TCP_ESTABLISHED] =
421 (u_int32_t)MINIMUM(rdr->conf.timeout.tv_sec,
424 if (strlen(rdr->conf.tag))
425 (void)strlcpy(rio.rule.tagname, rdr->conf.tag,
426 sizeof(rio.rule.tagname));
427 if (strlen(address->ifname))
428 (void)strlcpy(rio.rule.ifname, address->ifname,
429 sizeof(rio.rule.ifname));
431 if (address->ss.ss_family == AF_INET) {
432 sain = (struct sockaddr_in *)&address->ss;
434 rio.rule.dst.addr.v.a.addr.addr32[0] =
435 sain->sin_addr.s_addr;
436 rio.rule.dst.addr.v.a.mask.addr32[0] = 0xffffffff;
438 sain6 = (struct sockaddr_in6 *)&address->ss;
440 memcpy(&rio.rule.dst.addr.v.a.addr.v6,
441 &sain6->sin6_addr.s6_addr,
442 sizeof(sain6->sin6_addr.s6_addr));
443 memset(&rio.rule.dst.addr.v.a.mask.addr8, 0xff, 16);
446 rio.rule.nat.addr.type = PF_ADDR_NONE;
447 rio.rule.rdr.addr.type = PF_ADDR_TABLE;
448 if (strlen(t->conf.ifname))
449 (void)strlcpy(rio.rule.rdr.ifname, t->conf.ifname,
450 sizeof(rio.rule.rdr.ifname));
451 if (strlcpy(rio.rule.rdr.addr.v.tblname, rdr->conf.name,
452 sizeof(rio.rule.rdr.addr.v.tblname)) >=
453 sizeof(rio.rule.rdr.addr.v.tblname))
454 fatal("%s: table name too long", __func__);
456 if (address->port.op == PF_OP_EQ ||
457 rdr->table->conf.flags & F_PORT) {
458 rio.rule.rdr.proxy_port[0] =
459 ntohs(rdr->table->conf.port);
460 rio.rule.rdr.port_op = PF_OP_EQ;
463 switch (rdr->conf.mode) {
464 case RELAY_DSTMODE_RANDOM:
465 rio.rule.rdr.opts = PF_POOL_RANDOM;
467 case RELAY_DSTMODE_ROUNDROBIN:
468 rio.rule.rdr.opts = PF_POOL_ROUNDROBIN;
470 case RELAY_DSTMODE_SRCHASH:
471 rio.rule.rdr.opts = PF_POOL_SRCHASH;
473 case RELAY_DSTMODE_LEASTSTATES:
474 rio.rule.rdr.opts = PF_POOL_LEASTSTATES;
477 fatalx("%s: unsupported mode", __func__);
480 if (rdr->conf.flags & F_STICKY)
481 rio.rule.rdr.opts |= PF_POOL_STICKYADDR;
482 if (rdr->conf.flags & F_HASHKEY)
483 memcpy(rio.rule.rdr.key.key32, rdr->conf.key.data,
484 sizeof(rio.rule.rdr.key.key32));
486 if (rio.rule.rt == PF_ROUTETO) {
487 memcpy(&rio.rule.route, &rio.rule.rdr,
488 sizeof(rio.rule.route));
489 rio.rule.rdr.addr.type = PF_ADDR_NONE;
492 if (ioctl(env->sc_pf->dev, DIOCADDRULE, &rio) == -1)
493 fatal("cannot add rule");
494 log_debug("%s: rule added to anchor \"%s\"", __func__, anchor);
496 if (transaction_commit(env) == -1)
497 log_warn("%s: add rules transaction failed", __func__);
501 fatal("%s: name too long", __func__);
505 flush_rulesets(struct relayd *env)
508 char anchor[PF_ANCHOR_NAME_SIZE];
510 if (!(env->sc_conf.flags & F_NEEDPF))
514 TAILQ_FOREACH(rdr, env->sc_rdrs, entry) {
515 if (strlcpy(anchor, RELAYD_ANCHOR "/", sizeof(anchor)) >=
518 if (strlcat(anchor, rdr->conf.name, sizeof(anchor)) >=
521 if (transaction_init(env, anchor) == -1 ||
522 transaction_commit(env) == -1)
523 log_warn("%s: transaction for %s/ failed", __func__,
526 if (strlcpy(anchor, RELAYD_ANCHOR, sizeof(anchor)) >=
529 if (transaction_init(env, anchor) == -1 ||
530 transaction_commit(env) == -1)
531 log_warn("%s: transaction for %s failed", __func__,
533 log_debug("%s: flushed rules", __func__);
537 fatal("%s: name too long", __func__);
541 natlook(struct relayd *env, struct ctl_natlook *cnl)
543 struct pfioc_natlook pnl;
544 struct sockaddr_in *in, *out;
545 struct sockaddr_in6 *in6, *out6;
546 char ibuf[BUFSIZ], obuf[BUFSIZ];
548 if (!(env->sc_conf.flags & F_NEEDPF))
551 bzero(&pnl, sizeof(pnl));
553 if ((pnl.af = cnl->src.ss_family) != cnl->dst.ss_family)
554 fatalx("%s: illegal address families", __func__);
557 in = (struct sockaddr_in *)&cnl->src;
558 out = (struct sockaddr_in *)&cnl->dst;
559 bcopy(&in->sin_addr, &pnl.saddr.v4, sizeof(pnl.saddr.v4));
560 pnl.sport = in->sin_port;
561 bcopy(&out->sin_addr, &pnl.daddr.v4, sizeof(pnl.daddr.v4));
562 pnl.dport = out->sin_port;
565 in6 = (struct sockaddr_in6 *)&cnl->src;
566 out6 = (struct sockaddr_in6 *)&cnl->dst;
567 bcopy(&in6->sin6_addr, &pnl.saddr.v6, sizeof(pnl.saddr.v6));
568 pnl.sport = in6->sin6_port;
569 bcopy(&out6->sin6_addr, &pnl.daddr.v6, sizeof(pnl.daddr.v6));
570 pnl.dport = out6->sin6_port;
572 pnl.proto = cnl->proto;
573 pnl.direction = PF_IN;
576 if (ioctl(env->sc_pf->dev, DIOCNATLOOK, &pnl) == -1) {
577 pnl.direction = PF_OUT;
579 if (ioctl(env->sc_pf->dev, DIOCNATLOOK, &pnl) == -1) {
580 log_debug("%s: ioctl: %s", __func__, strerror(errno));
585 inet_ntop(pnl.af, &pnl.rsaddr, ibuf, sizeof(ibuf));
586 inet_ntop(pnl.af, &pnl.rdaddr, obuf, sizeof(obuf));
587 log_debug("%s: %s %s:%d -> %s:%d", __func__,
588 pnl.direction == PF_IN ? "in" : "out",
589 ibuf, ntohs(pnl.rsport), obuf, ntohs(pnl.rdport));
593 in = (struct sockaddr_in *)&cnl->rsrc;
594 out = (struct sockaddr_in *)&cnl->rdst;
595 bcopy(&pnl.rsaddr.v4, &in->sin_addr, sizeof(in->sin_addr));
596 in->sin_port = pnl.rsport;
597 bcopy(&pnl.rdaddr.v4, &out->sin_addr, sizeof(out->sin_addr));
598 out->sin_port = pnl.rdport;
601 in6 = (struct sockaddr_in6 *)&cnl->rsrc;
602 out6 = (struct sockaddr_in6 *)&cnl->rdst;
603 bcopy(&pnl.rsaddr.v6, &in6->sin6_addr, sizeof(in6->sin6_addr));
604 bcopy(&pnl.rdaddr.v6, &out6->sin6_addr,
605 sizeof(out6->sin6_addr));
608 cnl->rsrc.ss_family = pnl.af;
609 cnl->rdst.ss_family = pnl.af;
610 cnl->rsport = pnl.rsport;
611 cnl->rdport = pnl.rdport;
617 check_table(struct relayd *env, struct rdr *rdr, struct table *table)
619 struct pfioc_table io;
620 struct pfr_tstats tstats;
625 bzero(&io, sizeof(io));
626 io.pfrio_esize = sizeof(struct pfr_tstats);
628 io.pfrio_buffer = &tstats;
629 if (strlcpy(io.pfrio_table.pfrt_anchor, RELAYD_ANCHOR "/",
630 sizeof(io.pfrio_table.pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE)
632 if (strlcat(io.pfrio_table.pfrt_anchor, rdr->conf.name,
633 sizeof(io.pfrio_table.pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE)
635 if (strlcpy(io.pfrio_table.pfrt_name, rdr->conf.name,
636 sizeof(io.pfrio_table.pfrt_name)) >=
637 sizeof(io.pfrio_table.pfrt_name))
640 if (ioctl(env->sc_pf->dev, DIOCRGETTSTATS, &io) == -1)
641 fatal("%s: cannot get table stats for %s@%s", __func__,
642 io.pfrio_table.pfrt_name, io.pfrio_table.pfrt_anchor);
644 return (tstats.pfrts_match);
647 fatal("%s: name too long", __func__);