ipa_server_conn: Add server-side CCM handling

An ipa_server implementation may call ipa_server_conn_ccm for
the CCM processing on the server side of the connection.  This
function in turn will call a conn->ccm_cb() function for messages to be
handled by the server implementation, such as the ID_RESP message,
which contains the Unit ID of the client that has just connected.
diff --git a/include/osmocom/abis/ipa.h b/include/osmocom/abis/ipa.h
index 6447ccd..a157889 100644
--- a/include/osmocom/abis/ipa.h
+++ b/include/osmocom/abis/ipa.h
@@ -35,6 +35,8 @@
 	struct osmo_fd			ofd;
 	struct llist_head		tx_queue;
 	int (*closed_cb)(struct ipa_server_conn *peer);
+	int (*ccm_cb)(struct ipa_server_conn *peer, struct msgb *msg,
+			struct tlv_parsed *tlvp, struct ipaccess_unit *ud);
 	int (*cb)(struct ipa_server_conn *peer, struct msgb *msg);
 	void				*data;
 	struct msgb			*pending_msg;
@@ -51,6 +53,7 @@
 void ipa_server_conn_destroy(struct ipa_server_conn *peer);
 
 void ipa_server_conn_send(struct ipa_server_conn *peer, struct msgb *msg);
+int ipa_server_conn_ccm(struct ipa_server_conn *conn, struct msgb *msg);
 
 enum ipa_client_conn_state {
 	IPA_CLIENT_LINK_STATE_NONE         = 0,
diff --git a/src/input/ipa.c b/src/input/ipa.c
index bd1671b..f90d62c 100644
--- a/src/input/ipa.c
+++ b/src/input/ipa.c
@@ -434,6 +434,73 @@
 	return conn;
 }
 
+int ipa_server_conn_ccm(struct ipa_server_conn *conn, struct msgb *msg)
+{
+	struct tlv_parsed tlvp;
+	uint8_t msg_type = *(msg->l2h);
+	struct ipaccess_unit unit_data = {};
+	char *unitid;
+	int len, rc;
+
+	/* shared CCM handling on both server and client */
+	rc = ipa_ccm_rcvmsg_base(msg, &conn->ofd);
+	switch (rc) {
+	case -1:
+		/* error in IPA CCM processing */
+		goto err;
+	case 1:
+		/* IPA CCM message that was handled in _base */
+		return 0;
+	case 0:
+		/* IPA CCM message that we need to handle */
+		break;
+	default:
+		/* Error */
+		LOGP(DLINP, LOGL_ERROR, "Unexpected return from "
+		     "ipa_ccm_rcvmsg_base: %d\n", rc);
+		goto err;
+	}
+
+	switch (msg_type) {
+	case IPAC_MSGT_ID_RESP:
+		rc = ipa_ccm_idtag_parse(&tlvp, (uint8_t *)msg->l2h + 2,
+					 msgb_l2len(msg)-2);
+		if (rc < 0) {
+			LOGP(DLINP, LOGL_ERROR, "IPA CCM RESPonse with "
+				"malformed TLVs\n");
+			goto err;
+		}
+		if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT)) {
+			LOGP(DLINP, LOGL_ERROR, "IPA CCM RESP without "
+				"unit ID\n");
+			goto err;
+		}
+		len = TLVP_LEN(&tlvp, IPAC_IDTAG_UNIT);
+		if (len < 1) {
+			LOGP(DLINP, LOGL_ERROR, "IPA CCM RESP with short"
+				"unit ID\n");
+			goto err;
+		}
+		unitid = (char *) TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT);
+		unitid[len-1] = '\0';
+		ipa_parse_unitid(unitid, &unit_data);
+
+		/* FIXME */
+		rc = conn->ccm_cb(conn, msg, &tlvp, &unit_data);
+		if (rc < 0)
+			goto err;
+		break;
+	default:
+		LOGP(DLINP, LOGL_ERROR, "Unknown IPA message type\n");
+		break;
+	}
+	return 0;
+err:
+	/* in case of any error, we close the connection */
+	ipa_server_conn_destroy(conn);
+	return -1;
+}
+
 void ipa_server_conn_destroy(struct ipa_server_conn *conn)
 {
 	close(conn->ofd.fd);