[GPRS] LLC: Improve implementation compliance to spec

Don't allocate a LLC Entity just because BSSGP passes any random
SAPI/TLLI up to us.  We can only do this for XID and UI frames
of the GMM SAPI.

Furthermore, add more comments and debug messages.
diff --git a/openbsc/include/openbsc/gprs_llc.h b/openbsc/include/openbsc/gprs_llc.h
index 5a6682d..608fb05 100644
--- a/openbsc/include/openbsc/gprs_llc.h
+++ b/openbsc/include/openbsc/gprs_llc.h
@@ -26,7 +26,43 @@
 	GPRS_LLC_U_NULL_CMD		= 0x00,
 };
 
+/* TS 04.64 Section 7.1.2 Table 7: LLC layer primitives (GMM/SNDCP/SMS/TOM) */
+/* TS 04.65 Section 5.1.2 Table 2: Service primitives used by SNDCP */
+enum gprs_llc_primitive {
+	/* GMM <-> LLME */
+	LLGMM_ASSIGN_REQ,	/* GMM tells us new TLLI: TLLI old, TLLI new, Kc, CiphAlg */
+	LLGMM_RESET_REQ,	/* GMM tells us to perform XID negotiation: TLLI */
+	LLGMM_RESET_CNF,	/* LLC informs GMM that XID has completed: TLLI */
+	LLGMM_SUSPEND_REQ,	/* GMM tells us MS has suspended: TLLI, Page */
+	LLGMM_RESUME_REQ,	/* GMM tells us MS has resumed: TLLI */
+	LLGMM_PAGE_IND,		/* LLC asks GMM to page MS: TLLI */
+	LLGMM_IOV_REQ,		/* GMM tells us to perform XID: TLLI */
+	LLGMM_STATUS_IND,	/* LLC informs GMM about error: TLLI, Cause */
+	/* LLE <-> (GMM/SNDCP/SMS/TOM) */
+	LL_RESET_IND,		/* TLLI */
+	LL_ESTABLISH_REQ,	/* TLLI, XID Req */
+	LL_ESTABLISH_IND,	/* TLLI, XID Req, N201-I, N201-U */
+	LL_ESTABLISH_RESP,	/* TLLI, XID Negotiated */
+	LL_ESTABLISH_CONF,	/* TLLI, XID Neg, N201-i, N201-U */
+	LL_RELEASE_REQ,		/* TLLI, Local */
+	LL_RELEASE_IND,		/* TLLI, Cause */
+	LL_RELEASE_CONF,	/* TLLI */
+	LL_XID_REQ,		/* TLLI, XID Requested */
+	LL_XID_IND,		/* TLLI, XID Req, N201-I, N201-U */
+	LL_XID_RESP,		/* TLLI, XID Negotiated */
+	LL_XID_CONF,		/* TLLI, XID Neg, N201-I, N201-U */
+	LL_DATA_REQ,		/* TLLI, SN-PDU, Ref, QoS, Radio Prio, Ciph */
+	LL_DATA_IND,		/* TLLI, SN-PDU */
+	LL_DATA_CONF,		/* TLLI, Ref */
+	LL_UNITDATA_REQ,	/* TLLI, SN-PDU, Ref, QoS, Radio Prio, Ciph */
+	LL_UNITDATA_IND,	/* TLLI, SN-PDU */
+	LL_STATUS_IND,		/* TLLI, Cause */
+};
+
+/* BSSGP-UL-UNITDATA.ind */
 int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv);
+
+/* LL-UNITDATA.req */
 int gprs_llc_tx_ui(struct msgb *msg, uint8_t sapi, int command);
 
 #endif
diff --git a/openbsc/src/gprs/gprs_llc.c b/openbsc/src/gprs/gprs_llc.c
index 9c75a3d..c3df013 100644
--- a/openbsc/src/gprs/gprs_llc.c
+++ b/openbsc/src/gprs/gprs_llc.c
@@ -111,6 +111,7 @@
 	GPRS_LLC_SABM,
 	GPRS_LLC_FRMR,
 	GPRS_LLC_XID,
+	GPRS_LLC_UI,
 };
 
 struct gprs_llc_hdr_parsed {
@@ -156,13 +157,13 @@
 
 	switch (lle->state) {
 	case GPRS_LLS_LOCAL_EST:
-		/* retransmit SABM */
-		/* re-start T200 */
+		/* FIXME: retransmit SABM */
+		/* FIXME: re-start T200 */
 		lle->retrans_ctr++;
 		break;
 	case GPRS_LLS_LOCAL_REL:
-		/* retransmit DISC */
-		/* re-start T200 */
+		/* FIXME: retransmit DISC */
+		/* FIXME: re-start T200 */
 		lle->retrans_ctr++;
 		break;
 	}
@@ -174,8 +175,8 @@
 	struct gprs_llc_lle *lle = data;
 
 	if (lle->retrans_ctr < lle->n200) {
-		/* transmit apropriate supervisory frame (8.6.4.1) */
-		/* set timer T201 */
+		/* FIXME: transmit apropriate supervisory frame (8.6.4.1) */
+		/* FIXME: set timer T201 */
 		lle->retrans_ctr++;
 	}
 }
@@ -213,6 +214,7 @@
 
 	/* Identifiers passed down: (BVCI, NSEI) */
 
+	/* Send BSSGP-DL-UNITDATA.req */
 	return gprs_bssgp_tx_dl_ud(msg);
 }
 
@@ -272,6 +274,7 @@
 
 	/* Identifiers passed down: (BVCI, NSEI) */
 
+	/* Send BSSGP-DL-UNITDATA.req */
 	return gprs_bssgp_tx_dl_ud(msg);
 }
 
@@ -437,6 +440,7 @@
 		}
 	} else if ((ctrl[0] & 0xe0) == 0xc0) {
 		/* UI (Unconfirmed Inforamtion) format */
+		ghp->cmd = GPRS_LLC_UI;
 		ghp->data = ctrl + 2;
 		ghp->data_len = (llc_hdr + len - 3) - ghp->data;
 
@@ -491,6 +495,12 @@
 	ghp->fcs_calc = gprs_llc_fcs(llc_hdr, crc_length);
 
 	/* FIXME: parse sack frame */
+	if (ghp->cmd == GPRS_LLC_SACK) {
+		DEBUGP(DGPRS, "Unsupported SACK frame\n");
+		return -EIO;
+	}
+
+	return 0;
 }
 
 /* receive an incoming LLC PDU (BSSGP-UL-UNITDATA-IND, 7.2.4.2) */
@@ -505,37 +515,56 @@
 	/* Identifiers from DOWN: NSEI, BVCI, TLLI */
 
 	rc = gprs_llc_hdr_parse(&llhp, lh, TLVP_LEN(tv, BSSGP_IE_LLC_PDU));
-	/* FIXME */
-
 	gprs_llc_hdr_dump(&llhp);
+	if (rc < 0) {
+		DEBUGP(DGPRS, "Error during LLC header parsing\n");
+		return rc;
+	}
+
+	if (llhp.fcs != llhp.fcs_calc) {
+		DEBUGP(DGPRS, "Dropping frame with invalid FCS\n");
+		return -EIO;
+	}
 
 	/* find the LLC Entity for this TLLI+SAPI tuple */
 	lle = lle_by_tlli_sapi(msgb_tlli(msg), llhp.sapi);
-	/* allocate a new LLE if needed */
-	if (!lle)
+
+	/* 7.2.1.1 LLC belonging to unassigned TLLI+SAPI shall be discarded,
+	 * except UID and XID frames with SAPI=1 */
+	if (!lle && llhp.sapi == GPRS_SAPI_GMM &&
+	    (llhp.cmd == GPRS_LLC_XID || llhp.cmd == GPRS_LLC_UI)) {
+		/* FIXME: don't use the TLLI but the 0xFFFF unassigned? */
 		lle = lle_alloc(msgb_tlli(msg), llhp.sapi);
+	} else {
+		DEBUGP(DGPRS, "unknown TLLI/SAPI: Silently dropping\n");
+		return 0;
+	}
 
 	/* Update LLE's (BVCI, NSEI) tuple */
 	lle->bvci = msgb_bvci(msg);
 	lle->nsei = msgb_nsei(msg);
 
+	/* Receive and Process the actual LLC frame */
 	rc = gprs_llc_hdr_rx(&llhp, lle);
-	/* FIXME */
+	if (rc < 0)
+		return rc;
 
+	/* llhp.data is only set when we need to send LL_[UNIT]DATA_IND up */
 	if (llhp.data) {
 		msgb_gmmh(msg) = llhp.data;
 		switch (llhp.sapi) {
 		case GPRS_SAPI_GMM:
+			/* send LL_UNITDATA_IND to GMM */
 			rc = gsm0408_gprs_rcvmsg(msg);
 			break;
 		case GPRS_SAPI_TOM2:
 		case GPRS_SAPI_TOM8:
-			/* FIXME */
+			/* FIXME: send LL_DATA_IND/LL_UNITDATA_IND to TOM */
 		case GPRS_SAPI_SNDCP3:
 		case GPRS_SAPI_SNDCP5:
 		case GPRS_SAPI_SNDCP9:
 		case GPRS_SAPI_SNDCP11:
-			/* FIXME */
+			/* FIXME: send LL_DATA_IND/LL_UNITDATA_IND to SNDCP */
 		case GPRS_SAPI_SMS:
 			/* FIXME */
 		default: