Example #1
0
int lwm2m_data_parse(lwm2m_uri_t * uriP,
                     uint8_t * buffer,
                     size_t bufferLen,
                     lwm2m_media_type_t format,
                     lwm2m_data_t ** dataP)
{
    int res;

    LOG_ARG("format: %s, bufferLen: %d", STR_MEDIA_TYPE(format), bufferLen);
    LOG_URI(uriP);
    switch (format)
    {
    case LWM2M_CONTENT_TEXT:
        if (!LWM2M_URI_IS_SET_RESOURCE(uriP)) return 0;
        *dataP = lwm2m_data_new(1);
        if (*dataP == NULL) return 0;
        (*dataP)->id = uriP->resourceId;
        (*dataP)->type = LWM2M_TYPE_STRING;
        res = prv_setBuffer(*dataP, buffer, bufferLen);
        if (res == 0)
        {
            lwm2m_data_free(1, *dataP);
            *dataP = NULL;
        }
        return res;

    case LWM2M_CONTENT_OPAQUE:
        if (!LWM2M_URI_IS_SET_RESOURCE(uriP)) return 0;
        *dataP = lwm2m_data_new(1);
        if (*dataP == NULL) return 0;
        (*dataP)->id = uriP->resourceId;
        (*dataP)->type = LWM2M_TYPE_OPAQUE;
        res = prv_setBuffer(*dataP, buffer, bufferLen);
        if (res == 0)
        {
            lwm2m_data_free(1, *dataP);
            *dataP = NULL;
    }
        return res;

#ifdef LWM2M_OLD_CONTENT_FORMAT_SUPPORT
    case LWM2M_CONTENT_TLV_OLD:
#endif
    case LWM2M_CONTENT_TLV:
        return tlv_parse(buffer, bufferLen, dataP);

#ifdef LWM2M_SUPPORT_JSON
#ifdef LWM2M_OLD_CONTENT_FORMAT_SUPPORT
    case LWM2M_CONTENT_JSON_OLD:
#endif
    case LWM2M_CONTENT_JSON:
        return json_parse(uriP, buffer, bufferLen, dataP);
#endif

    default:
        return 0;
    }
}
Example #2
0
static int dump_fcp_template_msg(struct msgb *msg)
{
	struct tlv_parsed tp;
	int rc;

	rc = tlv_parse(&tp, &ts102221_fcp_tlv_def, msgb_apdu_de(msg)+2, msgb_apdu_le(msg)-2, 0, 0);
	if (rc < 0)
		return rc;

	return dump_fcp_template(&tp);
}
Example #3
0
void handle_rr(struct session_info *s, struct gsm48_hdr *dtap, unsigned len, uint32_t fn)
{
	struct gsm48_system_information_type_6 *si6;
	struct tlv_parsed tp;

	s->rat = RAT_GSM;
	assert(s->new_msg);

	if (!len) {
		return;
	}

	switch (dtap->msg_type) {
	case GSM48_MT_RR_SYSINFO_1:
		SET_MSG_INFO(s, "SYSTEM INFO 1");
		handle_sysinfo(s, dtap, len);
		break;
	case GSM48_MT_RR_SYSINFO_2:
		SET_MSG_INFO(s, "SYSTEM INFO 2");
		handle_sysinfo(s, dtap, len);
		break;
	case GSM48_MT_RR_SYSINFO_2bis:
		SET_MSG_INFO(s, "SYSTEM INFO 2bis");
		handle_sysinfo(s, dtap, len);
		break;
	case GSM48_MT_RR_SYSINFO_2ter:
		SET_MSG_INFO(s, "SYSTEM INFO 2ter");
		handle_sysinfo(s, dtap, len);
		break;
	case GSM48_MT_RR_SYSINFO_2quater:
		SET_MSG_INFO(s, "SYSTEM INFO 2quater");
		handle_sysinfo(s, dtap, len);
		break;
	case GSM48_MT_RR_SYSINFO_3:
		SET_MSG_INFO(s, "SYSTEM INFO 3");
		handle_sysinfo(s, dtap, len);
		break;
	case GSM48_MT_RR_SYSINFO_4:
		SET_MSG_INFO(s, "SYSTEM INFO 4");
		handle_sysinfo(s, dtap, len);
		break;
	case GSM48_MT_RR_SYSINFO_5:
		SET_MSG_INFO(s, "SYSTEM INFO 5");
		rand_check((uint8_t *)dtap, 18, &s->si5, s->cipher);
		handle_sysinfo(s, dtap, len);
		break;
	case GSM48_MT_RR_SYSINFO_5bis:
		SET_MSG_INFO(s, "SYSTEM INFO 5bis");
		rand_check((uint8_t *)dtap, 18, &s->si5bis, s->cipher);
		handle_sysinfo(s, dtap, len);
		break;
	case GSM48_MT_RR_SYSINFO_5ter:
		SET_MSG_INFO(s, "SYSTEM INFO 5ter");
		rand_check((uint8_t *)dtap, 18, &s->si5ter, s->cipher);
		handle_sysinfo(s, dtap, len);
		break;
	case GSM48_MT_RR_SYSINFO_6:
		SET_MSG_INFO(s, "SYSTEM INFO 6");
		rand_check((uint8_t *)dtap, 18, &s->si6, s->cipher);
		si6 = (struct gsm48_system_information_type_6 *) dtap;
		handle_lai(s, (uint8_t*)&si6->lai, htons(si6->cell_identity));
		handle_sysinfo(s, dtap, len);
		break;
	case GSM48_MT_RR_SYSINFO_13:
		SET_MSG_INFO(s, "SYSTEM INFO 13");
		handle_sysinfo(s, dtap, len);
		break;
	case GSM48_MT_RR_CHAN_REL:
		SET_MSG_INFO(s, "CHANNEL RELEASE");
		if (s->cipher && !s->fc.enc_rand)
			s->fc.predict++;

		s->release = 1;
		s->rr_cause = dtap->data[0];
		if ((len > 3) && ((dtap->data[1] & 0xf0) == 0xc0))
			s->have_gprs = 1;

		session_reset(&s[0], 0);
		if (auto_reset) {
			s[1].new_msg = NULL;
		}
		break;
	case GSM48_MT_RR_CLSM_ENQ:
		SET_MSG_INFO(s, "CLASSMARK ENQUIRY");
		break;
	case GSM48_MT_RR_MEAS_REP:
		SET_MSG_INFO(s, "MEASUREMENT REPORT");
		break;
	case GSM48_MT_RR_CLSM_CHG:
		SET_MSG_INFO(s, "CLASSMARK CHANGE");
		handle_classmark(s, &dtap->data[1], 2);
		break;
	case GSM48_MT_RR_PAG_REQ_1:
		SET_MSG_INFO(s, "PAGING REQ 1");
		handle_paging1(dtap, len);
		break;
	case GSM48_MT_RR_PAG_REQ_2:
		SET_MSG_INFO(s, "PAGING REQ 2");
		handle_paging2(dtap, len);
		break;
	case GSM48_MT_RR_PAG_REQ_3:
		SET_MSG_INFO(s, "PAGING REQ 3");
		handle_paging3();
		break;
	case GSM48_MT_RR_IMM_ASS:
		SET_MSG_INFO(s, "IMM ASSIGNMENT");
		break;
	case GSM48_MT_RR_IMM_ASS_EXT:
		SET_MSG_INFO(s, "IMM ASSIGNMENT EXT");
		break;
	case GSM48_MT_RR_IMM_ASS_REJ:
		SET_MSG_INFO(s, "IMM ASSIGNMENT REJECT");
		break;
	case GSM48_MT_RR_PAG_RESP:
		session_reset(s, 1);
		SET_MSG_INFO(s, "PAGING RESPONSE");
		handle_pag_resp(s, dtap->data);
		break;
	case GSM48_MT_RR_HANDO_CMD:
		SET_MSG_INFO(s, "HANDOVER COMMAND");
		parse_assignment(dtap, len, s->cell_arfcns, &s->ga);
		s->handover = 1;
		s->use_jump = 2;
		break;
	case GSM48_MT_RR_HANDO_COMPL:
		SET_MSG_INFO(s, "HANDOVER COMPLETE");
		break;
	case GSM48_MT_RR_ASS_CMD:
		SET_MSG_INFO(s, "ASSIGNMENT COMMAND");
		if ((s->fc.enc-s->fc.enc_null-s->fc.enc_si) == 1)
			s->forced_ho = 1;
		parse_assignment(dtap, len, s->cell_arfcns, &s->ga);
		s->assignment = 1;
		s->use_jump = 1;
		break;
	case GSM48_MT_RR_ASS_COMPL:
		SET_MSG_INFO(s, "ASSIGNMENT COMPLETE");
		s->assign_complete = 1;
		break;
	case GSM48_MT_RR_CIPH_M_COMPL:
		SET_MSG_INFO(s, "CIPHER MODE COMPLETE");
		if (s->cipher_missing < 0) {
			s->cipher_missing = 0;
		} else {
			s->cipher_missing = 1;
		}

		if (!s->cm_comp_first_fn) {
			if (fn) {
				s->cm_comp_first_fn = fn;
			} else {
				s->cm_comp_first_fn = GSM_MAX_FN;
			}
		}

		if (fn) {
			s->cm_comp_last_fn = fn;
		} else {
			s->cm_comp_last_fn = GSM_MAX_FN;
		}

		s->cm_comp_count++;

		if (dtap->data[0] == 0x2b)
			return;

		/* get IMEISV */
		tlv_parse(&tp, &gsm48_rr_att_tlvdef, dtap->data, len-2, 0, 0);
		if (TLVP_PRESENT(&tp, GSM48_IE_MOBILE_ID)) {
			uint8_t *v = (uint8_t *) TLVP_VAL(&tp, GSM48_IE_MOBILE_ID);
			handle_mi(s, &v[1], v[0], 0);
			s->cmc_imeisv = 1;
		}
		break;
	case GSM48_MT_RR_GPRS_SUSP_REQ:
		SET_MSG_INFO(s, "GPRS SUSPEND");
		s->have_gprs = 1;
		//tlli
		//rai (lai+rac)
		break;
	case GSM48_MT_RR_CIPH_M_CMD:
		if (!s->cm_cmd_fn) {
			if (fn) {
				s->cm_cmd_fn = fn;
			} else {
				s->cm_cmd_fn = GSM_MAX_FN;
			}
		}

		if (dtap->data[0] & 1) {
			s->cipher = 1 + ((dtap->data[0]>>1) & 7);
			if (!not_zero(s->key, 8))
				s->decoded = 0;
		}
		SET_MSG_INFO(s, "CIPHER MODE COMMAND, A5/%u", s->cipher);
		if (dtap->data[0] & 0x10) {
			s->cmc_imeisv = 1;

			if (s->cipher && !s->fc.enc_rand)
				s->fc.predict++;
		}
		s->cipher_missing = -1;
		break;
	case 0x60:
		SET_MSG_INFO(s, "UTRAN CLASSMARK");
		break;
	default:
		SET_MSG_INFO(s, "UNKNOWN RR (%02x)", dtap->msg_type);
		s->unknown = 1;
	}
Example #4
0
void handle_cc(struct session_info *s, struct gsm48_hdr *dtap, unsigned len, uint8_t ul)
{
	struct tlv_parsed tp;

	s->call = 1;
	s->call_presence = 1;

	switch (dtap->msg_type & 0x3f) {
	case 0x01:
		SET_MSG_INFO(s, "CALL ALERTING");
		break;
	case 0x02:
		SET_MSG_INFO(s, "CALL PROCEEDING");
		if (s->cipher && !s->fc.enc_rand && !ul)
			s->fc.predict++;
		if (!ul)
			s->mo = 1;
		break;
	case 0x03:
		SET_MSG_INFO(s, "CALL PROGRESS");
		break;
	case 0x05:
		SET_MSG_INFO(s, "CALL SETUP");
		if (!ul)
			s->mt = 1;
		else
			s->mo = 1;

		/* get MSISDN */
		tlv_parse(&tp, &gsm48_att_tlvdef, dtap->data, len-2, 0, 0);
		if (TLVP_PRESENT(&tp, GSM48_IE_CALLING_BCD)) {
			uint8_t *v = (uint8_t *) TLVP_VAL(&tp, GSM48_IE_CALLING_BCD);
			uint8_t v_len = TLVP_LEN(&tp, GSM48_IE_CALLING_BCD);
			handle_address(v, v_len, s->msisdn, 0);
		}
		if (TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) {
			uint8_t *v = (uint8_t *) TLVP_VAL(&tp, GSM48_IE_CALLED_BCD);
			uint8_t v_len = TLVP_LEN(&tp, GSM48_IE_CALLED_BCD);
			handle_address(v, v_len, s->msisdn, 0);
		}

		break;
	case 0x07:
		SET_MSG_INFO(s, "CALL CONNECT");
		break;
	case 0x08:
		SET_MSG_INFO(s, "CALL CONFIRMED");
		if (ul)
			s->mt = 1;
		else
			s->mo = 1;
		break;
	case 0x0f:
		SET_MSG_INFO(s, "CALL CONNECT ACK");
		break;
	case 0x25:
		SET_MSG_INFO(s, "CALL DISCONNECT");
		break;
	case 0x2a:
		SET_MSG_INFO(s, "CALL RELEASE COMPLETE");
		break;
	case 0x2d:
		SET_MSG_INFO(s, "CALL RELEASE");
		break;
	case 0x3a:
		SET_MSG_INFO(s, "CALL FACILITY");
		break;
	case 0x3d:
		SET_MSG_INFO(s, "CALL STATUS");
		break;
	default:
		SET_MSG_INFO(s, "UNKNOWN CC (%02x)", dtap->msg_type & 0x3f);
		s->unknown = 1;
	}
}
/*
 * Handle the assignment request message.
 *
 * See §3.2.1.1 for the message type
 */
static int bssmap_handle_assignm_req(struct osmo_bsc_sccp_con *conn,
				     struct msgb *msg, unsigned int length)
{
	struct msgb *resp;
	struct osmo_msc_data *msc;
	struct tlv_parsed tp;
	uint8_t *data;
	uint8_t timeslot;
	uint8_t multiplex;
	enum gsm48_chan_mode chan_mode = GSM48_CMODE_SIGN;
	int i, supported, port, full_rate = -1;

	if (!conn->conn) {
		LOGP(DMSC, LOGL_ERROR, "No lchan/msc_data in cipher mode command.\n");
		return -1;
	}

	tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, length - 1, 0, 0);

	if (!TLVP_PRESENT(&tp, GSM0808_IE_CHANNEL_TYPE)) {
		LOGP(DMSC, LOGL_ERROR, "Mandatory channel type not present.\n");
		goto reject;
	}

	if (!TLVP_PRESENT(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)) {
		LOGP(DMSC, LOGL_ERROR, "Identity code missing. Audio routing will not work.\n");
		goto reject;
	}

	conn->cic = ntohs(read_data16(TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)));
	timeslot = conn->cic & 0x1f;
	multiplex = (conn->cic & ~0x1f) >> 5;

	/*
	 * Currently we only support a limited subset of all
	 * possible channel types. The limitation ends by not using
	 * multi-slot, limiting the channel coding, speech...
	 */
	if (TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE) < 3) {
		LOGP(DMSC, LOGL_ERROR, "ChannelType len !=3 not supported: %d\n",
			TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE));
		goto reject;
	}

	/*
	 * Try to figure out if we support the proposed speech codecs. For
	 * now we will always pick the full rate codecs.
	 */

	data = (uint8_t *) TLVP_VAL(&tp, GSM0808_IE_CHANNEL_TYPE);
	if ((data[0] & 0xf) != 0x1) {
		LOGP(DMSC, LOGL_ERROR, "ChannelType != speech: %d\n", data[0]);
		goto reject;
	}

	/*
	 * go through the list of preferred codecs of our gsm network
	 * and try to find it among the permitted codecs. If we found
	 * it we will send chan_mode to the right mode and break the
	 * inner loop. The outer loop will exit due chan_mode having
	 * the correct value.
	 */
	full_rate = 0;
	msc = conn->msc;
	for (supported = 0;
		chan_mode == GSM48_CMODE_SIGN && supported < msc->audio_length;
		++supported) {

		int perm_val = audio_support_to_gsm88(msc->audio_support[supported]);
		for (i = 2; i < TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE); ++i) {
			if ((data[i] & 0x7f) == perm_val) {
				chan_mode = gsm88_to_chan_mode(perm_val);
				full_rate = (data[i] & 0x4) == 0;
				break;
			} else if ((data[i] & 0x80) == 0x00) {
				break;
			}
		}
	}

	if (chan_mode == GSM48_CMODE_SIGN) {
		LOGP(DMSC, LOGL_ERROR, "No supported audio type found.\n");
		goto reject;
	}

	/* map it to a MGCP Endpoint and a RTP port */
	port = mgcp_timeslot_to_endpoint(multiplex, timeslot);
	conn->rtp_port = rtp_calculate_port(port, msc->rtp_base);

	return gsm0808_assign_req(conn->conn, chan_mode, full_rate);

reject:
	resp = gsm0808_create_assignment_failure(GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL);
	if (!resp) {
		LOGP(DMSC, LOGL_ERROR, "Channel allocation failure.\n");
		return -1;
	}

	bsc_queue_for_msc(conn, resp);
	return -1;
}
/*
 * GSM 08.08 § 3.4.7 cipher mode handling. We will have to pick
 * the cipher to be used for this. In case we are already using
 * a cipher we will have to send cipher mode reject to the MSC,
 * otherwise we will have to pick something that we and the MS
 * is supporting. Currently we are doing it in a rather static
 * way by picking one ecnryption or no encrytpion.
 */
static int bssmap_handle_cipher_mode(struct osmo_bsc_sccp_con *conn,
				     struct msgb *msg, unsigned int payload_length)
{
	uint16_t len;
	struct gsm_network *network = NULL;
	const uint8_t *data;
	struct tlv_parsed tp;
	struct msgb *resp;
	int reject_cause = -1;
	int include_imeisv = 1;

	if (!conn->conn) {
		LOGP(DMSC, LOGL_ERROR, "No lchan/msc_data in cipher mode command.\n");
		goto reject;
	}

	if (conn->ciphering_handled) {
		LOGP(DMSC, LOGL_ERROR, "Already seen ciphering command. Protocol Error.\n");
		goto reject;
	}

	conn->ciphering_handled = 1;

	tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, payload_length - 1, 0, 0);
	if (!TLVP_PRESENT(&tp, GSM0808_IE_ENCRYPTION_INFORMATION)) {
		LOGP(DMSC, LOGL_ERROR, "IE Encryption Information missing.\n");
		goto reject;
	}

	/*
	 * check if our global setting is allowed
	 *  - Currently we check for A5/0 and A5/1
	 *  - Copy the key if that is necessary
	 *  - Otherwise reject
	 */
	len = TLVP_LEN(&tp, GSM0808_IE_ENCRYPTION_INFORMATION);
	if (len < 1) {
		LOGP(DMSC, LOGL_ERROR, "IE Encryption Information is too short.\n");
		goto reject;
	}

	network = conn->conn->bts->network;
	data = TLVP_VAL(&tp, GSM0808_IE_ENCRYPTION_INFORMATION);

	if (TLVP_PRESENT(&tp, GSM0808_IE_CIPHER_RESPONSE_MODE))
		include_imeisv = TLVP_VAL(&tp, GSM0808_IE_CIPHER_RESPONSE_MODE)[0] & 0x1;

	if (network->a5_encryption == 0 && (data[0] & 0x1) == 0x1) {
		gsm0808_cipher_mode(conn->conn, 0, NULL, 0, include_imeisv);
	} else if (network->a5_encryption != 0 && (data[0] & 0x2) == 0x2) {
		gsm0808_cipher_mode(conn->conn, 1, &data[1], len - 1, include_imeisv);
	} else {
		LOGP(DMSC, LOGL_ERROR, "Can not select encryption...\n");
		goto reject;
	}

	return 0;

reject:
	resp = gsm0808_create_cipher_reject(reject_cause);
	if (!resp) {
		LOGP(DMSC, LOGL_ERROR, "Sending the cipher reject failed.\n");
		return -1;
	}

	bsc_queue_for_msc(conn, resp);
	return -1;
}
/* GSM 08.08 § 3.2.1.19 */
static int bssmap_handle_paging(struct osmo_msc_data *msc,
				struct msgb *msg, unsigned int payload_length)
{
	struct gsm_subscriber *subscr;
	struct tlv_parsed tp;
	char mi_string[GSM48_MI_SIZE];
	uint32_t tmsi = GSM_RESERVED_TMSI;
	unsigned int lac = GSM_LAC_RESERVED_ALL_BTS;
	uint8_t data_length;
	const uint8_t *data;
	uint8_t chan_needed = RSL_CHANNEED_ANY;

	tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, payload_length - 1, 0, 0);

	if (!TLVP_PRESENT(&tp, GSM0808_IE_IMSI)) {
		LOGP(DMSC, LOGL_ERROR, "Mandatory IMSI not present.\n");
		return -1;
	} else if ((TLVP_VAL(&tp, GSM0808_IE_IMSI)[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_IMSI) {
		LOGP(DMSC, LOGL_ERROR, "Wrong content in the IMSI\n");
		return -1;
	}

	if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST)) {
		LOGP(DMSC, LOGL_ERROR, "Mandatory CELL IDENTIFIER LIST not present.\n");
		return -1;
	}

	if (TLVP_PRESENT(&tp, GSM0808_IE_TMSI) &&
	    TLVP_LEN(&tp, GSM0808_IE_TMSI) == 4) {
		tmsi = ntohl(tlvp_val32_unal(&tp, GSM0808_IE_TMSI));
	}

	/*
	 * parse the IMSI
	 */
	gsm48_mi_to_string(mi_string, sizeof(mi_string),
			   TLVP_VAL(&tp, GSM0808_IE_IMSI), TLVP_LEN(&tp, GSM0808_IE_IMSI));

	/*
	 * parse the cell identifier list
	 */
	data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST);
	data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST);

	/*
	 * Support paging to all network or one BTS at one LAC
	 */
	if (data_length == 3 && data[0] == CELL_IDENT_LAC) {
		lac = ntohs(read_data16(&data[1]));
	} else if (data_length > 1 || (data[0] & 0x0f) != CELL_IDENT_BSS) {
		LOGP(DMSC, LOGL_ERROR, "Unsupported Cell Identifier List: %s\n", osmo_hexdump(data, data_length));
		return -1;
	}

	if (TLVP_PRESENT(&tp, GSM0808_IE_CHANNEL_NEEDED) && TLVP_LEN(&tp, GSM0808_IE_CHANNEL_NEEDED) == 1)
		chan_needed = TLVP_VAL(&tp, GSM0808_IE_CHANNEL_NEEDED)[0] & 0x03;

	if (TLVP_PRESENT(&tp, GSM0808_IE_EMLPP_PRIORITY)) {
		LOGP(DMSC, LOGL_ERROR, "eMLPP is not handled\n");
	}

	subscr = subscr_get_or_create(msc->network->subscr_group, mi_string);
	if (!subscr) {
		LOGP(DMSC, LOGL_ERROR, "Failed to allocate a subscriber for %s\n", mi_string);
		return -1;
	}

	subscr->lac = lac;
	subscr->tmsi = tmsi;

	LOGP(DMSC, LOGL_INFO, "Paging request from MSC IMSI: '%s' TMSI: '0x%x/%u' LAC: 0x%x\n", mi_string, tmsi, tmsi, lac);
	bsc_grace_paging_request(subscr, chan_needed, msc);
	return 0;
}
void parse_assignment(struct gsm48_hdr *hdr, unsigned len, struct gsm_sysinfo_freq *cell_arfcns, struct gsm_assignment *ga)
{
	struct gsm48_ass_cmd *ac;
	struct gsm48_ho_cmd *hoc;
	struct gsm48_chan_desc *cd = NULL;
	int payload_len = 0; 
	uint8_t *payload_data = NULL;
	struct tlv_parsed tp;
	uint8_t *ma = 0;
	uint8_t ma_len;
	uint8_t ch_type, ch_subch, ch_ts;
	unsigned i, mask;

	if (!ga)
		return;

	memset(ga, 0, sizeof(*ga));

	/* handover */
	if (hdr->msg_type == 0x2b) {
		if (len < (sizeof(*hdr) + sizeof(*hoc))) {
			return;
		}
		hoc = (struct gsm48_ho_cmd *) hdr->data;
		cd = &hoc->chan_desc;
		payload_len = len - sizeof(*hdr) - sizeof(*hoc);
		payload_data = hoc->data;
		ga->bcch_arfcn = hoc->cell_desc.arfcn_lo;
		ga->bcch_arfcn |= (hoc->cell_desc.arfcn_hi << 8);
	}

	/* assignment */
	if (hdr->msg_type == 0x2e) {
		if (len < (sizeof(*hdr) + sizeof(*ac))) {
			return;
		}
		ac = (struct gsm48_ass_cmd *) hdr->data;
		cd = &ac->chan_desc;
		payload_len = len - sizeof(*hdr) - sizeof(*ac);
		payload_data = ac->data;
	}

	if (!cd)
		return;

	if (!payload_len)
		return;
		
	/* Parse TLV in the message */
	tlv_parse(&tp, &gsm48_rr_att_tlvdef, payload_data, payload_len, 0, 0);

	ma_len = 0;
	ma = NULL;
	mask = 0;

	/* Cell channel description */
	if (TLVP_PRESENT(&tp, GSM48_IE_CELL_CH_DESC)) {
		const uint8_t *v = TLVP_VAL(&tp, GSM48_IE_CELL_CH_DESC);
		uint8_t len = TLVP_LEN(&tp, GSM48_IE_CELL_CH_DESC);
		gsm48_decode_freq_list(cell_arfcns, (uint8_t *) v, len, 0xff, 0x02);
		mask = 0x02;
	} else if (TLVP_PRESENT(&tp, GSM48_IE_MA_AFTER)) {
		/* Mobile allocation */
		const uint8_t *v = TLVP_VAL(&tp, GSM48_IE_MA_AFTER);
		uint8_t len = TLVP_LEN(&tp, GSM48_IE_MA_AFTER);

		ma_len = len;
		ma = (uint8_t *) v;
		mask = 0x01;
	} else if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_L_AFTER)) {
		/* Frequency list after time */
		const uint8_t *v = TLVP_VAL(&tp, GSM48_IE_FREQ_L_AFTER);
		uint8_t len = TLVP_LEN(&tp, GSM48_IE_FREQ_L_AFTER);
		gsm48_decode_freq_list(cell_arfcns, (uint8_t *) v, len, 0xff, 0x04);
		ma_len = 0;
		ma = NULL;
		mask = 0x04;
	} else {
		/* Use the old one */
		for (i=0; i<1024; i++) {
			cell_arfcns[i].mask &= ~0x02;
			if (cell_arfcns[i].mask & 0x01) {
				cell_arfcns[i].mask |= 0x02;
				mask = 0x02;
			}
		}
	}

	/* Channel mode (HR/FR/EFR/AMR) */
	if (TLVP_PRESENT(&tp, GSM48_IE_CHANMODE_1)) {
		const uint8_t *v = TLVP_VAL(&tp, GSM48_IE_CHANMODE_1);
		//uint8_t len = TLVP_LEN(&tp, GSM48_IE_CHANMODE_1);
		ga->chan_mode = v[0];
	}

	/* Multirate configuration */
	if (TLVP_PRESENT(&tp, GSM48_IE_MUL_RATE_CFG)) {
		const uint8_t *v = TLVP_VAL(&tp, GSM48_IE_MUL_RATE_CFG);
		//uint8_t len = TLVP_LEN(&tp, GSM48_IE_MUL_RATE_CFG);
		ga->rate_conf = v[1];
	}

	rsl_dec_chan_nr(cd->chan_nr, &ch_type, &ch_subch, &ch_ts);

	ga->h = cd->h0.h;
	ga->chan_nr = cd->chan_nr;

	if (!ga->h) {
		/* Non-Hopping */
		uint16_t arfcn = cd->h0.arfcn_low | (cd->h0.arfcn_high << 8);

		ga->tsc = cd->h0.tsc;
		ga->h0.band_arfcn = arfcn;
	} else {
		/* Hopping */
		uint16_t arfcn;
		int i, j, k;

		ga->tsc = cd->h1.tsc;
		ga->h1.maio = cd->h1.maio_low | (cd->h1.maio_high << 2);;
		ga->h1.hsn = cd->h1.hsn;
		ga->h1.ma_len = 0;

		/* decode mobile allocation */
		if (ma) {
			if (ma_len == 0) {
				return;
			}
			for (i=1, j=0; i<=1024; i++) {
				arfcn = i & 1023;
				if (cell_arfcns[arfcn].mask & mask) {
					k = ma_len - (j>>3) - 1;
					if (ma[k] & (1 << (j&7))) {
						ga->h1.ma[ga->h1.ma_len++] = arfcn;
					}
					j++;
				}
			}
			if (ga->h1.ma_len == 0) {
				/* cell information not found */
				/* just compute ma_len */
				for (i=0; i<(ma_len*8); i++){
					int k = i/8;
	
					if (ma[k] & (1 << (i&7))) {
						ga->h1.ma_len++;
					}
				}
			}
		} else {
			for (i=1; i<=1024; i++) {