/* *************************************************************************** * Ralink Tech Inc. * 4F, No. 2 Technology 5th Rd. * Science-based Industrial Park * Hsin-chu, Taiwan, R.O.C. * * (c) Copyright 2002-2007, 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: cmm_mat_iparp.c Abstract: MAT convert engine subroutine for ip base protocols, currently now we just handle IP/ARP protocols. Revision History: Who When What -------- ---------- ---------------------------------------------- Shiang 02/26/07 Init version */ #ifdef MAT_SUPPORT #include "rt_config.h" static NDIS_STATUS MATProto_IP_Init(MAT_STRUCT *pMatCfg); static NDIS_STATUS MATProto_IP_Exit(MAT_STRUCT *pMatCfg); static PUCHAR MATProto_IP_Rx(MAT_STRUCT *pMatCfg, PNDIS_PACKET pSkb, PUCHAR pLayerHdr, PUCHAR pMacAddr); static PUCHAR MATProto_IP_Tx(MAT_STRUCT *pMatCfg, PNDIS_PACKET pSkb, PUCHAR pLayerHdr, PUCHAR pMacAddr); static NDIS_STATUS MATProto_ARP_Init(MAT_STRUCT *pMatCfg); static NDIS_STATUS MATProto_ARP_Exit(MAT_STRUCT *pMatCfg); static PUCHAR MATProto_ARP_Rx(MAT_STRUCT *pMatCfg, PNDIS_PACKET pSkb, PUCHAR pLayerHdr, PUCHAR pMacAddr); static PUCHAR MATProto_ARP_Tx(MAT_STRUCT *pMatCfg, PNDIS_PACKET pSkb,PUCHAR pLayerHdr, PUCHAR pMacAddr); #define IPV4_ADDR_LEN 4 #define NEED_UPDATE_IPMAC_TB(Mac, IP) (IS_UCAST_MAC(Mac) && IS_GOOD_IP(IP)) typedef struct _IPMacMappingEntry { UINT ipAddr; /* In network order */ UCHAR macAddr[MAC_ADDR_LEN]; ULONG lastTime; struct _IPMacMappingEntry *pNext; }IPMacMappingEntry, *PIPMacMappingEntry; typedef struct _IPMacMappingTable { BOOLEAN valid; IPMacMappingEntry *hash[MAT_MAX_HASH_ENTRY_SUPPORT+1]; /*0~63 for specific station, 64 for broadcast MacAddress */ UCHAR curMcastAddr[MAC_ADDR_LEN]; /* The multicast mac addr for currecnt received packet destined to ipv4 multicast addr */ }IPMacMappingTable; struct _MATProtoOps MATProtoIPHandle = { .init = MATProto_IP_Init, .tx = MATProto_IP_Tx, .rx = MATProto_IP_Rx, .exit = MATProto_IP_Exit, }; struct _MATProtoOps MATProtoARPHandle = { .init = MATProto_ARP_Init, .tx = MATProto_ARP_Tx, .rx = MATProto_ARP_Rx, .exit =MATProto_ARP_Exit, }; VOID dumpIPMacTb( IN MAT_STRUCT *pMatCfg, IN int index) { IPMacMappingTable *pIPMacTable; IPMacMappingEntry *pHead; int startIdx, endIdx; pIPMacTable = (IPMacMappingTable *)pMatCfg->MatTableSet.IPMacTable; if (!pIPMacTable) return; if (!pIPMacTable->valid) { DBGPRINT(RT_DEBUG_OFF, ("%s():IPMacTable not init yet, so cannot do dump!\n", __FUNCTION__)); return; } if(index < 0) { /* dump all. */ startIdx = 0; endIdx = MAT_MAX_HASH_ENTRY_SUPPORT; } else { /* dump specific hash index. */ startIdx = endIdx = index; } DBGPRINT(RT_DEBUG_OFF, ("%s():\n", __FUNCTION__)); for(; startIdx<= endIdx; startIdx++) { pHead = pIPMacTable->hash[startIdx]; while(pHead) { DBGPRINT(RT_DEBUG_OFF, ("IPMac[%d]:\n", startIdx)); DBGPRINT(RT_DEBUG_OFF, ("\t:IP=0x%x,Mac=%02x:%02x:%02x:%02x:%02x:%02x, lastTime=0x%lx, next=%p\n", pHead->ipAddr, pHead->macAddr[0],pHead->macAddr[1],pHead->macAddr[2], pHead->macAddr[3],pHead->macAddr[4],pHead->macAddr[5], pHead->lastTime, pHead->pNext)); pHead = pHead->pNext; } } DBGPRINT(RT_DEBUG_OFF, ("\t----EndOfDump!\n")); } static inline NDIS_STATUS getDstIPFromIpPkt( IN PUCHAR pIpHdr, IN UINT *dstIP) { if (!pIpHdr) return FALSE; NdisMoveMemory(dstIP, (pIpHdr + 16), 4); /*shift 16 for IP header len before DstIP. */ /* DBGPRINT(RT_DEBUG_TRACE, ("%s(): Get the dstIP=0x%x\n", __FUNCTION__, *dstIP)); */ return TRUE; } static inline NDIS_STATUS getSrcIPFromIpPkt( IN PUCHAR pIpHdr, IN UINT *pSrcIP) { if (!pIpHdr) return FALSE; NdisMoveMemory(pSrcIP, (pIpHdr + 12), 4); /*shift 12 for IP header len before DstIP. */ /* DBGPRINT(RT_DEBUG_TRACE, ("%s(): Get the srcIP=0x%x\n", __FUNCTION__, *pSrcIP)); */ return TRUE; } static NDIS_STATUS IPMacTableUpdate( IN MAT_STRUCT *pMatCfg, IN PUCHAR pMacAddr, IN UINT ipAddr) { UINT hashIdx; IPMacMappingTable *pIPMacTable; IPMacMappingEntry *pEntry = NULL, *pPrev = NULL, *pNewEntry =NULL; ULONG now; pIPMacTable = (IPMacMappingTable *)pMatCfg->MatTableSet.IPMacTable; if (!pIPMacTable) return FALSE; if (!pIPMacTable->valid) return FALSE; hashIdx = MAT_IP_ADDR_HASH_INDEX(ipAddr); pEntry = pPrev = pIPMacTable->hash[hashIdx]; while(pEntry) { NdisGetSystemUpTime(&now); /* Find a existed IP-MAC Mapping entry */ if (ipAddr == pEntry->ipAddr) { /* DBGPRINT(RT_DEBUG_TRACE, ("%s(): Got the Mac(%02x:%02x:%02x:%02x:%02x:%02x) of mapped IP(%d.%d.%d.%d)\n", __FUNCTION__, pEntry->macAddr[0],pEntry->macAddr[1],pEntry->macAddr[2], pEntry->macAddr[3],pEntry->macAddr[4], pEntry->macAddr[5], (ipAddr>>24) & 0xff, (ipAddr>>16) & 0xff, (ipAddr>>8) & 0xff, ipAddr & 0xff)); */ /* compare is useless. So we directly copy it into the entry. */ NdisMoveMemory(pEntry->macAddr, pMacAddr, 6); pEntry->lastTime = now; return TRUE; } else { /* handle the age-out situation */ /*if ((Now - pEntry->lastTime) > MAT_TB_ENTRY_AGEOUT_TIME) */ if (RTMP_TIME_AFTER(now, pEntry->lastTime + MAT_TB_ENTRY_AGEOUT_TIME)) { /* Remove the aged entry */ if (pEntry == pIPMacTable->hash[hashIdx]) { pIPMacTable->hash[hashIdx]= pEntry->pNext; pPrev = pIPMacTable->hash[hashIdx]; } else { pPrev->pNext = pEntry->pNext; } MATDBEntryFree(pMatCfg, (PUCHAR)pEntry); pEntry = (pPrev == NULL ? NULL: pPrev->pNext); pMatCfg->nodeCount--; } else { pPrev = pEntry; pEntry = pEntry->pNext; } } } /* Allocate a new IPMacMapping entry and insert into the hash */ pNewEntry = (IPMacMappingEntry *)MATDBEntryAlloc(pMatCfg, sizeof(IPMacMappingEntry)); if (pNewEntry != NULL) { pNewEntry->ipAddr = ipAddr; NdisMoveMemory(pNewEntry->macAddr, pMacAddr, 6); pNewEntry->pNext = NULL; NdisGetSystemUpTime(&pNewEntry->lastTime); if (pIPMacTable->hash[hashIdx] == NULL) { /* Hash list is empty, directly assign it. */ pIPMacTable->hash[hashIdx] = pNewEntry; } else { /* Ok, we insert the new entry into the root of hash[hashIdx] */ pNewEntry->pNext = pIPMacTable->hash[hashIdx]; pIPMacTable->hash[hashIdx] = pNewEntry; } /*dumpIPMacTb(pMatCfg, hashIdx); //for debug */ pMatCfg->nodeCount++; return TRUE; } return FALSE; } static PUCHAR IPMacTableLookUp( IN MAT_STRUCT *pMatCfg, IN UINT ipAddr) { IPMacMappingTable *pIPMacTable; UINT hashIdx, ip; IPMacMappingEntry *pEntry = NULL; PUCHAR pGroupMacAddr; pIPMacTable = (IPMacMappingTable *)pMatCfg->MatTableSet.IPMacTable; if (!pIPMacTable) return NULL; if (!pIPMacTable->valid) return NULL; /*if multicast ip, need converting multicast group address to ethernet address. */ ip = ntohl(ipAddr); if (IS_MULTICAST_IP(ip)) { pGroupMacAddr = (PUCHAR)(&pIPMacTable->curMcastAddr); ConvertMulticastIP2MAC((PUCHAR) &ipAddr, (UCHAR **)(&pGroupMacAddr), ETH_P_IP); return pIPMacTable->curMcastAddr; } /* Use hash to find out the location of that entry and get the Mac address. */ hashIdx = MAT_IP_ADDR_HASH_INDEX(ipAddr); /* spin_lock_irqsave(&IPMacTabLock, irqFlag); */ pEntry = pIPMacTable->hash[hashIdx]; while(pEntry) { if (pEntry->ipAddr == ipAddr) { /* DBGPRINT(RT_DEBUG_TRACE, ("%s(): dstMac=%02x:%02x:%02x:%02x:%02x:%02x for mapped dstIP(%d.%d.%d.%d)\n", __FUNCTION__, pEntry->macAddr[0],pEntry->macAddr[1],pEntry->macAddr[2], pEntry->macAddr[3],pEntry->macAddr[4],pEntry->macAddr[5], (ipAddr>>24) & 0xff, (ipAddr>>16) & 0xff, (ipAddr>>8) & 0xff, ipAddr & 0xff)); */ /*Update the lastTime to prevent the aging before pDA processed! */ NdisGetSystemUpTime(&pEntry->lastTime); return pEntry->macAddr; } else pEntry = pEntry->pNext; } /* We didn't find any matched Mac address, our policy is treat it as broadcast packet and send to all. */ return pIPMacTable->hash[IPMAC_TB_HASH_INDEX_OF_BCAST]->macAddr; } static NDIS_STATUS IPMacTable_RemoveAll( IN MAT_STRUCT *pMatCfg) { IPMacMappingEntry *pEntry; IPMacMappingTable *pIPMacTable; INT i; pIPMacTable = (IPMacMappingTable *)pMatCfg->MatTableSet.IPMacTable; if (!pIPMacTable) return TRUE; if (pIPMacTable->valid) { pIPMacTable->valid = FALSE; for (i=0; ihash[i]) != NULL) { pIPMacTable->hash[i] = pEntry->pNext; MATDBEntryFree(pMatCfg, (PUCHAR)pEntry); } } } /* kfree(pIPMacTable); */ os_free_mem(NULL, pIPMacTable); pMatCfg->MatTableSet.IPMacTable = NULL; return TRUE; } static NDIS_STATUS IPMacTable_init( IN MAT_STRUCT *pMatCfg) { IPMacMappingTable *pIPMacTable; IPMacMappingEntry *pEntry = NULL; if (pMatCfg->MatTableSet.IPMacTable != NULL) { pIPMacTable = (IPMacMappingTable *)pMatCfg->MatTableSet.IPMacTable; } else { /* pMatCfg->MatTableSet.IPMacTable = kmalloc(sizeof(IPMacMappingTable), GFP_KERNEL); */ os_alloc_mem_suspend(NULL, (UCHAR **)&(pMatCfg->MatTableSet.IPMacTable), sizeof(IPMacMappingTable)); if (pMatCfg->MatTableSet.IPMacTable) { pIPMacTable = (IPMacMappingTable *)pMatCfg->MatTableSet.IPMacTable; NdisZeroMemory(pIPMacTable, sizeof(IPMacMappingTable)); } else { DBGPRINT(RT_DEBUG_ERROR, ("IPMacTable_init(): Allocate memory for IPMacTable failed!\n")); return FALSE; } } if (pIPMacTable->valid == FALSE) { /*Set the last hash entry (hash[64]) as our default broadcast Mac address */ pEntry = (IPMacMappingEntry *)MATDBEntryAlloc(pMatCfg, sizeof(IPMacMappingEntry)); if (!pEntry) { DBGPRINT(RT_DEBUG_ERROR, ("IPMacTable_init(): Allocate memory for IPMacTable broadcast entry failed!\n")); return FALSE; } /*pEntry->ipAddr = 0; */ NdisZeroMemory(pEntry, sizeof(IPMacMappingEntry)); NdisMoveMemory(&pEntry->macAddr[0], &BROADCAST_ADDR[0], 6); pEntry->pNext = NULL; pIPMacTable->hash[IPMAC_TB_HASH_INDEX_OF_BCAST] = pEntry; pIPMacTable->valid = TRUE; } else { DBGPRINT(RT_DEBUG_TRACE, ("%s(): IPMacTable already inited!\n", __FUNCTION__)); } return TRUE; } static NDIS_STATUS MATProto_ARP_Exit( IN MAT_STRUCT *pMatCfg) { INT status; status = IPMacTable_RemoveAll(pMatCfg); return status; } static PUCHAR MATProto_ARP_Rx( IN MAT_STRUCT *pMatCfg, IN PNDIS_PACKET pSkb, IN PUCHAR pLayerHdr, IN PUCHAR pMacAddr) { PUCHAR pArpHdr = NULL, pRealMac = NULL; PUCHAR tgtMac, tgtIP; BOOLEAN isUcastMac, isGoodIP; pArpHdr = pLayerHdr; /*dumpPkt(RTPKT_TO_OSPKT(pSkb)->data, RTPKT_TO_OSPKT(pSkb)->len); */ /* We just take care about the target(Mac/IP address) fields. */ tgtMac = pArpHdr + 18; tgtIP = tgtMac + 6; /* isUcastMac = !(00:00:00:00:00:00|| mcastMac); */ isUcastMac = ((tgtMac[0]|tgtMac[1]|tgtMac[2]|tgtMac[3]|tgtMac[4]|tgtMac[5])!=0); isUcastMac &= ((tgtMac[0] & 0x1)==0); /* isGoodIP = ip address is not 0.0.0.0 */ isGoodIP = (*(UINT *)tgtIP != 0); if (isUcastMac && isGoodIP) pRealMac = IPMacTableLookUp(pMatCfg, *(UINT *)tgtIP); /* For need replaced mac, we need to replace the targetMAC as correct one to make the real receiver can receive that. */ if (isUcastMac && pRealMac) NdisMoveMemory(tgtMac, pRealMac, MAC_ADDR_LEN); if (pRealMac == NULL) pRealMac = &BROADCAST_ADDR[0]; /* pRealMac = pIPMacTable->hash[IPMAC_TB_HASH_INDEX_OF_BCAST]->macAddr; */ return pRealMac; } static PUCHAR MATProto_ARP_Tx( IN MAT_STRUCT *pMatCfg, IN PNDIS_PACKET pSkb, IN PUCHAR pLayerHdr, IN PUCHAR pMacAddr) { PUCHAR pSMac, pSIP; BOOLEAN isUcastMac, isGoodIP; NET_PRO_ARP_HDR *arpHdr; PUCHAR pPktHdr; PNDIS_PACKET newSkb = NULL; pPktHdr = GET_OS_PKT_DATAPTR(pSkb); arpHdr = (NET_PRO_ARP_HDR *)pLayerHdr; /* Check the arp header. We just handle ether type hardware address and IPv4 internet address type and opcode is ARP reuqest/response. */ if ((arpHdr->ar_hrd != OS_HTONS(ARPHRD_ETHER)) || (arpHdr->ar_pro != OS_HTONS(ETH_P_IP)) || (arpHdr->ar_op != OS_HTONS(ARPOP_REPLY) && arpHdr->ar_op != OS_HTONS(ARPOP_REQUEST))) return NULL; /* We just take care about the sender(Mac/IP address) fields. */ pSMac =(PUCHAR)(pLayerHdr + 8); pSIP = (PUCHAR)(pSMac + MAC_ADDR_LEN); isUcastMac = IS_UCAST_MAC(pSMac); isGoodIP = IS_GOOD_IP(get_unaligned32((PUINT) pSIP)); /* DBGPRINT(RT_DEBUG_TRACE,("%s(): ARP Pkt=>senderIP=%d.%d.%d.%d, senderMac=%02x:%02x:%02x:%02x:%02x:%02x\n", __FUNCTION__, pSIP[0], pSIP[1], pSIP[2], pSIP[3], pSMac[0],pSMac[1],pSMac[2],pSMac[3],pSMac[4],pSMac[5])); */ if (isUcastMac && isGoodIP) IPMacTableUpdate(pMatCfg, pSMac, get_unaligned32((PUINT) pSIP)); /* For outgoing unicast mac, we need to replace the senderMAC as ourself to make the receiver can send to us. */ if (isUcastMac) { if(OS_PKT_CLONED(pSkb)) { newSkb = (PNDIS_PACKET)OS_PKT_COPY(pSkb); if(newSkb) { if (IS_VLAN_PACKET(GET_OS_PKT_DATAPTR(newSkb))) pSMac = (PUCHAR)(GET_OS_PKT_DATAPTR(newSkb) + MAT_VLAN_ETH_HDR_LEN + 8); else pSMac = (PUCHAR)(GET_OS_PKT_DATAPTR(newSkb) + MAT_ETHER_HDR_LEN + 8); } } ASSERT(pMacAddr); NdisMoveMemory(pSMac, pMacAddr, MAC_ADDR_LEN); } return (PUCHAR)newSkb; } static NDIS_STATUS MATProto_ARP_Init( IN MAT_STRUCT *pMatCfg) { BOOLEAN status = FALSE; status = IPMacTable_init(pMatCfg); return status; } static NDIS_STATUS MATProto_IP_Exit( IN MAT_STRUCT *pMatCfg) { INT status; status = IPMacTable_RemoveAll(pMatCfg); return status; } static PUCHAR MATProto_IP_Rx( IN MAT_STRUCT *pMatCfg, IN PNDIS_PACKET pSkb, IN PUCHAR pLayerHdr, IN PUCHAR pDevMacAdr) { PUCHAR pMacAddr; UINT dstIP; /* Fetch the IP addres from the packet header. */ getDstIPFromIpPkt(pLayerHdr, &dstIP); pMacAddr = IPMacTableLookUp(pMatCfg, dstIP); return pMacAddr; } static UCHAR DHCP_MAGIC[]= {0x63, 0x82, 0x53, 0x63}; static PUCHAR MATProto_IP_Tx( IN MAT_STRUCT *pMatCfg, IN PNDIS_PACKET pSkb, IN PUCHAR pLayerHdr, IN PUCHAR pDevMacAdr) { PUCHAR pSrcMac; PUCHAR pSrcIP; BOOLEAN needUpdate; PUCHAR pPktHdr; pPktHdr = GET_OS_PKT_DATAPTR(pSkb); pSrcMac = pPktHdr + 6; pSrcIP = pLayerHdr + 12; needUpdate = NEED_UPDATE_IPMAC_TB(pSrcMac, get_unaligned32((PUINT)(pSrcIP))); if (needUpdate) IPMacTableUpdate(pMatCfg, pSrcMac, get_unaligned32((PUINT)(pSrcIP))); /*For UDP packet, we need to check about the DHCP packet, to modify the flag of DHCP discovey/request as broadcast. */ if (*(pLayerHdr + 9) == 0x11) { PUCHAR udpHdr; UINT16 srcPort, dstPort; udpHdr = pLayerHdr + 20; srcPort = OS_NTOHS(get_unaligned((PUINT16)(udpHdr))); dstPort = OS_NTOHS(get_unaligned((PUINT16)(udpHdr+2))); if (srcPort==68 && dstPort==67) /*It's a DHCP packet */ { PUCHAR bootpHdr; UINT16 bootpFlag; bootpHdr = udpHdr + 8; bootpFlag = OS_NTOHS(get_unaligned((PUINT16)(bootpHdr+10))); DBGPRINT(RT_DEBUG_TRACE, ("is bootp packet! bootpFlag=0x%x\n", bootpFlag)); if (bootpFlag != 0x8000) /*check if it's a broadcast request. */ { PUCHAR dhcpHdr; dhcpHdr = bootpHdr + 236; DBGPRINT(RT_DEBUG_TRACE, ("the DHCP flag is a unicast, dhcp_magic=%02x:%02x:%02x:%02x\n", dhcpHdr[0], dhcpHdr[1], dhcpHdr[2], dhcpHdr[3])); if (NdisEqualMemory(dhcpHdr, DHCP_MAGIC, 4)) { DBGPRINT(RT_DEBUG_TRACE, ("dhcp magic macthed!\n")); bootpFlag = OS_HTONS(0x8000); NdisMoveMemory((bootpHdr+10), &bootpFlag, 2); /*Set the bootp flag as broadcast */ NdisZeroMemory((udpHdr+6), 2); /*modify the UDP chksum as zero */ } } } } return NULL; } static NDIS_STATUS MATProto_IP_Init( IN MAT_STRUCT *pMatCfg) { BOOLEAN status; status = IPMacTable_init(pMatCfg); return status; } static inline void IPintToIPstr(int ipint, char Ipstr[20], ULONG BufLen) { int temp = 0; temp = ipint & 0x000FF; snprintf(Ipstr, BufLen, "%d.", temp); temp = (ipint>>8) & 0x000FF; snprintf(Ipstr, BufLen, "%s%d.", Ipstr, temp); temp = (ipint>>16) & 0x000FF; snprintf(Ipstr, BufLen, "%s%d.", Ipstr, temp); temp = (ipint>>24) & 0x000FF; snprintf(Ipstr, BufLen, "%s%d", Ipstr, temp); } VOID getIPMacTbInfo( IN MAT_STRUCT *pMatCfg, IN char *pOutBuf, IN ULONG BufLen) { IPMacMappingTable *pIPMacTable; IPMacMappingEntry *pHead; int startIdx, endIdx; char Ipstr[20] = {0}; pIPMacTable = (IPMacMappingTable *)pMatCfg->MatTableSet.IPMacTable; if ((!pIPMacTable) || (!pIPMacTable->valid)) { DBGPRINT(RT_DEBUG_TRACE, ("%s():IPMacTable not init yet!\n", __FUNCTION__)); return; } /* dump all. */ startIdx = 0; endIdx = MAT_MAX_HASH_ENTRY_SUPPORT; sprintf(pOutBuf, "\n"); sprintf(pOutBuf+strlen(pOutBuf), "%-18s%-20s\n", "IP", "MAC"); for(; startIdx< endIdx; startIdx++) { pHead = pIPMacTable->hash[startIdx]; while(pHead) { /* if (strlen(pOutBuf) > (IW_PRIV_SIZE_MASK - 30)) */ if (RtmpOsCmdDisplayLenCheck(strlen(pOutBuf), 30) == FALSE) break; NdisZeroMemory(Ipstr, 20); IPintToIPstr(pHead->ipAddr, Ipstr, sizeof(Ipstr)); sprintf(pOutBuf+strlen(pOutBuf), "%-18s%02x:%02x:%02x:%02x:%02x:%02x\n", Ipstr, pHead->macAddr[0],pHead->macAddr[1],pHead->macAddr[2], pHead->macAddr[3],pHead->macAddr[4],pHead->macAddr[5]); pHead = pHead->pNext; } } } #endif /* MAT_SUPPORT */