diff --git a/openbsc/include/sccp/sccp.h b/openbsc/include/sccp/sccp.h index d7b57c610..643479adc 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 + * (C) 2009, 2010 by Holger Hans Peter Freyther * * All Rights Reserved * @@ -143,4 +143,25 @@ extern const struct sockaddr_sccp sccp_ssn_bssap; 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 bbf3e4074..689555d34 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 - * (C) 2009 by on-waves.com + * (C) 2009, 2010 by Holger Hans Peter Freyther + * (C) 2009, 2010 by On-Waves * * All Rights Reserved * @@ -200,6 +200,277 @@ static int _sccp_parse_optional_data(const int offset, 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_send_data(int class, const struct sockaddr_sccp *in, 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 @@ static void _sccp_set_connection_state(struct sccp_connection *connection, int n 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 @@ static int _sccp_send_refuse(struct sccp_connection_request *req, int cause) 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_send_connection_released(struct sccp_connection *conn, int caus */ 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 @@ static int _sccp_handle_connection_request(struct msgb *msgb) * 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 @@ static int _sccp_handle_connection_request(struct msgb *msgb) 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 @@ static int _sccp_handle_connection_request(struct msgb *msgb) /* * 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 @@ found: } /* 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 @@ static int _sccp_send_connection_release_complete(struct sccp_connection *connec } /* 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 @@ static int _sccp_handle_connection_released(struct msgb *data) /* 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 @@ found: 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 @@ static int _sccp_handle_connection_refused(struct msgb *msgb) 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 @@ found: 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 @@ static int _sccp_handle_connection_confirm(struct msgb *msgb) 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 @@ struct sccp_source_reference sccp_src_ref_from_int(u_int32_t int_ref) 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 91a80858d..562e1345f 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 - * (C) 2009 by on-waves.com + * (C) 2009 by On-Waves * * All Rights Reserved *