// SPDX-License-Identifier: GPL-2.0-only
/******************************************************************************
*******************************************************************************
**
**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
**  Copyright (C) 2004-2008 Red Hat, Inc.  All rights reserved.
**
**
*******************************************************************************
******************************************************************************/

/*
 * midcomms.c
 *
 * This is the appallingly named "mid-level" comms layer.
 *
 * Its purpose is to take packets from the "real" comms layer,
 * split them up into packets and pass them to the interested
 * part of the locking mechanism.
 *
 * It also takes messages from the locking layer, formats them
 * into packets and sends them to the comms layer.
 */

#include <asm/unaligned.h>

#include "dlm_internal.h"
#include "lowcomms.h"
#include "config.h"
#include "lock.h"
#include "midcomms.h"

/*
 * Called from the low-level comms layer to process a buffer of
 * commands.
 */

int dlm_process_incoming_buffer(int nodeid, unsigned char *buf, int len)
{
	const unsigned char *ptr = buf;
	const struct dlm_header *hd;
	uint16_t msglen;
	int ret = 0;

	while (len >= sizeof(struct dlm_header)) {
		hd = (struct dlm_header *)ptr;

		/* no message should be more than this otherwise we
		 * cannot deliver this message to upper layers
		 */
		msglen = get_unaligned_le16(&hd->h_length);
		if (msglen > DEFAULT_BUFFER_SIZE ||
		    msglen < sizeof(struct dlm_header)) {
			log_print("received invalid length header: %u from node %d, will abort message parsing",
				  msglen, nodeid);
			return -EBADMSG;
		}

		/* caller will take care that leftover
		 * will be parsed next call with more data
		 */
		if (msglen > len)
			break;

		switch (hd->h_cmd) {
		case DLM_MSG:
			if (msglen < sizeof(struct dlm_message)) {
				log_print("dlm msg too small: %u, will skip this message",
					  msglen);
				goto skip;
			}

			break;
		case DLM_RCOM:
			if (msglen < sizeof(struct dlm_rcom)) {
				log_print("dlm rcom msg too small: %u, will skip this message",
					  msglen);
				goto skip;
			}

			break;
		default:
			log_print("unsupported h_cmd received: %u, will skip this message",
				  hd->h_cmd);
			goto skip;
		}

		/* for aligned memory access, we just copy current message
		 * to begin of the buffer which contains already parsed buffer
		 * data and should provide align access for upper layers
		 * because the start address of the buffer has a aligned
		 * address. This memmove can be removed when the upperlayer
		 * is capable of unaligned memory access.
		 */
		memmove(buf, ptr, msglen);
		dlm_receive_buffer((union dlm_packet *)buf, nodeid);

skip:
		ret += msglen;
		len -= msglen;
		ptr += msglen;
	}

	return ret;
}

