1/* 2 * H.323 'brute force' extension for H.323 connection tracking. 3 * Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> 4 * 5 * Based on ip_masq_h323.c for 2.2 kernels from CoRiTel, Sofia project. 6 * (http://www.coritel.it/projects/sofia/nat/) 7 * Uses Sampsa Ranta's excellent idea on using expectfn to 'bind' 8 * the unregistered helpers to the conntrack entries. 9 */ 10 11 12#include <linux/module.h> 13#include <linux/netfilter.h> 14#include <linux/ip.h> 15#include <net/checksum.h> 16#include <net/tcp.h> 17 18#include <linux/netfilter_ipv4/lockhelp.h> 19#include <linux/netfilter_ipv4/ip_conntrack.h> 20#include <linux/netfilter_ipv4/ip_conntrack_core.h> 21#include <linux/netfilter_ipv4/ip_conntrack_helper.h> 22#include <linux/netfilter_ipv4/ip_conntrack_tuple.h> 23#include <linux/netfilter_ipv4/ip_conntrack_h323.h> 24 25MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); 26MODULE_DESCRIPTION("H.323 'brute force' connection tracking module"); 27MODULE_LICENSE("GPL"); 28 29DECLARE_LOCK(ip_h323_lock); 30struct module *ip_conntrack_h323 = THIS_MODULE; 31 32#define DEBUGP(format, args...) 33 34static int h245_help(const struct iphdr *iph, size_t len, 35 struct ip_conntrack *ct, 36 enum ip_conntrack_info ctinfo) 37{ 38 struct tcphdr *tcph = (void *)iph + iph->ihl * 4; 39 unsigned char *data = (unsigned char *) tcph + tcph->doff * 4; 40 unsigned char *data_limit; 41 u_int32_t tcplen = len - iph->ihl * 4; 42 u_int32_t datalen = tcplen - tcph->doff * 4; 43 int dir = CTINFO2DIR(ctinfo); 44 struct ip_ct_h225_master *info = &ct->help.ct_h225_info; 45 struct ip_conntrack_expect expect, *exp = &expect; 46 struct ip_ct_h225_expect *exp_info = &exp->help.exp_h225_info; 47 u_int16_t data_port; 48 u_int32_t data_ip; 49 unsigned int i; 50 51 DEBUGP("ct_h245_help: help entered %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", 52 NIPQUAD(iph->saddr), ntohs(tcph->source), 53 NIPQUAD(iph->daddr), ntohs(tcph->dest)); 54 55 /* Can't track connections formed before we registered */ 56 if (!info) 57 return NF_ACCEPT; 58 59 /* Until there's been traffic both ways, don't look in packets. */ 60 if (ctinfo != IP_CT_ESTABLISHED 61 && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { 62 DEBUGP("ct_h245_help: Conntrackinfo = %u\n", ctinfo); 63 return NF_ACCEPT; 64 } 65 66 /* Not whole TCP header or too short packet? */ 67 if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4 + 5) { 68 DEBUGP("ct_h245_help: tcplen = %u\n", (unsigned)tcplen); 69 return NF_ACCEPT; 70 } 71 72 /* Checksum invalid? Ignore. */ 73 if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, 74 csum_partial((char *)tcph, tcplen, 0))) { 75 DEBUGP("ct_h245_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n", 76 tcph, tcplen, NIPQUAD(iph->saddr), 77 NIPQUAD(iph->daddr)); 78 return NF_ACCEPT; 79 } 80 81 data_limit = (unsigned char *) data + datalen; 82 /* bytes: 0123 45 83 ipadrr port */ 84 for (i = 0; data < (data_limit - 5); data++, i++) { 85 memcpy(&data_ip, data, sizeof(u_int32_t)); 86 if (data_ip == iph->saddr) { 87 memcpy(&data_port, data + 4, sizeof(u_int16_t)); 88 memset(&expect, 0, sizeof(expect)); 89 /* update the H.225 info */ 90 DEBUGP("ct_h245_help: new RTCP/RTP requested %u.%u.%u.%u:->%u.%u.%u.%u:%u\n", 91 NIPQUAD(ct->tuplehash[!dir].tuple.src.ip), 92 NIPQUAD(iph->saddr), ntohs(data_port)); 93 LOCK_BH(&ip_h323_lock); 94 info->is_h225 = H225_PORT + 1; 95 exp_info->port = data_port; 96 exp_info->dir = dir; 97 exp_info->offset = i; 98 99 exp->seq = ntohl(tcph->seq) + i; 100 101 exp->tuple = ((struct ip_conntrack_tuple) 102 { { ct->tuplehash[!dir].tuple.src.ip, 103 { 0 } }, 104 { data_ip, 105 { data_port }, 106 IPPROTO_UDP }}); 107 exp->mask = ((struct ip_conntrack_tuple) 108 { { 0xFFFFFFFF, { 0 } }, 109 { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }}); 110 111 exp->expectfn = NULL; 112 113 /* Ignore failure; should only happen with NAT */ 114 ip_conntrack_expect_related(ct, exp); 115 116 UNLOCK_BH(&ip_h323_lock); 117 } 118 } 119 120 return NF_ACCEPT; 121 122} 123 124/* H.245 helper is not registered! */ 125static struct ip_conntrack_helper h245 = 126 { { NULL, NULL }, 127 "H.245", /* name */ 128 IP_CT_HELPER_F_REUSE_EXPECT, /* flags */ 129 NULL, /* module */ 130 8, /* max_ expected */ 131 240, /* timeout */ 132 { { 0, { 0 } }, /* tuple */ 133 { 0, { 0 }, IPPROTO_TCP } }, 134 { { 0, { 0xFFFF } }, /* mask */ 135 { 0, { 0 }, 0xFFFF } }, 136 h245_help /* helper */ 137 }; 138 139static int h225_expect(struct ip_conntrack *ct) 140{ 141 WRITE_LOCK(&ip_conntrack_lock); 142 ct->helper = &h245; 143 DEBUGP("h225_expect: helper for %p added\n", ct); 144 WRITE_UNLOCK(&ip_conntrack_lock); 145 146 return NF_ACCEPT; /* unused */ 147} 148 149static int h225_help(const struct iphdr *iph, size_t len, 150 struct ip_conntrack *ct, 151 enum ip_conntrack_info ctinfo) 152{ 153 struct tcphdr *tcph = (void *)iph + iph->ihl * 4; 154 unsigned char *data = (unsigned char *) tcph + tcph->doff * 4; 155 unsigned char *data_limit; 156 u_int32_t tcplen = len - iph->ihl * 4; 157 u_int32_t datalen = tcplen - tcph->doff * 4; 158 int dir = CTINFO2DIR(ctinfo); 159 struct ip_ct_h225_master *info = &ct->help.ct_h225_info; 160 struct ip_conntrack_expect expect, *exp = &expect; 161 struct ip_ct_h225_expect *exp_info = &exp->help.exp_h225_info; 162 u_int16_t data_port; 163 u_int32_t data_ip; 164 unsigned int i; 165 166 DEBUGP("ct_h225_help: help entered %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", 167 NIPQUAD(iph->saddr), ntohs(tcph->source), 168 NIPQUAD(iph->daddr), ntohs(tcph->dest)); 169 170 /* Can't track connections formed before we registered */ 171 if (!info) 172 return NF_ACCEPT; 173 174 /* Until there's been traffic both ways, don't look in packets. */ 175 if (ctinfo != IP_CT_ESTABLISHED 176 && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { 177 DEBUGP("ct_h225_help: Conntrackinfo = %u\n", ctinfo); 178 return NF_ACCEPT; 179 } 180 181 /* Not whole TCP header or too short packet? */ 182 if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4 + 5) { 183 DEBUGP("ct_h225_help: tcplen = %u\n", (unsigned)tcplen); 184 return NF_ACCEPT; 185 } 186 187 /* Checksum invalid? Ignore. */ 188 if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, 189 csum_partial((char *)tcph, tcplen, 0))) { 190 DEBUGP("ct_h225_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n", 191 tcph, tcplen, NIPQUAD(iph->saddr), 192 NIPQUAD(iph->daddr)); 193 return NF_ACCEPT; 194 } 195 196 data_limit = (unsigned char *) data + datalen; 197 /* bytes: 0123 45 198 ipadrr port */ 199 for (i = 0; data < (data_limit - 5); data++, i++) { 200 memcpy(&data_ip, data, sizeof(u_int32_t)); 201 if (data_ip == iph->saddr) { 202 memcpy(&data_port, data + 4, sizeof(u_int16_t)); 203 if (data_port == tcph->source) { 204 /* Signal address */ 205 DEBUGP("ct_h225_help: sourceCallSignalAddress from %u.%u.%u.%u\n", 206 NIPQUAD(iph->saddr)); 207 /* Update the H.225 info so that NAT can mangle the address/port 208 even when we have no expected connection! */ 209#ifdef CONFIG_IP_NF_NAT_NEEDED 210 LOCK_BH(&ip_h323_lock); 211 info->dir = dir; 212 info->seq[IP_CT_DIR_ORIGINAL] = ntohl(tcph->seq) + i; 213 info->offset[IP_CT_DIR_ORIGINAL] = i; 214 UNLOCK_BH(&ip_h323_lock); 215#endif 216 } else { 217 memset(&expect, 0, sizeof(expect)); 218 219 /* update the H.225 info */ 220 LOCK_BH(&ip_h323_lock); 221 info->is_h225 = H225_PORT; 222 exp_info->port = data_port; 223 exp_info->dir = dir; 224 exp_info->offset = i; 225 226 exp->seq = ntohl(tcph->seq) + i; 227 228 exp->tuple = ((struct ip_conntrack_tuple) 229 { { ct->tuplehash[!dir].tuple.src.ip, 230 { 0 } }, 231 { data_ip, 232 { data_port }, 233 IPPROTO_TCP }}); 234 exp->mask = ((struct ip_conntrack_tuple) 235 { { 0xFFFFFFFF, { 0 } }, 236 { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }}); 237 238 exp->expectfn = h225_expect; 239 240 /* Ignore failure */ 241 ip_conntrack_expect_related(ct, exp); 242 243 DEBUGP("ct_h225_help: new H.245 requested %u.%u.%u.%u->%u.%u.%u.%u:%u\n", 244 NIPQUAD(ct->tuplehash[!dir].tuple.src.ip), 245 NIPQUAD(iph->saddr), ntohs(data_port)); 246 247 UNLOCK_BH(&ip_h323_lock); 248 } 249#ifdef CONFIG_IP_NF_NAT_NEEDED 250 } else if (data_ip == iph->daddr) { 251 memcpy(&data_port, data + 4, sizeof(u_int16_t)); 252 if (data_port == tcph->dest) { 253 /* Signal address */ 254 DEBUGP("ct_h225_help: destCallSignalAddress %u.%u.%u.%u\n", 255 NIPQUAD(iph->daddr)); 256 /* Update the H.225 info so that NAT can mangle the address/port 257 even when we have no expected connection! */ 258 LOCK_BH(&ip_h323_lock); 259 info->dir = dir; 260 info->seq[IP_CT_DIR_REPLY] = ntohl(tcph->seq) + i; 261 info->offset[IP_CT_DIR_REPLY] = i; 262 UNLOCK_BH(&ip_h323_lock); 263 } 264#endif 265 } 266 } 267 268 return NF_ACCEPT; 269 270} 271 272static struct ip_conntrack_helper h225 = 273 { { NULL, NULL }, 274 "H.225", /* name */ 275 IP_CT_HELPER_F_REUSE_EXPECT, /* flags */ 276 THIS_MODULE, /* module */ 277 2, /* max_expected */ 278 240, /* timeout */ 279 { { 0, { __constant_htons(H225_PORT) } }, /* tuple */ 280 { 0, { 0 }, IPPROTO_TCP } }, 281 { { 0, { 0xFFFF } }, /* mask */ 282 { 0, { 0 }, 0xFFFF } }, 283 h225_help /* helper */ 284 }; 285 286static int __init init(void) 287{ 288 return ip_conntrack_helper_register(&h225); 289} 290 291static void __exit fini(void) 292{ 293 /* Unregister H.225 helper */ 294 ip_conntrack_helper_unregister(&h225); 295} 296 297#ifdef CONFIG_IP_NF_NAT_NEEDED 298EXPORT_SYMBOL(ip_h323_lock); 299#endif 300 301module_init(init); 302module_exit(fini); 303