Merge branch 'on-waves/sccp'
diff --git a/openbsc/include/sccp/sccp.h b/openbsc/include/sccp/sccp.h
index 604a2ac..84ae914 100644
--- a/openbsc/include/sccp/sccp.h
+++ b/openbsc/include/sccp/sccp.h
@@ -148,6 +148,11 @@
 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);
 
+struct msgb *sccp_create_refuse(struct sccp_source_reference *src_ref, int cause, uint8_t *data, int length);
+struct msgb *sccp_create_cc(struct sccp_source_reference *src_ref, struct sccp_source_reference *dst_ref);
+struct msgb *sccp_create_rlsd(struct sccp_source_reference *src_ref, struct sccp_source_reference *dst_ref, int cause);
+struct msgb *sccp_create_dt1(struct sccp_source_reference *dst_ref, uint8_t *data, uint8_t len);
+
 /**
  * Below this are helper functions and structs for parsing SCCP messages
  */
diff --git a/openbsc/src/sccp/sccp.c b/openbsc/src/sccp/sccp.c
index de18614..f4335c0 100644
--- a/openbsc/src/sccp/sccp.c
+++ b/openbsc/src/sccp/sccp.c
@@ -638,7 +638,7 @@
 		connection->state_cb(connection, old_state);
 }
 
-static int _sccp_send_refuse(struct sccp_source_reference *src_ref, int cause)
+struct msgb *sccp_create_refuse(struct sccp_source_reference *src_ref, int cause, uint8_t *inp, int length)
 {
 	struct msgb *msgb;
 	struct sccp_connection_refused *ref;
@@ -646,6 +646,11 @@
 
 	msgb = msgb_alloc_headroom(SCCP_MSG_SIZE,
 				   SCCP_MSG_HEADROOM, "sccp ref");
+	if (!msgb) {
+		LOGP(DSCCP, LOGL_ERROR, "Failed to allocate refusal msg.\n");
+		return NULL;
+	}
+
 	msgb->l2h = &msgb->data[0];
 
 	ref = (struct sccp_connection_refused *) msgb_put(msgb, sizeof(*ref));
@@ -655,40 +660,70 @@
 	ref->cause = cause;
 	ref->optional_start = 1;
 
+	if (inp) {
+		data = msgb_put(msgb, 1 + 1 + length);
+		data[0] = SCCP_PNC_DATA;
+		data[1] = length;
+		memcpy(&data[2], inp, length);
+	}
+
 	data = msgb_put(msgb, 1);
 	data[0] = SCCP_PNC_END_OF_OPTIONAL;
+	return msgb;
+}
+
+static int _sccp_send_refuse(struct sccp_source_reference *src_ref, int cause)
+{
+	struct msgb *msgb = sccp_create_refuse(src_ref, cause, NULL, 0);
+	if (!msgb)
+		return -1;
 
 	_send_msg(msgb);
 	return 0;
 }
 
-static int _sccp_send_connection_confirm(struct sccp_connection *connection)
+struct msgb *sccp_create_cc(struct sccp_source_reference *src_ref,
+			    struct sccp_source_reference *dst_ref)
 {
 	struct msgb *response;
 	struct sccp_connection_confirm *confirm;
 	u_int8_t *optional_data;
 
-	if (assign_source_local_reference(connection) != 0)
-		return -1;
-
 	response = msgb_alloc_headroom(SCCP_MSG_SIZE,
 				       SCCP_MSG_HEADROOM, "sccp confirm");
+	if (!response) {
+		LOGP(DSCCP, LOGL_ERROR, "Failed to create SCCP Confirm.\n");
+		return NULL;
+	}
+
 	response->l2h = &response->data[0];
 
 	confirm = (struct sccp_connection_confirm *) msgb_put(response, sizeof(*confirm));
 
 	confirm->type = SCCP_MSG_TYPE_CC;
 	memcpy(&confirm->destination_local_reference,
-	       &connection->destination_local_reference,
-	       sizeof(connection->destination_local_reference));
+	       dst_ref, sizeof(*dst_ref));
 	memcpy(&confirm->source_local_reference,
-	       &connection->source_local_reference,
-	       sizeof(connection->source_local_reference));
+	       src_ref, sizeof(*src_ref));
 	confirm->proto_class = 2;
 	confirm->optional_start = 1;
 
 	optional_data = (u_int8_t *) msgb_put(response, 1);
 	optional_data[0] = SCCP_PNC_END_OF_OPTIONAL;
+	return response;
+}
+
+static int _sccp_send_connection_confirm(struct sccp_connection *connection)
+{
+	struct msgb *response;
+
+	if (assign_source_local_reference(connection) != 0)
+		return -1;
+
+	response = sccp_create_cc(&connection->source_local_reference,
+				  &connection->destination_local_reference);
+	if (!response)
+		return -1;
 
 	_send_msg(response);
 	_sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_ESTABLISHED);
@@ -755,34 +790,49 @@
 	return 0;
 }
 
-static int _sccp_send_connection_data(struct sccp_connection *conn, struct msgb *_data)
+struct msgb *sccp_create_dt1(struct sccp_source_reference *dst_ref, uint8_t *inp_data, uint8_t len)
 {
 	struct msgb *msgb;
 	struct sccp_data_form1 *dt1;
 	u_int8_t *data;
-	int extra_size;
+
+	msgb = msgb_alloc_headroom(SCCP_MSG_SIZE,
+				   SCCP_MSG_HEADROOM, "sccp dt1");
+	if (!msgb) {
+		LOGP(DSCCP, LOGL_ERROR, "Failed to create DT1 msg.\n");
+		return NULL;
+	}
+
+	msgb->l2h = &msgb->data[0];
+
+	dt1 = (struct sccp_data_form1 *) msgb_put(msgb, sizeof(*dt1));
+	dt1->type = SCCP_MSG_TYPE_DT1;
+	memcpy(&dt1->destination_local_reference, dst_ref,
+	       sizeof(struct sccp_source_reference));
+	dt1->segmenting = 0;
+
+	/* copy the data */
+	dt1->variable_start = 1;
+	data = msgb_put(msgb, 1 + len);
+	data[0] = len;
+	memcpy(&data[1], inp_data, len);
+
+	return msgb;
+}
+
+static int _sccp_send_connection_data(struct sccp_connection *conn, struct msgb *_data)
+{
+	struct msgb *msgb;
 
 	if (msgb_l3len(_data) < 2 || msgb_l3len(_data) > 256) {
 		LOGP(DSCCP, LOGL_ERROR, "data size too big, segmenting unimplemented.\n");
 		return -1;
 	}
 
-	extra_size = 1 + msgb_l3len(_data);
-	msgb = msgb_alloc_headroom(SCCP_MSG_SIZE,
-				   SCCP_MSG_HEADROOM, "sccp dt1");
-	msgb->l2h = &msgb->data[0];
-
-	dt1 = (struct sccp_data_form1 *) msgb_put(msgb, sizeof(*dt1));
-	dt1->type = SCCP_MSG_TYPE_DT1;
-	memcpy(&dt1->destination_local_reference, &conn->destination_local_reference,
-	       sizeof(struct sccp_source_reference));
-	dt1->segmenting = 0;
-
-	/* copy the data */
-	dt1->variable_start = 1;
-	data = msgb_put(msgb, extra_size);
-	data[0] = extra_size - 1;
-	memcpy(&data[1], _data->l3h, extra_size - 1);
+	msgb = sccp_create_dt1(&conn->destination_local_reference,
+			       _data->l3h, msgb_l3len(_data));
+	if (!msgb)
+		return -1;
 
 	_send_msg(msgb);
 	return 0;
@@ -811,7 +861,8 @@
 	return 0;
 }
 
-static int _sccp_send_connection_released(struct sccp_connection *conn, int cause)
+struct msgb *sccp_create_rlsd(struct sccp_source_reference *src_ref,
+			      struct sccp_source_reference *dst_ref, int cause)
 {
 	struct msgb *msg;
 	struct sccp_connection_released *rel;
@@ -819,19 +870,36 @@
 
 	msg = msgb_alloc_headroom(SCCP_MSG_SIZE, SCCP_MSG_HEADROOM,
 				  "sccp: connection released");
+	if (!msg) {
+		LOGP(DSCCP, LOGL_ERROR, "Failed to allocate RLSD.\n");
+		return NULL;
+	}
+
 	msg->l2h = &msg->data[0];
 	rel = (struct sccp_connection_released *) msgb_put(msg, sizeof(*rel));
 	rel->type = SCCP_MSG_TYPE_RLSD;
 	rel->release_cause = cause;
 
 	/* copy the source references */
-	memcpy(&rel->destination_local_reference, &conn->destination_local_reference,
+	memcpy(&rel->destination_local_reference, dst_ref,
 	       sizeof(struct sccp_source_reference));
-	memcpy(&rel->source_local_reference, &conn->source_local_reference,
+	memcpy(&rel->source_local_reference, src_ref,
 	       sizeof(struct sccp_source_reference));
 
 	data = msgb_put(msg, 1);
 	data[0] = SCCP_PNC_END_OF_OPTIONAL;
+	return msg;
+}
+
+static int _sccp_send_connection_released(struct sccp_connection *conn, int cause)
+{
+	struct msgb *msg;
+
+	msg = sccp_create_rlsd(&conn->source_local_reference,
+			       &conn->destination_local_reference,
+			       cause);
+	if (!msg)
+		return -1;
 
 	_sccp_set_connection_state(conn, SCCP_CONNECTION_STATE_RELEASE);
 	_send_msg(msg);