Merge branch 'on-waves/sccp'
diff --git a/openbsc/include/sccp/sccp.h b/openbsc/include/sccp/sccp.h
index d7b57c6..643479a 100644
--- a/openbsc/include/sccp/sccp.h
+++ b/openbsc/include/sccp/sccp.h
@@ -1,7 +1,7 @@
 /*
  * SCCP management code
  *
- * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009, 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
  *
  * All Rights Reserved
  *
@@ -143,4 +143,25 @@
 u_int32_t sccp_src_ref_to_int(struct sccp_source_reference *ref);
 struct sccp_source_reference sccp_src_ref_from_int(u_int32_t);
 
+/**
+ * Below this are helper functions and structs for parsing SCCP messages
+ */
+struct sccp_parse_result {
+	struct sccp_address called;
+	struct sccp_address calling;
+
+	/* point to the msg packet */
+	struct sccp_source_reference *source_local_reference;
+	struct sccp_source_reference *destination_local_reference;
+
+	/* data pointer */
+	int data_len;
+};
+
+/*
+ * helper functions for the nat code
+ */
+int sccp_determine_msg_type(struct msgb *msg);
+int sccp_parse_header(struct msgb *msg, struct sccp_parse_result *result);
+
 #endif
diff --git a/openbsc/src/sccp/sccp.c b/openbsc/src/sccp/sccp.c
index bbf3e40..689555d 100644
--- a/openbsc/src/sccp/sccp.c
+++ b/openbsc/src/sccp/sccp.c
@@ -1,8 +1,8 @@
 /*
  * SCCP management code
  *
- * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2009 by on-waves.com
+ * (C) 2009, 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009, 2010 by On-Waves
  *
  * All Rights Reserved
  *
@@ -200,6 +200,277 @@
 	return -1;
 }
 
+int _sccp_parse_connection_request(struct msgb *msgb, struct sccp_parse_result *result)
+{
+	static const u_int32_t header_size =
+			sizeof(struct sccp_connection_request);
+	static const u_int32_t optional_offset =
+			offsetof(struct sccp_connection_request, optional_start);
+	static const u_int32_t called_offset =
+			offsetof(struct sccp_connection_request, variable_called);
+
+	struct sccp_connection_request *req = (struct sccp_connection_request *)msgb->data;
+	struct sccp_optional_data optional_data;
+
+	/* header check */
+	if (msgb_l2len(msgb) < header_size) {
+		DEBUGP(DSCCP, "msgb < header_size %u %u\n",
+		        msgb_l2len(msgb), header_size);
+		return -1;
+	}
+
+	/* copy out the calling and called address. Add the offset */
+	if (copy_address(&result->called, called_offset + req->variable_called, msgb) != 0)
+		return -1;
+
+	if (check_address(&result->called) != 0) {
+		DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
+			*(u_int8_t *)&result->called.address, result->called.ssn);
+		return -1;
+	}
+
+	result->source_local_reference = &req->source_local_reference;
+
+	/*
+	 * parse optional data.
+	 */
+	memset(&optional_data, 0, sizeof(optional_data));
+	if (_sccp_parse_optional_data(optional_offset + req->optional_start, msgb, &optional_data) != 0) {
+		DEBUGP(DSCCP, "parsing of optional data failed.\n");
+		return -1;
+	}
+
+	if (optional_data.data_len != 0) {
+		msgb->l3h = &msgb->l2h[optional_data.data_start];
+		result->data_len = optional_data.data_len;
+	} else {
+		result->data_len = 0;
+	}
+
+	return 0;
+}
+
+int _sccp_parse_connection_released(struct msgb *msgb, struct sccp_parse_result *result)
+{
+	static int header_size = sizeof(struct sccp_connection_released);
+	static int optional_offset = offsetof(struct sccp_connection_released, optional_start);
+
+	struct sccp_optional_data optional_data;
+	struct sccp_connection_released *rls = (struct sccp_connection_released *) msgb->l2h;
+
+	/* we don't have enough size for the struct */
+	if (msgb_l2len(msgb) < header_size) {
+		DEBUGP(DSCCP, "msgb > header_size %u %u\n",
+		        msgb_l2len(msgb), header_size);
+		return -1;
+	}
+
+	memset(&optional_data, 0, sizeof(optional_data));
+	if (_sccp_parse_optional_data(optional_offset + rls->optional_start, msgb, &optional_data) != 0) {
+		DEBUGP(DSCCP, "parsing of optional data failed.\n");
+		return -1;
+	}
+
+	result->source_local_reference = &rls->source_local_reference;
+	result->destination_local_reference = &rls->destination_local_reference;
+
+	if (optional_data.data_len != 0) {
+		msgb->l3h = &msgb->l2h[optional_data.data_start];
+		result->data_len = optional_data.data_len;
+	} else {
+		result->data_len = 0;
+	}
+
+	return 0;
+}
+
+int _sccp_parse_connection_refused(struct msgb *msgb, struct sccp_parse_result *result)
+{
+	static const u_int32_t header_size =
+			sizeof(struct sccp_connection_refused);
+	static int optional_offset = offsetof(struct sccp_connection_refused, optional_start);
+
+	struct sccp_optional_data optional_data;
+	struct sccp_connection_refused *ref;
+
+	/* header check */
+	if (msgb_l2len(msgb) < header_size) {
+		DEBUGP(DSCCP, "msgb < header_size %u %u\n",
+		        msgb_l2len(msgb), header_size);
+		return -1;
+	}
+
+	ref = (struct sccp_connection_refused *) msgb->l2h;
+
+	result->destination_local_reference = &ref->destination_local_reference;
+
+	memset(&optional_data, 0, sizeof(optional_data));
+	if (_sccp_parse_optional_data(optional_offset + ref->optional_start, msgb, &optional_data) != 0) {
+		DEBUGP(DSCCP, "parsing of optional data failed.\n");
+		return -1;
+	}
+
+	/* optional data */
+	if (optional_data.data_len != 0) {
+		msgb->l3h = &msgb->l2h[optional_data.data_start];
+		result->data_len = optional_data.data_len;
+	} else {
+		result->data_len = 0;
+	}
+
+	return 0;
+}
+
+int _sccp_parse_connection_confirm(struct msgb *msgb, struct sccp_parse_result *result)
+{
+	static u_int32_t header_size =
+		    sizeof(struct sccp_connection_confirm);
+	static const u_int32_t optional_offset =
+			offsetof(struct sccp_connection_confirm, optional_start);
+
+	struct sccp_optional_data optional_data;
+	struct sccp_connection_confirm *con;
+
+	/* header check */
+	if (msgb_l2len(msgb) < header_size) {
+		DEBUGP(DSCCP, "msgb < header_size %u %u\n",
+		        msgb_l2len(msgb), header_size);
+		return -1;
+	}
+
+	con = (struct sccp_connection_confirm *) msgb->l2h;
+	result->destination_local_reference = &con->destination_local_reference;
+	result->source_local_reference = &con->source_local_reference;
+
+	memset(&optional_data, 0, sizeof(optional_data));
+	if (_sccp_parse_optional_data(optional_offset + con->optional_start, msgb, &optional_data) != 0) {
+		DEBUGP(DSCCP, "parsing of optional data failed.\n");
+		return -1;
+	}
+
+	if (optional_data.data_len != 0) {
+		msgb->l3h = &msgb->l2h[optional_data.data_start];
+		result->data_len = optional_data.data_len;
+	} else {
+		result->data_len = 0;
+	}
+
+	return 0;
+}
+
+int _sccp_parse_connection_release_complete(struct msgb *msgb, struct sccp_parse_result *result)
+{
+	static int header_size = sizeof(struct sccp_connection_release_complete);
+
+	struct sccp_connection_release_complete *cmpl;
+
+	/* header check */
+	if (msgb_l2len(msgb) < header_size) {
+		DEBUGP(DSCCP, "msgb < header_size %u %u\n",
+		        msgb_l2len(msgb), header_size);
+		return -1;
+	}
+
+	cmpl = (struct sccp_connection_release_complete *) msgb->l2h;
+	result->source_local_reference = &cmpl->source_local_reference;
+	result->destination_local_reference = &cmpl->destination_local_reference;
+
+	return 0;
+}
+
+int _sccp_parse_connection_dt1(struct msgb *msgb, struct sccp_parse_result *result)
+{
+	static int header_size = sizeof(struct sccp_data_form1);
+	static int variable_offset = offsetof(struct sccp_data_form1, variable_start);
+
+	struct sccp_data_form1 *dt1 = (struct sccp_data_form1 *)msgb->l2h;
+
+	/* we don't have enough size for the struct */
+	if (msgb_l2len(msgb) < header_size) {
+		DEBUGP(DSCCP, "msgb > header_size %u %u\n",
+		        msgb_l2len(msgb), header_size);
+		return -1;
+	}
+
+	if (dt1->segmenting != 0) {
+		DEBUGP(DSCCP, "This packet has segmenting, not supported: %d\n", dt1->segmenting);
+		return -1;
+	}
+
+	result->destination_local_reference = &dt1->destination_local_reference;
+
+	/* some more  size checks in here */
+	if (msgb_l2len(msgb) < variable_offset + dt1->variable_start + 1) {
+		DEBUGP(DSCCP, "Not enough space for variable start: %u %u\n",
+			msgb_l2len(msgb), dt1->variable_start);
+		return -1;
+	}
+
+	result->data_len = msgb->l2h[variable_offset + dt1->variable_start];
+	msgb->l3h = &msgb->l2h[dt1->variable_start + variable_offset + 1];
+
+	if (msgb_l3len(msgb) < result->data_len) {
+		DEBUGP(DSCCP, "Not enough room for the payload: %u %u\n",
+			msgb_l3len(msgb), result->data_len);
+		return -1;
+	}
+
+	return 0;
+}
+
+int _sccp_parse_udt(struct msgb *msgb, struct sccp_parse_result *result)
+{
+	static const u_int32_t header_size = sizeof(struct sccp_data_unitdata);
+	static const u_int32_t called_offset = offsetof(struct sccp_data_unitdata, variable_called);
+	static const u_int32_t calling_offset = offsetof(struct sccp_data_unitdata, variable_calling);
+	static const u_int32_t data_offset = offsetof(struct sccp_data_unitdata, variable_data);
+
+	struct sccp_data_unitdata *udt = (struct sccp_data_unitdata *)msgb->l2h;
+
+	if (msgb_l2len(msgb) < header_size) {
+		DEBUGP(DSCCP, "msgb < header_size %u %u\n",
+		        msgb_l2len(msgb), header_size);
+		return -1;
+	}
+
+	/* copy out the calling and called address. Add the off */
+	if (copy_address(&result->called, called_offset + udt->variable_called, msgb) != 0)
+		return -1;
+
+	if (check_address(&result->called) != 0) {
+		DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
+			*(u_int8_t *)&result->called.address, result->called.ssn);
+		return -1;
+	}
+
+	if (copy_address(&result->calling, calling_offset + udt->variable_calling, msgb) != 0)
+		return -1;
+
+	if (check_address(&result->calling) != 0) {
+		DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
+			*(u_int8_t *)&result->called.address, result->called.ssn);
+	}
+
+	/* we don't have enough size for the data */
+	if (msgb_l2len(msgb) < data_offset + udt->variable_data + 1) {
+		DEBUGP(DSCCP, "msgb < header + offset %u %u %u\n",
+			msgb_l2len(msgb), header_size, udt->variable_data);
+		return -1;
+	}
+
+
+	msgb->l3h = &udt->data[udt->variable_data];
+
+	if (msgb_l3len(msgb) !=  msgb->l3h[-1]) {
+		DEBUGP(DSCCP, "msgb is truncated is: %u should: %u\n",
+			msgb_l3len(msgb), msgb->l3h[-1]);
+		return -1;
+	}
+
+	return 0;
+}
+
+
 /*
  * Send UDT. Currently we have a fixed address...
  */
@@ -250,59 +521,15 @@
 
 static int _sccp_handle_read(struct msgb *msgb)
 {
-	static const u_int32_t header_size = sizeof(struct sccp_data_unitdata);
-	static const u_int32_t called_offset = offsetof(struct sccp_data_unitdata, variable_called);
-	static const u_int32_t calling_offset = offsetof(struct sccp_data_unitdata, variable_calling);
-	static const u_int32_t data_offset = offsetof(struct sccp_data_unitdata, variable_data);
-
 	struct sccp_data_callback *cb;
-	struct sccp_data_unitdata *udt = (struct sccp_data_unitdata *)msgb->l2h;
-	struct sccp_address called, calling;
+	struct sccp_parse_result result;
 
-	/* we don't have enough size for the struct */
-	if (msgb_l2len(msgb) < header_size) {
-		DEBUGP(DSCCP, "msgb < header_size %u %u\n",
-		        msgb_l2len(msgb), header_size);
-		return -1;
-	}
-
-	/* copy out the calling and called address. Add the off */
-	if (copy_address(&called, called_offset + udt->variable_called, msgb) != 0)
+	if (_sccp_parse_udt(msgb, &result) != 0)
 		return -1;
 
-	if (check_address(&called) != 0) {
-		DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
-			*(u_int8_t *)&called.address, called.ssn);
-		return -1;
-	}
-
-	cb = _find_ssn(called.ssn);
+	cb = _find_ssn(result.called.ssn);
 	if (!cb || !cb->read_cb) {
-		DEBUGP(DSCCP, "No routing for UDT for called SSN: %u\n", called.ssn);
-		return -1;
-	}
-
-	if (copy_address(&calling, calling_offset + udt->variable_calling, msgb) != 0)
-		return -1;
-
-	if (check_address(&calling) != 0) {
-		DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
-			*(u_int8_t *)&called.address, called.ssn);
-	}
-
-	/* we don't have enough size for the data */
-	if (msgb_l2len(msgb) < data_offset + udt->variable_data + 1) {
-		DEBUGP(DSCCP, "msgb < header + offset %u %u %u\n",
-			msgb_l2len(msgb), header_size, udt->variable_data);
-		return -1;
-	}
-
-
-	msgb->l3h = &udt->data[udt->variable_data];
-
-	if (msgb_l3len(msgb) !=  msgb->l3h[-1]) {
-		DEBUGP(DSCCP, "msgb is truncated %u %u\n",
-			msgb_l3len(msgb), msgb->l3h[-1]);
+		DEBUGP(DSCCP, "No routing for UDT for called SSN: %u\n", result.called.ssn);
 		return -1;
 	}
 
@@ -375,7 +602,7 @@
 		connection->state_cb(connection, old_state);
 }
 
-static int _sccp_send_refuse(struct sccp_connection_request *req, int cause)
+static int _sccp_send_refuse(struct sccp_source_reference *src_ref, int cause)
 {
 	struct msgb *msgb;
 	struct sccp_connection_refused *ref;
@@ -388,7 +615,7 @@
 
 	ref = (struct sccp_connection_refused *) msgb_put(msgb, sizeof(*ref));
 	ref->type = SCCP_MSG_TYPE_CREF;
-	memcpy(&ref->destination_local_reference, &req->source_local_reference,
+	memcpy(&ref->destination_local_reference, src_ref,
 	       sizeof(struct sccp_source_reference));
 	ref->cause = cause;
 	ref->optional_start = 1;
@@ -602,39 +829,17 @@
  */
 static int _sccp_handle_connection_request(struct msgb *msgb)
 {
-	static const u_int32_t header_size =
-			sizeof(struct sccp_connection_request);
-	static const u_int32_t optional_offset =
-			offsetof(struct sccp_connection_request, optional_start);
-	static const u_int32_t called_offset =
-			offsetof(struct sccp_connection_request, variable_called);
+	struct sccp_parse_result result;
 
 	struct sccp_data_callback *cb;
-	struct sccp_connection_request *req = (struct sccp_connection_request *)msgb->data;
-	struct sccp_address called;
 	struct sccp_connection *connection;
-	struct sccp_optional_data optional_data;
 
-	/* header check */
-	if (msgb_l2len(msgb) < header_size) {
-		DEBUGP(DSCCP, "msgb < header_size %u %u\n",
-		        msgb_l2len(msgb), header_size);
-		return -1;
-	}
-
-	/* copy out the calling and called address. Add the offset */
-	if (copy_address(&called, called_offset + req->variable_called, msgb) != 0)
+	if (_sccp_parse_connection_request(msgb, &result) != 0)
 		return -1;
 
-	if (check_address(&called) != 0) {
-		DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
-			*(u_int8_t *)&called.address, called.ssn);
-		return -1;
-	}
-
-	cb = _find_ssn(called.ssn);
+	cb = _find_ssn(result.called.ssn);
 	if (!cb || !cb->accept_cb) {
-		DEBUGP(DSCCP, "No routing for CR for called SSN: %u\n", called.ssn);
+		DEBUGP(DSCCP, "No routing for CR for called SSN: %u\n", result.called.ssn);
 		return -1;
 	}
 
@@ -652,28 +857,18 @@
 	 * and send a connection confirm, otherwise we will send a refuseed
 	 * one....
 	 */
-	if (destination_local_reference_is_free(&req->source_local_reference) != 0) {
+	if (destination_local_reference_is_free(result.source_local_reference) != 0) {
 		DEBUGP(DSCCP, "Need to reject connection with existing reference\n");
-		_sccp_send_refuse(req, SCCP_REFUSAL_SCCP_FAILURE);
+		_sccp_send_refuse(result.source_local_reference, SCCP_REFUSAL_SCCP_FAILURE);
 		talloc_free(connection);
 		return -1;
 	}
 
 	connection->incoming = 1;
-	connection->destination_local_reference = req->source_local_reference;
-
-	/*
-	 * parse optional data.
-	 */
-	memset(&optional_data, 0, sizeof(optional_data));
-	if (_sccp_parse_optional_data(optional_offset + req->optional_start, msgb, &optional_data) != 0) {
-		DEBUGP(DSCCP, "parsing of optional data failed.\n");
-		talloc_free(connection);
-		return -1;
-	}
+	connection->destination_local_reference = *result.source_local_reference;
 
 	if (cb->accept_cb(connection, cb->accept_context) != 0) {
-		_sccp_send_refuse(req, SCCP_REFUSAL_END_USER_ORIGINATED);
+		_sccp_send_refuse(result.source_local_reference, SCCP_REFUSAL_END_USER_ORIGINATED);
 		_sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_REFUSED);
 		talloc_free(connection);
 		return 0;
@@ -685,7 +880,7 @@
 	if (_sccp_send_connection_confirm(connection) != 0) {
 		DEBUGP(DSCCP, "Sending confirm failed... no available source reference?\n");
 
-		_sccp_send_refuse(req, SCCP_REFUSAL_SCCP_FAILURE);
+		_sccp_send_refuse(result.source_local_reference, SCCP_REFUSAL_SCCP_FAILURE);
 		_sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_REFUSED);
 		llist_del(&connection->list);
 		talloc_free(connection);
@@ -696,39 +891,30 @@
 	/*
 	 * If we have data let us forward things.
 	 */
-	if (optional_data.data_len != 0 && connection->data_cb) {
-		msgb->l3h = &msgb->l2h[optional_data.data_start];
-		connection->data_cb(connection, msgb, optional_data.data_len);
+	if (result.data_len != 0 && connection->data_cb) {
+		connection->data_cb(connection, msgb, result.data_len);
 	}
 
 	return 0;
 }
 
 /* Handle the release confirmed */
-static int _sccp_handle_connection_release_complete(struct msgb *data)
+static int _sccp_handle_connection_release_complete(struct msgb *msgb)
 {
-	static int header_size = sizeof(struct sccp_connection_release_complete);
-
-	struct sccp_connection_release_complete *cmpl;
+	struct sccp_parse_result result;
 	struct sccp_connection *conn;
 
-	/* header check */
-	if (msgb_l2len(data) < header_size) {
-		DEBUGP(DSCCP, "msgb < header_size %u %u\n",
-		        msgb_l2len(data), header_size);
+	if (_sccp_parse_connection_release_complete(msgb, &result) != 0)
 		return -1;
-	}
-
-	cmpl = (struct sccp_connection_release_complete *) data->l2h;
 
 	/* find the connection */
 	llist_for_each_entry(conn, &sccp_connections, list) {
 		if (conn->data_cb
 		    && memcmp(&conn->source_local_reference,
-			      &cmpl->destination_local_reference,
+			      result.destination_local_reference,
 			      sizeof(conn->source_local_reference)) == 0
 		    && memcmp(&conn->destination_local_reference,
-			      &cmpl->source_local_reference,
+			      result.source_local_reference,
 			      sizeof(conn->destination_local_reference)) == 0) {
 		    goto found;
 		}
@@ -745,57 +931,30 @@
 }
 
 /* Handle the Data Form 1 message */
-static int _sccp_handle_connection_dt1(struct msgb *data)
+static int _sccp_handle_connection_dt1(struct msgb *msgb)
 {
-	static int variable_offset = offsetof(struct sccp_data_form1, variable_start);
-	static int header_size = sizeof(struct sccp_data_form1);
-
-	struct sccp_data_form1 *dt1 = (struct sccp_data_form1 *)data->l2h;
+	struct sccp_parse_result result;
 	struct sccp_connection *conn;
-	int size;
 
-	/* we don't have enough size for the struct */
-	if (msgb_l2len(data) < header_size) {
-		DEBUGP(DSCCP, "msgb > header_size %u %u\n",
-		        msgb_l2len(data), header_size);
+	if (_sccp_parse_connection_dt1(msgb, &result) != 0)
 		return -1;
-	}
-
-	if (dt1->segmenting != 0) {
-		DEBUGP(DSCCP, "This packet has segmenting, not supported: %d\n", dt1->segmenting);
-		return -1;
-	}
 
 	/* lookup if we have a connection with the given reference */
 	llist_for_each_entry(conn, &sccp_connections, list) {
 		if (conn->data_cb
 		    && memcmp(&conn->source_local_reference,
-			      &dt1->destination_local_reference,
+			      result.destination_local_reference,
 			      sizeof(conn->source_local_reference)) == 0) {
-
-			/* some more  size checks in here */
-			if (msgb_l2len(data) < variable_offset + dt1->variable_start + 1) {
-				DEBUGP(DSCCP, "Not enough space for variable start: %u %u\n",
-					msgb_l2len(data), dt1->variable_start);
-				return -1;
-			}
-
-			size = data->l2h[variable_offset + dt1->variable_start];
-			data->l3h = &data->l2h[dt1->variable_start + variable_offset + 1];
-
-			if (msgb_l3len(data) < size) {
-				DEBUGP(DSCCP, "Not enough room for the payload: %u %u\n",
-					msgb_l3len(data), size);
-				return -1;
-			}
-
-			conn->data_cb(conn, data, size);
-			return 0;
+			goto found;
 		}
 	}
 
 	DEBUGP(DSCCP, "No connection found for dt1 data\n");
 	return -1;
+
+found:
+	conn->data_cb(conn, msgb, result.data_len);
+	return 0;
 }
 
 /* confirm a connection release */
@@ -830,30 +989,22 @@
 }
 
 /* connection released, send a released confirm */
-static int _sccp_handle_connection_released(struct msgb *data)
+static int _sccp_handle_connection_released(struct msgb *msgb)
 {
-	static int header_size = sizeof(struct sccp_connection_released);
-	static int optional_offset = offsetof(struct sccp_connection_released, optional_start);
-
-	struct sccp_optional_data optional_data;
-	struct sccp_connection_released *rls = (struct sccp_connection_released *)data->l2h;
+	struct sccp_parse_result result;
 	struct sccp_connection *conn;
 
-	/* we don't have enough size for the struct */
-	if (msgb_l2len(data) < header_size) {
-		DEBUGP(DSCCP, "msgb > header_size %u %u\n",
-		        msgb_l2len(data), header_size);
+	if (_sccp_parse_connection_released(msgb, &result) == -1)
 		return -1;
-	}
 
 	/* lookup if we have a connection with the given reference */
 	llist_for_each_entry(conn, &sccp_connections, list) {
 		if (conn->data_cb
 		    && memcmp(&conn->source_local_reference,
-			      &rls->destination_local_reference,
+			      result.destination_local_reference,
 			      sizeof(conn->source_local_reference)) == 0
 		    && memcmp(&conn->destination_local_reference,
-			      &rls->source_local_reference,
+			      result.source_local_reference,
 			      sizeof(conn->destination_local_reference)) == 0) {
 		    goto found;
 		}
@@ -865,16 +1016,9 @@
 
 	/* we have found a connection */
 found:
-	memset(&optional_data, 0, sizeof(optional_data));
-	if (_sccp_parse_optional_data(optional_offset + rls->optional_start, data, &optional_data) != 0) {
-		DEBUGP(DSCCP, "parsing of optional data failed.\n");
-		return -1;
-	}
-
 	/* optional data */
-	if (optional_data.data_len != 0 && conn->data_cb) {
-		data->l3h = &data->l2h[optional_data.data_start];
-		conn->data_cb(conn, data, optional_data.data_len);
+	if (result.data_len != 0 && conn->data_cb) {
+		conn->data_cb(conn, msgb, result.data_len);
 	}
 
 	/* generate a response */
@@ -888,28 +1032,17 @@
 
 static int _sccp_handle_connection_refused(struct msgb *msgb)
 {
-	static const u_int32_t header_size =
-			sizeof(struct sccp_connection_refused);
-	static int optional_offset = offsetof(struct sccp_connection_refused, optional_start);
-
-	struct sccp_optional_data optional_data;
+	struct sccp_parse_result result;
 	struct sccp_connection *conn;
-	struct sccp_connection_refused *ref;
 
-	/* header check */
-	if (msgb_l2len(msgb) < header_size) {
-		DEBUGP(DSCCP, "msgb < header_size %u %u\n",
-		        msgb_l2len(msgb), header_size);
+	if (_sccp_parse_connection_refused(msgb, &result) != 0)
 		return -1;
-	}
-
-	ref = (struct sccp_connection_refused *) msgb->l2h;
 
 	/* lookup if we have a connection with the given reference */
 	llist_for_each_entry(conn, &sccp_connections, list) {
 		if (conn->incoming == 0 && conn->data_cb
 		    && memcmp(&conn->source_local_reference,
-			      &ref->destination_local_reference,
+			      result.destination_local_reference,
 			      sizeof(conn->source_local_reference)) == 0) {
 		    goto found;
 		}
@@ -919,16 +1052,9 @@
 	return -1;
 
 found:
-	memset(&optional_data, 0, sizeof(optional_data));
-	if (_sccp_parse_optional_data(optional_offset + ref->optional_start, msgb, &optional_data) != 0) {
-		DEBUGP(DSCCP, "parsing of optional data failed.\n");
-		return -1;
-	}
-
 	/* optional data */
-	if (optional_data.data_len != 0 && conn->data_cb) {
-		msgb->l3h = &msgb->l2h[optional_data.data_start];
-		conn->data_cb(conn, msgb, optional_data.data_len);
+	if (result.data_len != 0 && conn->data_cb) {
+		conn->data_cb(conn, msgb, result.data_len);
 	}
 
 
@@ -939,29 +1065,17 @@
 
 static int _sccp_handle_connection_confirm(struct msgb *msgb)
 {
-	static u_int32_t header_size =
-		    sizeof(struct sccp_connection_confirm);
-	static const u_int32_t optional_offset =
-			offsetof(struct sccp_connection_confirm, optional_start);
-
-	struct sccp_optional_data optional_data;
+	struct sccp_parse_result result;
 	struct sccp_connection *conn;
-	struct sccp_connection_confirm *con;
 
-	/* header check */
-	if (msgb_l2len(msgb) < header_size) {
-		DEBUGP(DSCCP, "msgb < header_size %u %u\n",
-		        msgb_l2len(msgb), header_size);
+	if (_sccp_parse_connection_confirm(msgb, &result) != 0)
 		return -1;
-	}
-
-	con = (struct sccp_connection_confirm *) msgb->l2h;
 
 	/* lookup if we have a connection with the given reference */
 	llist_for_each_entry(conn, &sccp_connections, list) {
 		if (conn->incoming == 0 && conn->data_cb
 		    && memcmp(&conn->source_local_reference,
-			      &con->destination_local_reference,
+			      result.destination_local_reference,
 			      sizeof(conn->source_local_reference)) == 0) {
 		    goto found;
 		}
@@ -972,19 +1086,12 @@
 
 found:
 	/* copy the addresses of the connection */
-	conn->destination_local_reference = con->source_local_reference;
+	conn->destination_local_reference = *result.source_local_reference;
 	_sccp_set_connection_state(conn, SCCP_CONNECTION_STATE_ESTABLISHED);
 
-	memset(&optional_data, 0, sizeof(optional_data));
-	if (_sccp_parse_optional_data(optional_offset + con->optional_start, msgb, &optional_data) != 0) {
-		DEBUGP(DSCCP, "parsing of optional data failed.\n");
-		return -1;
-	}
-
 	/* optional data */
-	if (optional_data.data_len != 0 && conn->data_cb) {
-		msgb->l3h = &msgb->l2h[optional_data.data_start];
-		conn->data_cb(conn, msgb, optional_data.data_len);
+	if (result.data_len != 0 && conn->data_cb) {
+		conn->data_cb(conn, msgb, result.data_len);
 	}
 
 	return 0;
@@ -1161,6 +1268,49 @@
 	return ref;
 }
 
+int sccp_determine_msg_type(struct msgb *msg)
+{
+	if (msgb_l2len(msg) < 1)
+		return -1;
+
+	return msg->l2h[0];
+}
+
+int sccp_parse_header(struct msgb *msg, struct sccp_parse_result *result)
+{
+	int type;
+
+	if (msgb_l2len(msg) < 1)
+		return -1;
+
+	type = msg->l2h[0];
+	switch(type) {
+	case SCCP_MSG_TYPE_CR:
+		return _sccp_parse_connection_request(msg, result);
+		break;
+	case SCCP_MSG_TYPE_RLSD:
+		return _sccp_parse_connection_released(msg, result);
+		break;
+	case SCCP_MSG_TYPE_CREF:
+		return _sccp_parse_connection_refused(msg, result);
+		break;
+	case SCCP_MSG_TYPE_CC:
+		return _sccp_parse_connection_confirm(msg, result);
+		break;
+	case SCCP_MSG_TYPE_RLC:
+		return _sccp_parse_connection_release_complete(msg, result);
+		break;
+	case SCCP_MSG_TYPE_DT1:
+		return _sccp_parse_connection_dt1(msg, result);
+		break;
+	case SCCP_MSG_TYPE_UDT:
+		return _sccp_parse_udt(msg, result);
+		break;
+	};
+
+	return -1;
+}
+
 static __attribute__((constructor)) void on_dso_load(void)
 {
 	tall_sccp_ctx = talloc_named_const(NULL, 1, "sccp");
diff --git a/openbsc/tests/sccp/sccp_test.c b/openbsc/tests/sccp/sccp_test.c
index 91a8085..562e134 100644
--- a/openbsc/tests/sccp/sccp_test.c
+++ b/openbsc/tests/sccp/sccp_test.c
@@ -2,7 +2,7 @@
  * SCCP testing code
  *
  * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2009 by on-waves.com
+ * (C) 2009 by On-Waves
  *
  * All Rights Reserved
  *