diff -Nur linux-2.6.14/include/linux/netfilter_ipv4/ip_conntrack_h323.h linux-2.6.14-h323/include/linux/netfilter_ipv4/ip_conntrack_h323.h --- linux-2.6.14/include/linux/netfilter_ipv4/ip_conntrack_h323.h 1969-12-31 18:00:00.000000000 -0600 +++ linux-2.6.14-h323/include/linux/netfilter_ipv4/ip_conntrack_h323.h 2005-12-31 16:11:48.602899418 -0600 @@ -0,0 +1,38 @@ +#ifndef _IP_CONNTRACK_H323_H +#define _IP_CONNTRACK_H323_H +/* H.323 connection tracking. */ + +#ifdef __KERNEL__ + +/* Default H.225 port */ +#define H225_PORT 1720 + +struct ip_conntrack_expect; +struct ip_conntrack; +struct ip_conntrack_helper; + +extern int (*ip_nat_h245_hook)(struct sk_buff **pskb, + enum ip_conntrack_info ctinfo, + unsigned int offset, + struct ip_conntrack_expect *exp); + +extern int (*ip_nat_h225_hook)(struct sk_buff **pskb, + enum ip_conntrack_info ctinfo, + unsigned int offset, + struct ip_conntrack_expect *exp); + +extern void (*ip_nat_h225_signal_hook)(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + unsigned int offset, + int dir, + int orig_dir); + +extern struct ip_conntrack_helper ip_conntrack_helper_h225; + +void ip_conntrack_h245_expect(struct ip_conntrack *new, + struct ip_conntrack_expect *this); + +#endif /* __KERNEL__ */ + +#endif /* _IP_CONNTRACK_H323_H */ diff -Nur linux-2.6.14/net/ipv4/netfilter/Kconfig linux-2.6.14-h323/net/ipv4/netfilter/Kconfig --- linux-2.6.14/net/ipv4/netfilter/Kconfig 2005-12-29 15:02:57.000000000 -0600 +++ linux-2.6.14-h323/net/ipv4/netfilter/Kconfig 2005-12-31 16:00:31.338838860 -0600 @@ -69,6 +69,24 @@ If you want to compile it as a module, say M here and read . If unsure, say `N'. +config IP_NF_NAT_H323 + tristate + depends on IP_NF_CONNTRACK!=n && IP_NF_NAT!=n + default IP_NF_NAT if IP_NF_H323=y + default m if IP_NF_H323=m + +config IP_NF_H323 + tristate 'H.323 (netmeeting) support' + depends on IP_NF_CONNTRACK + help + H.323 is a standard signalling protocol used by teleconferencing + softwares like netmeeting. With the ip_conntrack_h323 and + the ip_nat_h323 modules you can support the protocol on a connection + tracking/NATing firewall. + + If you want to compile it as a module, say 'M' here and read + Documentation/modules.txt. If unsure, say 'N'. + config IP_NF_FTP tristate "FTP protocol support" depends on IP_NF_CONNTRACK diff -Nur linux-2.6.14/net/ipv4/netfilter/Makefile linux-2.6.14-h323/net/ipv4/netfilter/Makefile --- linux-2.6.14/net/ipv4/netfilter/Makefile 2005-12-29 15:02:57.000000000 -0600 +++ linux-2.6.14-h323/net/ipv4/netfilter/Makefile 2005-12-31 16:00:55.811733444 -0600 @@ -20,6 +20,12 @@ # SCTP protocol connection tracking obj-$(CONFIG_IP_NF_CT_PROTO_SCTP) += ip_conntrack_proto_sctp.o +# H.323 support +obj-$(CONFIG_IP_NF_H323) += ip_conntrack_h323.o +ip_conntrack_h323-objs := ip_conntrack_h323_core.o ip_conntrack_h323_h225.o ip_conntrack_h323_h245.o asn1_per.o +obj-$(CONFIG_IP_NF_NAT_H323) += ip_nat_h323.o + + # connection tracking helpers obj-$(CONFIG_IP_NF_PPTP) += ip_conntrack_pptp.o obj-$(CONFIG_IP_NF_AMANDA) += ip_conntrack_amanda.o diff -Nur linux-2.6.14/net/ipv4/netfilter/asn1_per.c linux-2.6.14-h323/net/ipv4/netfilter/asn1_per.c --- linux-2.6.14/net/ipv4/netfilter/asn1_per.c 1969-12-31 18:00:00.000000000 -0600 +++ linux-2.6.14-h323/net/ipv4/netfilter/asn1_per.c 2005-12-31 15:59:21.996637836 -0600 @@ -0,0 +1,353 @@ +/* + * Tiny ASN.1 packet encoding rules (PER) library. + * + * This is a tiny library which helps parsing ASN.1/PER packets + * (i.e. read only). It is meant to be secure and small. + * + * Warning, this library may still be incomplete and buggy. + * + * (c) 2005 Max Kellermann + */ + +#include +#include + +#include "asn1_per.h" + +void asn1_per_initialize(struct asn1_per_buffer *bb, + const unsigned char *data, + unsigned length, unsigned position) { + *bb = (struct asn1_per_buffer){ + .data = data, + .length = length, + .i = position, + .bit = 8, + .error = 0, + }; +} + +int asn1_per_read_bit(struct asn1_per_buffer *bb) { + int value; + + if (bb->error) + return 0; + + if (bb->i >= bb->length) { + bb->error = 1; + return 0; + } + + bb->bit--; + + value = (bb->data[bb->i] & (1 << bb->bit)) != 0; + + if (bb->bit == 0) { + bb->bit = 8; + bb->i++; + } + + return value; +} + +unsigned asn1_per_read_bits(struct asn1_per_buffer *bb, unsigned count) { + unsigned value; + + if (bb->error) + return 0; + if (bb->i >= bb->length) { + bb->error = 1; + return 0; + } + + if (count > 32) { + /* XXX support more than 32 bits in the future here? */ + bb->error = 1; + return 0; + } + + if (count <= bb->bit) { + value = (bb->data[bb->i] >> (bb->bit - count)) & (0xff >> (8 - count)); + + bb->bit -= count; + if (bb->bit == 0) { + bb->bit = 8; + bb->i++; + } + + return value; + } + + count -= bb->bit; + + value = bb->data[bb->i] & (0xff >> (8 - bb->bit)); + bb->i++; + + while (count >= 8) { + if (bb->i >= bb->length) { + bb->error = 1; + return 0; + } + + value = (value << 8) | bb->data[bb->i]; + + bb->i++; + count -= 8; + } + + if (count > 0) { + if (bb->i >= bb->length) { + bb->error = 1; + return 0; + } + + value = (value << count) | (bb->data[bb->i] >> (8 - count)); + } + + bb->bit = 8 - count; + + return value; +} + +void asn1_per_read_bitmap(struct asn1_per_buffer *bb, unsigned count, + struct asn1_per_bitmap *bitmap) { + unsigned char *p; + + memset(bitmap, 0, sizeof(*bitmap)); + + if (bb->error) + return; + + if (count > sizeof(bitmap->data) * 8) { + /* XXX limited bit map support */ + bb->error = 1; + return; + } + + for (p = bitmap->data; count > 8; count -= 8) + *p++ = (unsigned char)asn1_per_read_bits(bb, 8); + + if (count > 0) + *p = asn1_per_read_bits(bb, count) << (8 - count); + + return; +} + +void asn1_per_read_bytes(struct asn1_per_buffer *bb, + void *buffer, unsigned count) { + if (bb->error) + return; + + if (bb->bit != 8) { + bb->error = 1; + return; + } + + if (bb->i + count > bb->length) { + bb->error = 1; + return; + } + + memcpy(buffer, bb->data + bb->i, count); + + bb->i += count; +} + +void asn1_per_byte_align(struct asn1_per_buffer *bb) { + if (bb->bit < 8) { + bb->bit = 8; + bb->i++; + } +} + +static unsigned count_bits(unsigned range) { + unsigned bits = 0; + + if (range == 0) + return 32; + + if (range == 1) + return 1; + + while (bits < 32 && range > (unsigned)(1 << bits)) + bits++; + + return bits; +} + +unsigned asn1_per_read_unsigned(struct asn1_per_buffer *bb, + unsigned lower, unsigned upper) { + unsigned range = (upper - lower) + 1; + unsigned bits = count_bits(range); + + if (lower == upper) + return lower; + + if (range == 0 || range > 255) { + if (bits > 16) + bits = asn1_per_read_length(bb, 1, (bits+7)/8) * 8; + else if (bits > 8) + bits = 16; + asn1_per_byte_align(bb); + } + + return lower + asn1_per_read_bits(bb, bits); +} + +unsigned asn1_per_read_length(struct asn1_per_buffer *bb, + unsigned lower, unsigned upper) { + if (upper < 65536) + return asn1_per_read_unsigned(bb, lower, upper); + + asn1_per_byte_align(bb); + + if (!asn1_per_read_bit(bb)) + return asn1_per_read_bits(bb, 7); + + if (!asn1_per_read_bit(bb)) + return asn1_per_read_bits(bb, 14); + + bb->error = 1; + return 0; +} + +unsigned asn1_per_read_small(struct asn1_per_buffer *bb) { + unsigned length; + + if (!asn1_per_read_bit(bb)) + return asn1_per_read_bits(bb, 6); + + length = asn1_per_read_length(bb, 0, INT_MAX); + + asn1_per_byte_align(bb); + + return asn1_per_read_bits(bb, length * 8); +} + +unsigned asn1_per_read_choice_header(struct asn1_per_buffer *bb, + int extendable, + unsigned options, unsigned *after) { + int extended; + unsigned choice; + + extended = extendable && asn1_per_read_bit(bb); + if (extended) { + unsigned length; + + choice = asn1_per_read_small(bb) + options; + length = asn1_per_read_length(bb, 0, INT_MAX); + *after = bb->i + length; + } else if (options < 2) { + choice = 0; + *after = 0; + } else { + choice = asn1_per_read_bits(bb, count_bits(options)); + *after = 0; + } + + return choice; +} + +void asn1_per_read_sequence_header(struct asn1_per_buffer *bb, int extendable, + unsigned optional_count, + struct asn1_per_sequence_header *hdr) { + hdr->extended = extendable && asn1_per_read_bit(bb); + asn1_per_read_bitmap(bb, optional_count, &hdr->present); +} + +void asn1_per_read_sequence_extension_header(struct asn1_per_buffer *bb, + const struct asn1_per_sequence_header *hdr, + struct asn1_per_sequence_extension_header *ext) { + if (!hdr->extended) { + memset(ext, 0, sizeof(*ext)); + return; + } + + ext->count = asn1_per_read_small(bb) + 1; + if (bb->error) + return; + + asn1_per_read_bitmap(bb, ext->count, &ext->present); +} + +void asn1_per_skip_sequence_extension(struct asn1_per_buffer *bb, + const struct asn1_per_sequence_header *hdr) { + struct asn1_per_sequence_extension_header ext; + unsigned i; + + asn1_per_read_sequence_extension_header(bb, hdr, &ext); + if (bb->error) + return; + + for (i = 0; i < ext.count && !bb->error; i++) { + if (asn1_per_bitmap_get(&ext.present, i)) + asn1_per_skip_octet_string(bb); + } +} + +void asn1_per_skip_object_id(struct asn1_per_buffer *bb) { + unsigned length; + + length = asn1_per_read_length(bb, 0, 255); + switch (length) { + case 0: + break; + + case 1: + asn1_per_read_bits(bb, 8); + break; + + case 2: + asn1_per_read_bits(bb, 16); + break; + + default: + asn1_per_byte_align(bb); + + bb->i += length; + if (bb->i > bb->length) + bb->error = 1; + } +} + +unsigned asn1_per_read_octet_string_header(struct asn1_per_buffer *bb) { + unsigned length; + + length = asn1_per_read_length(bb, 0, INT_MAX); + if (length > 2) + asn1_per_byte_align(bb); + + return length; +} + +void asn1_per_skip_octet_string(struct asn1_per_buffer *bb) { + unsigned length; + + length = asn1_per_read_length(bb, 0, INT_MAX); + switch (length) { + case 0: + break; + + case 1: + asn1_per_read_bits(bb, 8); + break; + + case 2: + asn1_per_read_bits(bb, 16); + break; + + default: + asn1_per_byte_align(bb); + + bb->i += length; + if (bb->i > bb->length) + bb->error = 1; + } +} + + +int asn1_per_bitmap_get(const struct asn1_per_bitmap *bitmap, unsigned i) { + if (i >= sizeof(bitmap->data) * 8) + return 0; + + return (bitmap->data[i / 8] & (1 << (7 - (i % 8)))) != 0; +} diff -Nur linux-2.6.14/net/ipv4/netfilter/asn1_per.h linux-2.6.14-h323/net/ipv4/netfilter/asn1_per.h --- linux-2.6.14/net/ipv4/netfilter/asn1_per.h 1969-12-31 18:00:00.000000000 -0600 +++ linux-2.6.14-h323/net/ipv4/netfilter/asn1_per.h 2005-12-31 15:59:22.004636821 -0600 @@ -0,0 +1,83 @@ +/* + * Tiny ASN.1 packet encoding rules (PER) library. + * + * This is a tiny library which helps parsing ASN.1/PER packets + * (i.e. read only). It is meant to be secure and small. + * + * Warning, this library may still be incomplete and buggy. + * + * (c) 2005 Max Kellermann + */ + +#ifndef __ASN1_PER_H +#define __ASN1_PER_H + +struct asn1_per_buffer { + const unsigned char *data; + unsigned length, i, bit; + int error; +}; + +struct asn1_per_bitmap { + unsigned char data[16]; +}; + +struct asn1_per_sequence_header { + int extended; + struct asn1_per_bitmap present; +}; + +struct asn1_per_sequence_extension_header { + unsigned count; + struct asn1_per_bitmap present; +}; + +void asn1_per_initialize(struct asn1_per_buffer *bb, + const unsigned char *data, + unsigned length, unsigned position); + +int asn1_per_read_bit(struct asn1_per_buffer *bb); + +unsigned asn1_per_read_bits(struct asn1_per_buffer *bb, unsigned count); + +void asn1_per_read_bitmap(struct asn1_per_buffer *bb, unsigned count, + struct asn1_per_bitmap *bitmap); + +void asn1_per_read_bytes(struct asn1_per_buffer *bb, + void *buffer, unsigned count); + +void asn1_per_byte_align(struct asn1_per_buffer *bb); + +unsigned asn1_per_read_unsigned(struct asn1_per_buffer *bb, + unsigned lower, unsigned upper); + +unsigned asn1_per_read_length(struct asn1_per_buffer *bb, + unsigned lower, unsigned upper); + +unsigned asn1_per_read_small(struct asn1_per_buffer *bb); + +unsigned asn1_per_read_choice_header(struct asn1_per_buffer *bb, + int extendable, + unsigned options, unsigned *after); + +void asn1_per_read_sequence_header(struct asn1_per_buffer *bb, int extendable, + unsigned optional_count, + struct asn1_per_sequence_header *hdr); + +void asn1_per_read_sequence_extension_header(struct asn1_per_buffer *bb, + const struct asn1_per_sequence_header *hdr, + struct asn1_per_sequence_extension_header *ext); + +void asn1_per_skip_sequence_extension(struct asn1_per_buffer *bb, + const struct asn1_per_sequence_header *hdr); + +void asn1_per_skip_object_id(struct asn1_per_buffer *bb); + +unsigned asn1_per_read_octet_string_header(struct asn1_per_buffer *bb); + +void asn1_per_skip_octet_string(struct asn1_per_buffer *bb); + + +int asn1_per_bitmap_get(const struct asn1_per_bitmap *bitmap, unsigned i); + +#endif diff -Nur linux-2.6.14/net/ipv4/netfilter/ip_conntrack_h323_core.c linux-2.6.14-h323/net/ipv4/netfilter/ip_conntrack_h323_core.c --- linux-2.6.14/net/ipv4/netfilter/ip_conntrack_h323_core.c 1969-12-31 18:00:00.000000000 -0600 +++ linux-2.6.14-h323/net/ipv4/netfilter/ip_conntrack_h323_core.c 2005-12-31 15:59:36.382812345 -0600 @@ -0,0 +1,37 @@ +/* + * H.323 connection tracking helper + * (c) 2005 Max Kellermann + * + * Based on the 'brute force' H.323 connection tracking module by + * Jozsef Kadlecsik + */ + + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Max Kellermann "); +MODULE_DESCRIPTION("H.323 connection tracking helper"); +MODULE_LICENSE("GPL"); + +static int __init init(void) +{ + return ip_conntrack_helper_register(&ip_conntrack_helper_h225); +} + +static void __exit fini(void) +{ + ip_conntrack_helper_unregister(&ip_conntrack_helper_h225); +} + +module_init(init); +module_exit(fini); diff -Nur linux-2.6.14/net/ipv4/netfilter/ip_conntrack_h323_h225.c linux-2.6.14-h323/net/ipv4/netfilter/ip_conntrack_h323_h225.c --- linux-2.6.14/net/ipv4/netfilter/ip_conntrack_h323_h225.c 1969-12-31 18:00:00.000000000 -0600 +++ linux-2.6.14-h323/net/ipv4/netfilter/ip_conntrack_h323_h225.c 2005-12-31 16:04:26.608984943 -0600 @@ -0,0 +1,414 @@ +/* + * H.323/H.225 connection tracking helper + * (c) 2005 Max Kellermann + * + * Uses Sampsa Ranta's excellent idea on using expectfn to 'bind' + * the unregistered helpers to the conntrack entries. + * + * NOTICE: + * modified by Moises Silva + * to work with kernel 2.6.14. + * changes are: + * ip_conntrack_expect_free() was replaced by ip_conntrack_expect_put() + * ip_conntrack_expect_alloc() now receives as argument a reference + * to the connection tracking in order to modify the "master" member + * automatically + */ + + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "asn1_per.h" + +/* This is slow, but it's simple. --RR */ +static char h225_buffer[65536]; + +static DEFINE_SPINLOCK(ip_h225_lock); + +int (*ip_nat_h225_hook)(struct sk_buff **pskb, + enum ip_conntrack_info ctinfo, + unsigned int offset, + struct ip_conntrack_expect *exp); +EXPORT_SYMBOL_GPL(ip_nat_h225_hook); + +void (*ip_nat_h225_signal_hook)(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + unsigned int offset, + int dir, + int orig_dir); +EXPORT_SYMBOL_GPL(ip_nat_h225_signal_hook); + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +/** + * Parse an H.225 TransportAddress and return the position of the IP + * address (if present). Returns 1 on success. + */ +static int h225_parse_transport_address(struct asn1_per_buffer *bb, unsigned *i, + u_int32_t *ip, u_int16_t *port) { + unsigned choice, after; + + choice = asn1_per_read_choice_header(bb, 1, 7, &after); + if (bb->error) + return 0; + + switch (choice) { + case 0: /* ipAddress */ + asn1_per_byte_align(bb); + *i = bb->i; + asn1_per_read_bytes(bb, ip, sizeof(*ip)); + asn1_per_read_bytes(bb, port, sizeof(*port)); + return !bb->error; + + default: + if (after == 0) { + DEBUGP("TransportAddress %u not yet supported\n", choice); + bb->error = 1; + } else { + bb->i = after; + } + return 0; + } +} + +/** + * Parse a H.225 Connect-UUIE packet and handle NAT/expectations for + * the H.245 transport address. + */ +static int h225_parse_connect_uuie(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + struct asn1_per_buffer *bb) { + struct asn1_per_sequence_header hdr; + + asn1_per_read_sequence_header(bb, 1, 1, &hdr); + + /* protocolIdentifier */ + asn1_per_skip_object_id(bb); + + /* h245Address */ + if (asn1_per_bitmap_get(&hdr.present, 0)) { + int dir = CTINFO2DIR(ctinfo); + struct ip_conntrack_expect *exp; + int ret; + unsigned i; + u_int32_t ip; + u_int16_t port; + + ret = h225_parse_transport_address(bb, &i, &ip, &port); + if (ret) { + DEBUGP("H.245 transportAddress: %u.%u.%u.%u:%u\n", + NIPQUAD(ip), ntohs(port)); + } + if (ret && ip == ct->tuplehash[dir].tuple.src.ip) { + /* match found: create an expectation */ + exp = ip_conntrack_expect_alloc(ct); + if (exp == NULL) + return NF_ACCEPT; + + exp->tuple = ((struct ip_conntrack_tuple) + { { ct->tuplehash[!dir].tuple.src.ip, + { 0 } }, + { ct->tuplehash[!dir].tuple.dst.ip, + { .tcp = { port } }, + IPPROTO_TCP }}); + exp->mask = ((struct ip_conntrack_tuple) + { { 0xFFFFFFFF, { 0 } }, + { 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFF }}); + + exp->expectfn = ip_conntrack_h245_expect; + /*exp->master = ct;*/ + + /* call NAT hook and register expectation */ + if (ip_nat_h225_hook != NULL) { + return ip_nat_h225_hook(pskb, ctinfo, i, + exp); + } else { + /* Can't expect this? Best to drop packet now. */ + if (ip_conntrack_expect_related(exp) != 0) { + ip_conntrack_expect_put(exp); + return NF_DROP; + } else { + return NF_ACCEPT; + } + } + } + } + + /* XXX */ + bb->error = 1; + + asn1_per_skip_sequence_extension(bb, &hdr); + + return NF_ACCEPT; +} + +/** + * Parse a H.225 H323-UU-PDU packet and handle NAT/expectations for + * the H.245 transport address. + */ +static int h225_parse_uu_pdu(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + struct asn1_per_buffer *bb) { + struct asn1_per_sequence_header hdr; + unsigned choice, after; + int ret; + + asn1_per_read_sequence_header(bb, 1, 1, &hdr); + + /* h323-message-body */ + choice = asn1_per_read_choice_header(bb, 1, 7, &after); + switch (choice) { + case 2: /* connect */ + ret = h225_parse_connect_uuie(pskb, ct, ctinfo, bb); + if (ret != NF_ACCEPT) + return ret; + break; + + default: + if (after == 0) { + bb->error = 1; + return NF_ACCEPT; + } + + bb->i = after; + } + + asn1_per_skip_sequence_extension(bb, &hdr); + + return NF_ACCEPT; +} + +/** + * Parse a H.225 packet and handle NAT/expectations for the H.245 + * transport address. + */ +static int h225_parse(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + struct asn1_per_buffer *bb) { + struct asn1_per_sequence_header hdr; + + asn1_per_read_sequence_header(bb, 1, 1, &hdr); + + return h225_parse_uu_pdu(pskb, ct, ctinfo, bb); +} + +/** + * Parse a Q.931 CONNECT packet and handle NAT/expectations for the + * H.245 transport address. + */ +static int h225_parse_q931_connect(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + const unsigned char *data, + unsigned i, unsigned length) +{ + struct asn1_per_buffer bb; + + if (i + 2 > length) + return NF_ACCEPT; + + if (data[i++] != 0x05) /* X.208 / X.209 */ + return NF_ACCEPT; + + asn1_per_initialize(&bb, data, length, i); + + return h225_parse(pskb, ct, ctinfo, &bb); +} + +/** + * Scan a Q.931 packet for a user-to-user information element + * (IE). Return the index, or 0 if none found. + */ +static unsigned q931_find_u2u(const unsigned char *data, + unsigned datalen, + unsigned int i, + unsigned *lengthp) { + unsigned char type; + unsigned length; + + /* traverse all Q.931 information elements (IE) */ + while (i + 2 <= datalen) { + type = data[i++]; + + /* highest bit set means one-byte IE */ + if (type & 0x80) + continue; + + length = data[i++]; + + if (type == 0x7e) { /* user-to-user */ + /* user-to-user IEs have a 16 bit length + field */ + length = (length << 8) | data[i++]; + if (i + length > datalen) + return 0; + + *lengthp = length; + return i; + } + + i += length; + } + + return 0; +} + +/** + * Parse a Q.931/H.225 packet and handle NAT/expectations for the + * H.245 transport address (if applicable). + */ +static int h225_parse_q931(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + const unsigned char *data, + unsigned datalen, unsigned i) { + u_int8_t q931_message_type; + unsigned length; + + if (i + 3 > datalen) + return NF_ACCEPT; + + /* parse Q.931 packet */ + if (data[i++] != 0x08) /* protocol discriminator */ + return NF_ACCEPT; + + /* call reference */ + i += 1 + data[i]; + if (i >= datalen) + return NF_ACCEPT; + + /* only some Q.931 message types can contain a H.245 transport + address - we can ignore the rest in this module */ + q931_message_type = data[i++]; + if (q931_message_type == 0x07) { + /* CONNECT */ + + /* find a user-to-user information element (IE) */ + i = q931_find_u2u(data, datalen, i, &length); + if (i == 0) + return NF_ACCEPT; + + /* the length returned by q931_find_u2u() is relative + to i */ + length += i; + + return h225_parse_q931_connect(pskb, ct, ctinfo, + data, i, length); + } else { + /* XXX handle q931_message_type 0x01, 0x02, 0x03 */ + return NF_ACCEPT; + } +} + +/** + * Parse a TPKT/Q.931/H.225 packet and handle NAT/expectations for the + * H.245 transport address (if applicable). + */ +static int h225_parse_tpkt(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + const unsigned char *data, + unsigned datalen) { + unsigned int i = 0; + u_int16_t tpkt_len; + + if (i + 4 > datalen) + return NF_ACCEPT; + + /* expect TPKT header, see RFC 1006 */ + if (data[0] != 0x03 || data[1] != 0x00) + return NF_ACCEPT; + + i += 2; + + tpkt_len = ntohs(*(u_int16_t*)(data + i)); + if (tpkt_len < datalen) + datalen = tpkt_len; + + i += 2; + + /* parse Q.931 packet */ + return h225_parse_q931(pskb, ct, ctinfo, + data, datalen, i); +} + +static int h225_help(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) +{ + struct tcphdr _tcph, *tcph; + unsigned char *data; + unsigned dataoff, datalen; + int ret = NF_ACCEPT; + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + DEBUGP("ct_h225_help: Conntrackinfo = %u\n", ctinfo); + return NF_ACCEPT; + } + + tcph = skb_header_pointer((*pskb), (*pskb)->nh.iph->ihl*4, + sizeof(_tcph), &_tcph); + if (tcph == NULL) + return NF_ACCEPT; + + DEBUGP("ct_h225_help: help entered %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD((*pskb)->nh.iph->saddr), ntohs(tcph->source), + NIPQUAD((*pskb)->nh.iph->daddr), ntohs(tcph->dest)); + + dataoff = (*pskb)->nh.iph->ihl*4 + tcph->doff*4; + /* No data? */ + if (dataoff >= (*pskb)->len) { + DEBUGP("ct_h225_help: skblen = %u\n", (*pskb)->len); + return NF_ACCEPT; + } + datalen = (*pskb)->len - dataoff; + + if (datalen < 16) + return NF_ACCEPT; + + /* get data portion, and evaluate it */ + spin_lock_bh(&ip_h225_lock); + data = skb_header_pointer((*pskb), dataoff, + datalen, h225_buffer); + BUG_ON(data == NULL); + + ret = h225_parse_tpkt(pskb, ct, ctinfo, + data, datalen); + + spin_unlock_bh(&ip_h225_lock); + return ret; +} + +struct ip_conntrack_helper ip_conntrack_helper_h225 = +{ + .name = "H.225", + .me = THIS_MODULE, + .max_expected = 2, + .timeout = 240, + .tuple = { .src = { .u = { __constant_htons(H225_PORT) } }, + .dst = { .protonum = IPPROTO_TCP } }, + .mask = { .src = { .u = { 0xFFFF } }, + .dst = { .protonum = 0xFF } }, + .help = h225_help +}; +EXPORT_SYMBOL_GPL(ip_conntrack_helper_h225); diff -Nur linux-2.6.14/net/ipv4/netfilter/ip_conntrack_h323_h245.c linux-2.6.14-h323/net/ipv4/netfilter/ip_conntrack_h323_h245.c --- linux-2.6.14/net/ipv4/netfilter/ip_conntrack_h323_h245.c 1969-12-31 18:00:00.000000000 -0600 +++ linux-2.6.14-h323/net/ipv4/netfilter/ip_conntrack_h323_h245.c 2005-12-31 16:04:26.610984689 -0600 @@ -0,0 +1,968 @@ +/* + * H.323/H.245 connection tracking helper + * (c) 2005 Max Kellermann + * + * Based on the 'brute force' H.323 connection tracking module by + * Jozsef Kadlecsik + * NOTICE: + * modified by Moises Silva + * to work with kernel 2.6.14. + * changes are: + * ip_conntrack_expect_free() was replaced by ip_conntrack_expect_put() + * ip_conntrack_expect_alloc() now receives as argument a reference + * to the connection tracking in order to modify the "master" member + * automatically + * + */ + + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "asn1_per.h" + +/* This is slow, but it's simple. --RR */ +static char h245_buffer[65536]; + +static DEFINE_SPINLOCK(ip_h245_lock); + +struct module *ip_conntrack_h245 = THIS_MODULE; + +int (*ip_nat_h245_hook)(struct sk_buff **pskb, + enum ip_conntrack_info ctinfo, + unsigned int offset, + struct ip_conntrack_expect *exp); +EXPORT_SYMBOL_GPL(ip_nat_h245_hook); + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +/** + * Skip an H.245 NonStandardIdentifier, discarding its value. + */ +static void h245_skip_nonstandard_id(struct asn1_per_buffer *bb) { + unsigned choice; + + choice = asn1_per_read_bits(bb, 1); + switch (choice) { + case 0: + asn1_per_skip_object_id(bb); + break; + + case 1: + asn1_per_read_unsigned(bb, 0, 255); + asn1_per_read_unsigned(bb, 0, 255); + asn1_per_read_unsigned(bb, 0, 65535); + break; + } +} + +/** + * Skip an H.245 NonStandardParameter, discarding its value. + */ +static void h245_skip_nonstandard_param(struct asn1_per_buffer *bb) { + h245_skip_nonstandard_id(bb); + asn1_per_skip_octet_string(bb); +} + +/** + * Skip an H.245 VideoCapability, discarding its value. + */ +static void h245_skip_video_capability(struct asn1_per_buffer *bb) { + unsigned choice, after; + + choice = asn1_per_read_choice_header(bb, 1, 5, &after); + DEBUGP("video_capability: choice=%u after=%u error=%d\n", + choice, after, bb->error); + + if (bb->error) + return; + + /* XXX support the rest */ + switch (choice) { + case 0: /* nonStandard */ + h245_skip_nonstandard_param(bb); + break; + + default: + if (after == 0) { + DEBUGP("unsupported audio_capability %u\n", choice); + bb->error = 1; + } + } + + if (after > 0) + bb->i = after; +} + +/** + * Skip an H.245 AudioCapability, discarding its value. + */ +static void h245_skip_audio_capability(struct asn1_per_buffer *bb) { + unsigned choice, after; + + choice = asn1_per_read_choice_header(bb, 1, 14, &after); + DEBUGP("audio_capability: audio_capability=%u after=%u error=%d\n", choice, after, bb->error); + + if (bb->error) + return; + + /* XXX support the rest */ + switch (choice) { + unsigned value; + + case 0: /* nonStandard */ + h245_skip_nonstandard_param(bb); + break; + + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 9: + case 10: + case 11: + case 14: + case 17: + value = asn1_per_read_unsigned(bb, 1, 256); + DEBUGP("value %u = %u\n", choice, value); + break; + default: + if (after == 0) { + DEBUGP("unsupported audio_capability %u\n", choice); + bb->error = 1; + } + } + + if (after > 0) + bb->i = after; +} + +/** + * Skip an H.245 DataType, discarding its value. + */ +static void h245_skip_data_type(struct asn1_per_buffer *bb) { + unsigned choice, after; + + choice = asn1_per_read_choice_header(bb, 1, 6, &after); + + if (bb->error) + return; + + /* XXX support the rest */ + switch (choice) { + case 0: /* nonStandard */ + h245_skip_nonstandard_param(bb); + break; + + case 1: /* nullData */ + break; + + case 2: /* videoData */ + h245_skip_video_capability(bb); + break; + + case 3: /* audioData */ + h245_skip_audio_capability(bb); + break; + + default: + if (after == 0) { + DEBUGP("unsupported data_type %u\n", choice); + bb->error = 1; + } + } + + if (after > 0) + bb->i = after; +} + +/** + * Parse an H.245 UnicastAddress and return the position of the IP + * address (if present). Returns 1 on success. + */ +static int h245_parse_unicast_address(struct asn1_per_buffer *bb, unsigned *i, + u_int32_t *ip, u_int16_t *port) { + unsigned choice, after; + + choice = asn1_per_read_choice_header(bb, 1, 5, &after); + DEBUGP("Parsing UnicastAddress choice=%u after=%u\n", choice, after); + switch (choice) { + case 0: /* iPAddress */ + asn1_per_read_bit(bb); /* XXX use this bit */ + asn1_per_byte_align(bb); + *i = bb->i; + asn1_per_read_bytes(bb, ip, sizeof(*ip)); + asn1_per_read_bytes(bb, port, sizeof(*port)); + return !bb->error; + default: + if (after == 0) { + DEBUGP("UnicastAddress %u not yet supported\n", choice); + bb->error = 1; + } else { + bb->i = after; + } + return 0; + } +} + +/** + * Parse an H.245 TransportAddress and return the position of the + * Unicast IP address (if present). Returns 1 on success. + */ +static int h245_parse_transport_address(struct asn1_per_buffer *bb, unsigned *i, + u_int32_t *ip, u_int16_t *port) { + unsigned choice, after; + + choice = asn1_per_read_choice_header(bb, 1, 2, &after); + switch (choice) { + case 0: /* UnicastAddress */ + return h245_parse_unicast_address(bb, i, ip, port); + case 1: /* MulticastAddress */ + /* XXX */ + DEBUGP("MulticastAddress not yet supported\n"); + bb->error = 1; + return 0; + default: + if (after == 0) { + DEBUGP("ERROR7\n"); + bb->error = 1; + } else { + bb->i = after; + } + return 0; + } +} + +/** + * Skip an H.245 TerminalLabel, discarding its value. + */ +static void h245_skip_terminal_label(struct asn1_per_buffer *bb) { + struct asn1_per_sequence_header hdr; + + asn1_per_read_sequence_header(bb, 1, 0, &hdr); + + /* mcuNumber */ + asn1_per_read_unsigned(bb, 0, 192); + /* terminalNumber */ + asn1_per_read_unsigned(bb, 0, 192); + + asn1_per_skip_sequence_extension(bb, &hdr); +} + +/** + * Parse an H.245 H2250LogicalChannelParameters request packet and + * handle NAT/expectations for the logical channel address. + */ +static int h245_parse_h2250_lchannel_params(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + struct asn1_per_buffer *bb) { + struct asn1_per_sequence_header hdr; + unsigned session_id; + + asn1_per_read_sequence_header(bb, 1, 10, &hdr); + + /* nonStandard */ + if (asn1_per_bitmap_get(&hdr.present, 0)) + h245_skip_nonstandard_param(bb); + + /* sessionID */ + session_id = asn1_per_read_unsigned(bb, 0, 255); + + /* associatedSessionID */ + if (asn1_per_bitmap_get(&hdr.present, 1)) + asn1_per_read_unsigned(bb, 1, 255); + + /* mediaChannel */ + DEBUGP("lchannel_params mediaChannel: i=%u bit=%u\n", bb->i, bb->bit); + if (asn1_per_bitmap_get(&hdr.present, 2)) { + int dir = CTINFO2DIR(ctinfo); + struct ip_conntrack_expect *exp; + int ret; + unsigned i; + u_int32_t ip; + u_int16_t port; + + ret = h245_parse_transport_address(bb, &i, &ip, &port); + if (ret) + DEBUGP("mediaChannel IPv4 address: %u.%u.%u.%u:%u\n", + NIPQUAD(ip), ntohs(port)); + if (ret && ip == ct->tuplehash[dir].tuple.src.ip) { + /* match found: create an expectation */ + exp = ip_conntrack_expect_alloc(ct); + if (exp == NULL) + return NF_ACCEPT; + + exp->tuple = ((struct ip_conntrack_tuple) + { { ct->tuplehash[!dir].tuple.src.ip, + { 0 } }, + { ct->tuplehash[!dir].tuple.dst.ip, + { .udp = { port } }, + IPPROTO_UDP }}); + exp->mask = ((struct ip_conntrack_tuple) + { { 0xFFFFFFFF, { 0 } }, + { 0xFFFFFFFF, { .udp = { 0xFFFF } }, 0xFF }}); + + /*exp->master = ct;*/ + + /* call NAT hook and register expectation */ + if (ip_nat_h245_hook != NULL) { + return ip_nat_h245_hook(pskb, ctinfo, i, + exp); + } else { + /* Can't expect this? Best to drop packet now. */ + if (ip_conntrack_expect_related(exp) != 0) { + ip_conntrack_expect_put(exp); + return NF_DROP; + } else { + return NF_ACCEPT; + } + } + } + } + + /* mediaGuaranteedDelivery */ + if (asn1_per_bitmap_get(&hdr.present, 3)) + asn1_per_read_bit(bb); + + /* mediaControlChannel */ + DEBUGP("lchannel_params controlChannel: i=%u bit=%u\n", bb->i, bb->bit); + if (asn1_per_bitmap_get(&hdr.present, 4)) { + int dir = CTINFO2DIR(ctinfo); + struct ip_conntrack_expect *exp; + int ret; + unsigned i; + u_int32_t ip; + u_int16_t port; + + ret = h245_parse_transport_address(bb, &i, &ip, &port); + if (ret) + DEBUGP("mediaControlChannel IPv4 address: %u.%u.%u.%u:%u\n", + NIPQUAD(ip), ntohs(port)); + if (ret && ip == ct->tuplehash[dir].tuple.src.ip) { + /* match found: create an expectation */ + exp = ip_conntrack_expect_alloc(ct); + if (exp == NULL) + return NF_ACCEPT; + + exp->tuple = ((struct ip_conntrack_tuple) + { { ct->tuplehash[!dir].tuple.src.ip, + { 0 } }, + { ct->tuplehash[!dir].tuple.dst.ip, + { .udp = { port } }, + IPPROTO_UDP }}); + exp->mask = ((struct ip_conntrack_tuple) + { { 0xFFFFFFFF, { 0 } }, + { 0xFFFFFFFF, { .udp = { 0xFFFF } }, 0xFF }}); + + /*exp->master = ct;*/ + + /* call NAT hook and register expectation */ + if (ip_nat_h245_hook != NULL) { + return ip_nat_h245_hook(pskb, ctinfo, i, + exp); + } else { + /* Can't expect this? Best to drop packet now. */ + if (ip_conntrack_expect_related(exp) != 0) { + ip_conntrack_expect_put(exp); + return NF_DROP; + } else { + return NF_ACCEPT; + } + } + } + } + + /* mediaControlGuaranteedDelivery */ + if (asn1_per_bitmap_get(&hdr.present, 5)) + asn1_per_read_bit(bb); + + /* silenceSuppression */ + if (asn1_per_bitmap_get(&hdr.present, 6)) + asn1_per_read_bit(bb); + + /* destination */ + if (asn1_per_bitmap_get(&hdr.present, 7)) + h245_skip_terminal_label(bb); + + /* dynamicRTPPayloadType */ + if (asn1_per_bitmap_get(&hdr.present, 8)) + asn1_per_read_unsigned(bb, 96, 127); + + /* mediaPacketization */ + if (asn1_per_bitmap_get(&hdr.present, 9)) { + unsigned choice, after; + + choice = asn1_per_read_choice_header(bb, 1, 1, &after); + switch (choice) { + case 0: /* h261aVideoPacketization */ + break; + + default: + if (after == 0) { + DEBUGP("ERROR7\n"); + bb->error = 1; + return NF_ACCEPT; + } + + bb->i = after; + } + } + + /* XXX */ + + asn1_per_skip_sequence_extension(bb, &hdr); + + return NF_ACCEPT; +} + +/** + * Parse an H.245 OpenLogicalChannel request packet and handle + * NAT/expectations for the logical channel address. + */ +static int h245_parse_open_lchannel(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + struct asn1_per_buffer *bb) { + struct asn1_per_sequence_header hdr, hdr2; + unsigned forwardLogicalChannelNumber; + unsigned choice, after; + + asn1_per_read_sequence_header(bb, 1, 1, &hdr); + + forwardLogicalChannelNumber = asn1_per_read_unsigned(bb, 1, 65535); + + /* entering forwardLogicalChannelParameters */ + asn1_per_read_sequence_header(bb, 1, 1, &hdr2); + if (asn1_per_bitmap_get(&hdr2.present, 0)) + asn1_per_read_unsigned(bb, 0, 65535); + + h245_skip_data_type(bb); + + /* multiplexParameters */ + choice = asn1_per_read_choice_header(bb, 1, 3, &after); + if (bb->error) + return NF_ACCEPT; + + switch (choice) { + case 3: /* h2250LogicalChannelParameters */ + h245_parse_h2250_lchannel_params(pskb, ct, ctinfo, bb); + break; + default: + if (after == 0) { + DEBUGP("unsupported multiplex_parameter %u\n", choice); + bb->error = 1; + return NF_ACCEPT; + } + } + + if (bb->error) + return NF_ACCEPT; + + if (after > 0) + bb->i = after; + + asn1_per_skip_sequence_extension(bb, &hdr2); + + /* leaving multiplexParameters, forwardLogicalChannelParameters */ + + /* reverseLogicalChannelParameters */ + if (asn1_per_bitmap_get(&hdr.present, 0)) { + asn1_per_read_sequence_header(bb, 1, 1, &hdr2); + + h245_skip_data_type(bb); + + /* multiplexParameters */ + if (asn1_per_bitmap_get(&hdr2.present, 0)) { + choice = asn1_per_read_choice_header(bb, 1, 2, &after); + if (bb->error) + return NF_ACCEPT; + + DEBUGP("reverse_parameter multiplex=%u after=%u\n", choice, after); + + switch (choice) { + case 2: /* h2250LogicalChannelParameters */ + h245_parse_h2250_lchannel_params(pskb, ct, ctinfo, bb); + break; + default: + if (after == 0) { + DEBUGP("unsupported multiplex_parameter %u\n", choice); + bb->error = 1; + return NF_ACCEPT; + } + } + + if (bb->error) + return NF_ACCEPT; + + if (after > 0) + bb->i = after; + } + + asn1_per_skip_sequence_extension(bb, &hdr2); + } + + asn1_per_skip_sequence_extension(bb, &hdr); + + /* XXX */ + return NF_ACCEPT; +} + +/** + * Parse an H.245 request packet and handle NAT/expectations for the + * logical channel address. + */ +static int h245_parse_request(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + struct asn1_per_buffer *bb) { + unsigned choice, after; + + choice = asn1_per_read_choice_header(bb, 1, 11, &after); + DEBUGP("H.245: message_type=%u\n", choice); + switch (choice) { + case 3: + return h245_parse_open_lchannel(pskb, ct, ctinfo, bb); + default: + return NF_ACCEPT; + } +} + +/** + * Parse an H.245 H222LogicalChannelParameters response packet and + * handle NAT/expectations for the logical channel address. + */ +static int h245_parse_h222_lchannel_params(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + struct asn1_per_buffer *bb) { + struct asn1_per_sequence_header hdr; + + asn1_per_read_sequence_header(bb, 1, 3, &hdr); + + if (bb->error) + return NF_ACCEPT; + + /* resourceID */ + asn1_per_read_unsigned(bb, 0, 65535); + + /* subChannelID */ + asn1_per_read_unsigned(bb, 0, 8191); + + /* pcr-pid */ + if (asn1_per_bitmap_get(&hdr.present, 0)) + asn1_per_read_unsigned(bb, 0, 8191); + + /* programDescriptors */ + if (asn1_per_bitmap_get(&hdr.present, 1)) + asn1_per_skip_octet_string(bb); + + /* streamDescriptors */ + if (asn1_per_bitmap_get(&hdr.present, 2)) + asn1_per_skip_octet_string(bb); + + asn1_per_skip_sequence_extension(bb, &hdr); + + return NF_ACCEPT; +} + +/** + * Parse an H.245 H2250LogicalChannelAckParameters response packet and + * handle NAT/expectations for the logical channel address. + */ +static int h245_parse_h2250_lchannel_ack_params(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + struct asn1_per_buffer *bb) { + struct asn1_per_sequence_header hdr; + unsigned count; + + DEBUGP("entering h245_parse_h2250_lchannel_ack_params\n"); + + asn1_per_read_sequence_header(bb, 1, 5, &hdr); + + /* nonStandard */ + if (asn1_per_bitmap_get(&hdr.present, 0)) { + count = asn1_per_read_length(bb, 0, UINT_MAX); + while (count > 0) { + h245_skip_nonstandard_param(bb); + if (bb->error) + return NF_ACCEPT; + } + } + + /* sessionID */ + if (asn1_per_bitmap_get(&hdr.present, 1)) + asn1_per_read_unsigned(bb, 1, 255); + + /* mediaChannel */ + if (asn1_per_bitmap_get(&hdr.present, 2)) { + int ret; + unsigned i; + u_int32_t ip; + u_int16_t port; + int dir = CTINFO2DIR(ctinfo); + struct ip_conntrack_expect *exp; + + ret = h245_parse_transport_address(bb, &i, &ip, &port); + DEBUGP("entering mediaChannel ret=%d i=%u ip=%x port=%u\n", + ret, i, ip, port); + if (ret && ip == ct->tuplehash[dir].tuple.src.ip) { + /* match found: create an expectation */ + exp = ip_conntrack_expect_alloc(ct); + if (exp == NULL) + return NF_ACCEPT; + + exp->tuple = ((struct ip_conntrack_tuple) + { { ct->tuplehash[!dir].tuple.src.ip, + { 0 } }, + { ct->tuplehash[!dir].tuple.dst.ip, + { .udp = { port } }, + IPPROTO_UDP }}); + exp->mask = ((struct ip_conntrack_tuple) + { { 0xFFFFFFFF, { 0 } }, + { 0xFFFFFFFF, { .udp = { 0xFFFF } }, 0xFF }}); + + /*exp->master = ct;*/ + + /* call NAT hook and register expectation */ + if (ip_nat_h245_hook != NULL) { + ret = ip_nat_h245_hook(pskb, ctinfo, i, exp); + if (ret != NF_ACCEPT) + return ret; + } else { + /* Can't expect this? Best to drop packet now. */ + if (ip_conntrack_expect_related(exp) != 0) { + ip_conntrack_expect_put(exp); + return NF_DROP; + } else { + return NF_ACCEPT; + } + } + } + } + + /* mediaControlChannel */ + if (asn1_per_bitmap_get(&hdr.present, 3)) { + int ret; + unsigned i; + u_int32_t ip; + u_int16_t port; + int dir = CTINFO2DIR(ctinfo); + struct ip_conntrack_expect *exp; + + ret = h245_parse_transport_address(bb, &i, &ip, &port); + DEBUGP("entering mediaControlChannel ret=%d i=%u ip=%x port=%u\n", + ret, i, ip, port); + if (ret && ip == ct->tuplehash[dir].tuple.src.ip) { + /* match found: create an expectation */ + exp = ip_conntrack_expect_alloc(ct); + if (exp == NULL) + return NF_ACCEPT; + + exp->tuple = ((struct ip_conntrack_tuple) + { { ct->tuplehash[!dir].tuple.src.ip, + { 0 } }, + { ct->tuplehash[!dir].tuple.dst.ip, + { .udp = { port } }, + IPPROTO_UDP }}); + exp->mask = ((struct ip_conntrack_tuple) + { { 0xFFFFFFFF, { 0 } }, + { 0xFFFFFFFF, { .udp = { 0xFFFF } }, 0xFF }}); + + /*exp->master = ct;*/ + + /* call NAT hook and register expectation */ + if (ip_nat_h245_hook != NULL) { + ret = ip_nat_h245_hook(pskb, ctinfo, i, exp); + if (ret != NF_ACCEPT) + return ret; + } else { + /* Can't expect this? Best to drop packet now. */ + if (ip_conntrack_expect_related(exp) != 0) { + ip_conntrack_expect_put(exp); + return NF_DROP; + } else { + return NF_ACCEPT; + } + } + } + } + + /* dynamicRTPPayloadType */ + if (asn1_per_bitmap_get(&hdr.present, 1)) + asn1_per_read_unsigned(bb, 96, 127); + + asn1_per_skip_sequence_extension(bb, &hdr); + + return NF_ACCEPT; +} + +/** + * Parse an H.245 OpenLogicalChannelAck response packet and handle + * NAT/expectations for the logical channel address. + */ +static int h245_parse_open_lchannel_ack(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + struct asn1_per_buffer *bb) { + struct asn1_per_sequence_header hdr, hdr2; + struct asn1_per_sequence_extension_header ext; + unsigned forwardLogicalChannelNumber; + unsigned choice, after, after2, i; + + asn1_per_read_sequence_header(bb, 1, 1, &hdr); + + forwardLogicalChannelNumber = asn1_per_read_unsigned(bb, 1, 65535); + DEBUGP("forwardLogicalChannelNumber=%u\n", forwardLogicalChannelNumber); + + /* reverseLogicalChannelParameters */ + if (asn1_per_bitmap_get(&hdr.present, 0)) { + DEBUGP("reverseLogicalChannelParameters present\n"); + asn1_per_read_sequence_header(bb, 1, 2, &hdr2); + + /* reverseLogicalChannelNumber */ + asn1_per_read_unsigned(bb, 1, 65535); + + /* portNumber */ + if (asn1_per_bitmap_get(&hdr.present, 0)) + asn1_per_read_unsigned(bb, 0, 65535); + + /* multiplexParameters */ + if (asn1_per_bitmap_get(&hdr2.present, 1)) { + choice = asn1_per_read_choice_header(bb, 1, 1, &after); + if (bb->error) + return NF_ACCEPT; + + switch (choice) { + case 0: /* h222LogicalChannelParameters */ + h245_parse_h222_lchannel_params(pskb, ct, + ctinfo, bb); + break; + case 1: /* h2250LogicalChannelParameters */ + h245_parse_h2250_lchannel_params(pskb, ct, + ctinfo, bb); + break; + default: + if (after == 0) { + DEBUGP("unsupported multiplex_parameter %u\n", choice); + bb->error = 1; + return NF_ACCEPT; + } + } + + if (bb->error) + return NF_ACCEPT; + + if (after > 0) + bb->i = after; + } + + asn1_per_skip_sequence_extension(bb, &hdr2); + } + + asn1_per_read_sequence_extension_header(bb, &hdr, &ext); + if (bb->error) + return NF_ACCEPT; + + /* separateStack */ + if (asn1_per_bitmap_get(&ext.present, 0)) + asn1_per_skip_octet_string(bb); + + /* forwardMultiplexAckParameters */ + if (asn1_per_bitmap_get(&ext.present, 1)) { + DEBUGP("forwardMultiplexAckParameters present\n"); + + after = asn1_per_read_octet_string_header(bb); + DEBUGP("forwardMultiplexAckParameters present length=%u i=%u after=%u end=%u\n", + after, bb->i, bb->i + after, bb->length); + after += bb->i; + + choice = asn1_per_read_choice_header(bb, 1, 1, &after2); + if (bb->error) + return NF_ACCEPT; + + DEBUGP("entering forwardMultiplexAckParameters choice=%u after=%u\n", choice, after2); + + switch (choice) { + case 0: /* h2250LogicalChannelAckParameters */ + h245_parse_h2250_lchannel_ack_params(pskb, ct, + ctinfo, bb); + break; + } + + if (bb->error) + return NF_ACCEPT; + + bb->i = after; + } + + for (i = 2; i < ext.count; i++) + if (asn1_per_bitmap_get(&ext.present, i)) + asn1_per_skip_octet_string(bb); + + return NF_ACCEPT; +} + +/** + * Parse an H.245 response packet and handle NAT/expectations for the + * logical channel address. + */ +static int h245_parse_response(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + struct asn1_per_buffer *bb) { + unsigned choice, after; + + choice = asn1_per_read_choice_header(bb, 1, 19, &after); + DEBUGP("H.245: response type=%u\n", choice); + switch (choice) { + case 5: /* openLogicalChannelAck */ + return h245_parse_open_lchannel_ack(pskb, ct, ctinfo, bb); + + default: + return NF_ACCEPT; + } +} + +/** + * Parse an H.245 packet and handle NAT/expectations for the logical + * channel address. + */ +static int h245_parse(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + struct asn1_per_buffer *bb) { + unsigned choice, after; + + choice = asn1_per_read_choice_header(bb, 1, 4, &after); + DEBUGP("H.245: message_class=%u\n", choice); + switch (choice) { + case 0: + return h245_parse_request(pskb, ct, ctinfo, bb); + case 1: + return h245_parse_response(pskb, ct, ctinfo, bb); + default: + return NF_ACCEPT; + } +} + +/** + * Parse a TPKT/H.245 packet and handle NAT/expectations for the + * logical channel transport address (if applicable). + */ +static int h245_parse_tpkt(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + const unsigned char *data, + unsigned datalen) { + unsigned int i = 0; + u_int16_t tpkt_len; + struct asn1_per_buffer bb; + + if (i + 4 > datalen) + return NF_ACCEPT; + + /* expect TPKT header, see RFC 1006 */ + if (data[0] != 0x03 || data[1] != 0x00) + return NF_ACCEPT; + + i += 2; + + tpkt_len = ntohs(*(u_int16_t*)(data + i)); + if (tpkt_len < datalen) + datalen = tpkt_len; + + i += 2; + + /* parse H.245 packet (ASN.1 PER) */ + asn1_per_initialize(&bb, data, datalen, i); + + return h245_parse(pskb, ct, ctinfo, &bb); +} + +static int h245_help(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) +{ + struct tcphdr _tcph, *tcph; + unsigned char *data; + unsigned dataoff, datalen; + int ret; + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + DEBUGP("ct_h245_help: Conntrackinfo = %u\n", ctinfo); + return NF_ACCEPT; + } + + tcph = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl*4, + sizeof(_tcph), &_tcph); + if (tcph == NULL) + return NF_ACCEPT; + + DEBUGP("ct_h245_help: help entered %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD((*pskb)->nh.iph->saddr), ntohs(tcph->source), + NIPQUAD((*pskb)->nh.iph->daddr), ntohs(tcph->dest)); + + dataoff = (*pskb)->nh.iph->ihl*4 + tcph->doff*4; + /* No data? */ + if (dataoff >= (*pskb)->len) { + DEBUGP("ct_h245_help: skblen = %u\n", (*pskb)->len); + return NF_ACCEPT; + } + datalen = (*pskb)->len - dataoff; + + if (datalen < 16) + return NF_ACCEPT; + + spin_lock_bh(&ip_h245_lock); + data = skb_header_pointer((*pskb), dataoff, + datalen, h245_buffer); + BUG_ON(data == NULL); + + ret = h245_parse_tpkt(pskb, ct, ctinfo, + data, datalen); + + spin_unlock_bh(&ip_h245_lock); + return ret; +} + +/* H.245 helper is not registered! */ +static struct ip_conntrack_helper h245 = +{ + .name = "H.245", + .max_expected = 8, + .timeout = 240, + .tuple = { .dst = { .protonum = IPPROTO_TCP } }, + .mask = { .src = { .u = { 0xFFFF } }, + .dst = { .protonum = 0xFF } }, + .help = h245_help +}; + +void ip_conntrack_h245_expect(struct ip_conntrack *new, + struct ip_conntrack_expect *this) +{ + write_lock_bh(&ip_conntrack_lock); + new->helper = &h245; + DEBUGP("h225_expect: helper for %p added\n", new); + write_unlock_bh(&ip_conntrack_lock); +} +EXPORT_SYMBOL_GPL(ip_conntrack_h245_expect); diff -Nur linux-2.6.14/net/ipv4/netfilter/ip_nat_h323.c linux-2.6.14-h323/net/ipv4/netfilter/ip_nat_h323.c --- linux-2.6.14/net/ipv4/netfilter/ip_nat_h323.c 1969-12-31 18:00:00.000000000 -0600 +++ linux-2.6.14-h323/net/ipv4/netfilter/ip_nat_h323.c 2005-12-31 16:04:26.611984563 -0600 @@ -0,0 +1,202 @@ +/* + * H.323 'brute force' extension for NAT alteration. + * Jozsef Kadlecsik + * (c) 2005 Max Kellermann + * + * Based on ip_masq_h323.c for 2.2 kernels from CoRiTel, Sofia project. + * (http://www.coritel.it/projects/sofia/nat.html) + * Uses Sampsa Ranta's excellent idea on using expectfn to 'bind' + * the unregistered helpers to the conntrack entries. + * + * NOTE: + * modified by Moises Silva + * to work with kernel 2.6.14. + * changes are: + * ip_conntrack_expect_free() was replaced by ip_conntrack_expect_put() + */ + + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("H.323 'brute force' connection tracking module"); +MODULE_LICENSE("GPL"); + +struct module *ip_nat_h323 = THIS_MODULE; + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +static void ip_nat_h225_signal(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + unsigned int offset, + int dir, + int orig_dir) +{ + struct { + u_int32_t ip; + u_int16_t port; + } __attribute__ ((__packed__)) newdata; + + /* Change address inside packet to match way we're mapping + this connection. */ + if (dir == IP_CT_DIR_ORIGINAL) { + newdata.ip = ct->tuplehash[!orig_dir].tuple.dst.ip; + newdata.port = ct->tuplehash[!orig_dir].tuple.dst.u.tcp.port; + } else { + newdata.ip = ct->tuplehash[!orig_dir].tuple.src.ip; + newdata.port = ct->tuplehash[!orig_dir].tuple.src.u.tcp.port; + } + + /* Modify the packet */ + ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, + offset, + sizeof(newdata), + (const char*)&newdata, sizeof(newdata)); +} + +/** + * This conntrack expect function replaces ip_conntrack_h245_expect() + * which was set by ip_conntrack_h323.c. It calls both + * ip_nat_follow_master() and ip_conntrack_h245_expect(). + */ +static void ip_nat_h245_expect(struct ip_conntrack *new, + struct ip_conntrack_expect *this) +{ + ip_nat_follow_master(new, this); + ip_conntrack_h245_expect(new, this); +} + +static int ip_nat_h225(struct sk_buff **pskb, + enum ip_conntrack_info ctinfo, + unsigned int offset, + struct ip_conntrack_expect *exp) +{ + u_int16_t port; + struct { + u_int32_t ip; + u_int16_t port; + } __attribute__ ((__packed__)) newdata; + int dir = CTINFO2DIR(ctinfo); + struct ip_conntrack *ct = exp->master; + int ret; + + /* Connection will come from wherever this packet goes, hence !dir */ + newdata.ip = ct->tuplehash[!dir].tuple.dst.ip; + exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; + exp->dir = !dir; + + /* When you see the packet, we need to NAT it the same as the + * this one. */ + BUG_ON(exp->expectfn != ip_conntrack_h245_expect); + exp->expectfn = ip_nat_h245_expect; + + /* Try to get same port: if not, try to change it. */ + for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) { + exp->tuple.dst.u.tcp.port = htons(port); + if (ip_conntrack_expect_related(exp) == 0) + break; + } + + if (port == 0) { + ip_conntrack_expect_put(exp); + return NF_DROP; + } + + newdata.port = htons(port); + + /* now mangle packet */ + ret = ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, + offset, + sizeof(newdata), + (const char*)&newdata, sizeof(newdata)); + if (!ret) + return NF_DROP; + + return NF_ACCEPT; +} + +static int ip_nat_h245(struct sk_buff **pskb, + enum ip_conntrack_info ctinfo, + unsigned int offset, + struct ip_conntrack_expect *exp) +{ + u_int16_t port; + struct { + u_int32_t ip; + u_int16_t port; + } __attribute__ ((__packed__)) newdata; + int dir = CTINFO2DIR(ctinfo); + struct ip_conntrack *ct = exp->master; + int ret; + + /* Connection will come from wherever this packet goes, hence !dir */ + newdata.ip = ct->tuplehash[!dir].tuple.dst.ip; + exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; + exp->dir = !dir; + + /* When you see the packet, we need to NAT it the same as the + * this one. */ + exp->expectfn = ip_nat_follow_master; + + /* Try to get same port: if not, try to change it. */ + for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) { + exp->tuple.dst.u.tcp.port = htons(port); + if (ip_conntrack_expect_related(exp) == 0) + break; + } + + if (port == 0) { + ip_conntrack_expect_put(exp); + return NF_DROP; + } + + newdata.port = htons(port); + + /* now mangle packet */ + ret = ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, + offset, + sizeof(newdata), + (const char*)&newdata, sizeof(newdata)); + if (!ret) + return NF_DROP; + + return NF_ACCEPT; +} + +static int __init init(void) +{ + BUG_ON(ip_nat_h225_hook != NULL); + BUG_ON(ip_nat_h245_hook != NULL); + + ip_nat_h225_hook = ip_nat_h225; + ip_nat_h225_signal_hook = ip_nat_h225_signal; + ip_nat_h245_hook = ip_nat_h245; + + return 0; +} + +static void __exit fini(void) +{ + ip_nat_h225_hook = NULL; + ip_nat_h225_signal_hook = NULL; + ip_nat_h245_hook = NULL; +} + +module_init(init); +module_exit(fini);