[PATCH] libosmocore[master]: WIP: CRC and FSM for IuUP (user plane) as used in 3G RTP data

Harald Welte gerrit-no-reply at lists.osmocom.org
Fri Feb 9 21:39:57 UTC 2018


Review at  https://gerrit.osmocom.org/6357

WIP: CRC and FSM for IuUP (user plane) as used in 3G RTP data

Change-Id: Ibe356fa7b1abaca0091e368db8478e79c09c6cb0
---
M include/Makefile.am
M include/osmocom/gsm/prim.h
A include/osmocom/gsm/protocol/gsm_25_415.h
M src/gsm/Makefile.am
A src/gsm/iu_up.c
A src/gsm/iuup_crc.c
6 files changed, 814 insertions(+), 1 deletion(-)


  git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/57/6357/1

diff --git a/include/Makefile.am b/include/Makefile.am
index f95d90c..c109bd9 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -110,6 +110,7 @@
                        osmocom/gsm/protocol/gsm_09_02.h \
                        osmocom/gsm/protocol/gsm_12_21.h \
 		       osmocom/gsm/protocol/gsm_23_003.h \
+		       osmocom/gsm/protocol/gsm_25_415.h \
                        osmocom/gsm/protocol/gsm_44_318.h \
                        osmocom/gsm/protocol/ipaccess.h \
                        osmocom/gsm/protocol/smpp34_osmocom.h \
diff --git a/include/osmocom/gsm/prim.h b/include/osmocom/gsm/prim.h
index 386b7d8..85725e2 100644
--- a/include/osmocom/gsm/prim.h
+++ b/include/osmocom/gsm/prim.h
@@ -14,4 +14,7 @@
 	SAP_BSSGP_LL,
 	SAP_BSSGP_NM,
 	SAP_BSSGP_PFM,
+
+	SAP_IUUP_TNL,
+	SAP_IUUP_RNL,
 };
diff --git a/include/osmocom/gsm/protocol/gsm_25_415.h b/include/osmocom/gsm/protocol/gsm_25_415.h
new file mode 100644
index 0000000..90f7683
--- /dev/null
+++ b/include/osmocom/gsm/protocol/gsm_25_415.h
@@ -0,0 +1,116 @@
+#pragma once
+/* Iu User Plane (IuUP) Definitions as per 3GPP TS 25.415 */
+/* (C) 2017 by Harald Welte <laforge at gnumonks.org>
+ * All Rights Reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <stdint.h>
+
+/* 3GPP TS 25.415 Section 6.6.2.1 */
+struct iuup_pdutype0_hdr {
+	/* control part */
+	uint8_t frame_nr:4,
+		pdu_type:4;
+	uint8_t rfci:6,
+		fqc:2;
+	/* checksum part */
+	uint16_t payload_crc:10,
+		 header_crc:6;
+	/* payload part */
+	uint8_t payload[0];
+} __attribute__((packed));
+
+/* 3GPP TS 25.415 Section 6.6.2.2 */
+struct iuup_pdutype1_hdr {
+	/* control part */
+	uint8_t frame_nr:4,
+		pdu_type:4;
+	uint8_t rfci:6,
+		fqc:2;
+	/* checksum part */
+	uint8_t spare:2,
+		header_crc:6;
+	/* payload part */
+	uint8_t payload[0];
+} __attribute__((packed));
+
+/* 3GPP TS 25.415 Section 6.6.2.3 */
+struct iuup_pdutype14_hdr {
+	/* control part */
+	uint8_t frame_nr:2,
+		ack_nack:2,
+		pdu_type:4;
+	uint8_t proc_ind:4,
+		mode_version:4;
+	/* checksum part */
+	uint16_t payload_crc:10,
+		 header_crc:6;
+	/* payload part */
+	uint8_t payload[0];
+} __attribute__((packed));
+
+/* 3GPP TS 25.415 Section 6.6.2 + 6.6.3.1 */
+enum iuup_pdu_type {
+	IUUP_PDU_T_DATA_CRC	= 0,
+	IUUP_PDU_T_DATA_NOCRC	= 1,
+	IUUP_PDU_T_CONTROL	= 14,
+};
+
+/* 3GPP TS 25.415 Section 6.6.3.2 */
+enum iuup_ack_nack {
+	IUUP_AN_PROCEDURE	= 0,
+	IUUP_AN_ACK		= 1,
+	IUUP_AN_NACK		= 2,
+};
+
+/* 3GPP TS 25.415 Section 6.6.3.5 */
+enum iuup_fqc {
+	IUUP_FQC_FRAME_GOOD	= 0,
+	IUUP_FQC_FRAME_BAD	= 1,
+	IUUP_FQC_FRAME_BAD_RADIO= 2,
+};
+
+/* 3GPP TS 25.415 Section 6.6.3.7 */
+enum iuup_procedure {
+	IUUP_PROC_INIT		= 0,
+	IUUP_PROC_RATE_CTRL	= 1,
+	IUUP_PROC_TIME_ALIGN	= 2,
+	IUUP_PROC_ERR_EVENT	= 3,
+};
+
+
+/* 3GPP TS 25.415 Section 6.6.3.15 */
+enum iuup_error_distance {
+	IUUP_ERR_DIST_LOCAL		= 0,
+	IUUP_ERR_DIST_FIRST_FWD		= 1,
+	IUUP_ERR_DIST_SECOND_FWD	= 2,
+	IUUP_ERR_DIST_RESERVED		= 3,
+};
+
+
+/* 3GPP TS 25.415 Section 6.6.3.16 */
+enum iuup_error_cause {
+	IUUP_ERR_CAUSE_CRC_ERR_HDR	= 0,
+	IUUP_ERR_CAUSE_CRC_ERR_DATA	= 1,
+	IUUP_ERR_CAUSE_UNEXPECTED_FN	= 2,
+	IUUP_ERR_CAUSE_FRAME_LOSS	= 3,
+	IUUP_ERR_CAUSE_UNKNOWN_PDUTYPE	= 4,
+	IUUP_ERR_CAUSE_UNKNOWN_PROC	= 5,
+	IUUP_ERR_CAUSE_UNKNNOWN_RES_VAL	= 6,
+	IUUP_ERR_CAUSE_UNKNNOWN_FIELD	= 7,
+	IUUP_ERR_CAUSE_FRAME_TOO_SHORT	= 8,
+	IUUP_ERR_CAUSE_MISSING_FIELDS	= 9,
+	IUUP_ERR_CAUSE_UNEXPECTED_PDU_T	= 16,
+	IUUP_ERR_CAUSE_UNEXPECTED_PROC	= 18,
+	IUUP_ERR_CAUSE_UNEXPECTED_RFCI	= 19,
+	IUUP_ERR_CAUSE_UNEXPECTED_VALUE	= 20,
+	IUUP_ERR_CAUSE_INIT_FAILURE	= 42,
+	IUUP_ERR_CAUSE_INIT_FAILURE_NET_TMR = 43,
+	IUUP_ERR_CAUSE_INIT_FAILURE_REP_NACK = 44,
+	IUUP_ERR_CAUSE_RATE_CTRL_FAILURE= 45,
+	IUUP_ERR_CAUSE_ERR_EVENT_FAIL	= 46,
+	IUUP_ERR_CAUSE_TIME_ALIGN_NOTSUPP = 47,
+	IUUP_ERR_CAUSE_REQ_TIME_ALIGN_NOTPOSS = 48,
+};
diff --git a/src/gsm/Makefile.am b/src/gsm/Makefile.am
index f85aba3..601e1cb 100644
--- a/src/gsm/Makefile.am
+++ b/src/gsm/Makefile.am
@@ -30,7 +30,7 @@
 			milenage/aes-internal.c milenage/aes-internal-enc.c \
 			milenage/milenage.c gan.c ipa.c gsm0341.c apn.c \
 			gsup.c gprs_gea.c gsm0503_conv.c oap.c gsm0808_utils.c \
-			gsm23003.c mncc.c
+			gsm23003.c mncc.c iu_up.c
 libgsmint_la_LDFLAGS = -no-undefined
 libgsmint_la_LIBADD = $(top_builddir)/src/libosmocore.la
 
diff --git a/src/gsm/iu_up.c b/src/gsm/iu_up.c
new file mode 100644
index 0000000..e6fd52d
--- /dev/null
+++ b/src/gsm/iu_up.c
@@ -0,0 +1,598 @@
+/*! \file iu_up.c
+ * IuUP (Iu User Plane) according to 3GPP TS 25.415 */
+/*
+ * (C) 2017 by Harald Welte <laforge at gnumonks.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301, USA.
+ */
+
+#include <errno.h>
+
+#include <osmocom/core/crc8gen.h>
+#include <osmocom/core/crc16gen.h>
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/prim.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/logging.h>
+
+#include <osmocom/gsm/prim.h>
+#include <osmocom/gsm/protocol/gsm_25_415.h>
+
+/***********************************************************************
+ * CRC Calculation
+ ***********************************************************************/
+
+/* Section 6.6.3.8 Header CRC */
+const struct osmo_crc8gen_code iuup_hdr_crc_code = {
+	.bits = 6,
+	.poly = 47,
+	.init = 0,
+	.remainder = 0,
+};
+
+/* Section 6.6.3.9 Payload CRC */
+const struct osmo_crc16gen_code iuup_data_crc_code = {
+	.bits = 10,
+	.poly = 563,
+	.init = 0,
+	.remainder = 0,
+};
+
+static int iuup_get_payload_offset(const uint8_t *iuup_pdu)
+{
+	uint8_t pdu_type = iuup_pdu[0] >> 4;
+	switch (pdu_type) {
+	case 0:
+	case 14:
+		return 4;
+	case 1:
+		return 3;
+	default:
+		return -1;
+	}
+}
+
+int osmo_iuup_compute_payload_crc(const uint8_t *iuup_pdu, unsigned int pdu_len)
+{
+	ubit_t buf[1024*8];
+	uint8_t pdu_type;
+	int offset, payload_len_bytes;
+
+	if (pdu_len < 1)
+		return -1;
+
+	pdu_type = iuup_pdu[0] >> 4;
+
+	/* Type 1 has no CRC */
+	if (pdu_type == 1)
+		return 0;
+
+	offset = iuup_get_payload_offset(iuup_pdu);
+	if (offset < 0)
+		return offset;
+
+	if (pdu_len < offset)
+		return -1;
+
+	payload_len_bytes = pdu_len - offset;
+	osmo_pbit2ubit(buf, iuup_pdu+offset, payload_len_bytes*8);
+	return osmo_crc16gen_compute_bits(&iuup_data_crc_code, buf, payload_len_bytes*8);
+}
+
+int osmo_iuup_compute_header_crc(const uint8_t *iuup_pdu, unsigned int pdu_len)
+{
+	ubit_t buf[2*8];
+
+	if (pdu_len < 2)
+		return -1;
+
+	osmo_pbit2ubit(buf, iuup_pdu, 2*8);
+	return osmo_crc8gen_compute_bits(&iuup_hdr_crc_code, buf, 2*8);
+}
+
+/***********************************************************************
+ * Primitives towards the lower layers (typically RTP transport)
+ ***********************************************************************/
+enum osmo_tnl_iuup_prim_type {
+	OSMO_TNL_IUUP_UNITDATA,
+};
+
+struct osmo_tnl_iuup_prim {
+	struct osmo_prim_hdr oph;
+};
+
+/***********************************************************************
+ * Primitives towards the upper layers at the RNL SAP
+ ***********************************************************************/
+
+/* 3GPP TS 25.415 Section 7.2.1 */
+enum osmo_rnl_iuup_prim_type {
+	OSMO_RNL_IUUP_CONFIG,
+	OSMO_RNL_IUUP_DATA,
+	OSMO_RNL_IUUP_STATUS,
+	OSMO_RNL_IUUP_UNIT_DATA,
+};
+
+struct osmo_rnl_iuup_config {
+	/* transparent (true) or SMpSDU (false) */
+	bool transparent;
+	/* should we actively transmit INIT in SmpSDU mode? */
+	bool active;
+};
+
+struct osmo_rnl_iuup_data {
+	uint8_t rfci;
+	uint8_t frame_nr;
+	uint8_t fqc;
+};
+
+struct osmo_rnl_iuup_status {
+	enum iuup_procedure procedure;
+	union {
+		struct {
+			enum iuup_error_cause cause;
+			enum iuup_error_distance distance;
+		} error_event;
+		struct {
+		} initialization;
+		struct {
+		} rate_control;
+		struct {
+		} time_alignment;
+	} u;
+};
+
+/* SAP on the upper side of IuUP, towards the user */
+struct osmo_rnl_iuup_prim {
+	struct osmo_prim_hdr oph;
+	union {
+		struct osmo_rnl_iuup_config config;
+		struct osmo_rnl_iuup_data data;
+		struct osmo_rnl_iuup_status status;
+		//struct osmo_rnl_iuup_unitdata unitdata;
+	} u;
+};
+
+/***********************************************************************
+ * Internal State / FSM (Annex B)
+ ***********************************************************************/
+
+#define S(x)	(1 << (x))
+
+struct osmo_timer_nt {
+	uint32_t t_ms;	/* time in ms */
+	uint32_t n;	/* number of repetitons */
+	struct osmo_timer_list timer;
+};
+
+struct osmo_iuup_instance {
+	struct {
+		bool transparent;
+		bool active;
+		bool use_type0_crc;
+	} config;
+	struct osmo_fsm_inst *fi;
+	struct {
+		struct osmo_timer_nt init;
+		struct osmo_timer_nt ta;
+		struct osmo_timer_nt rc;
+	} timer;
+	/* call-back function to pass primitives up to the user */
+	osmo_prim_cb	user_prim_cb;
+	osmo_prim_cb	transport_prim_cb;
+};
+
+enum iuup_fsm_state {
+	IUUP_FSM_ST_NULL,
+	IUUP_FSM_ST_INIT,
+	IUUP_FSM_ST_TrM_DATA_XFER_READY,
+	IUUP_FSM_ST_SMpSDU_DATA_XFER_READY,
+};
+
+enum iuup_fsm_event {
+	IUUP_FSM_EVT_IUUP_CONFIG_REQ,
+	IUUP_FSM_EVT_IUUP_DATA_REQ,
+	IUUP_FSM_EVT_IUUP_DATA_IND,
+	IUUP_FSM_EVT_SSASAR_UNITDATA_REQ,
+	IUUP_FSM_EVT_SSASAR_UNITDATA_IND,
+	IUUP_FSM_EVT_IUUP_UNITDATA_REQ,
+	IUUP_FSM_EVT_IUUP_UNITDATA_IND,
+	IUUP_FSM_EVT_INIT,
+	IUUP_FSM_EVT_LAST_INIT_ACK,
+	IUUP_FSM_EVT_INIT_NACK,
+};
+
+static const struct value_string iuup_fsm_event_names[] = {
+	{ IUUP_FSM_EVT_IUUP_CONFIG_REQ, 	"IuUP-CONFIG.req" },
+	{ IUUP_FSM_EVT_IUUP_DATA_REQ, 		"IuUP-DATA.req" },
+	{ IUUP_FSM_EVT_IUUP_DATA_IND, 		"IuUP-DATA.ind" },
+	{ IUUP_FSM_EVT_SSASAR_UNITDATA_REQ, 	"SSSAR-UNITDATA.req" },
+	{ IUUP_FSM_EVT_SSASAR_UNITDATA_IND, 	"SSSAR-UNITDATA.ind" },
+	{ IUUP_FSM_EVT_IUUP_UNITDATA_REQ,	"IuUP-UNITDATA.req" },
+	{ IUUP_FSM_EVT_IUUP_UNITDATA_IND,	"IuUP-UNITDATA.ind" },
+	{ IUUP_FSM_EVT_INIT,			"INIT" },
+	{ IUUP_FSM_EVT_LAST_INIT_ACK,		"LAST_INIT_ACK" },
+	{ IUUP_FSM_EVT_INIT_NACK,		"INIT_NACK" },
+	{ 0, NULL }
+};
+
+static inline uint8_t iuup_get_pdu_type(const uint8_t *data)
+{
+	return data[0] >> 4;
+}
+
+static inline uint8_t iuup_get_hdr_crc(const uint8_t *data)
+{
+	return data[2] >> 2;
+}
+
+static void iuup_fsm_null(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+	struct osmo_iuup_instance *iui = fi->priv;
+	struct osmo_rnl_iuup_prim *user_prim = NULL;
+
+	switch (event) {
+	case IUUP_FSM_EVT_IUUP_CONFIG_REQ:
+		user_prim = data;
+		if (user_prim->u.config.transparent)
+			osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_TrM_DATA_XFER_READY, 0, 0);
+		else {
+			osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_INIT, 0, 0);
+			if (iui->config.active) {
+				/* FIXME: Active side: Send Frame + Start timer */
+			}
+		}
+		break;
+	}
+}
+
+/* transform a RNL primitive into a TNL primitive (down the stack) */
+static struct osmo_tnl_iuup_prim *rnl_to_tnl_data(struct osmo_iuup_instance *iui,
+						  struct osmo_rnl_iuup_prim *rip)
+{
+	struct osmo_tnl_iuup_prim *tip;
+	struct osmo_rnl_iuup_data dt;
+	struct msgb *msg;
+
+	OSMO_ASSERT(OSMO_PRIM_HDR(&rip->oph) == OSMO_PRIM(OSMO_RNL_IUUP_DATA, PRIM_OP_REQUEST));
+
+	msg = rip->oph.msg;
+	dt = rip->u.data;
+
+	/* pull up to the IuUP payload and push a new primitive header in front */
+	msgb_pull_to_l3(msg);
+
+	/* push the PDU TYPE 0 / 1 header in front of the payload */
+	if (iui->config.use_type0_crc) {
+		struct iuup_pdutype0_hdr *h0 = msg->l2h = msgb_push(msg, sizeof(*h0));
+		h0->frame_nr = dt.frame_nr;
+		h0->pdu_type = IUUP_PDU_T_DATA_CRC;
+		h0->rfci = dt.rfci;
+		h0->fqc = dt.fqc;
+		h0->header_crc = osmo_iuup_compute_header_crc(msgb_l2(msg), msgb_l2len(msg));
+		h0->payload_crc = osmo_iuup_compute_payload_crc(msgb_l2(msg), msgb_l2len(msg));
+	} else {
+		struct iuup_pdutype1_hdr *h1 = msg->l2h = msgb_push(msg, sizeof(*h1));
+		h1->frame_nr = dt.frame_nr;
+		h1->pdu_type = IUUP_PDU_T_DATA_NOCRC;
+		h1->rfci = dt.rfci;
+		h1->fqc = dt.fqc;
+		h1->header_crc = osmo_iuup_compute_header_crc(msgb_l2(msg), msgb_l2len(msg));
+		h1->spare = 0;
+	}
+
+	tip = (struct osmo_tnl_iuup_prim *) msgb_push(msg, sizeof(*tip));
+	osmo_prim_init(&tip->oph, SAP_IUUP_TNL, OSMO_TNL_IUUP_UNITDATA, PRIM_OP_REQUEST, msg);
+
+	return tip;
+}
+
+/* transform a TNL primitive into a RNL primitive (up the stack) */
+static struct osmo_rnl_iuup_prim *tnl_to_rnl_data(struct osmo_tnl_iuup_prim *tip)
+{
+	struct msgb *msg;
+	struct iuup_pdutype0_hdr *h0;
+	struct iuup_pdutype1_hdr *h1;
+	struct osmo_rnl_iuup_data dt;
+	struct osmo_rnl_iuup_prim *rip;
+
+	msg = tip->oph.msg;
+
+	OSMO_ASSERT(OSMO_PRIM_HDR(&tip->oph) == OSMO_PRIM(OSMO_TNL_IUUP_UNITDATA, PRIM_OP_INDICATION));
+
+	switch (iuup_get_pdu_type(msgb_l2(msg))) {
+	case IUUP_PDU_T_DATA_CRC:
+		h0 = (struct iuup_pdutype0_hdr *) msgb_l2(msg);
+		dt.rfci = h0->rfci;
+		dt.frame_nr = h0->frame_nr;
+		dt.fqc = h0->frame_nr;
+		break;
+	case IUUP_PDU_T_DATA_NOCRC:
+		h1 = (struct iuup_pdutype1_hdr *) msgb_l2(msg);
+		dt.rfci = h1->rfci;
+		dt.frame_nr = h1->frame_nr;
+		dt.fqc = h1->frame_nr;
+		break;
+	}
+
+	/* pull up to the IuUP payload and push a new primitive header in front */
+	msgb_pull_to_l3(msg);
+	rip = (struct osmo_rnl_iuup_prim *) msgb_push(msg, sizeof(*rip));
+	osmo_prim_init(&rip->oph, SAP_IUUP_RNL, OSMO_RNL_IUUP_DATA, PRIM_OP_INDICATION, msg);
+	rip->u.data = dt;
+
+	return rip;
+}
+
+/* transparent mode data transfer */
+static void iuup_fsm_trm_data(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+	//struct osmo_iuup_instance *iui = fi->priv;
+
+	switch (event) {
+	case IUUP_FSM_EVT_IUUP_CONFIG_REQ:
+		osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_NULL, 0, 0);
+		break;
+	case IUUP_FSM_EVT_IUUP_DATA_REQ:
+		/* Data coming down from RNL (user) towards TNL (transport) */
+		break;
+	case IUUP_FSM_EVT_IUUP_DATA_IND:
+		/* Data coming up from TNL (transport) towards RNL (user) */
+		break;
+	case IUUP_FSM_EVT_IUUP_UNITDATA_REQ:
+	case IUUP_FSM_EVT_IUUP_UNITDATA_IND:
+	case IUUP_FSM_EVT_SSASAR_UNITDATA_REQ:
+	case IUUP_FSM_EVT_SSASAR_UNITDATA_IND:
+		/* no state change */
+		break;
+	}
+}
+
+static void iuup_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+	//struct osmo_iuup_instance *iui = fi->priv;
+
+	switch (event) {
+	case IUUP_FSM_EVT_IUUP_CONFIG_REQ:
+		/* the only permitted 'config req' type is the request to release the instance */
+		osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_NULL, 0, 0);
+		break;
+	case IUUP_FSM_EVT_LAST_INIT_ACK:
+		/* last INIT ACK was received, transition to DATA_XFER_READY state */
+		osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_SMpSDU_DATA_XFER_READY, 0, 0);
+		break;
+	}
+}
+
+static void iuup_fsm_smpsdu_data(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+	struct osmo_iuup_instance *iui = fi->priv;
+	struct osmo_rnl_iuup_prim *rip = NULL;
+	struct osmo_tnl_iuup_prim *tip = NULL;
+
+	switch (event) {
+	case IUUP_FSM_EVT_IUUP_CONFIG_REQ:
+		rip = data;
+		osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_NULL, 0, 0);
+		break;
+	case IUUP_FSM_EVT_IUUP_DATA_REQ:
+		/* Data coming down from RNL (user) towards TNL (transport) */
+		rip = data;
+		tip = rnl_to_tnl_data(iui, rip);
+		iui->transport_prim_cb(&tip->oph, iui);
+		break;
+	case IUUP_FSM_EVT_IUUP_DATA_IND:
+		/* Data coming up from TNL (transport) towards RNL (user) */
+		tip = data;
+		rip = tnl_to_rnl_data(tip);
+		iui->user_prim_cb(&rip->oph, iui);
+		break;
+	case IUUP_FSM_EVT_IUUP_UNITDATA_REQ:
+	case IUUP_FSM_EVT_IUUP_UNITDATA_IND:
+	case IUUP_FSM_EVT_SSASAR_UNITDATA_REQ:
+	case IUUP_FSM_EVT_SSASAR_UNITDATA_IND:
+		/* no state change */
+		break;
+	}
+}
+
+static const struct osmo_fsm_state iuup_fsm_states[] = {
+	[IUUP_FSM_ST_NULL] = {
+		.in_event_mask = S(IUUP_FSM_EVT_IUUP_CONFIG_REQ),
+		.out_state_mask = S(IUUP_FSM_ST_INIT) |
+				  S(IUUP_FSM_ST_TrM_DATA_XFER_READY),
+		.name = "NULL",
+		.action = iuup_fsm_null,
+	},
+	[IUUP_FSM_ST_TrM_DATA_XFER_READY] = {
+		.in_event_mask = S(IUUP_FSM_EVT_IUUP_CONFIG_REQ) |
+				 S(IUUP_FSM_EVT_IUUP_DATA_REQ) |
+				 S(IUUP_FSM_EVT_IUUP_DATA_IND) |
+				 S(IUUP_FSM_EVT_IUUP_UNITDATA_REQ) |
+				 S(IUUP_FSM_EVT_IUUP_UNITDATA_IND) |
+				 S(IUUP_FSM_EVT_SSASAR_UNITDATA_REQ) |
+				 S(IUUP_FSM_EVT_SSASAR_UNITDATA_IND),
+		.out_state_mask = S(IUUP_FSM_ST_NULL),
+		.name = "TrM Data Transfer Ready",
+		.action = iuup_fsm_trm_data,
+	},
+	[IUUP_FSM_ST_INIT] = {
+		//.in_event_mask = ,
+		.out_state_mask = S(IUUP_FSM_ST_NULL) |
+				  S(IUUP_FSM_ST_SMpSDU_DATA_XFER_READY),
+		.name = "Initialisation",
+		.action = iuup_fsm_init,
+	},
+	[IUUP_FSM_ST_SMpSDU_DATA_XFER_READY] = {
+		.in_event_mask = S(IUUP_FSM_EVT_IUUP_DATA_REQ) |
+				 S(IUUP_FSM_EVT_IUUP_DATA_IND),
+		.out_state_mask = S(IUUP_FSM_ST_NULL) |
+				  S(IUUP_FSM_ST_INIT),
+		.name = "SMpSDU Data Transfer Ready",
+		.action = iuup_fsm_smpsdu_data,
+	},
+};
+
+struct osmo_fsm iuup_fsm = {
+	.name = "IuUP",
+	.states = iuup_fsm_states,
+	.num_states = ARRAY_SIZE(iuup_fsm_states),
+	.log_subsys = DLMGCP,
+	.event_names = iuup_fsm_event_names,
+};
+
+static int iuup_verify_pdu(const uint8_t *data, unsigned int len)
+{
+	int header_crc_computed, payload_crc_computed;
+	uint8_t pdu_type = iuup_get_pdu_type(data);
+	struct iuup_pdutype0_hdr *t0h;
+
+	if (len < 3)
+		return -EINVAL;
+
+	header_crc_computed = osmo_iuup_compute_header_crc(data, len);
+	if (iuup_get_hdr_crc(data) != header_crc_computed) {
+		return -EIO;
+	}
+	switch (pdu_type) {
+	case IUUP_PDU_T_DATA_NOCRC:
+		if (len < 4)
+			return -EINVAL;
+		break;
+	case IUUP_PDU_T_DATA_CRC:
+	case IUUP_PDU_T_CONTROL:
+		t0h = (struct iuup_pdutype0_hdr *) data;
+		payload_crc_computed = osmo_iuup_compute_payload_crc(data, len);
+		if (t0h->payload_crc != payload_crc_computed)
+			return -EIO;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/* A IuUP TNL SAP primitive from transport (lower layer) */
+int osmo_iuup_tnl_prim_up(struct osmo_iuup_instance *inst, struct osmo_prim_hdr *oph)
+{
+	struct osmo_tnl_iuup_prim *tnp = (struct osmo_tnl_iuup_prim *) oph;
+	struct iuup_pdutype14_hdr *t14h;
+	int rc = 0;
+
+	OSMO_ASSERT(oph->sap == SAP_IUUP_TNL);
+
+	switch (OSMO_PRIM_HDR(oph)) {
+	case OSMO_PRIM(OSMO_TNL_IUUP_UNITDATA, PRIM_OP_INDICATION):
+		if (iuup_verify_pdu(msgb_l2(oph->msg), msgb_l2len(oph->msg)) < 0) {
+			LOGPFSML(inst->fi, LOGL_NOTICE, "Discarding invalid IuUP PDU");
+			/* don't return error as the caller is not responsible for the PDU which
+			 * was transmitted from some remote peer */
+			return 0;
+		}
+		switch (iuup_get_pdu_type(msgb_l2(oph->msg))) {
+		case IUUP_PDU_T_DATA_CRC:
+			oph->msg->l3h = msgb_l2(oph->msg) + sizeof(struct iuup_pdutype0_hdr);
+			rc = osmo_fsm_inst_dispatch(inst->fi, IUUP_FSM_EVT_IUUP_DATA_IND, tnp);
+			break;
+		case IUUP_PDU_T_DATA_NOCRC:
+			oph->msg->l3h = msgb_l2(oph->msg) + sizeof(struct iuup_pdutype1_hdr);
+			rc = osmo_fsm_inst_dispatch(inst->fi, IUUP_FSM_EVT_IUUP_DATA_IND, tnp);
+			break;
+		case IUUP_PDU_T_CONTROL:
+			t14h = (struct iuup_pdutype14_hdr *) msgb_l2(oph->msg);
+			switch (t14h->ack_nack) {
+			case IUUP_AN_PROCEDURE:
+				switch (t14h->proc_ind) {
+				case IUUP_PROC_INIT:
+					rc = osmo_fsm_inst_dispatch(inst->fi, IUUP_FSM_EVT_INIT, tnp);
+					break;
+				case IUUP_PROC_RATE_CTRL:
+				case IUUP_PROC_TIME_ALIGN:
+				case IUUP_PROC_ERR_EVENT:
+					LOGPFSML(inst->fi, LOGL_NOTICE, "Received Request for "
+						"unsupported IuUP procedure %u\n", t14h->proc_ind);
+					break;
+				default:
+					LOGPFSML(inst->fi, LOGL_NOTICE, "Received Request for "
+						"unknown IuUP procedure %u\n", t14h->proc_ind);
+					break;
+				}
+				break;
+			case IUUP_AN_ACK:
+				switch (t14h->proc_ind) {
+				case IUUP_PROC_INIT:
+					rc = osmo_fsm_inst_dispatch(inst->fi, 
+								    IUUP_FSM_EVT_LAST_INIT_ACK, tnp);
+					break;
+				default:
+					LOGPFSML(inst->fi, LOGL_ERROR, "Received ACK for "
+						"unknown IuUP procedure %u\n", t14h->proc_ind);
+					break;
+				}
+				break;
+			case IUUP_AN_NACK:
+				switch (t14h->proc_ind) {
+				case IUUP_PROC_INIT:
+					rc = osmo_fsm_inst_dispatch(inst->fi,
+								    IUUP_FSM_EVT_INIT_NACK, tnp);
+					break;
+				default:
+					LOGPFSML(inst->fi, LOGL_ERROR, "Received NACK for "
+						"unknown IuUP procedure %u\n", t14h->proc_ind);
+					break;
+				}
+				break;
+			default:
+				LOGPFSML(inst->fi, LOGL_ERROR, "Received unknown IuUP ACK/NACK\n");
+				break;
+			}
+			break;
+		default:
+			LOGPFSML(inst->fi, LOGL_NOTICE, "Received unknown IuUP PDU type %u\n",
+				iuup_get_pdu_type(msgb_l2(oph->msg)));
+			break;
+		}
+		break;
+	default:
+		/* exception: return an error code due to a wrong primitive */
+		return -EINVAL;
+	}
+
+	return rc;
+}
+
+/* A IuUP RNL SAP primitive from user (higher layer) */
+int osmo_iuup_rnl_prim_down(struct osmo_iuup_instance *inst, struct osmo_prim_hdr *oph)
+{
+	struct osmo_rnl_iuup_prim *rnp = (struct osmo_rnl_iuup_prim *) oph;
+	int rc;
+
+	OSMO_ASSERT(oph->sap == SAP_IUUP_RNL);
+
+	switch (OSMO_PRIM_HDR(oph)) {
+	case OSMO_PRIM(OSMO_RNL_IUUP_CONFIG, PRIM_OP_REQUEST):
+		rc = osmo_fsm_inst_dispatch(inst->fi, IUUP_FSM_EVT_IUUP_CONFIG_REQ, rnp);
+		break;
+	case OSMO_PRIM(OSMO_RNL_IUUP_DATA, PRIM_OP_REQUEST):
+		rc = osmo_fsm_inst_dispatch(inst->fi, IUUP_FSM_EVT_IUUP_DATA_REQ, rnp);
+		break;
+	default:
+		rc = -EINVAL;
+	}
+	return rc;
+}
diff --git a/src/gsm/iuup_crc.c b/src/gsm/iuup_crc.c
new file mode 100644
index 0000000..1cefe5a
--- /dev/null
+++ b/src/gsm/iuup_crc.c
@@ -0,0 +1,95 @@
+#include <stdint.h>
+#include <stdio.h>
+
+#include <osmocom/core/crc8gen.h>
+#include <osmocom/core/crc16gen.h>
+
+static const struct osmo_crc8gen_code iuup_hdr_crc_code = {
+	.bits = 6,
+	.poly = 47,
+	.init = 0,
+	.remainder = 0,
+};
+
+static const struct osmo_crc16gen_code iuup_data_crc_code = {
+	.bits = 10,
+	.poly = 563,
+	.init = 0,
+	.remainder = 0,
+};
+
+static int iuup_get_payload_offset(const uint8_t *iuup_pdu)
+{
+	uint8_t pdu_type = iuup_pdu[0] >> 4;
+	switch (pdu_type) {
+	case 0:
+	case 14:
+		return 4;
+	case 1:
+		return 3;
+	default:
+		return -1;
+	}
+}
+
+int osmo_iuup_compute_payload_crc(const uint8_t *iuup_pdu, unsigned int pdu_len)
+{
+	ubit_t buf[1024*8];
+	uint8_t pdu_type;
+	int offset, payload_len_bytes;
+
+	if (pdu_len < 1)
+		return -1;
+
+	pdu_type = iuup_pdu[0] >> 4;
+
+	/* Type 1 has no CRC */
+	if (pdu_type == 1)
+		return 0;
+
+	offset = iuup_get_payload_offset(iuup_pdu, pdu_len);
+	if (offset < 0)
+		return offset;
+
+	if (pdu_len < offset)
+		return -1;
+
+	payload_len_bytes = pdu_eln - offset;
+	osmo_pbit2ubit(buf, iuup_pdu+offset, payload_len_bytes*8);
+	return osmo_crc16gen_compute_bits(&iuup_data_crc_code, buf, payload_len_bytes*8);
+}
+
+int osmo_iuup_compute_header_crc(const uint8_t *iuup_pdu, unsigned int pdu_len)
+{
+	ubit_t buf[2*8];
+
+	if (pdu_len < 2)
+		return -1;
+
+	osmo_pbit2ubit(buf, iuup_pdu, 2*8);
+	return osmo_crc8gen_compute_bits(&iuup_hdr_crc_code, buf, 2*8);
+}
+
+#if 0
+/* Frame 29 of MobileOriginatingCall_AMR.cap */
+const uint8_t iuup_frame29[] = {
+	0x02, 0x00, 0x7d, 0x27, 0x55, 0x00, 0x88, 0xb6, 0x66, 0x79, 0xe1, 0xe0,
+	0x01, 0xe7, 0xcf, 0xf0, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+
+int main(int argc, char **argv)
+{
+	ubit_t buf[1024*8];
+	int rc;
+
+	osmo_pbit2ubit(buf, iuup_frame29, 2*8);
+	rc = osmo_crc8gen_compute_bits(&iuup_hdr_crc_code, buf, 2*8);
+	printf("Header CRC = 0x%02x\n", rc);
+
+	osmo_pbit2ubit(buf, iuup_frame29+4, 31*8);
+	rc = osmo_crc16gen_compute_bits(&iuup_data_crc_code, buf, 31*8);
+	printf("Payload CRC = 0x%03x\n", rc);
+}
+#endif

-- 
To view, visit https://gerrit.osmocom.org/6357
To unsubscribe, visit https://gerrit.osmocom.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ibe356fa7b1abaca0091e368db8478e79c09c6cb0
Gerrit-PatchSet: 1
Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Owner: Harald Welte <laforge at gnumonks.org>


More information about the gerrit-log mailing list