Hi,
Looking into pf_ioctl.c and pfvar.h I've found that there is an
undocumented (for some unknown reason) IOCTL - DIOCKILLSRCNODES.
Further investigation revealed that it's purpose is to remove single
node from source tracking tree.
So the simplest way is find out what connections should be removed and
kill them. But in sync_table we have only the final table, so
connections to remove
are in the pf table but not in the final table. To find them it is
simplest to get previous table from pf, and subtract from it the final
table.
And then to remove found items from source tracking tree.
That's exactly what is done in the diff below.
best regards
MichaE Koc
Index: pfe_filter.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/pfe_filter.c,v
retrieving revision 1.27
diff -u -r1.27 pfe_filter.c
--- pfe_filter.c 16 May 2008 14:47:58 -0000 1.27
+++ pfe_filter.c 17 May 2008 13:46:43 -0000
@@ -157,8 +157,12 @@
sync_table(struct relayd *env, struct rdr *rdr, struct table *table)
{
int i;
+ int j;
+ int cs;
struct pfioc_table io;
+ struct pfioc_src_node_kill iok;
struct pfr_addr *addlist;
+ struct pfr_addr *curlist;
struct sockaddr_in *sain;
struct sockaddr_in6 *sain6;
struct host *host;
@@ -179,9 +183,7 @@
memset(&io, 0, sizeof(io));
io.pfrio_esize = sizeof(struct pfr_addr);
- io.pfrio_size = table->up;
io.pfrio_size2 = 0;
- io.pfrio_buffer = addlist;
if (strlcpy(io.pfrio_table.pfrt_anchor, RELAYD_ANCHOR "/",
sizeof(io.pfrio_table.pfrt_anchor)) >= PF_ANCHOR_NAME_SIZE)
goto toolong;
@@ -193,6 +195,28 @@
sizeof(io.pfrio_table.pfrt_name))
goto toolong;
+ cs = 0;
+ curlist = 0;
+
+ if (rdr->conf.flags & F_STICKY) {
+ io.pfrio_size = 0;
+ io.pfrio_buffer = 0;
+ if (ioctl(env->sc_pf->dev, DIOCRGETADDRS, &io) == -1)
+ fatal("sync_table: cannot get number of address");
+
+ if ((cs = io.pfrio_size)) {
+ if ((curlist = calloc(cs, sizeof(*curlist))) == NULL)
+ fatal("calloc");
+
+ io.pfrio_buffer = curlist;
+ if (ioctl(env->sc_pf->dev, DIOCRGETADDRS, &io) == -1)
+ fatal("sync_table: cannot get address list");
+ }
+ }
+
+ io.pfrio_size = table->up;
+ io.pfrio_buffer = addlist;
+
i = 0;
TAILQ_FOREACH(host, &table->hosts, entry) {
if (host->up != HOST_UP)
@@ -205,6 +229,11 @@
memcpy(&(addlist[i].pfra_ip4addr), &sain->sin_addr,
sizeof(sain->sin_addr));
addlist[i].pfra_net = 32;
+ for (j = 0; j < cs; ++j)
+ if (!memcmp(&sain->sin_addr,
+ &(curlist[j].pfra_ip4addr),
+ sizeof(sain->sin_addr)))
+ break;
break;
case AF_INET6:
sain6 = (struct sockaddr_in6 *)&host->conf.ss;
@@ -212,11 +241,17 @@
memcpy(&(addlist[i].pfra_ip6addr), &sain6->sin6_addr,
sizeof(sain6->sin6_addr));
addlist[i].pfra_net = 128;
+ for (j = 0; j < cs; ++j)
+ if (!memcmp(&sain6->sin6_addr,
+ &(curlist[j].pfra_ip6addr),
+ sizeof(sain6->sin6_addr)))
+ break;
break;
default:
fatalx("sync_table: unknown address family");
break;
}
+ if (j != cs) curlist[j].pfra_fback = 1;
i++;
}
if (i != table->up)
@@ -224,16 +259,48 @@
if (ioctl(env->sc_pf->dev, DIOCRSETADDRS, &io) == -1)
fatal("sync_table: cannot set address list");
- if (rdr->conf.flags & F_STICKY) {
- if (ioctl(env->sc_pf->dev, DIOCCLRSRCNODES, 0) == -1)
- fatal("sync_table: cannot clear the tree of "
- "source tracking nodes");
- }
free(addlist);
log_debug("sync_table: table %s: %d added, %d deleted, %d changed",
io.pfrio_table.pfrt_name,
io.pfrio_nadd, io.pfrio_ndel, io.pfrio_nchange);
+
+ if (cs && (rdr->conf.flags & F_STICKY)) {
+
+ memset(&iok.psnk_src, 0, sizeof(iok.psnk_src));
+ memset(&iok.psnk_dst, 0xff, sizeof(iok.psnk_dst));
+ iok.psnk_src.port_op = PF_OP_NONE;
+ iok.psnk_dst.port[0] = rdr->conf.port;
+ iok.psnk_dst.neg = 0;
+ iok.psnk_dst.port_op = PF_OP_EQ;
+
+ for (i = 0; i < cs; ++i)
+ if (!curlist[i].pfra_fback) {
+ iok.psnk_af = curlist[i].pfra_af;
+ switch (iok.psnk_af) {
+ case AF_INET:
+ memcpy(&iok.psnk_dst.addr.v.a.addr.v4,
+ &curlist[i].pfra_ip4addr,
+ sizeof(curlist[i].pfra_ip4addr));
+ break;
+ case AF_INET6:
+ memcpy(&iok.psnk_dst.addr.v.a.addr.v6,
+ &curlist[i].pfra_ip6addr,
+ sizeof(curlist[i].pfra_ip6addr));
+ break;
+ default:
+ fatalx("sync_table: unknown address family");
+ break;
+ }
+
+ if (ioctl(env->sc_pf->dev, DIOCKILLSRCNODES, &iok) == -1)
+ fatal("sync_table: cannot remove"
+ "source from tracking list");
+ }
+
+ free(curlist);
+ }
+
return;
toolong: