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