Hi,

Is anyone interested in giving this conntracker a little swing ?
It's actualy my first attempt at netfilter hacking (or kernel
hacking for that matter), so any constructive criticism is more
than welcome.

One thing I am not too sure about is the locking I used when
adding an expected connection. I've based this code on the tftp
helper, which does not lock anything, although I fail to understand
why... Also, I hope I followed the correct way to add expectations
within the newnat framework.

Anyway here's a little more info for those of you claiming that
Quake III is already working fine through their firewalls :-) :

What happens in Internet multiplayer mode is, the client connects
to a master server on port 27950/UDP and requests a list of game
servers (IP address + UDP port pairs). The master server responds 
with one or more packets, each containing a bunch of ip address-port
pairs. This module will track these responses and add the necessary
expectations. It's imperative to patch the kernel with newnat
support before applying this patch.

The format of the master server response packet is:

- udp header: 8 bytes
- 4 bytes of filler info set to 0xFF, 0xFF, 0xFF, 0xFF
- The string "getserversResponse" (18 bytes)
- a number of occurences of the following sequence:
  - a "\" character
  - an IP address (4 bytes)
  - a port number (2 bytes)

One more thing: since all traffic is originated by the client,
I did not write a NAT module, since imho the normal NAT
framework is able to cope with this protocol.

Thanks to the people behind ethereal for including Quake III
protocol recognition in their sniffer.

Regards,
Filip

diff -urN -X dontdiff linux-2.4.18-newnat/net/ipv4/netfilter/ip_conntrack_quake3.c linux-2.4.18-newnat-quake3/net/ipv4/netfilter/ip_conntrack_quake3.c
--- linux-2.4.18-newnat/net/ipv4/netfilter/ip_conntrack_quake3.c	Thu Jan  1 01:00:00 1970
+++ linux-2.4.18-newnat-quake3/net/ipv4/netfilter/ip_conntrack_quake3.c	Thu Apr  4 03:05:03 2002
@@ -0,0 +1,151 @@
+/*
+ * Licensed under GNU GPL version 2 Copyright Filip Sneppe <[EMAIL PROTECTED]>
+ * Version: 0.0.1
+ *
+ * Thanks to the Ethereal folks for their analysis of the Quake3 protocol.
+ */
+
+#include <linux/module.h>
+#include <linux/ip.h>
+#include <linux/udp.h>
+
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
+
+DECLARE_LOCK(ip_quake3_lock);
+struct module *ip_conntrack_quake3 = THIS_MODULE;
+
+/* Don't confuse with 27960, often used as the Server Port */
+#define QUAKE3_MASTER_PORT 27950
+
+MODULE_AUTHOR("Filip Sneppe <[EMAIL PROTECTED]>");
+MODULE_DESCRIPTION("Netfilter connection tracking module for Quake III Arena");
+MODULE_LICENSE("GPL");
+
+#define MAX_PORTS 8
+static int ports[MAX_PORTS];
+#ifdef MODULE_PARM
+MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
+MODULE_PARM_DESC(ports, "port numbers of Quake III master servers");
+#endif
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+static struct quake3_search {
+	const char marker[4]; /* always 0xff 0xff 0xff 0xff ? */
+	const char *pattern;
+	size_t plen;
+} search = {
+	"****",
+	"getserversResponse",
+	sizeof("getserversResponse") - 1,
+};
+
+static int quake3_help(const struct iphdr *iph, size_t len,
+	struct ip_conntrack *ct,
+	enum ip_conntrack_info ctinfo)
+{
+	struct udphdr *udph = (void *)iph + iph->ihl * 4;
+	int dir = CTINFO2DIR(ctinfo);
+	struct ip_conntrack_expect expect, *exp = &expect;
+	int i;
+	
+        /* Until there's been traffic both ways, don't look in packets. note: it's UDP ! */
+	if (ctinfo != IP_CT_ESTABLISHED
+	    && ctinfo != IP_CT_IS_REPLY) {
+	        DEBUGP("ip_ct_quake3: not ok ! Conntrackinfo = %u\n", ctinfo);
+	        return NF_ACCEPT;
+	} else { DEBUGP("ip_ct_quake3: it's ok ! Conntrackinfo = %u\n", ctinfo); }
+/*
+	DEBUGP("%s --- %s\n", (const char *)udph+12, search.pattern);
+*/	
+	if (strnicmp((const char *)udph + 12, search.pattern, search.plen) == 0) {
+		DEBUGP("ip_ct_quake3: %u.%u.%u.%u:%u <-> %u.%u.%u.%u:%u - %u.%u.%u.%u:%u <-> %u.%u.%u.%u:%u\n",
+			NIPQUAD(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip),
+			ntohs(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port),
+			NIPQUAD(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip),
+			ntohs(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.udp.port),
+			NIPQUAD(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip),
+			ntohs(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.udp.port),
+			NIPQUAD(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip),
+			ntohs(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port));
+
+		for(i=31;    /* 8 bytes UDP hdr, 4 bytes filler, 18 bytes "getserversResponse", 1 byte "\" */
+		    i + 6 < ntohs(udph->len);
+		    i += 7) {
+			DEBUGP("Adding Server: %u-%u %u.%u.%u.%u : %u\n", i, ntohs(udph->len),
+			       NIPQUAD( (u_int32_t) *( (u_int32_t *)( (int)udph + i ) ) ), 
+			       ntohs((__u16) *( (__u16 *)( (int)udph + i + 4 ) ) ) );
+
+			memset(&expect, 0, sizeof(expect));
+
+			LOCK_BH(&ip_quake3_lock);
+			exp->tuple = ((struct ip_conntrack_tuple)
+				      { { ct->tuplehash[!dir].tuple.src.ip, { 0 } },
+				      { (u_int32_t) *((u_int32_t *)((int)udph + i)), 
+				        { (__u16) *((__u16 *)((int)udph+i+4)) }, 
+					IPPROTO_UDP } }
+				     );
+			exp->mask  = ((struct ip_conntrack_tuple)
+				     { { 0xFFFFFFFF, { 0 } },
+		                       { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }});
+			exp->expectfn = NULL;
+			ip_conntrack_expect_related(ct, &expect);
+			UNLOCK_BH(&ip_quake3_lock);
+		};
+
+	}
+	
+	return(NF_ACCEPT);
+}
+
+static struct ip_conntrack_helper quake3[MAX_PORTS];
+
+static void fini(void)
+{
+	int i;
+
+	for(i = 0 ; (i < MAX_PORTS) && ports[i] ; i++) {
+		DEBUGP("<1> Unregistering ip_conntrack_quake3.o for port %d\n",
+					ports[i]);
+		ip_conntrack_helper_unregister(&quake3[i]);
+	} 
+}
+
+static int __init init(void)
+{
+	int i, ret;
+
+	if(!ports[0])
+		ports[0]=QUAKE3_MASTER_PORT;
+
+	for(i = 0 ; (i < MAX_PORTS) && ports[i] ; i++) {
+		/* Create helper structure */
+		memset(&quake3[i], 0, sizeof(struct ip_conntrack_helper));
+
+		quake3[i].tuple.dst.protonum = IPPROTO_UDP;
+		quake3[i].tuple.src.u.udp.port = htons(ports[i]);
+		quake3[i].mask.dst.protonum = 0xFFFF;
+		quake3[i].mask.src.u.udp.port = 0xFFFF;
+		quake3[i].help = quake3_help;
+
+		DEBUGP("<1> Registering ip_conntrack_quake3.o for master port %d\n",
+					ports[i]);
+
+		ret=ip_conntrack_helper_register(&quake3[i]);
+		if(ret) {
+			fini();
+			return(ret);
+		}
+	}
+
+	return(0);
+}
+
+module_init(init);
+module_exit(fini);
  dep_tristate '  FTP protocol support' CONFIG_IP_NF_FTP $CONFIG_IP_NF_CONNTRACK
  dep_tristate '  Quake III protocol support' CONFIG_IP_NF_QUAKE3 
$CONFIG_IP_NF_CONNTRACK
CONFIG_IP_NF_FTP
Quake III Arena protocol support
CONFIG_IP_NF_QUAKE3
  Quake III Arena  connection tracking helper. This module allows for a
  stricter firewall rulebase if one only allows traffic to a master
  server. Connections to Quake III server IP addresses and ports returned
  by the master server will be tracked automatically.

  If you want to compile it as a module, say M here and read
  Documentation/modules.txt.  If unsure, say `Y'.

Author: Filip Sneppe <[EMAIL PROTECTED]>
Status: WorksForMe (tm)

This adds CONFIG_IP_NF_QUAKE3: a Quake III Arena support module for
netfilter connection tracking. There is no specific NAT module.

Quake III Arena Internet multiplayer works by querying an Internet
master server on UDP port 27950 for specific Quake III servers. The
master server responds with the IP addresses and UDP ports of
registered game servers. Those UDP ports can be randomly chosen
(although 27960 is often used).

This connection tracking module will listen for UDP replies
from a master server, and will add the necessary expectations
for connections from client to game servers.

Note: Requires newnat !
obj-$(CONFIG_IP_NF_NAT_IRC) += ip_nat_irc.o

# Quake III Arena support
obj-$(CONFIG_IP_NF_QUAKE3) += ip_conntrack_quake3.o

Reply via email to