/*
 ***************************************************************************
 * Ralink Tech Inc.
 * 4F, No. 2 Technology 5th Rd.
 * Science-based Industrial Park
 * Hsin-chu, Taiwan, R.O.C.
 *
 * (c) Copyright 2002-2006, Ralink Technology, Inc.
 *
 * All rights reserved.	Ralink's source	code is	an unpublished work	and	the
 * use of a	copyright notice does not imply	otherwise. This	source code
 * contains	confidential trade secret material of Ralink Tech. Any attemp
 * or participation	in deciphering,	decoding, reverse engineering or in	any
 * way altering	the	source code	is stricitly prohibited, unless	the	prior
 * written consent of Ralink Technology, Inc. is obtained.
 ***************************************************************************

	Module Name:
	mesh_link_mng.c

	Abstract:

	Revision History:
	Who			When			What
	--------	----------		----------------------------------------------
	Fonchi		2007-07-23      For mesh (802.11s) support.
*/
#ifdef MESH_SUPPORT


#include "rt_config.h"
#include "mesh_def.h"
#include "mesh_sanity.h"

BOOLEAN MeshPeerBeaconAndProbeSanity(
	IN PRTMP_ADAPTER pAd,
	IN VOID *Msg,
	IN ULONG MsgLen,
	OUT PUCHAR pHostName,
	OUT PUCHAR pHostNameLen,
	OUT PUCHAR pMeshId,
	OUT PUCHAR pMeshIdLen,
	OUT PMESH_CONFIGURAION_IE pMeshConfiguration)
{
	CHAR *Ptr;
	PFRAME_802_11 pFrame;
	PEID_STRUCT pEid;
	UCHAR SubType;
	ULONG Length = 0;

	pFrame = (PFRAME_802_11)Msg;
    
	/* get subtype from header */
	SubType = (UCHAR)pFrame->Hdr.FC.SubType;

	/*hex_dump("Beacon", Msg, MsgLen); */

	Ptr = (PCHAR)pFrame->Octet;
	Length += LENGTH_802_11;

	/* skip timestamp from payload and advance the pointer */
	Ptr += TIMESTAMP_LEN;
	Length += TIMESTAMP_LEN;

	/* skip beacon interval from payload and advance the pointer */
	Ptr += 2;
	Length += 2;

	/* skip capability info from payload and advance the pointer */
	Ptr += 2;
	Length += 2;

	pEid = (PEID_STRUCT) Ptr;

	/* get variable fields from payload and advance the pointer */
	while ((Length + 2 + pEid->Len) <= MsgLen)    
	{
		switch(pEid->Eid)
		{
			case IE_VENDOR_SPECIFIC:
				if (NdisEqualMemory(pEid->Octet, MeshOUI, 3))
				{
					PUCHAR ptr = (PUCHAR)(pEid->Octet + 3);
					*pHostNameLen = pEid->Len -3;
					NdisMoveMemory(pHostName, ptr, *pHostNameLen);
				}
				break;

			case IE_MESH_ID:
				NdisMoveMemory(pMeshId, pEid->Octet, pEid->Len);
				*pMeshIdLen = pEid->Len;
				break;

			case IE_MESH_CONFIGURATION:
				{
					PUCHAR ptr;
					NdisMoveMemory(&pMeshConfiguration->Version, pEid->Octet, 1);

					if (RTMPEqualMemory(pEid->Octet + 1, MeshOUI, 3))
						NdisMoveMemory(&pMeshConfiguration->PathSelProtocolId, pEid->Octet + 4, 1);

					if (RTMPEqualMemory(pEid->Octet + 5, MeshOUI, 3))
						NdisMoveMemory(&pMeshConfiguration->PathSelMetricId, pEid->Octet + 8, 1);

					ptr = (PUCHAR)&pEid->Octet[0];
					NdisMoveMemory(&pMeshConfiguration->CPI, ptr + 13, 4);
					pMeshConfiguration->CPI = le2cpu32(*(UINT32 *)(&pMeshConfiguration->CPI));

					NdisMoveMemory(&pMeshConfiguration->MeshCapability.word, ptr + 17, 2);
					pMeshConfiguration->MeshCapability.word = le2cpu16(*(UINT16 *)(&pMeshConfiguration->MeshCapability.word));
				}
				break;

			default:
#ifdef RELEASE_EXCLUDE
				DBGPRINT(RT_DEBUG_INFO, ("MeshPeerBeaconAndProbeSanity - unrecognized EID = %d\n", pEid->Eid));
#endif /* RELEASE_EXCLUDE */
				break;
		}
        
		Length = Length + 2 + pEid->Len;  /* Eid[1] + Len[1]+ content[Len] */
		pEid = (PEID_STRUCT)((UCHAR*)pEid + 2 + pEid->Len);        
	}

	return TRUE;
}

BOOLEAN MeshLinkMngOpenSanity(
		IN PRTMP_ADAPTER pAd,
		IN VOID *pMsg,
		IN ULONG MsgLen,
		OUT UINT16 *pCapabilityInfo,
		OUT UCHAR SupRate[],
		OUT PUCHAR pSupRateLen,
		OUT PUCHAR pMeshIdLen,
		OUT	PUCHAR pMeshId,
		OUT PUCHAR pMeshSelPathId,
		OUT PUCHAR pMeshSelMetricId,
		OUT UINT32 *pCPI,
		OUT PMESH_CAPABILITY pMeshCapabilty,
		OUT UINT16 *pPeerLinkId,
		OUT PMESH_SECURITY_CAPABILITY_IE pMscIe,
		OUT PUCHAR pMsaIe,
		OUT PUCHAR pMsaIeLen,
		OUT PUCHAR pRsnIe,
		OUT PUCHAR pRsnIeLen,
		OUT BOOLEAN *pbWmmCapable,
	    OUT HT_CAPABILITY_IE *pHtCapability,
		OUT UCHAR *pHtCapabilityLen)
{
	PEID_STRUCT eid_ptr;
	UCHAR       WPA1_OUI[4]={0x00,0x50,0xF2,0x01};
    UCHAR       WPA2_OUI[3]={0x00,0x0F,0xAC};

	*pMsaIeLen = 0;
	*pRsnIeLen = 0;

	NdisMoveMemory(pCapabilityInfo, pMsg, 2);
	pMsg += 2;
	MsgLen -= 2;
	*pbWmmCapable = FALSE;

	eid_ptr = (PEID_STRUCT)pMsg;
	while (((UCHAR*)eid_ptr + eid_ptr->Len + 1) < ((PUCHAR)pMsg + MsgLen))
	{
		switch(eid_ptr->Eid)
		{
            case IE_SUPP_RATES:
                if ((eid_ptr->Len <= MAX_LEN_OF_SUPPORTED_RATES) && (eid_ptr->Len > 0))
                {
                    NdisMoveMemory(SupRate, eid_ptr->Octet, eid_ptr->Len);
#ifdef RELEASE_EXCLUDE
                    DBGPRINT(RT_DEBUG_INFO, ("%s - IE_SUPP_RATES., Len=%d. Rates[0]=%x\n", __FUNCTION__, eid_ptr->Len, SupRate[0]));
                    DBGPRINT(RT_DEBUG_INFO, ("SupRate[1]=%x %x %x %x %x %x %x\n",
						SupRate[1], SupRate[2], SupRate[3], SupRate[4], SupRate[5], SupRate[6], SupRate[7]));
#endif /* RELEASE_EXCLUDE */
                    
                    *pSupRateLen = eid_ptr->Len;
                }
                else
                {
                	/* HT rate not ready yet. return true temporarily. */
                    /*DBGPRINT(RT_DEBUG_TRACE, ("PeerAssocReqSanity - wrong IE_SUPP_RATES\n")); */
                    *pSupRateLen = 8;
					SupRate[0] = 0x82;
					SupRate[1] = 0x84;
					SupRate[2] = 0x8b;
					SupRate[3] = 0x96;
					SupRate[4] = 0x12;
					SupRate[5] = 0x24;
					SupRate[6] = 0x48;
					SupRate[7] = 0x6c;
#ifdef RELEASE_EXCLUDE
                    DBGPRINT(RT_DEBUG_INFO, ("PeerAssocReqSanity - wrong IE_SUPP_RATES., Len=%d\n",eid_ptr->Len));
#endif /* RELEASE_EXCLUDE */
                    /*return FALSE; */
                }
                break;
                
            case IE_EXT_SUPP_RATES:
                if (eid_ptr->Len + *pSupRateLen <= MAX_LEN_OF_SUPPORTED_RATES)
                {
                    NdisMoveMemory(&SupRate[*pSupRateLen], eid_ptr->Octet, eid_ptr->Len);
                    *pSupRateLen = (*pSupRateLen) + eid_ptr->Len;
                }
                else
                {
                    NdisMoveMemory(&SupRate[*pSupRateLen], eid_ptr->Octet, MAX_LEN_OF_SUPPORTED_RATES - (*pSupRateLen));
                    *pSupRateLen = MAX_LEN_OF_SUPPORTED_RATES;
                }
                break;

			case IE_WPA:  
            case IE_WPA2:
				if (NdisEqualMemory(eid_ptr->Octet, WME_INFO_ELEM, 6) && (eid_ptr->Len == 7))
				{
					*pbWmmCapable = TRUE;
					break;
				}

				if (pAd->MeshTab.wdev.AuthMode < Ndis802_11AuthModeWPA)
				{
					DBGPRINT(RT_DEBUG_TRACE, ("The AuthMode isn't WPA!!!%d \n",pAd->MeshTab.wdev.AuthMode));
                    break;
				}

				/* If this IE did not begins with "0x00:0x50:0xf2:0x01" or "0x00:0x0f:0xac", it would be proprietary.  So we ignore it. */
                if (!NdisEqualMemory(eid_ptr->Octet, WPA1_OUI, sizeof(WPA1_OUI))
                    && !NdisEqualMemory(&eid_ptr->Octet[2], WPA2_OUI, sizeof(WPA2_OUI)))
                {
                    DBGPRINT(RT_DEBUG_TRACE, ("Not RSN IE, maybe proprietary IE!!!\n"));
                    break;                          
                }

				if (/*(eid_ptr->Len <= MAX_LEN_OF_RSNIE) && */(eid_ptr->Len > MIN_LEN_OF_RSNIE))
				{
					NdisMoveMemory(pRsnIe, eid_ptr, eid_ptr->Len + 2);
					*pRsnIeLen = eid_ptr->Len + 2;
				}
				else
                {
                    *pRsnIeLen = 0;
                    DBGPRINT(RT_DEBUG_TRACE, ("The received the length(%d) of RSN_IE is invalid\n",eid_ptr->Len));
                    break;  
                }
				DBGPRINT(RT_DEBUG_TRACE, ("Link Msg - RSNIE(%d), and its len(%d)!!!\n", eid_ptr->Eid, *pRsnIeLen));
				break;
					
			case IE_MESH_ID:
				if (eid_ptr->Len < MAX_MESH_ID_LEN)
					NdisMoveMemory(pMeshId, eid_ptr->Octet, eid_ptr->Len);
				break;

			case IE_MESH_CONFIGURATION:
				if (NdisEqualMemory(eid_ptr->Octet + 1, MeshOUI, 3))
					NdisMoveMemory(pMeshSelPathId, eid_ptr->Octet + 4, 1);

				if (NdisEqualMemory(eid_ptr->Octet + 5, MeshOUI, 3))
					NdisMoveMemory(pMeshSelMetricId, eid_ptr->Octet + 8, 1);

				NdisMoveMemory(pCPI, eid_ptr->Octet + 13, 4);
				*pCPI = le2cpu32(*(UINT32 *)pCPI);
				NdisMoveMemory(&pMeshCapabilty->word, eid_ptr->Octet + 17, 2);
				pMeshCapabilty->word = le2cpu16(*(UINT16 *)(&pMeshCapabilty->word));
				break;

			case IE_MESH_PEER_LINK_MANAGEMENT:
				if ((*eid_ptr->Octet == SUBTYPE_PEER_LINK_OPEN) /* subtype 1 means Peer-Link-Open frame. */
					&& (eid_ptr->Len == LenPeerLinkMngIE[SUBTYPE_PEER_LINK_OPEN])) /* Length of Peer-Link-open must be 3. */
				{
					NdisMoveMemory(pPeerLinkId, eid_ptr->Octet + 1, 2);
					*pPeerLinkId = le2cpu16(*pPeerLinkId);
				}
				break;

			case IE_MESH_MSCIE:
				if (eid_ptr->Len == 7)  /* its Length MUST be 7 */
				{
					NdisMoveMemory(pMscIe, eid_ptr->Octet, eid_ptr->Len);
				}
				break;

			case IE_MESH_MSAIE:
				if (eid_ptr->Len >= sizeof(MSA_HANDSHAKE_IE))
				{					
					NdisMoveMemory(pMsaIe, eid_ptr->Octet, eid_ptr->Len);
					*pMsaIeLen = eid_ptr->Len;
				}
				break;

            case IE_HT_CAP:
				if (eid_ptr->Len >= sizeof(HT_CAPABILITY_IE))
				{
					NdisMoveMemory(pHtCapability, eid_ptr->Octet, SIZE_HT_CAP_IE);

					*(USHORT *)(&pHtCapability->HtCapInfo) = cpu2le16(*(USHORT *)(&pHtCapability->HtCapInfo));
					*(USHORT *)(&pHtCapability->ExtHtCapInfo) = cpu2le16(*(USHORT *)(&pHtCapability->ExtHtCapInfo));

					*pHtCapabilityLen = SIZE_HT_CAP_IE;
					DBGPRINT(RT_DEBUG_WARN, ("PeerAssocReqSanity - IE_HT_CAP\n"));
				}
				else
				{
					DBGPRINT(RT_DEBUG_WARN, ("PeerAssocReqSanity - wrong IE_HT_CAP.eid_ptr->Len = %d\n", eid_ptr->Len));
				}
				break;

			default:
				break;
		}
		eid_ptr = (PEID_STRUCT)((UCHAR*)eid_ptr + 2 + eid_ptr->Len);        
	}

	return TRUE;
}

BOOLEAN MeshLinkMngCfnSanity(
		IN PRTMP_ADAPTER pAd,
		IN VOID *pMsg,
		IN ULONG MsgLen,
		OUT UINT16 *pCapabilityInfo,
		OUT UINT16 *pStatusCode,
		OUT UINT16 *pAid,
		OUT UCHAR SupRate[],
		OUT PUCHAR pSupRateLen,
		OUT PUCHAR pMeshIdLen,
		OUT	PUCHAR pMeshId,
		OUT PUCHAR pMeshSelPathId,
		OUT PUCHAR pMeshSelMetricId,
		OUT UINT32 *pCPI,
		OUT PMESH_CAPABILITY pMeshCapabilty,
		OUT UINT16 *pLocalLinkId,
		OUT UINT16 *pPeerLinkId,
		OUT PMESH_SECURITY_CAPABILITY_IE pMscIe,
		OUT PUCHAR pMsaIe,
		OUT PUCHAR pMsaIeLen,
		OUT PUCHAR pRsnIe,
		OUT PUCHAR pRsnIeLen,
		OUT HT_CAPABILITY_IE *pHtCapability,
		OUT UCHAR *pHtCapabilityLen)
{
	PEID_STRUCT eid_ptr;
	UCHAR       WPA1_OUI[4]={0x00,0x50,0xF2,0x01};
    UCHAR       WPA2_OUI[3]={0x00,0x0F,0xAC};

	*pMsaIeLen = 0;
	*pRsnIeLen = 0;

	NdisMoveMemory(pCapabilityInfo, pMsg, 2);
	pMsg += 2;
	MsgLen -= 2;

	NdisMoveMemory(pStatusCode, pMsg, 2);
	pMsg += 2;
	MsgLen -= 2;

	NdisMoveMemory(pAid, pMsg, 2);
	pMsg += 2;
	MsgLen -= 2;


	eid_ptr = (PEID_STRUCT)pMsg;
	while (((UCHAR*)eid_ptr + eid_ptr->Len + 1) < ((PUCHAR)pMsg + MsgLen))
	{
		switch(eid_ptr->Eid)
		{
            case IE_SUPP_RATES:
                if ((eid_ptr->Len <= MAX_LEN_OF_SUPPORTED_RATES) && (eid_ptr->Len > 0))
                {
                    NdisMoveMemory(SupRate, eid_ptr->Octet, eid_ptr->Len);
#ifdef RELEASE_EXCLUDE
                    DBGPRINT(RT_DEBUG_INFO, ("%s - IE_SUPP_RATES., Len=%d. Rates[0]=%x\n", __FUNCTION__, eid_ptr->Len, SupRate[0]));
                    DBGPRINT(RT_DEBUG_INFO, ("SupRate[1]=%x %x %x %x %x %x %x\n",
						SupRate[1], SupRate[2], SupRate[3], SupRate[4], SupRate[5], SupRate[6], SupRate[7]));
#endif /* RELEASE_EXCLUDE */
                    
                    *pSupRateLen = eid_ptr->Len;
                }
                else
                {
                	/* HT rate not ready yet. return true temporarily. */
                    /*DBGPRINT(RT_DEBUG_TRACE, ("PeerAssocReqSanity - wrong IE_SUPP_RATES\n")); */
                    *pSupRateLen = 8;
					SupRate[0] = 0x82;
					SupRate[1] = 0x84;
					SupRate[2] = 0x8b;
					SupRate[3] = 0x96;
					SupRate[4] = 0x12;
					SupRate[5] = 0x24;
					SupRate[6] = 0x48;
					SupRate[7] = 0x6c;
#ifdef RELEASE_EXCLUDE
                    DBGPRINT(RT_DEBUG_INFO, ("PeerAssocReqSanity - wrong IE_SUPP_RATES., Len=%d\n",eid_ptr->Len));
#endif /* RELEASE_EXCLUDE */
                    
                    /*return FALSE; */
                }
                break;
                
            case IE_EXT_SUPP_RATES:
                if (eid_ptr->Len + *pSupRateLen <= MAX_LEN_OF_SUPPORTED_RATES)
                {
                    NdisMoveMemory(&SupRate[*pSupRateLen], eid_ptr->Octet, eid_ptr->Len);
                    *pSupRateLen = (*pSupRateLen) + eid_ptr->Len;
                }
                else
                {
                    NdisMoveMemory(&SupRate[*pSupRateLen], eid_ptr->Octet, MAX_LEN_OF_SUPPORTED_RATES - (*pSupRateLen));
                    *pSupRateLen = MAX_LEN_OF_SUPPORTED_RATES;
                }
                break;

			case IE_WPA:  
            case IE_WPA2:
				if (pAd->MeshTab.wdev.AuthMode < Ndis802_11AuthModeWPA)
                    break;

				/* If this IE did not begins with "0x00:0x50:0xf2:0x01" or "0x00:0x0f:0xac", it would be proprietary.  So we ignore it. */
                if (!NdisEqualMemory(eid_ptr->Octet, WPA1_OUI, sizeof(WPA1_OUI))
                    && !NdisEqualMemory(&eid_ptr->Octet[2], WPA2_OUI, sizeof(WPA2_OUI)))
                {
                    DBGPRINT(RT_DEBUG_TRACE, ("Not RSN IE, maybe proprietary IE!!!\n"));
                    break;                          
                }

				if (/*(eid_ptr->Len <= MAX_LEN_OF_RSNIE) && */(eid_ptr->Len > MIN_LEN_OF_RSNIE))
				{
					NdisMoveMemory(pRsnIe, eid_ptr, eid_ptr->Len + 2);
					*pRsnIeLen = eid_ptr->Len + 2;
				}
				else
                {
                    *pRsnIeLen = 0;
                    DBGPRINT(RT_DEBUG_TRACE, ("The received the length(%d) of RSN_IE is invalid\n",eid_ptr->Len));
                    break;  
                }  

				break;
					
			case IE_MESH_ID:
				if (eid_ptr->Len < MAX_MESH_ID_LEN)
					NdisMoveMemory(pMeshId, eid_ptr->Octet, eid_ptr->Len);
				break;

			case IE_MESH_CONFIGURATION:
				if (NdisEqualMemory(eid_ptr->Octet + 1, MeshOUI, 3))
				{
					*pMeshSelPathId = eid_ptr->Octet[4];
				}
				if (NdisEqualMemory(eid_ptr->Octet + 5, MeshOUI, 3))
				{
					*pMeshSelMetricId = eid_ptr->Octet[8];
				}
				NdisMoveMemory(pCPI, eid_ptr->Octet + 13, 4);
				*pCPI = le2cpu32(*pCPI);
				NdisMoveMemory(&pMeshCapabilty->word, eid_ptr->Octet + 17, 2);
				pMeshCapabilty->word = le2cpu16(pMeshCapabilty->word);
				break;

			case IE_MESH_PEER_LINK_MANAGEMENT:
				{
					if ((eid_ptr->Octet[0] == SUBTYPE_PEER_LINK_CONFIRM) /* subtype 1 means Peer-Link-Confirm frame. */
						&& (eid_ptr->Len == LenPeerLinkMngIE[SUBTYPE_PEER_LINK_CONFIRM])) /* Length of Peer-Link-close must be 3. */
					{
						NdisMoveMemory(pLocalLinkId, eid_ptr->Octet + 1, 2);
						*pLocalLinkId = le2cpu16(*(UINT16 *)(pLocalLinkId));
						NdisMoveMemory(pPeerLinkId, eid_ptr->Octet + 3, 2);
						*pPeerLinkId = le2cpu16(*(UINT16 *)(pPeerLinkId));
					}
				}
				break;

			case IE_MESH_MSCIE:
				if (eid_ptr->Len == 7)  /* its Length MUST be 7 */
				{
					NdisMoveMemory(pMscIe,eid_ptr->Octet, eid_ptr->Len);
				}
				break;

			case IE_MESH_MSAIE:
				if (eid_ptr->Len >= sizeof(MSA_HANDSHAKE_IE))
				{					
					NdisMoveMemory(pMsaIe,eid_ptr->Octet, eid_ptr->Len);
					*pMsaIeLen = eid_ptr->Len;
				}
				break;

            case IE_HT_CAP:
				if (eid_ptr->Len >= sizeof(HT_CAPABILITY_IE))
				{
					NdisMoveMemory(pHtCapability, eid_ptr->Octet, SIZE_HT_CAP_IE);

					*(USHORT *)(&pHtCapability->HtCapInfo) = cpu2le16(*(USHORT *)(&pHtCapability->HtCapInfo));
					*(USHORT *)(&pHtCapability->ExtHtCapInfo) = cpu2le16(*(USHORT *)(&pHtCapability->ExtHtCapInfo));

					*pHtCapabilityLen = SIZE_HT_CAP_IE;
					DBGPRINT(RT_DEBUG_WARN, ("PeerAssocReqSanity - IE_HT_CAP\n"));
				}
				else
				{
					DBGPRINT(RT_DEBUG_WARN, ("PeerAssocReqSanity - wrong IE_HT_CAP.eid_ptr->Len = %d\n", eid_ptr->Len));
				}
				break;

			default:
				break;
		}
		eid_ptr = (PEID_STRUCT)((UCHAR*)eid_ptr + 2 + eid_ptr->Len);        
	}

	return TRUE;
}

BOOLEAN MeshLinkMngClsSanity(
		IN PRTMP_ADAPTER pAd,
		IN VOID *pMsg,
		IN ULONG MsgLen,
		OUT UINT16 *pLocalLinkId,
		OUT UINT16 *pPeerLinkId,
		OUT UINT16 *pReasonCode)
{
	PEID_STRUCT eid_ptr;

	eid_ptr = (PEID_STRUCT)pMsg;
	while (((UCHAR*)eid_ptr + eid_ptr->Len + 1) < ((PUCHAR)pMsg + MsgLen))
	{
		switch(eid_ptr->Eid)
		{
			case IE_MESH_PEER_LINK_MANAGEMENT:
				{
					if ((eid_ptr->Octet[0] == SUBTYPE_PEER_LINK_CLOSE) /* subtype 1 means Peer-Link-Close frame. */
						&& (eid_ptr->Len == LenPeerLinkMngIE[SUBTYPE_PEER_LINK_CLOSE])) /* Length of Peer-Link-close must be 7. */
					{
						NdisMoveMemory(pLocalLinkId, eid_ptr->Octet + 1, 2);
						*pLocalLinkId = le2cpu16(*(UINT16 *)(pLocalLinkId));
						NdisMoveMemory(pPeerLinkId, eid_ptr->Octet + 3, 2);
						*pPeerLinkId = le2cpu16(*(UINT16 *)(pPeerLinkId));
						NdisMoveMemory(pReasonCode, eid_ptr->Octet + 5, 2);
						*pReasonCode = le2cpu16(*(UINT16 *)(pReasonCode));
					}
				}
				break;

			default:
				break;
		}
		eid_ptr = (PEID_STRUCT)((UCHAR*)eid_ptr + 2 + eid_ptr->Len);        
	}

	return TRUE;
}

BOOLEAN MeshPathSelMultipathNoticeSanity(
		IN PRTMP_ADAPTER pAd,
		IN VOID *pMsg,
		IN ULONG MsgLen,
		OUT UINT8 *pFlag,
		OUT PUCHAR pMeshSA)
{
	PEID_STRUCT eid_ptr;

	eid_ptr = (PEID_STRUCT)pMsg;
	while (((UCHAR*)eid_ptr + eid_ptr->Len + 1) < ((PUCHAR)pMsg + MsgLen))
	{
		switch(eid_ptr->Eid)
		{
			case IE_MESH_MULITI_PATH_NOTICE_IE:
				{
					if (eid_ptr->Len == 7) /* Length must be 7. */
					{
						NdisMoveMemory(pFlag, eid_ptr->Octet, 1);
						NdisMoveMemory(pMeshSA, eid_ptr->Octet + 1, MAC_ADDR_LEN);
					}
				}
				break;

			default:
				break;
		}
		eid_ptr = (PEID_STRUCT)((UCHAR*)eid_ptr + 2 + eid_ptr->Len);        
	}

	return TRUE;
}


BOOLEAN MeshChannelSwitchAnnouncementSanity(
		IN PRTMP_ADAPTER pAd,
		IN VOID *pMsg,
		IN ULONG MsgLen,
		OUT UINT8 *pChSwMode,
		OUT UINT8 *pNewCh,
		OUT UINT32 *pNewCPI,
		OUT UINT8 *pChSwCnt,
		OUT PUCHAR pMeshSA)
{
	PEID_STRUCT eid_ptr;

	eid_ptr = (PEID_STRUCT)pMsg;
	while (((UCHAR*)eid_ptr + eid_ptr->Len + 1) < ((PUCHAR)pMsg + MsgLen))
	{
		switch(eid_ptr->Eid)
		{
			case IE_MESH_CHANNEL_SWITCH_ANNOUNCEMENT:
				{
					if (eid_ptr->Len == 13) /* Length must be 7. */
					{
						NdisMoveMemory(pChSwMode, eid_ptr->Octet, 1);
						NdisMoveMemory(pNewCh, eid_ptr->Octet + 1, 1);
						NdisMoveMemory(pNewCPI, eid_ptr->Octet + 2, 4);
						*pNewCPI = le2cpu32(*pNewCPI);
						NdisMoveMemory(pChSwCnt, eid_ptr->Octet + 6, 1);
						NdisMoveMemory(pMeshSA, eid_ptr->Octet + 7, MAC_ADDR_LEN);
					}
				}
				break;

			default:
				break;
		}
		eid_ptr = (PEID_STRUCT)((UCHAR*)eid_ptr + 2 + eid_ptr->Len);
	}

	return TRUE;
}

/*
    ==========================================================================
    Description:
       Check sanity RSN IE and get PMKID offset.
    Return:
         TRUE if match
         FALSE otherwise
    ==========================================================================
*/
BOOLEAN MeshValidateRSNIE(
    IN 	PRTMP_ADAPTER    pAd,
    IN 	PUCHAR			pRsnIe,
    IN	USHORT			peerRsnIeLen,
    OUT	UCHAR			*PureRsnLen,
    OUT	UCHAR			*PmkIdLen)
{	
	PEID_STRUCT      eid_ptr;
	PUCHAR			 pBufTmp;
	USHORT			 Count;
	UCHAR			 offset = 0;
	BOOLEAN			 isValid = FALSE;
	BOOLEAN			 RsnEid = IE_WPA;

	/* Check if both MPs are RSNIE */
	if (pAd->MeshTab.wdev.AuthMode < Ndis802_11AuthModeWPA)
	{
		if (peerRsnIeLen == 0)
		{
			/* Don't check it */
			return TRUE;
		}
		else
		{
			DBGPRINT(RT_DEBUG_ERROR, ("MeshValidateRSNIE, the local MP has no RSNIE \n"));
			return FALSE;
		}
	}
	else
	{
		if (peerRsnIeLen == 0)
		{
			DBGPRINT(RT_DEBUG_ERROR, ("MeshValidateRSNIE, the peer MP has no RSNIE \n"));
			return FALSE;
		}		
	}
		
	/*	hex_dump("Peer RSNIE", pRsnIe, peerRsnIeLen); */
	eid_ptr = (PEID_STRUCT)pRsnIe;

	if (pAd->MeshTab.wdev.AuthMode == Ndis802_11AuthModeWPA2PSK ||
		pAd->MeshTab.wdev.AuthMode == Ndis802_11AuthModeWPA2)
		RsnEid = IE_WPA2;
	
	if (eid_ptr->Eid != IE_WPA && eid_ptr->Eid != IE_WPA2)
	{
		DBGPRINT(RT_DEBUG_ERROR, ("MeshValidateRSNIE has unknown element ID(%d) \n", eid_ptr->Eid));
	    return FALSE;
	}

	if (eid_ptr->Len < sizeof(RSNIE2))
	{
		DBGPRINT(RT_DEBUG_ERROR, ("MeshValidateRSNIE, its length is too short(%d)\n", eid_ptr->Len));
	    return FALSE;
	}

	*PureRsnLen = 0;	
	*PmkIdLen = 0;

	/* Store STA RSN_IE capability */
	pBufTmp = (PUCHAR)&eid_ptr->Octet[0];

#ifdef RELEASE_EXCLUDE
	/* the offset of group cipher */
	/* If WPA2, skip version(2-bytes) */
	/* If WPA,  skip OUI(4-bytes) and version(2-bytes) */
#endif /* RELEASE_EXCLUDE */
	offset = (eid_ptr->Eid == IE_WPA2) ? 2 : 6;
	
	/* Check group cipher */
	if (MeshCheckGroupCipher(pAd, pBufTmp + offset, RsnEid))
	{
		/* skip group cipher, then point to pairwise-cipher-count */
		offset += 4;	
	}
	else
	{
		DBGPRINT(RT_DEBUG_ERROR, ("MeshValidateRSNIE, the group cipher isn't supported\n"));
		return FALSE;
	}
	
	/* Store pairwise-cipher-count */
    NdisMoveMemory(&Count, pBufTmp + offset, sizeof(USHORT));
#ifdef RT_BIG_ENDIAN
    Count = SWAP16(Count);		
#endif

	if (Count > 0)
	{
		/* skip pairwise-cipher-count, then point to pairwise-cipher-suite-list */
		offset += sizeof(USHORT);		    	
	}
	else
	{
		DBGPRINT(RT_DEBUG_ERROR, ("MeshValidateRSNIE, the count of piarwise-cipher is invalid(%d)\n", Count));
		return FALSE;
	}

	/* clear this flag */
	isValid = FALSE;
	while (Count > 0)
	{
		if ((isValid == FALSE) && (MeshCheckPairwiseCipher(pAd, pBufTmp + offset, RsnEid)))
		{
			isValid = TRUE;			
		}

		offset += 4;
		Count--;
	}
        	
    if (!isValid)    
	{
		DBGPRINT(RT_DEBUG_ERROR, ("MeshValidateRSNIE, the pairwise cipher isn't supported\n"));
		return FALSE;
	}

	/* Store AKM-suite-count */
    NdisMoveMemory(&Count, pBufTmp + offset, sizeof(USHORT));
#ifdef RT_BIG_ENDIAN
    Count = SWAP16(Count);		
#endif
	
	if (Count > 0)
	{
		/* skip AKM-suite-count, then pointer to AKM-suite-list */
		offset += sizeof(USHORT);		
	}
	else
	{
		DBGPRINT(RT_DEBUG_ERROR, ("MeshValidateRSNIE, the count of AKM-suite is invalid(%d)\n", Count));
		return FALSE;
	}

	/* clear this flag */
	isValid = FALSE;
	while (Count > 0)
	{
		if ((isValid == FALSE) && (MeshCheckAKMSuite(pAd, pBufTmp + offset, RsnEid)))
		{
			isValid = TRUE;			
		}

		offset += 4;
		Count--;
	}
        	
    if (!isValid)    
	{
		DBGPRINT(RT_DEBUG_ERROR, ("MeshValidateRSNIE, the AKM suite isn't supported\n"));
		return FALSE;
	}

	DBGPRINT(RT_DEBUG_TRACE, ("MeshValidateRSNIE is completed \n"));

	/* Check if the remaining length is larger than the size of RSN capability(2-bytes). */
	if (eid_ptr->Len - offset >= 2)
	{
		/* skip RSN capability(2-bytes) */
		offset += sizeof(USHORT);		
	}	

	*PureRsnLen = offset;	/* the length is the RSNIE except elementID, length and PMKID */

#ifdef RELEASE_EXCLUDE
	/* decide PMKID length */
	/* If PMKID exists, the remaining length shall be */
	/* 18 = PMKID_count(2-bytes) + one PMKID(16-bytes) or */
	/* 34 = PMKID_count(2-bytes) + two PMKIDs(32-bytes) */
#endif /* RELEASE_EXCLUDE */
	if (eid_ptr->Len - offset >= 2)
	{
		NdisMoveMemory(&Count, pBufTmp + offset, sizeof(USHORT));
#ifdef RT_BIG_ENDIAN
    	Count = SWAP16(Count);		
#endif
		offset += sizeof(USHORT);	

		if (Count == 2 && (eid_ptr->Len - offset == (2*LEN_PMKID)))		
			*PmkIdLen = 2*LEN_PMKID;		
		else if (Count == 1 && (eid_ptr->Len - offset == LEN_PMKID))					
			*PmkIdLen = LEN_PMKID;		
		else
			*PmkIdLen = 0;	

		if (*PmkIdLen > 0)
		{		
			DBGPRINT(RT_DEBUG_TRACE, ("MeshValidateRSNIE, %d PMKID exist and its len is %d \n", Count, (eid_ptr->Len - offset)));
		}
	}
						
    return TRUE;
}

/*
    ==========================================================================
    Description:
       Check sanity of group cipher selector
    Return:
         TRUE if match
         FALSE otherwise
    ==========================================================================
*/
BOOLEAN MeshCheckGroupCipher(
    IN PRTMP_ADAPTER    pAd,
    IN PUCHAR			pData,
    IN UCHAR			Eid)
{	

	/* WPA and WPA2 format not the same in RSN_IE */
	if (Eid == IE_WPA2)
	{
		/* skip version(2-bytes) */
		if (NdisEqualMemory(pData, &pAd->MeshTab.RSN_IE[2], 4))
			return TRUE;
	}
	else if (Eid == IE_WPA)
	{
		/* skip OUI(4-bytes) and version(2-bytes) */
		if (NdisEqualMemory(pData, &pAd->MeshTab.RSN_IE[6], 4))
			return TRUE;
	}	        

    return FALSE;
}

/*
    ==========================================================================
    Description:
       Check sanity of unicast cipher selector.
    Return:
         TRUE if match
         FALSE otherwise
    ==========================================================================
*/
BOOLEAN MeshCheckPairwiseCipher(
    IN PRTMP_ADAPTER    pAd,
    IN PUCHAR			pData,
    IN UCHAR			Eid)
{
	PUCHAR pTmp;
	USHORT Count;

	pTmp = &pAd->MeshTab.RSN_IE[0];

	if(Eid == IE_WPA2)
    /* skip Version(2),Multicast cipter(4) 2+4==6 */
        pTmp += 6;
    else	
    /*skip OUI(4),Vesrion(2),Multicast cipher(4) 4+2+4==10 */
        pTmp += 10;/*point to number of unicast */
		
    NdisMoveMemory(&Count, pTmp, sizeof(USHORT));	
#ifdef RT_BIG_ENDIAN
    Count = SWAP16(Count);		
#endif
    pTmp   += sizeof(USHORT);/*pointer to unicast cipher */
   
    while (Count > 0)
    {		
		if(RTMPEqualMemory(pData, pTmp, 4))
		   return TRUE;
    	else
		{
			pTmp += 4;
			Count--;
		}
    }
    return FALSE;/* do not match the unicast cipher */

}



/*
    ==========================================================================
    Description:
       Check invalidity of authentication method selection.
    Return:
         TRUE if match
         FALSE otherwise
    ==========================================================================
*/
BOOLEAN MeshCheckAKMSuite(
    IN PRTMP_ADAPTER    pAd,
    IN PUCHAR			pData,
    IN UCHAR			Eid)
{
	PUCHAR pTmp;
	USHORT Count;

	pTmp = &pAd->MeshTab.RSN_IE[0];

	if(Eid == IE_WPA2)
    /* skip Version(2),Multicast cipter(4) 2+4==6 */
        pTmp +=6;
    else	
    /*skip OUI(4),Vesrion(2),Multicast cipher(4) 4+2+4==10 */
        pTmp += 10;/*point to number of unicast */
	    
    NdisMoveMemory(&Count, pTmp, sizeof(USHORT));	
#ifdef RT_BIG_ENDIAN
    Count = SWAP16(Count);		
#endif
    pTmp   += sizeof(USHORT);/*pointer to unicast cipher */

    /* Skip all unicast cipher suite */
    while (Count > 0)
    	{
		/* Skip OUI */
		pTmp += 4;
		Count--;
	}

	NdisMoveMemory(&Count, pTmp, sizeof(USHORT));
#ifdef RT_BIG_ENDIAN
    Count = SWAP16(Count);		
#endif
    pTmp   += sizeof(USHORT);/*pointer to AKM cipher */
    
    while (Count > 0)
    {		
		if(RTMPEqualMemory(pData, pTmp, 4))
		   return TRUE;
    	else
		{
			pTmp += 4;
			Count--;
		}
    }
    return FALSE;/* do not match the AKM */

}

UINT16	MeshValidateOpenAndCfnPeerLinkMsg(
	IN PRTMP_ADAPTER    	pAd,
	IN UCHAR				state,
	IN PMESH_LINK_ENTRY		pMeshLinkEntry,
	IN PUCHAR				pRcvdMscIe,
	IN PUCHAR				pRcvdMsaIe,
	IN UCHAR				RcvdMsaIeLen,
	IN PUCHAR				pRcvdRsnIe,
	IN UCHAR				RcvdRsnIeLen,
	IN UCHAR				pure_rsn_len,
	IN UCHAR				pmkid_len)
{			
#ifdef RELEASE_EXCLUDE
	/* MSCIE is identical to the MSCIE included in the peer link */
	/* open (or confirm) frame received from the candidate peer MP. */
#endif /* RELEASE_EXCLUDE */
	if (!RTMPEqualMemory(pRcvdMscIe, (PUCHAR)&pMeshLinkEntry->RcvdMscIe, sizeof(MESH_SECURITY_CAPABILITY_IE)))
	{
		DBGPRINT(RT_DEBUG_ERROR, ("The MSCIE is different between peer link open and confirm frame \n"));		
		return MESH_SECURITY_FAILED_VERIFICATION;		
	}

#ifdef RELEASE_EXCLUDE
	/* The Handshake Control field of MSAIE is identical to that */
	/* included in the received peer link open (or confirm) frame. */
#endif /* RELEASE_EXCLUDE */
	if (!RTMPEqualMemory(pRcvdMsaIe, pMeshLinkEntry->RcvdMsaIe, sizeof(MESH_HANDSHAKE_CONTROL)))
	{
		DBGPRINT(RT_DEBUG_ERROR, ("The Handshake Control field of MSAIE is different between peer link open and confirm frame \n"));		
		return MESH_SECURITY_FAILED_VERIFICATION;		
	}	

	if (pAd->MeshTab.wdev.AuthMode >= Ndis802_11AuthModeWPA)
	{						
#ifdef RELEASE_EXCLUDE
		/* RSNIE is identical to the RSNIE included in the peer link */
		/* confirm frame received from the candidate peer MP, except the PMKID list. */
#endif /* RELEASE_EXCLUDE */
		if (pure_rsn_len != pMeshLinkEntry->RcvdRSNIE_Len || 
			!RTMPEqualMemory(pRcvdRsnIe + 2, pMeshLinkEntry->RcvdRSNIE, pure_rsn_len))
		{
			DBGPRINT(RT_DEBUG_ERROR, ("The RSNIE is different between candidate MP's link open and confirm frame \n"));			
			return MESH_SECURITY_FAILED_VERIFICATION;			
		}	
				
#ifdef RELEASE_EXCLUDE
		/* the local MP shall verify that the PMK-MAName value contained in the received */
		/* peer link confirm frame identifies the key chosen by the key selection procedure, */
		/* or is empty if Initial MSA Authentication shall occur. */
#endif /* RELEASE_EXCLUDE */
		if (pmkid_len > 0 && pMeshLinkEntry->RcvdPMKID_Len > 0)
		{
			UCHAR pmkid_offset = RcvdRsnIeLen - pmkid_len;
								
			if (pmkid_len != pMeshLinkEntry->RcvdPMKID_Len ||
				!NdisEqualMemory(pRcvdRsnIe + pmkid_offset, pMeshLinkEntry->RcvdPMKID, pmkid_len))
			{
				DBGPRINT(RT_DEBUG_ERROR, ("The result of Key Selection is different between candidate MP's link open and confirm frame\n"));	
				hex_dump("Receive PMKID ", pRcvdRsnIe + pmkid_offset, pmkid_len);
				hex_dump("Desired PMKID ", pMeshLinkEntry->RcvdPMKID, pMeshLinkEntry->RcvdPMKID_Len);
				return MESH_SECURITY_FAILED_VERIFICATION;
				
			}	
		}
		else if (pmkid_len == 0 && pMeshLinkEntry->RcvdPMKID_Len == 0)
		{
			;
		}
		else
		{
			DBGPRINT(RT_DEBUG_ERROR, ("The PMKID is different between candidate MP's link open and confirm frame\n"));	
			return MESH_SECURITY_FAILED_VERIFICATION;
		}		

#ifdef RELEASE_EXCLUDE
		/* If the candidate peer MP is the selector MP, the values in the Selected AKM Suite and */
		/* Selected Pairwise Cipher Suite fields are identical to the values received in peer link frame. */
#endif /* RELEASE_EXCLUDE */
		if (!pMeshLinkEntry->bValidLocalMpAsSelector)
		{
			UCHAR	akm_offset = 0, cipher_offset = 0;
			
#ifdef RELEASE_EXCLUDE
			/* the Selected AKM offset - */
			/* skip the Handshake Control field(1) + MA_ID(6) */
#endif /* RELEASE_EXCLUDE */
			akm_offset = sizeof(MESH_HANDSHAKE_CONTROL) + MAC_ADDR_LEN;

#ifdef RELEASE_EXCLUDE
			/* the Selected Pairwise-cipher offset - */
			/* skip the Handshake Control field(1) + MA_ID(6) + Selected_AKM(4) */
#endif /* RELEASE_EXCLUDE */
			cipher_offset = sizeof(MESH_HANDSHAKE_CONTROL) + MAC_ADDR_LEN + LEN_OUI_SUITE;
			
			if (!RTMPEqualMemory(pRcvdMsaIe + akm_offset, &pMeshLinkEntry->RcvdMsaIe[akm_offset], LEN_OUI_SUITE))
			{
				DBGPRINT(RT_DEBUG_ERROR, ("The Selected AKM Suite of MSAIE is different between peer link open and confirm frame \n"));				
				return MESH_SECURITY_FAILED_VERIFICATION;				
			}	
			
			if (!RTMPEqualMemory(pRcvdMsaIe + cipher_offset, &pMeshLinkEntry->RcvdMsaIe[cipher_offset], LEN_OUI_SUITE))
			{
				DBGPRINT(RT_DEBUG_ERROR, ("The Selected Pairwise Cipher Suite of MSAIE is different between peer link open and confirm frame \n"));				
				return MESH_SECURITY_FAILED_VERIFICATION;				
			}	
		}	
		
	}
		
#ifdef RELEASE_EXCLUDE
	/* the local MP shall verify that the MA-ID value received in the peer link confirm */
	/* frame matches the result of the 802.1X role selection procedure. */
#endif /* RELEASE_EXCLUDE */
	{
		UCHAR LocalMaAddr[MAC_ADDR_LEN];
		UCHAR PeerMaAddr[MAC_ADDR_LEN];
			
		if (pMeshLinkEntry->bValidLocalMpAsAuthenticator)
			NdisMoveMemory(LocalMaAddr, pAd->MeshTab.wdev.if_addr, MAC_ADDR_LEN);
		else
			NdisMoveMemory(LocalMaAddr, pMeshLinkEntry->PeerMacAddr, MAC_ADDR_LEN);

		if (state == SUBTYPE_PEER_LINK_CONFIRM)		
			NdisMoveMemory(PeerMaAddr, pRcvdMsaIe + 1, MAC_ADDR_LEN);
		else
			NdisMoveMemory(PeerMaAddr, &pMeshLinkEntry->RcvdMsaIe[1], MAC_ADDR_LEN);
							
		if (!NdisEqualMemory(LocalMaAddr, PeerMaAddr, MAC_ADDR_LEN))
		{
			DBGPRINT(RT_DEBUG_ERROR, ("The MA-ID of MSAIE doesn't match \n"));			
			return MESH_SECURITY_FAILED_VERIFICATION;			
		}
	}
	

	return MLME_SUCCESS;
}

UINT16 MeshCheckPeerMsaIeCipherValidity(
	IN PRTMP_ADAPTER    	pAd,
	IN UCHAR				state,
	IN PMESH_LINK_ENTRY		pMeshLinkEntry,
	IN PUCHAR				pRcvdMsaIe)
{
	if (pAd->MeshTab.wdev.AuthMode >= Ndis802_11AuthModeWPA)
	{			
		UCHAR	akm_offset = 0, cipher_offset = 0;
				
#ifdef RELEASE_EXCLUDE
		/* the Selected AKM offset - */
		/* skip the Handshake Control field(1) + MA_ID(6) */
#endif /* RELEASE_EXCLUDE */
		akm_offset = sizeof(MESH_HANDSHAKE_CONTROL) + MAC_ADDR_LEN;

#ifdef RELEASE_EXCLUDE
		/* the Selected Pairwise-cipher offset - */
		/* skip the Handshake Control field(1) + MA_ID(6) + Selected_AKM(4) */
#endif /* RELEASE_EXCLUDE */
		cipher_offset = akm_offset + LEN_OUI_SUITE;
	
#ifdef RELEASE_EXCLUDE
		/* Verify that the AKM suite and pairwise cipher suite selected */
		/* in the MSAIE are among those supported by the local MP if */
		/* 1. the local MP is not the Selector MP and received peer link open frame */
		/* 2. received peer link confirm frame */
#endif /* RELEASE_EXCLUDE */
		if (((state == SUBTYPE_PEER_LINK_OPEN) && (!pMeshLinkEntry->bValidLocalMpAsSelector)) ||
			 (state == SUBTYPE_PEER_LINK_CONFIRM))
		{
			UCHAR	 RsnEid = IE_WPA;
						
			if (pAd->MeshTab.wdev.AuthMode == Ndis802_11AuthModeWPA2PSK ||
				pAd->MeshTab.wdev.AuthMode == Ndis802_11AuthModeWPA2)
				RsnEid = IE_WPA2;
			
			if (!MeshCheckPairwiseCipher(pAd, &pRcvdMsaIe[cipher_offset], RsnEid))
			{
				DBGPRINT(RT_DEBUG_ERROR, ("The Selected Pairwise Cipher isn't supported by local MP\n"));				
				return MESH_SECURITY_ROLE_NEGOTIATION_DIFFERS;				
			}

			if (!MeshCheckAKMSuite(pAd, &pRcvdMsaIe[akm_offset], RsnEid))
			{
				DBGPRINT(RT_DEBUG_ERROR, ("The Selected AKM isn't supported by local MP \n"));				
				return MESH_SECURITY_ROLE_NEGOTIATION_DIFFERS;				
			}	

			/* verify peer AKM and pairwise-cipher OK, update them to local MSAIE */
			if (state == SUBTYPE_PEER_LINK_OPEN)			
				NdisMoveMemory(&pMeshLinkEntry->LocalMsaIe[akm_offset], &pRcvdMsaIe[akm_offset], 2*LEN_OUI_SUITE);

		}	

#ifdef RELEASE_EXCLUDE
		/* Upon reception of a peer link confirm frame, */
		/* If the local MP is the selector MP, it shall verify that the selected AKM suite and pairwise cipher */
		/* suite values match its selections that have been sent to the candidate peer MP in the peer link open */
		/* frame. */
#endif /* RELEASE_EXCLUDE */
		if ((state == SUBTYPE_PEER_LINK_CONFIRM) && (pMeshLinkEntry->bValidLocalMpAsSelector))
		{											
			if (!NdisEqualMemory(&pRcvdMsaIe[akm_offset], &pMeshLinkEntry->LocalMsaIe[akm_offset], LEN_OUI_SUITE))
			{
				DBGPRINT(RT_DEBUG_ERROR, ("The Selected AKM in P.L. confirm frame isn't supported by local Selector MP \n"));						
				return MESH_SECURITY_FAILED_VERIFICATION;				
			}

			if (!NdisEqualMemory(&pRcvdMsaIe[cipher_offset], &pMeshLinkEntry->LocalMsaIe[cipher_offset], LEN_OUI_SUITE))				
			{
				DBGPRINT(RT_DEBUG_ERROR, ("The Selected Pairwise Cipher in P.L. confirm frame isn't supported by local Selector MP \n"));										
				return MESH_SECURITY_FAILED_VERIFICATION;			
			}	
		}
	}
							
	return MLME_SUCCESS;

}

BOOLEAN MeshPathRequestSanity(
		IN PRTMP_ADAPTER pAd,
		IN VOID		*pMsg,
		IN ULONG		MsgLen,
		OUT UINT8	*pFlag,
		OUT UINT8 	*pHopCount,
		OUT UINT8	*pTTL,
		OUT UINT32	*pID,
		OUT PUCHAR	pOrigMac,
		OUT UINT32	*pOrigDsn,
		OUT PUCHAR	pProxyMac,
		OUT UINT32	*pLifeTime,
		OUT UINT32	*pMetric,
		OUT UINT8	*pDestCount,
		OUT PMESH_DEST_ENTRY	pDestEntry)
{
	PEID_STRUCT eid_ptr;
	UINT8	VarOffset = 0;
	UINT8	EntryCount = 0;
	MESH_PREQ_FLAG PreqFlag;

	eid_ptr = (PEID_STRUCT)pMsg;

	while (((UCHAR*)eid_ptr + eid_ptr->Len + 1) < ((PUCHAR)pMsg + MsgLen))
	{
		switch(eid_ptr->Eid)
		{
			case IE_MESH_PREQ:
				{
					NdisMoveMemory(pFlag, eid_ptr->Octet, 1);
					VarOffset += 1;
					NdisMoveMemory(pHopCount, eid_ptr->Octet + VarOffset, 1);
					VarOffset += 1;
					NdisMoveMemory(pTTL, eid_ptr->Octet + VarOffset, 1);
					VarOffset += 1;
					NdisMoveMemory(pID, eid_ptr->Octet + VarOffset, 4);
					VarOffset += 4;
					*pID = le2cpu32(*pID);
					NdisMoveMemory(pOrigMac, eid_ptr->Octet + VarOffset, MAC_ADDR_LEN);
					VarOffset += MAC_ADDR_LEN;
					NdisMoveMemory(pOrigDsn, eid_ptr->Octet + VarOffset, 4);
					VarOffset += 4;
					*pOrigDsn = le2cpu32(*pOrigDsn);
					PreqFlag = (MESH_PREQ_FLAG)(*pFlag);
					if (PreqFlag.field.AE == 1)
					{
						NdisMoveMemory(pProxyMac, eid_ptr->Octet + VarOffset, MAC_ADDR_LEN);
						VarOffset += MAC_ADDR_LEN;
					}
					NdisMoveMemory(pLifeTime, eid_ptr->Octet + VarOffset, 4);
					VarOffset += 4;
					*pLifeTime = le2cpu32(*pLifeTime);
					NdisMoveMemory(pMetric, eid_ptr->Octet + VarOffset, 4);
					VarOffset += 4;
					*pMetric = le2cpu32(*pMetric);
					NdisMoveMemory(pDestCount, eid_ptr->Octet + VarOffset, 1);
					VarOffset += 1;
					EntryCount = (*pDestCount);
					if (EntryCount >= 1)
						NdisMoveMemory(pDestEntry, eid_ptr->Octet + VarOffset, 11*EntryCount);
				}
				break;

			default:
				break;
		}
		eid_ptr = (PEID_STRUCT)((UCHAR*)eid_ptr + 2 + eid_ptr->Len);        
	}

	return TRUE;
}

BOOLEAN MeshPathResponseSanity(
		IN PRTMP_ADAPTER pAd,
		IN VOID		*pMsg,
		IN ULONG		MsgLen,
		OUT UINT8	*pFlag,
		OUT UINT8 	*pHopCount,
		OUT UINT8	*pTTL,
		OUT PUCHAR	pDestMac,
		OUT UINT32	*pDesDsn,
		OUT PUCHAR	pProxyMac,
		OUT UINT32	*pLifeTime,
		OUT UINT32	*pMetric,
		OUT PUCHAR	pOrigMac,
		OUT UINT32	*pOrigDsn,
		OUT UINT8	*pDependMPCount,
		OUT PMESH_DEPENDENT_ENTRY	pDependEntry)
{
	PEID_STRUCT eid_ptr;
	UINT8	VarOffset = 0;
	UINT8	EntryCount = 0;
	MESH_PREP_FLAG PrepFlag;

	eid_ptr = (PEID_STRUCT)pMsg;

	while (((UCHAR*)eid_ptr + eid_ptr->Len + 1) < ((PUCHAR)pMsg + MsgLen))
	{
		switch(eid_ptr->Eid)
		{
			case IE_MESH_PREP:
				{
					NdisMoveMemory(pFlag, eid_ptr->Octet, 1);
					VarOffset += 1;
					NdisMoveMemory(pHopCount, eid_ptr->Octet + VarOffset, 1);
					VarOffset += 1;
					NdisMoveMemory(pTTL, eid_ptr->Octet + VarOffset, 1);
					VarOffset += 1;
					NdisMoveMemory(pDestMac, eid_ptr->Octet + VarOffset, MAC_ADDR_LEN);
					VarOffset += MAC_ADDR_LEN;
					NdisMoveMemory(pDesDsn, eid_ptr->Octet + VarOffset, 4);
					VarOffset += 4;
					*pDesDsn = le2cpu32(*pDesDsn);
					PrepFlag = (MESH_PREP_FLAG)(*pFlag);
					if (PrepFlag.field.AE == 1)
					{
						NdisMoveMemory(pProxyMac, eid_ptr->Octet + VarOffset, MAC_ADDR_LEN);
						VarOffset += MAC_ADDR_LEN;
					}
					NdisMoveMemory(pLifeTime, eid_ptr->Octet + VarOffset, 4);
					VarOffset += 4;
					*pLifeTime = le2cpu32(*pLifeTime);
					NdisMoveMemory(pMetric, eid_ptr->Octet + VarOffset, 4);
					VarOffset += 4;
					*pMetric = le2cpu32(*pMetric);
					NdisMoveMemory(pOrigMac, eid_ptr->Octet + VarOffset, MAC_ADDR_LEN);
					VarOffset += MAC_ADDR_LEN;
					NdisMoveMemory(pOrigDsn, eid_ptr->Octet + VarOffset, 4);
					VarOffset += 4;
					*pOrigDsn = le2cpu32(*pOrigDsn);
					NdisMoveMemory(pDependMPCount, eid_ptr->Octet + VarOffset, 1);
					VarOffset += 1;
					EntryCount = (*pDependMPCount);
					if (EntryCount >= 1)
						NdisMoveMemory(pDependEntry, eid_ptr->Octet + VarOffset, 10*EntryCount);
				}
				break;

			default:
				break;
		}
		eid_ptr = (PEID_STRUCT)((UCHAR*)eid_ptr + 2 + eid_ptr->Len);        
	}

	return TRUE;
}

BOOLEAN MeshPathErrorSanity(
		IN PRTMP_ADAPTER pAd,
		IN VOID		*pMsg,
		IN ULONG		MsgLen,
		OUT UINT8	*pFlag,
		OUT UINT8	*pDestNum,
		OUT PMESH_PERR_ENTRY	pErrorEntry)
{
	PEID_STRUCT eid_ptr;
	UINT8	VarOffset = 0;
	UINT8	EntryCount = 0;

	eid_ptr = (PEID_STRUCT)pMsg;

	while (((UCHAR*)eid_ptr + eid_ptr->Len + 1) < ((PUCHAR)pMsg + MsgLen))
	{
		switch(eid_ptr->Eid)
		{
			case IE_MESH_PREP:
				{
					NdisMoveMemory(pFlag, eid_ptr->Octet, 1);
					VarOffset += 1;
					NdisMoveMemory(pDestNum, eid_ptr->Octet + VarOffset, 1);
					VarOffset += 1;
					EntryCount = (*pDestNum);
					if (EntryCount >= 1)
						NdisMoveMemory(pErrorEntry, eid_ptr->Octet + VarOffset, 10*EntryCount);
				}
				break;

			default:
				break;
		}
		eid_ptr = (PEID_STRUCT)((UCHAR*)eid_ptr + 2 + eid_ptr->Len);        
	}

	return TRUE;
}

BOOLEAN MeshLinkMetricReportSanity(
		IN PRTMP_ADAPTER pAd,
		IN VOID *pMsg,
		IN ULONG MsgLen,
		OUT PUINT32 pLinkMetric)
{
	PEID_STRUCT eid_ptr;
	UINT32 LinkMetric;

	eid_ptr = (PEID_STRUCT)pMsg;

	while (((UCHAR*)eid_ptr + eid_ptr->Len + 1) < ((PUCHAR)pMsg + MsgLen))
	{
		switch(eid_ptr->Eid)
		{
			case IE_MESH_LINK_METRIC_REPORT:
				{
					if (eid_ptr->Len == 4)
					{
						NdisMoveMemory(&LinkMetric, eid_ptr->Octet, eid_ptr->Len);
						*pLinkMetric = le2cpu32(LinkMetric);
					}
				}
				break;

			default:
				break;
		}
		eid_ptr = (PEID_STRUCT)((UCHAR*)eid_ptr + 2 + eid_ptr->Len);        
	}

	return TRUE;
}

#endif /* MESH_SUPPORT */
