Change in libosmocore[master]: lapdm: Split lapd_rx_u() spaghetti into one function per message type

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

pespin gerrit-no-reply at lists.osmocom.org
Wed Oct 21 13:03:30 UTC 2020


pespin has submitted this change. ( https://gerrit.osmocom.org/c/libosmocore/+/20819 )

Change subject: lapdm: Split lapd_rx_u() spaghetti into one function per message type
......................................................................

lapdm: Split lapd_rx_u() spaghetti into one function per message type

Change-Id: Ia1a49f7ae1d133174fad7a396db72867612fa619
---
M src/gsm/lapd_core.c
1 file changed, 451 insertions(+), 402 deletions(-)

Approvals:
  laforge: Looks good to me, but someone else must approve
  fixeria: Looks good to me, approved
  Jenkins Builder: Verified



diff --git a/src/gsm/lapd_core.c b/src/gsm/lapd_core.c
index 6a56572..bcb64b0 100644
--- a/src/gsm/lapd_core.c
+++ b/src/gsm/lapd_core.c
@@ -814,422 +814,471 @@
 
 /* L1 -> L2 */
 
-/* Receive a LAPD U (Unnumbered) message from L1 */
-static int lapd_rx_u(struct msgb *msg, struct lapd_msg_ctx *lctx)
+/* Receive a LAPD U SABM(E) message from L1 */
+static int lapd_rx_u_sabm(struct msgb *msg, struct lapd_msg_ctx *lctx)
 {
 	struct lapd_datalink *dl = lctx->dl;
 	int length = lctx->length;
 	int rc = 0;
 	uint8_t prim, op;
 
-	switch (lctx->s_u) {
-	case LAPD_U_SABM:
-	case LAPD_U_SABME:
-		prim = PRIM_DL_EST;
-		op = PRIM_OP_INDICATION;
+	prim = PRIM_DL_EST;
+	op = PRIM_OP_INDICATION;
 
-		LOGDL(dl, LOGL_INFO, "SABM(E) received in state %s\n", lapd_state_name(dl->state));
-		/* 5.7.1 */
-		dl->seq_err_cond = 0;
-		/* G.2.2 Wrong value of the C/R bit */
-		if (lctx->cr == dl->cr.rem2loc.resp) {
-			LOGDL(dl, LOGL_ERROR, "SABM response error\n");
-			msgb_free(msg);
-			mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
-			return -EINVAL;
-		}
-
-		/* G.4.5 If SABM is received with L>N201 or with M bit
-		 * set, AN MDL-ERROR-INDICATION is sent to MM.
-		 */
-		if (lctx->more || length > lctx->n201) {
-			LOGDL(dl, LOGL_ERROR, "SABM too large error\n");
-			msgb_free(msg);
-			mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
-			return -EIO;
-		}
-
-		switch (dl->state) {
-		case LAPD_STATE_IDLE:
-			break;
-		case LAPD_STATE_MF_EST:
-			LOGDL(dl, LOGL_INFO, "SABM command, multiple frame established state\n");
-			/* If link is lost on the remote side, we start over
-			 * and send DL-ESTABLISH indication again. */
-			/* Additionally, continue in case of content resoltion
-			 * (GSM network). This happens, if the mobile has not
-			 * yet received UA or another mobile (collision) tries
-			 * to establish connection. The mobile must receive
-			 * UA again. */
-			/* 5.4.2.1 */
-			if (!length) {
-				/* If no content resolution, this is a
-				 * re-establishment. */
-				LOGDL(dl, LOGL_INFO, "Remote reestablish\n");
-				break;
-			}
-			if (!dl->cont_res) {
-				LOGDL(dl, LOGL_INFO, "SABM command not allowed in state %s\n",
-				      lapd_state_name(dl->state));
-				mdl_error(MDL_CAUSE_SABM_MF, lctx);
-				msgb_free(msg);
-				return 0;
-			}
-			/* Ignore SABM if content differs from first SABM. */
-			if (dl->mode == LAPD_MODE_NETWORK && length) {
-#ifdef TEST_CONTENT_RESOLUTION_NETWORK
-				dl->cont_res->data[0] ^= 0x01;
-#endif
-				if (memcmp(dl->cont_res->data, msg->data,
-								length)) {
-					LOGDL(dl, LOGL_INFO, "Another SABM with different content - "
-					     "ignoring!\n");
-					msgb_free(msg);
-					return 0;
-				}
-			}
-			/* send UA again */
-			lapd_send_ua(lctx, length, msg->l3h);
-			msgb_free(msg);
-			return 0;
-		case LAPD_STATE_DISC_SENT:
-			/* 5.4.6.2 send DM with F=P */
-			lapd_send_dm(lctx);
-			/* stop Timer T200 */
-			lapd_stop_t200(dl);
-			msgb_free(msg);
-			return send_dl_simple(prim, op, lctx);
-		default:
-			/* collision: Send UA, but still wait for rx UA, then
-			 * change to MF_EST state.
-			 */
-			/* check for contention resoultion */
-			if (dl->tx_hist[0].msg && dl->tx_hist[0].msg->len) {
-				LOGDL(dl, LOGL_NOTICE, "SABM not allowed during contention "
-				      "resolution (state=%s)\n", lapd_state_name(dl->state));
-				mdl_error(MDL_CAUSE_SABM_INFO_NOTALL, lctx);
-			}
-			lapd_send_ua(lctx, length, msg->l3h);
-			msgb_free(msg);
-			return 0;
-		}
-		/* save message context for further use */
-		memcpy(&dl->lctx, lctx, sizeof(dl->lctx));
-#ifndef TEST_CONTENT_RESOLUTION_NETWORK
-		/* send UA response */
-		lapd_send_ua(lctx, length, msg->l3h);
-#endif
-		/* set Vs, Vr and Va to 0 */
-		dl->v_send = dl->v_recv = dl->v_ack = 0;
-		/* clear tx_hist */
-		lapd_dl_flush_hist(dl);
-		/* enter multiple-frame-established state */
-		lapd_dl_newstate(dl, LAPD_STATE_MF_EST);
-		/* store content resolution data on network side
-		 * Note: cont_res will be removed when changing state again,
-		 * so it must be allocated AFTER lapd_dl_newstate(). */
-		if (dl->mode == LAPD_MODE_NETWORK && length) {
-			dl->cont_res = lapd_msgb_alloc(length, "CONT RES");
-			memcpy(msgb_put(dl->cont_res, length), msg->l3h,
-				length);
-			LOGDL(dl, LOGL_NOTICE, "Store content res.\n");
-		}
-		/* send notification to L3 */
-		if (length == 0) {
-			/* 5.4.1.2 Normal establishment procedures */
-			rc = send_dl_simple(prim, op, lctx);
-			msgb_free(msg);
-		} else {
-			/* 5.4.1.4 Contention resolution establishment */
-			msgb_trim(msg, length);
-			rc = send_dl_l3(prim, op, lctx, msg);
-		}
-		break;
-	case LAPD_U_DM:
-		LOGDL(dl, LOGL_INFO, "DM received in state %s\n", lapd_state_name(dl->state));
-		/* G.2.2 Wrong value of the C/R bit */
-		if (lctx->cr == dl->cr.rem2loc.cmd) {
-			LOGDL(dl, LOGL_ERROR, "DM command error\n");
-			msgb_free(msg);
-			mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
-			return -EINVAL;
-		}
-		if (!lctx->p_f) {
-			/* 5.4.1.2 DM responses with the F bit set to "0"
-			 * shall be ignored.
-			 */
-			msgb_free(msg);
-			return 0;
-		}
-		switch (dl->state) {
-		case LAPD_STATE_SABM_SENT:
-			break;
-		case LAPD_STATE_MF_EST:
-			if (lctx->p_f) {
-				LOGDL(dl, LOGL_INFO, "unsolicited DM response\n");
-				mdl_error(MDL_CAUSE_UNSOL_DM_RESP, lctx);
-			} else {
-				LOGDL(dl, LOGL_INFO, "unsolicited DM response, "
-					"multiple frame established state\n");
-				mdl_error(MDL_CAUSE_UNSOL_DM_RESP_MF, lctx);
-				/* reestablish */
-				if (!dl->reestablish) {
-					msgb_free(msg);
-					return 0;
-				}
-				LOGDL(dl, LOGL_NOTICE, "Performing reestablishment\n");
-				lapd_reestablish(dl);
-			}
-			msgb_free(msg);
-			return 0;
-		case LAPD_STATE_TIMER_RECOV:
-			/* FP = 0 (DM is normal in case PF = 1) */
-			if (!lctx->p_f) {
-				LOGDL(dl, LOGL_INFO, "unsolicited DM response, multiple frame "
-				      "established state\n");
-				mdl_error(MDL_CAUSE_UNSOL_DM_RESP_MF, lctx);
-				msgb_free(msg);
-				/* reestablish */
-				if (!dl->reestablish)
-					return 0;
-				LOGDL(dl, LOGL_NOTICE, "Performing reestablishment\n");
-				return lapd_reestablish(dl);
-			}
-			break;
-		case LAPD_STATE_DISC_SENT:
-			/* stop Timer T200 */
-			lapd_stop_t200(dl);
-			/* go to idle state */
-			lapd_dl_flush_tx(dl);
-			lapd_dl_flush_send(dl);
-			lapd_dl_newstate(dl, LAPD_STATE_IDLE);
-			rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, lctx);
-			msgb_free(msg);
-			return 0;
-		case LAPD_STATE_IDLE:
-			/* 5.4.5 all other frame types shall be discarded */
-		default:
-			LOGDL(dl, LOGL_INFO, "unsolicited DM response! (discarding)\n");
-			msgb_free(msg);
-			return 0;
-		}
-		/* stop timer T200 */
-		lapd_stop_t200(dl);
-		/* go to idle state */
-		lapd_dl_newstate(dl, LAPD_STATE_IDLE);
-		rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION, lctx);
-		msgb_free(msg);
-		break;
-	case LAPD_U_UI:
-		LOGDL(dl, LOGL_INFO, "UI received\n");
-		/* G.2.2 Wrong value of the C/R bit */
-		if (lctx->cr == dl->cr.rem2loc.resp) {
-			LOGDL(dl, LOGL_ERROR, "UI indicates response error\n");
-			msgb_free(msg);
-			mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
-			return -EINVAL;
-		}
-
-		/* G.4.5 If UI is received with L>N201 or with M bit
-		 * set, AN MDL-ERROR-INDICATION is sent to MM.
-		 */
-		if (length > lctx->n201 || lctx->more) {
-			LOGDL(dl, LOGL_ERROR, "UI too large error (%d > N201(%d) or M=%d)\n",
-			      length, lctx->n201, lctx->more);
-			msgb_free(msg);
-			mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
-			return -EIO;
-		}
-
-		/* do some length checks */
-		if (length == 0) {
-			/* 5.3.3 UI frames received with the length indicator
-			 * set to "0" shall be ignored
-			 */
-			LOGDL(dl, LOGL_INFO, "length=0 (discarding)\n");
-			msgb_free(msg);
-			return 0;
-		}
-		msgb_trim(msg, length);
-		rc = send_dl_l3(PRIM_DL_UNIT_DATA, PRIM_OP_INDICATION, lctx,
-				msg);
-		break;
-	case LAPD_U_DISC:
-		prim = PRIM_DL_REL;
-		op = PRIM_OP_INDICATION;
-
-		LOGDL(dl, LOGL_INFO, "DISC received in state %s\n", lapd_state_name(dl->state));
-		/* flush tx and send buffers */
-		lapd_dl_flush_tx(dl);
-		lapd_dl_flush_send(dl);
-		/* 5.7.1 */
-		dl->seq_err_cond = 0;
-		/* G.2.2 Wrong value of the C/R bit */
-		if (lctx->cr == dl->cr.rem2loc.resp) {
-			LOGDL(dl, LOGL_ERROR, "DISC response error\n");
-			msgb_free(msg);
-			mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
-			return -EINVAL;
-		}
-		if (length > 0 || lctx->more) {
-			/* G.4.4 If a DISC or DM frame is received with L>0 or
-			 * with the M bit set to "1", an MDL-ERROR-INDICATION
-			 * primitive with cause "U frame with incorrect
-			 * parameters" is sent to the mobile management entity.
-			 */
-			LOGDL(dl, LOGL_ERROR, "U frame iwth incorrect parameters\n");
-			msgb_free(msg);
-			mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
-			return -EIO;
-		}
-		switch (dl->state) {
-		case LAPD_STATE_IDLE:
-			LOGDL(dl, LOGL_INFO, "DISC in idle state\n");
-			/* send DM with F=P */
-			msgb_free(msg);
-			return lapd_send_dm(lctx);
-		case LAPD_STATE_SABM_SENT:
-			LOGDL(dl, LOGL_INFO, "DISC in SABM state\n");
-			/* 5.4.6.2 send DM with F=P */
-			lapd_send_dm(lctx);
-			/* stop Timer T200 */
-			lapd_stop_t200(dl);
-			/* go to idle state */
-			lapd_dl_newstate(dl, LAPD_STATE_IDLE);
-			msgb_free(msg);
-			return send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION,
-				lctx);
-		case LAPD_STATE_MF_EST:
-		case LAPD_STATE_TIMER_RECOV:
-			LOGDL(dl, LOGL_INFO, "DISC in est state\n");
-			break;
-		case LAPD_STATE_DISC_SENT:
-			LOGDL(dl, LOGL_INFO, "DISC in disc state\n");
-			prim = PRIM_DL_REL;
-			op = PRIM_OP_CONFIRM;
-			break;
-		default:
-			lapd_send_ua(lctx, length, msg->l3h);
-			msgb_free(msg);
-			return 0;
-		}
-		/* send UA response */
-		lapd_send_ua(lctx, length, msg->l3h);
-		/* stop Timer T200 */
-		lapd_stop_t200(dl);
-		/* enter idle state, keep tx-buffer with UA response */
-		lapd_dl_newstate(dl, LAPD_STATE_IDLE);
-		/* send notification to L3 */
-		rc = send_dl_simple(prim, op, lctx);
-		msgb_free(msg);
-		break;
-	case LAPD_U_UA:
-		LOGDL(dl, LOGL_INFO, "UA received in state %s\n", lapd_state_name(dl->state));
-		/* G.2.2 Wrong value of the C/R bit */
-		if (lctx->cr == dl->cr.rem2loc.cmd) {
-			LOGDL(dl, LOGL_ERROR, "UA indicates command error\n");
-			msgb_free(msg);
-			mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
-			return -EINVAL;
-		}
-
-		/* G.4.5 If UA is received with L>N201 or with M bit
-		 * set, AN MDL-ERROR-INDICATION is sent to MM.
-		 */
-		if (lctx->more || length > lctx->n201) {
-			LOGDL(dl, LOGL_ERROR, "UA too large error\n");
-			msgb_free(msg);
-			mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
-			return -EIO;
-		}
-
-		if (!lctx->p_f) {
-			/* 5.4.1.2 A UA response with the F bit set to "0"
-			 * shall be ignored.
-			 */
-			LOGDL(dl, LOGL_INFO, "F=0 (discarding)\n");
-			msgb_free(msg);
-			return 0;
-		}
-		switch (dl->state) {
-		case LAPD_STATE_SABM_SENT:
-			break;
-		case LAPD_STATE_MF_EST:
-		case LAPD_STATE_TIMER_RECOV:
-			LOGDL(dl, LOGL_INFO, "unsolicited UA response! (discarding)\n");
-			mdl_error(MDL_CAUSE_UNSOL_UA_RESP, lctx);
-			msgb_free(msg);
-			return 0;
-		case LAPD_STATE_DISC_SENT:
-			LOGDL(dl, LOGL_INFO, "UA in disconnect state\n");
-			/* stop Timer T200 */
-			lapd_stop_t200(dl);
-			/* go to idle state */
-			lapd_dl_flush_tx(dl);
-			lapd_dl_flush_send(dl);
-			lapd_dl_newstate(dl, LAPD_STATE_IDLE);
-			rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, lctx);
-			msgb_free(msg);
-			return 0;
-		case LAPD_STATE_IDLE:
-			/* 5.4.5 all other frame types shall be discarded */
-		default:
-			LOGDL(dl, LOGL_INFO, "unsolicited UA response! (discarding)\n");
-			msgb_free(msg);
-			return 0;
-		}
-		LOGDL(dl, LOGL_INFO, "UA in SABM state\n");
-		/* stop Timer T200 */
-		lapd_stop_t200(dl);
-		/* compare UA with SABME if contention resolution is applied */
-		if (dl->tx_hist[0].msg->len) {
-			if (length != (dl->tx_hist[0].msg->len)
-			 || !!memcmp(dl->tx_hist[0].msg->data, msg->l3h,
-			 					length)) {
-				LOGDL(dl, LOGL_INFO, "**** UA response mismatches ****\n");
-				rc = send_dl_simple(PRIM_DL_REL,
-					PRIM_OP_INDICATION, lctx);
-				msgb_free(msg);
-				/* go to idle state */
-				lapd_dl_flush_tx(dl);
-				lapd_dl_flush_send(dl);
-				lapd_dl_newstate(dl, LAPD_STATE_IDLE);
-				return 0;
-			}
-		}
-		/* set Vs, Vr and Va to 0 */
-		dl->v_send = dl->v_recv = dl->v_ack = 0;
-		/* clear tx_hist */
-		lapd_dl_flush_hist(dl);
-		/* enter multiple-frame-established state */
-		lapd_dl_newstate(dl, LAPD_STATE_MF_EST);
-		/* send outstanding frames, if any (resume / reconnect) */
-		lapd_send_i(lctx, __LINE__);
-		/* send notification to L3 */
-		rc = send_dl_simple(PRIM_DL_EST, PRIM_OP_CONFIRM, lctx);
-		msgb_free(msg);
-		break;
-	case LAPD_U_FRMR:
-		LOGDL(dl, LOGL_NOTICE, "Frame reject received\n");
-		/* send MDL ERROR INIDCATION to L3 */
-		mdl_error(MDL_CAUSE_FRMR, lctx);
-		msgb_free(msg);
-		/* reestablish */
-		if (!dl->reestablish)
-			break;
-		LOGDL(dl, LOGL_NOTICE, "Performing reestablishment\n");
-		rc = lapd_reestablish(dl);
-		break;
-	default:
-		/* G.3.1 */
-		LOGDL(dl, LOGL_NOTICE, "Unnumbered frame not allowed\n");
+	LOGDL(dl, LOGL_INFO, "SABM(E) received in state %s\n", lapd_state_name(dl->state));
+	/* 5.7.1 */
+	dl->seq_err_cond = 0;
+	/* G.2.2 Wrong value of the C/R bit */
+	if (lctx->cr == dl->cr.rem2loc.resp) {
+		LOGDL(dl, LOGL_ERROR, "SABM response error\n");
 		msgb_free(msg);
 		mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
 		return -EINVAL;
 	}
+
+	/* G.4.5 If SABM is received with L>N201 or with M bit
+	 * set, AN MDL-ERROR-INDICATION is sent to MM.
+	 */
+	if (lctx->more || length > lctx->n201) {
+		LOGDL(dl, LOGL_ERROR, "SABM too large error\n");
+		msgb_free(msg);
+		mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
+		return -EIO;
+	}
+
+	switch (dl->state) {
+	case LAPD_STATE_IDLE:
+		break;
+	case LAPD_STATE_MF_EST:
+		LOGDL(dl, LOGL_INFO, "SABM command, multiple frame established state\n");
+		/* If link is lost on the remote side, we start over
+		 * and send DL-ESTABLISH indication again. */
+		/* Additionally, continue in case of content resoltion
+		 * (GSM network). This happens, if the mobile has not
+		 * yet received UA or another mobile (collision) tries
+		 * to establish connection. The mobile must receive
+		 * UA again. */
+		/* 5.4.2.1 */
+		if (!length) {
+			/* If no content resolution, this is a
+			 * re-establishment. */
+			LOGDL(dl, LOGL_INFO, "Remote reestablish\n");
+			break;
+		}
+		if (!dl->cont_res) {
+			LOGDL(dl, LOGL_INFO, "SABM command not allowed in state %s\n",
+			      lapd_state_name(dl->state));
+			mdl_error(MDL_CAUSE_SABM_MF, lctx);
+			msgb_free(msg);
+			return 0;
+		}
+		/* Ignore SABM if content differs from first SABM. */
+		if (dl->mode == LAPD_MODE_NETWORK && length) {
+#ifdef TEST_CONTENT_RESOLUTION_NETWORK
+			dl->cont_res->data[0] ^= 0x01;
+#endif
+			if (memcmp(dl->cont_res->data, msg->data,
+							length)) {
+				LOGDL(dl, LOGL_INFO, "Another SABM with different content - "
+				     "ignoring!\n");
+				msgb_free(msg);
+				return 0;
+			}
+		}
+		/* send UA again */
+		lapd_send_ua(lctx, length, msg->l3h);
+		msgb_free(msg);
+		return 0;
+	case LAPD_STATE_DISC_SENT:
+		/* 5.4.6.2 send DM with F=P */
+		lapd_send_dm(lctx);
+		/* stop Timer T200 */
+		lapd_stop_t200(dl);
+		msgb_free(msg);
+		return send_dl_simple(prim, op, lctx);
+	default:
+		/* collision: Send UA, but still wait for rx UA, then
+		 * change to MF_EST state.
+		 */
+		/* check for contention resoultion */
+		if (dl->tx_hist[0].msg && dl->tx_hist[0].msg->len) {
+			LOGDL(dl, LOGL_NOTICE, "SABM not allowed during contention "
+			      "resolution (state=%s)\n", lapd_state_name(dl->state));
+			mdl_error(MDL_CAUSE_SABM_INFO_NOTALL, lctx);
+		}
+		lapd_send_ua(lctx, length, msg->l3h);
+		msgb_free(msg);
+		return 0;
+	}
+	/* save message context for further use */
+	memcpy(&dl->lctx, lctx, sizeof(dl->lctx));
+#ifndef TEST_CONTENT_RESOLUTION_NETWORK
+	/* send UA response */
+	lapd_send_ua(lctx, length, msg->l3h);
+#endif
+	/* set Vs, Vr and Va to 0 */
+	dl->v_send = dl->v_recv = dl->v_ack = 0;
+	/* clear tx_hist */
+	lapd_dl_flush_hist(dl);
+	/* enter multiple-frame-established state */
+	lapd_dl_newstate(dl, LAPD_STATE_MF_EST);
+	/* store content resolution data on network side
+	 * Note: cont_res will be removed when changing state again,
+	 * so it must be allocated AFTER lapd_dl_newstate(). */
+	if (dl->mode == LAPD_MODE_NETWORK && length) {
+		dl->cont_res = lapd_msgb_alloc(length, "CONT RES");
+		memcpy(msgb_put(dl->cont_res, length), msg->l3h,
+			length);
+		LOGDL(dl, LOGL_NOTICE, "Store content res.\n");
+	}
+	/* send notification to L3 */
+	if (length == 0) {
+		/* 5.4.1.2 Normal establishment procedures */
+		rc = send_dl_simple(prim, op, lctx);
+		msgb_free(msg);
+	} else {
+		/* 5.4.1.4 Contention resolution establishment */
+		msgb_trim(msg, length);
+		rc = send_dl_l3(prim, op, lctx, msg);
+	}
 	return rc;
 }
 
+/* Receive a LAPD U DM message from L1 */
+static int lapd_rx_u_dm(struct msgb *msg, struct lapd_msg_ctx *lctx)
+{
+	struct lapd_datalink *dl = lctx->dl;
+	int rc = 0;
+
+	LOGDL(dl, LOGL_INFO, "DM received in state %s\n", lapd_state_name(dl->state));
+	/* G.2.2 Wrong value of the C/R bit */
+	if (lctx->cr == dl->cr.rem2loc.cmd) {
+		LOGDL(dl, LOGL_ERROR, "DM command error\n");
+		msgb_free(msg);
+		mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
+		return -EINVAL;
+	}
+	if (!lctx->p_f) {
+		/* 5.4.1.2 DM responses with the F bit set to "0"
+		 * shall be ignored.
+		 */
+		msgb_free(msg);
+		return 0;
+	}
+	switch (dl->state) {
+	case LAPD_STATE_SABM_SENT:
+		break;
+	case LAPD_STATE_MF_EST:
+		if (lctx->p_f) {
+			LOGDL(dl, LOGL_INFO, "unsolicited DM response\n");
+			mdl_error(MDL_CAUSE_UNSOL_DM_RESP, lctx);
+		} else {
+			LOGDL(dl, LOGL_INFO, "unsolicited DM response, "
+				"multiple frame established state\n");
+			mdl_error(MDL_CAUSE_UNSOL_DM_RESP_MF, lctx);
+			/* reestablish */
+			if (!dl->reestablish) {
+				msgb_free(msg);
+				return 0;
+			}
+			LOGDL(dl, LOGL_NOTICE, "Performing reestablishment\n");
+			lapd_reestablish(dl);
+		}
+		msgb_free(msg);
+		return 0;
+	case LAPD_STATE_TIMER_RECOV:
+		/* FP = 0 (DM is normal in case PF = 1) */
+		if (!lctx->p_f) {
+			LOGDL(dl, LOGL_INFO, "unsolicited DM response, multiple frame "
+			      "established state\n");
+			mdl_error(MDL_CAUSE_UNSOL_DM_RESP_MF, lctx);
+			msgb_free(msg);
+			/* reestablish */
+			if (!dl->reestablish)
+				return 0;
+			LOGDL(dl, LOGL_NOTICE, "Performing reestablishment\n");
+			return lapd_reestablish(dl);
+		}
+		break;
+	case LAPD_STATE_DISC_SENT:
+		/* stop Timer T200 */
+		lapd_stop_t200(dl);
+		/* go to idle state */
+		lapd_dl_flush_tx(dl);
+		lapd_dl_flush_send(dl);
+		lapd_dl_newstate(dl, LAPD_STATE_IDLE);
+		rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, lctx);
+		msgb_free(msg);
+		return 0;
+	case LAPD_STATE_IDLE:
+		/* 5.4.5 all other frame types shall be discarded */
+	default:
+		LOGDL(dl, LOGL_INFO, "unsolicited DM response! (discarding)\n");
+		msgb_free(msg);
+		return 0;
+	}
+	/* stop timer T200 */
+	lapd_stop_t200(dl);
+	/* go to idle state */
+	lapd_dl_newstate(dl, LAPD_STATE_IDLE);
+	rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION, lctx);
+	msgb_free(msg);
+	return rc;
+}
+
+/* Receive a LAPD U UI message from L1 */
+static int lapd_rx_u_ui(struct msgb *msg, struct lapd_msg_ctx *lctx)
+{
+	struct lapd_datalink *dl = lctx->dl;
+	int length = lctx->length;
+
+	LOGDL(dl, LOGL_INFO, "UI received\n");
+	/* G.2.2 Wrong value of the C/R bit */
+	if (lctx->cr == dl->cr.rem2loc.resp) {
+		LOGDL(dl, LOGL_ERROR, "UI indicates response error\n");
+		msgb_free(msg);
+		mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
+		return -EINVAL;
+	}
+
+	/* G.4.5 If UI is received with L>N201 or with M bit
+	 * set, AN MDL-ERROR-INDICATION is sent to MM.
+	 */
+	if (length > lctx->n201 || lctx->more) {
+		LOGDL(dl, LOGL_ERROR, "UI too large error (%d > N201(%d) or M=%d)\n",
+		      length, lctx->n201, lctx->more);
+		msgb_free(msg);
+		mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
+		return -EIO;
+	}
+
+	/* do some length checks */
+	if (length == 0) {
+		/* 5.3.3 UI frames received with the length indicator
+		 * set to "0" shall be ignored
+		 */
+		LOGDL(dl, LOGL_INFO, "length=0 (discarding)\n");
+		msgb_free(msg);
+		return 0;
+	}
+	msgb_trim(msg, length);
+	return send_dl_l3(PRIM_DL_UNIT_DATA, PRIM_OP_INDICATION, lctx, msg);
+}
+
+/* Receive a LAPD U DISC message from L1 */
+static int lapd_rx_u_disc(struct msgb *msg, struct lapd_msg_ctx *lctx)
+{
+	struct lapd_datalink *dl = lctx->dl;
+	int length = lctx->length;
+	int rc = 0;
+	uint8_t prim, op;
+
+	prim = PRIM_DL_REL;
+	op = PRIM_OP_INDICATION;
+
+	LOGDL(dl, LOGL_INFO, "DISC received in state %s\n", lapd_state_name(dl->state));
+	/* flush tx and send buffers */
+	lapd_dl_flush_tx(dl);
+	lapd_dl_flush_send(dl);
+	/* 5.7.1 */
+	dl->seq_err_cond = 0;
+	/* G.2.2 Wrong value of the C/R bit */
+	if (lctx->cr == dl->cr.rem2loc.resp) {
+		LOGDL(dl, LOGL_ERROR, "DISC response error\n");
+		msgb_free(msg);
+		mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
+		return -EINVAL;
+	}
+	if (length > 0 || lctx->more) {
+		/* G.4.4 If a DISC or DM frame is received with L>0 or
+		 * with the M bit set to "1", an MDL-ERROR-INDICATION
+		 * primitive with cause "U frame with incorrect
+		 * parameters" is sent to the mobile management entity.
+		 */
+		LOGDL(dl, LOGL_ERROR, "U frame iwth incorrect parameters\n");
+		msgb_free(msg);
+		mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
+		return -EIO;
+	}
+	switch (dl->state) {
+	case LAPD_STATE_IDLE:
+		LOGDL(dl, LOGL_INFO, "DISC in idle state\n");
+		/* send DM with F=P */
+		msgb_free(msg);
+		return lapd_send_dm(lctx);
+	case LAPD_STATE_SABM_SENT:
+		LOGDL(dl, LOGL_INFO, "DISC in SABM state\n");
+		/* 5.4.6.2 send DM with F=P */
+		lapd_send_dm(lctx);
+		/* stop Timer T200 */
+		lapd_stop_t200(dl);
+		/* go to idle state */
+		lapd_dl_newstate(dl, LAPD_STATE_IDLE);
+		msgb_free(msg);
+		return send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION,
+			lctx);
+	case LAPD_STATE_MF_EST:
+	case LAPD_STATE_TIMER_RECOV:
+		LOGDL(dl, LOGL_INFO, "DISC in est state\n");
+		break;
+	case LAPD_STATE_DISC_SENT:
+		LOGDL(dl, LOGL_INFO, "DISC in disc state\n");
+		prim = PRIM_DL_REL;
+		op = PRIM_OP_CONFIRM;
+		break;
+	default:
+		lapd_send_ua(lctx, length, msg->l3h);
+		msgb_free(msg);
+		return 0;
+	}
+	/* send UA response */
+	lapd_send_ua(lctx, length, msg->l3h);
+	/* stop Timer T200 */
+	lapd_stop_t200(dl);
+	/* enter idle state, keep tx-buffer with UA response */
+	lapd_dl_newstate(dl, LAPD_STATE_IDLE);
+	/* send notification to L3 */
+	rc = send_dl_simple(prim, op, lctx);
+	msgb_free(msg);
+	return rc;
+}
+
+/* Receive a LAPD U UA message from L1 */
+static int lapd_rx_u_ua(struct msgb *msg, struct lapd_msg_ctx *lctx)
+{
+	struct lapd_datalink *dl = lctx->dl;
+	int length = lctx->length;
+	int rc = 0;
+
+	LOGDL(dl, LOGL_INFO, "UA received in state %s\n", lapd_state_name(dl->state));
+	/* G.2.2 Wrong value of the C/R bit */
+	if (lctx->cr == dl->cr.rem2loc.cmd) {
+		LOGDL(dl, LOGL_ERROR, "UA indicates command error\n");
+		msgb_free(msg);
+		mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
+		return -EINVAL;
+	}
+
+	/* G.4.5 If UA is received with L>N201 or with M bit
+	 * set, AN MDL-ERROR-INDICATION is sent to MM.
+	 */
+	if (lctx->more || length > lctx->n201) {
+		LOGDL(dl, LOGL_ERROR, "UA too large error\n");
+		msgb_free(msg);
+		mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
+		return -EIO;
+	}
+
+	if (!lctx->p_f) {
+		/* 5.4.1.2 A UA response with the F bit set to "0"
+		 * shall be ignored.
+		 */
+		LOGDL(dl, LOGL_INFO, "F=0 (discarding)\n");
+		msgb_free(msg);
+		return 0;
+	}
+	switch (dl->state) {
+	case LAPD_STATE_SABM_SENT:
+		break;
+	case LAPD_STATE_MF_EST:
+	case LAPD_STATE_TIMER_RECOV:
+		LOGDL(dl, LOGL_INFO, "unsolicited UA response! (discarding)\n");
+		mdl_error(MDL_CAUSE_UNSOL_UA_RESP, lctx);
+		msgb_free(msg);
+		return 0;
+	case LAPD_STATE_DISC_SENT:
+		LOGDL(dl, LOGL_INFO, "UA in disconnect state\n");
+		/* stop Timer T200 */
+		lapd_stop_t200(dl);
+		/* go to idle state */
+		lapd_dl_flush_tx(dl);
+		lapd_dl_flush_send(dl);
+		lapd_dl_newstate(dl, LAPD_STATE_IDLE);
+		rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, lctx);
+		msgb_free(msg);
+		return 0;
+	case LAPD_STATE_IDLE:
+		/* 5.4.5 all other frame types shall be discarded */
+	default:
+		LOGDL(dl, LOGL_INFO, "unsolicited UA response! (discarding)\n");
+		msgb_free(msg);
+		return 0;
+	}
+	LOGDL(dl, LOGL_INFO, "UA in SABM state\n");
+	/* stop Timer T200 */
+	lapd_stop_t200(dl);
+	/* compare UA with SABME if contention resolution is applied */
+	if (dl->tx_hist[0].msg->len) {
+		if (length != (dl->tx_hist[0].msg->len)
+		 || !!memcmp(dl->tx_hist[0].msg->data, msg->l3h,
+							length)) {
+			LOGDL(dl, LOGL_INFO, "**** UA response mismatches ****\n");
+			rc = send_dl_simple(PRIM_DL_REL,
+				PRIM_OP_INDICATION, lctx);
+			msgb_free(msg);
+			/* go to idle state */
+			lapd_dl_flush_tx(dl);
+			lapd_dl_flush_send(dl);
+			lapd_dl_newstate(dl, LAPD_STATE_IDLE);
+			return 0;
+		}
+	}
+	/* set Vs, Vr and Va to 0 */
+	dl->v_send = dl->v_recv = dl->v_ack = 0;
+	/* clear tx_hist */
+	lapd_dl_flush_hist(dl);
+	/* enter multiple-frame-established state */
+	lapd_dl_newstate(dl, LAPD_STATE_MF_EST);
+	/* send outstanding frames, if any (resume / reconnect) */
+	lapd_send_i(lctx, __LINE__);
+	/* send notification to L3 */
+	rc = send_dl_simple(PRIM_DL_EST, PRIM_OP_CONFIRM, lctx);
+	msgb_free(msg);
+	return rc;
+}
+
+/* Receive a LAPD U FRMR message from L1 */
+static int lapd_rx_u_frmr(struct msgb *msg, struct lapd_msg_ctx *lctx)
+{
+	struct lapd_datalink *dl = lctx->dl;
+
+	LOGDL(dl, LOGL_NOTICE, "Frame reject received\n");
+	/* send MDL ERROR INIDCATION to L3 */
+	mdl_error(MDL_CAUSE_FRMR, lctx);
+	msgb_free(msg);
+	/* reestablish */
+	if (!dl->reestablish)
+		return 0;
+	LOGDL(dl, LOGL_NOTICE, "Performing reestablishment\n");
+	return lapd_reestablish(dl);
+}
+
+/* Receive a LAPD U (Unnumbered) message from L1 */
+static int lapd_rx_u(struct msgb *msg, struct lapd_msg_ctx *lctx)
+{
+	switch (lctx->s_u) {
+	case LAPD_U_SABM:
+	case LAPD_U_SABME:
+		return lapd_rx_u_sabm(msg, lctx);
+	case LAPD_U_DM:
+		return lapd_rx_u_dm(msg, lctx);
+	case LAPD_U_UI:
+		return lapd_rx_u_ui(msg, lctx);
+	case LAPD_U_DISC:
+		return lapd_rx_u_disc(msg, lctx);
+	case LAPD_U_UA:
+		return lapd_rx_u_ua(msg, lctx);
+	case LAPD_U_FRMR:
+		return lapd_rx_u_frmr(msg, lctx);
+	default:
+		/* G.3.1 */
+		LOGDL(lctx->dl, LOGL_NOTICE, "Unnumbered frame not allowed\n");
+		msgb_free(msg);
+		mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
+		return -EINVAL;
+	}
+}
+
 /* Receive a LAPD S (Supervisory) message from L1 */
 static int lapd_rx_s(struct msgb *msg, struct lapd_msg_ctx *lctx)
 {

-- 
To view, visit https://gerrit.osmocom.org/c/libosmocore/+/20819
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Change-Id: Ia1a49f7ae1d133174fad7a396db72867612fa619
Gerrit-Change-Number: 20819
Gerrit-PatchSet: 2
Gerrit-Owner: pespin <pespin at sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <vyanitskiy at sysmocom.de>
Gerrit-Reviewer: laforge <laforge at osmocom.org>
Gerrit-Reviewer: pespin <pespin at sysmocom.de>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20201021/1e2b42be/attachment.htm>


More information about the gerrit-log mailing list