To put an end to a teaser.

Along with Laurent and Victor, we've written a Netfilter connection tracking manipulation tool based on the fabulous Wolfenstein 3d game.

You need only two dependencies: SDL library and libnetfilter_conntrack.

Description from the release notes:

Tears were flowing from our bellowed
Administrators out there.

Connection tracking is not always easy,
hence Wolfotrack, the conntrack killer that
aims to reduce the firewall use difficulty
that many people complained about for years.
This software makes this time gone! We are now
enhancing netfilter at the user level.

The idea is simple: with statefull firewall such as Netfilter, the Linux kernel firewall, connection states are kept in memory and allow you to use this simple rule to only allow answers to a previously initiated connection:
# iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

Because of the great work put by Netfilter core team into nfnetlink, and especially with the nfnetlink_conntrack socket, it is made trivial to grab the information Netfilter has with any connection state.

For example this code registers the callback function that is then used to set the players connection:
void ct_list_create(void)
{
        int ret;
        u_int8_t family = AF_INET;

        h = nfct_open(CONNTRACK, 0);
        if (!h) {
                perror("nfct_open error: Oh my god! this is terrible! you cannot kill conntracks out from Netfilter!!");
                return;
        }
        nfct_callback_register(h, NFCT_T_ALL, ct_cb, NULL);
        ret = nfct_query(h, NFCT_Q_DUMP, &family);
        if ( ret == -1 ) {
                exit(EXIT_FAILURE);
        }
}


The, the callback prototype is:
int ct_cb(enum nf_conntrack_msg_type type,
                struct nf_conntrack *ct,
                void *data)

And to set the source/destination ip and port out from the nf_conntrack structure:
        if (nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO) == AF_INET) {
                ip_src = nfct_get_attr_u32(ct, ATTR_ORIG_IPV4_SRC);
                saddr = strdup(inet_ntoa(ip_src));
                ip_dst = nfct_get_attr_u32(ct, ATTR_ORIG_IPV4_DST);
                daddr = strdup(inet_ntoa(ip_dst));
                port_src = nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC);
                sport = strdup(port_ntoa(port_src));
                port_dst = nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST);
                dport = strdup(port_ntoa(port_dst));
        }


Finally, when you kill the actor, we run the following function:
void ct_remove_from_id(int id)
{
        if (ct_list_get(id))
                nfct_query(h, NFCT_Q_DESTROY, ct_list_get(id)->ct);
}


And that's all for the netfilter code (modulo a few tricks to set a connection tracking to a non-dead actor, ..). And here begins the Carmack magic...

I have a high respect for this guy, he started great games that I enjoyed playing in my childhood and I am still amazed by this:
i  = 0x5f3759df - ( i >> 1 );

Yes, this is the famous magic constant to calculate the square root of a number with NO loop of any sort.

Putting the hands in the code, there was no PrintXY, so we wrote the function:
void US_PrintXY(char *str, word X, word Y)
{
    char c, *se, *s, *sz = strdup(str);
    word w, h;
    s = sz;
   
    while (*s)
    {
        se = s;
        while ((c = *se) && (c != '\n'))
            se++;
        *se = '\0';

        USL_MeasureString(s,&w,&h);
        px = X;
        py = Y;
        USL_DrawString(s);

        s = se;
        if (c)
        {
            *se = c;
            s++;

            X = WindowX;
            Y += h;
        }
        else
            X += w;
    }
   
    px = X;
    py = Y;
   
    free(sz);
}


and in wl_draw.c, to draw the text about connection trackings, we need to have the actor in our visual spot:
        if (*visspot
        || (*(visspot-1) && !*(tilespot-1))
        || (*(visspot+1) && !*(tilespot+1))
        || (*(visspot-65) && !*(tilespot-65))
        || (*(visspot-64) && !*(tilespot-64))
        || (*(visspot-63) && !*(tilespot-63))
        || (*(visspot+65) && !*(tilespot+65))
        || (*(visspot+64) && !*(tilespot+64))
        || (*(visspot+63) && !*(tilespot+63)))


and there while browsing the linked list of every object, we need to know if this actor is not dead, so we added this function:
int ActorDead(objtype *obj)
{
        int retval = 0;

        switch(obj->state) {
                case s_grddie4:
                        retval = 1;
                        break;
                default:
                        retval = 0;
        }
        return retval;
}


and then, in the code we use it:
if ( ( obj->obclass == guardobj ) && ( ! ActorDead(obj)) ) {

ok, shame on me, we put the connection tracking only to guards. But if you want to improve the code, this is what you must patch.

And then, if we get an appropriate connection tracking object, we run:
source = malloc(strlen(entry->saddr) +
                strlen(":") +
                strlen(entry->sport) + 1);
target = malloc(strlen(entry->daddr) +
                strlen(":") +
                strlen(entry->dport) + 1);
sprintf(source, "%s:%s", ct_list_get(obj->id)->saddr, ct_list_get(obj->id)->sport );
sprintf(target, "%s:%s", ct_list_get(obj->id)->daddr, ct_list_get(obj->id)->dport );

SETFONTCOLOR(68, BKGDCOLOR);
US_PrintXY(source, 30, 20);
US_PrintXY(target, 30, 30);
SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR);

free(source);
free(target);


Then, we go into the function KillActor (objtype *ob) and if the object is a guard, we run the killing function:
ct_remove_from_id(ob->id);

And this is it! So in summary:
  • That was fun to do
  • The Wolf3d source code is crystal clear: I have never looked for hours where this or that function was. I am really amazed by the work done by the ID software team back in the early '90. Everything is very logical and I am not involved in video games in any way, so there is a lot of things I don't know
  • We need people to improve now. Please go to the Wolfotrack project page and download, send patches etc..

And congrats to Laurent and Victor, that was fun working in team on this kind of project ;-)