Skip to contentSkip to conversation

H.323: implement a "real" H.245 parser

PATCH In review Max Kellermann 3 participants 13 messages May 10, 2005 – May 17, 2005 netfilter

lore
Share & tools
lore
#0 lore raw

cover H.323: implement a "real" H.245 parser

Hi,

the following patchset replaces Jozsef's "brute force" parser with a real H.245 parser. It adds a very simple ASN.1/PER parsing library, and parses very few H.245 structures (enough for all H.323 software I tested).

My next task will be the rewrite of the H.225 parser using my ASN.1/PER library.

Max

#1 lore raw

PATCH 1/6 H.323: remove struct ip_ct_h225_master

h323-01-remove_ip_ct_h225_master.patch
- remove struct ip_ct_h225_master

Tue Apr 19 21:28:07 CEST 2005 [email protected]
* removed dummy struct ip_ct_h225_master

h323-conntrack-nat/linux-2.6.11/include/linux/netfilter_ipv4/ip_conntrack.h.ladd +0 -2
diff -rN -u old-h323-4/h323-conntrack-nat/linux-2.6.11/include/linux/netfilter_ipv4/ip_conntrack.h.ladd new-h323-4/h323-conntrack-nat/linux-2.6.11/include/linux/netfilter_ipv4/ip_conntrack.h.ladd
@@ -1,2 +0,0 @@
-	/* insert conntrack helper private data (master) here */
-	struct ip_ct_h225_master ct_h225_info;
h323-conntrack-nat/linux-2.6.11/include/linux/netfilter_ipv4/ip_conntrack.h.ladd_2 +0 -2
diff -rN -u old-h323-4/h323-conntrack-nat/linux-2.6.11/include/linux/netfilter_ipv4/ip_conntrack.h.ladd_2 new-h323-4/h323-conntrack-nat/linux-2.6.11/include/linux/netfilter_ipv4/ip_conntrack.h.ladd_2
@@ -1,2 +0,0 @@
-/* Add protocol helper include file here */
-#include <linux/netfilter_ipv4/ip_conntrack_h323.h>
h323-conntrack-nat/linux-2.6.11/include/linux/netfilter_ipv4/ip_conntrack_h323.h +0 -5
diff -rN -u old-h323-4/h323-conntrack-nat/linux-2.6.11/include/linux/netfilter_ipv4/ip_conntrack_h323.h new-h323-4/h323-conntrack-nat/linux-2.6.11/include/linux/netfilter_ipv4/ip_conntrack_h323.h
@@ -7,11 +7,6 @@
 /* Default H.225 port */
 #define H225_PORT	1720
 
-/* This structure exists only once per master */
-struct ip_ct_h225_master {
-	int dummy;
-};
-
 struct ip_conntrack_expect;
 struct ip_conntrack;
 
#2 lore raw

PATCH 2/6 add ip_nat_h245()

In reply to Max Kellermann

h323-02-add_ip_nat_h245.patch
- add ip_nat_h245(), separate fro ip_nat_h225()

Tue Apr 19 22:45:33 CEST 2005 [email protected]
* ip_nat_h245_hook has its own implementation, ip_nat_h245()

h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/ip_nat_h323.c +49 -1
diff -rN -u old-h323-4/h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/ip_nat_h323.c new-h323-4/h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/ip_nat_h323.c
@@ -126,6 +126,54 @@
 	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_free(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);
@@ -133,7 +181,7 @@
 
 	ip_nat_h225_hook = ip_nat_h225;
 	ip_nat_h225_signal_hook = ip_nat_h225_signal;
-	ip_nat_h245_hook = ip_nat_h225;
+	ip_nat_h245_hook = ip_nat_h245;
 
 	return 0;
 }
#3 lore raw

PATCH 3/6 H.323: minor code style fixes

In reply to Max Kellermann

h323-03-code_style.patch
- minor code style fixes

Tue Apr 19 22:46:24 CEST 2005 [email protected]
* minor code style fixes

h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/ip_conntrack_h323.c +4 -2
diff -rN -u old-h323-4/h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/ip_conntrack_h323.c new-h323-4/h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/ip_conntrack_h323.c
@@ -160,7 +160,8 @@
 }
 
 /* H.245 helper is not registered! */
-static struct ip_conntrack_helper h245 = {
+static struct ip_conntrack_helper h245 =
+{
 	.name = "H.245",
 	.max_expected = 8,
 	.timeout = 240,
@@ -419,7 +420,8 @@
 	return ret;
 }
 
-static struct ip_conntrack_helper h225 = {
+static struct ip_conntrack_helper h225 =
+{
 	.name = "H.225",
 	.me = THIS_MODULE,
 	.max_expected = 2,
#4 lore raw

PATCH 4/6 H.323: splitted ip_conntrack_h323.c into 3 sources

In reply to Max Kellermann

h323-04-split_conntrack_into_3.patch
- splitted ip_conntrack_h323.c into 3 sources

Tue May 10 23:26:46 CEST 2005 [email protected]
* splitted ip_conntrack_h323.c into 3 sources IV

h323-conntrack-nat/linux-2.6.11/include/linux/netfilter_ipv4/ip_conntrack_h323.h +3 -0
diff -rN -u old-h323-4/h323-conntrack-nat/linux-2.6.11/include/linux/netfilter_ipv4/ip_conntrack_h323.h new-h323-4/h323-conntrack-nat/linux-2.6.11/include/linux/netfilter_ipv4/ip_conntrack_h323.h
@@ -9,6 +9,7 @@
 
 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,
@@ -27,6 +28,8 @@
 				       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);
 
h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/Makefile.ladd +1 -0
diff -rN -u old-h323-4/h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/Makefile.ladd new-h323-4/h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/Makefile.ladd
@@ -2,5 +2,6 @@
  
 # 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
 obj-$(CONFIG_IP_NF_NAT_H323) += ip_nat_h323.o
 
h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/ip_conntrack_h323.c +0 -448

448 lines not on this page · raw message

h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/ip_conntrack_h323_core.c +38 -0
diff -rN -u old-h323-4/h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/ip_conntrack_h323_core.c new-h323-4/h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/ip_conntrack_h323_core.c
@@ -0,0 +1,38 @@
+/*
+ * H.323 connection tracking helper
+ * (c) 2005 Max Kellermann <[email protected]>
+ *
+ * Based on the 'brute force' H.323 connection tracking module by
+ * Jozsef Kadlecsik <[email protected]>
+ */
+
+
+#include <linux/module.h>
+#include <linux/netfilter.h>
+#include <linux/ip.h>
+#include <net/checksum.h>
+#include <net/tcp.h>
+
+#include <linux/netfilter_ipv4/lockhelp.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ip_conntrack_core.h>
+#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
+#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
+#include <linux/netfilter_ipv4/ip_conntrack_h323.h>
+
+MODULE_AUTHOR("Max Kellermann <[email protected]>");
+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);
h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/ip_conntrack_h323_h225.c +299 -0

299 lines not on this page · raw message

h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/ip_conntrack_h323_h245.c +161 -0

161 lines not on this page · raw message

#5 lore raw

Re: [PATCH pom-ng 4/6] H.323: splitted ip_conntrack_h323.c into 3 sources

h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/ip_conntrack_h323_core.c

Quoted 6 lines · Max Kellermann

h323-04-split_conntrack_into_3.patch
- splitted ip_conntrack_h323.c into 3 sources

diff -rN -u old-h323-4/h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/ip_conntrack_h323_core.c new-h323-4/h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/ip_conntrack_h323_core.c

h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/ip_conntrack_h323_core.c +0 -0

This file looks a bit unnecessary, I suggest to put register/unregister in ip_conntrack_h323_h225.c.

Regards
Patrick

#6 lore raw

Re: [PATCH pom-ng 4/6] H.323: splitted ip_conntrack_h323.c into 3 sources

Quoted 10 lines · Patrick McHardy

h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/ip_conntrack_h323_core.c

Quoted 6 lines · Max Kellermann

h323-04-split_conntrack_into_3.patch
- splitted ip_conntrack_h323.c into 3 sources

diff -rN -u old-h323-4/h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/ip_conntrack_h323_core.c new-h323-4/h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/ip_conntrack_h323_core.c

h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/ip_conntrack_h323_core.c +0 -0

This file looks a bit unnecessary, I suggest to put register/unregister in ip_conntrack_h323_h225.c.

I splitted the file because it became very large (I dislike sources > 500 lines). I decided to make a _core.c because this way, the _h225.c and _h245.c contain only protocol parser code, and nothing else - still having the option to add more "common" code to _core.c (whatever happens in further development).

In the compiled binary, there is no advantage of merging _core and _h225.c.

A matter of personal taste. If you vote me down on this one, I'll change it.

Max

#7 lore raw

Re: [PATCH pom-ng 4/6] H.323: splitted ip_conntrack_h323.c into 3 sources

Quoted 11 lines · Max Kellermann
Quoted 2 lines · Patrick McHardy

This file looks a bit unnecessary, I suggest to put register/unregister in ip_conntrack_h323_h225.c.

I splitted the file because it became very large (I dislike sources > 500 lines). I decided to make a _core.c because this way, the _h225.c and _h245.c contain only protocol parser code, and nothing else - still having the option to add more "common" code to _core.c (whatever happens in further development).

Quoted 5 lines

In the compiled binary, there is no advantage of merging _core and _h225.c.

A matter of personal taste. If you vote me down on this one, I'll change it.

Well, lets keep it this way for now and wait if something else is added. If we decide to merge it some day we can still change it.

Regards
Patrick

#8 lore raw

PATCH 5/6 H.323: ASN.1/PER parser

In reply to Max Kellermann

h323-05-simple_asn1_per_parser.patch
- added an ASN.1/PER parser

Tue May 10 23:28:17 CEST 2005 [email protected]
* added an ASN.1/PER parser IV

h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/Makefile.ladd +1 -1
diff -rN -u old-h323-4/h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/Makefile.ladd new-h323-4/h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/Makefile.ladd
@@ -2,6 +2,6 @@
  
 # 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
+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
 
h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/asn1_per.c +353 -0

353 lines not on this page · raw message

h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/asn1_per.h +83 -0

83 lines not on this page · raw message

#9 lore raw

PATCH 6/6 H.323: H.245/ASN.1 parser

In reply to Max Kellermann

h323-06-replace_bruteforce_with_h245_parser.patch
- implement a real H.245 parser

Tue May 10 23:35:13 CEST 2005 [email protected]
* H.245/ASN.1 parser VII

h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/ip_conntrack_h323_h245.c +852 -56
diff -rN -u old-h323-4/h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/ip_conntrack_h323_h245.c new-h323-4/h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/ip_conntrack_h323_h245.c
@@ -20,6 +20,8 @@
 #include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
 #include <linux/netfilter_ipv4/ip_conntrack_h323.h>
 
+#include "asn1_per.h"
+
 /* This is slow, but it's simple. --RR */
 static char h245_buffer[65536];
 
@@ -39,20 +41,861 @@
 #define DEBUGP(format, args...)
 #endif
 
-/* FIXME: This should be in userspace.  Later. */
+/**
+ * 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();
+			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_free(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();
+			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_free(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();
+			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_free(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();
+			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_free(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;
+
+	/* 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 < 16)
+		return NF_ACCEPT;
+
+	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 char *data_limit;
 	unsigned dataoff, datalen;
-	int dir = CTINFO2DIR(ctinfo);
-	struct ip_conntrack_expect *exp;
-	u_int16_t data_port;
-	u_int32_t data_ip;
-	unsigned int i;
 	int ret;
 
 	/* Until there's been traffic both ways, don't look in packets. */
@@ -84,56 +927,9 @@
 				  datalen, h245_buffer);
 	BUG_ON(data == NULL);
 
-	data_limit = data + datalen - 6;
-	/* bytes: 0123   45
-	          ipadrr port */
-	for (i = 0; data <= data_limit; data++, i++) {
-		data_ip = *((u_int32_t *)data);
-		if (data_ip == ct->tuplehash[dir].tuple.src.ip) {
-			data_port = *((u_int16_t *)(data + 4));
-
-			/* update the H.225 info */
-			DEBUGP("ct_h245_help: new RTCP/RTP requested %u.%u.%u.%u:->%u.%u.%u.%u:%u\n",
-				NIPQUAD(ct->tuplehash[!dir].tuple.src.ip),
-				NIPQUAD((*pskb)->nh.iph->saddr), ntohs(data_port));
-
-			exp = ip_conntrack_expect_alloc();
-			if (exp == NULL) {
-				ret = NF_ACCEPT;
-				goto out;
-			}
-
-			exp->tuple = ((struct ip_conntrack_tuple)
-				{ { ct->tuplehash[!dir].tuple.src.ip,
-				    { 0 } },
-				  { data_ip,
-				    { .tcp = { data_port } },
-				    IPPROTO_UDP }});
-			exp->mask = ((struct ip_conntrack_tuple)
-				{ { 0xFFFFFFFF, { 0 } },
-				  { 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFF }});
-
-			exp->expectfn = NULL;
-			exp->master = ct;
-
-			if (ip_nat_h245_hook != NULL) {
-				ret = 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_free(exp);
-					ret = NF_DROP;
-				} else
-					ret = NF_ACCEPT;
-			}
-
-			break;
-		}
-	}
+	ret = h245_parse_tpkt(pskb, ct, ctinfo,
+			      data, datalen);
 
-	ret = NF_ACCEPT;
- out:
 	UNLOCK_BH(&ip_h245_lock);
 	return ret;
 }
#10 lore raw
In reply to Max Kellermann
Quoted 6 lines · Max Kellermann

Hi,

the following patchset replaces Jozsef's "brute force" parser with a real H.245 parser. It adds a very simple ASN.1/PER parsing library, and parses very few H.245 structures (enough for all H.323 software I tested).

Thanks, this is great news. Including the list of tested h.323 software somwhere in the 'info' or 'help' file would definitely be a good idea.

I'd happily apply your patches but leave it up to Jozsef, since he's more familiar with the protocol and the helper he wrote.

Signature · 5 lines
- Harald Welte <[email protected]>                 http://netfilter.org/
============================================================================
  "Fragmentation is like classful addressing -- an interesting early
   architectural error that shows how much experimentation was going
   on while IP was being designed."                    -- Paul Vixie
#11 lore raw

[PATCH pom-ng 7/6] H.323: reimplement the H.225 parser using the ASN.1/PER library

In reply to Max Kellermann

An additional patch to this patch series, which I developed today:

h323-07-reimplement_h225_parser_with_asn1_per.patch
- reimplement the H.225 parser using the ASN.1/PER library

Wed May 11 11:35:03 CEST 2005 [email protected]
* reimplement the H.225 parser using the ASN.1/PER library

h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/ip_conntrack_h323_h225.c +160 -58
diff -rN -u old-h323-0/h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/ip_conntrack_h323_h225.c new-h323-0/h323-conntrack-nat/linux-2.6.11/net/ipv4/netfilter/ip_conntrack_h323_h225.c
@@ -20,6 +20,8 @@
 #include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
 #include <linux/netfilter_ipv4/ip_conntrack_h323.h>
 
+#include "asn1_per.h"
+
 /* This is slow, but it's simple. --RR */
 static char h225_buffer[65536];
 
@@ -46,6 +48,161 @@
 #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();
+			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_free(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.
  */
@@ -55,10 +212,7 @@
 				   const unsigned char *data,
 				   unsigned i, unsigned length)
 {
-	int dir = CTINFO2DIR(ctinfo);
-	u_int32_t data_ip;
-	u_int16_t data_port;
-	struct ip_conntrack_expect *exp;
+	struct asn1_per_buffer bb;
 
 	/* protocol(1) + header(3) + protocolIdentifier(6) +
 	   h245ipAddress(1) + h245ipv4(4) + h245ipv4port(2) */
@@ -68,61 +222,9 @@
 	if (data[i++] != 0x05) /* X.208 / X.209 */
 		return NF_ACCEPT;
 
-	/* XXX: h225 header connect? */
-	if (data[i++] != 0x22 || data[i++] != 0xc0 || data[i++] != 0x06)
-		return NF_ACCEPT;
+	asn1_per_initialize(&bb, data, length, i);
 
-	/* protocolIdentifier, ignore the last 2 bytes (minor
-	   version) */
-	if (memcmp(data + i, "\x00\x08\x91\x4a", 4) != 0)
-		return NF_ACCEPT;
-
-	i += 6;
-
-	if (data[i++] != 0x00) /* h245ipAddress? */
-		return NF_ACCEPT;
-
-	/* compare the IP address - this is only a valid H.245
-	   transport address, if it equals the source address of the
-	   packet */
-	data_ip = *(u_int32_t *)(data + i);
-	if (data_ip != ct->tuplehash[dir].tuple.src.ip)
-		return NF_ACCEPT;
-
-
-	data_port = *((u_int16_t *)(data + i + 4));
-
-	/* match found: create an expectation */
-	exp = ip_conntrack_expect_alloc();
-	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 = { data_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_free(exp);
-			return NF_DROP;
-		} else {
-			return NF_ACCEPT;
-		}
-	}
+	return h225_parse(pskb, ct, ctinfo, &bb);
 }
 
 /**
#12 lore raw

Re: [PATCH pom-ng 7/6] H.323: reimplement the H.225 parser using the ASN.1/PER library

Quoted 4 lines · Max Kellermann

An additional patch to this patch series, which I developed today:

h323-07-reimplement_h225_parser_with_asn1_per.patch
- reimplement the H.225 parser using the ASN.1/PER library

All applied, great work Max.