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:

Reply via email to