static uint32_t prvParseDNSReply( uint8_t *pucUDPPayloadBuffer, TickType_t xIdentifier ) { DNSMessage_t *pxDNSMessageHeader; uint32_t ulIPAddress = 0UL; #if( ipconfigUSE_LLMNR == 1 ) char *pcRequestedName = NULL; #endif uint8_t *pucByte; uint16_t x, usDataLength, usQuestions; #if( ipconfigUSE_LLMNR == 1 ) uint16_t usType = 0, usClass = 0; #endif #if( ipconfigUSE_DNS_CACHE == 1 ) char pcName[128] = ""; /*_RB_ What is the significance of 128? Probably too big to go on the stack for a small MCU but don't know how else it could be made re-entrant. Might be necessary. */ #endif pxDNSMessageHeader = ( DNSMessage_t * ) pucUDPPayloadBuffer; if( pxDNSMessageHeader->usIdentifier == ( uint16_t ) xIdentifier ) { /* Start at the first byte after the header. */ pucByte = pucUDPPayloadBuffer + sizeof( DNSMessage_t ); /* Skip any question records. */ usQuestions = FreeRTOS_ntohs( pxDNSMessageHeader->usQuestions ); for( x = 0; x < usQuestions; x++ ) { #if( ipconfigUSE_LLMNR == 1 ) { if( x == 0 ) { pcRequestedName = ( char * ) pucByte; } } #endif #if( ipconfigUSE_DNS_CACHE == 1 ) if( x == 0 ) { pucByte = prvReadNameField( pucByte, pcName, sizeof( pcName ) ); } else #endif /* ipconfigUSE_DNS_CACHE */ { /* Skip the variable length pcName field. */ pucByte = prvSkipNameField( pucByte ); } #if( ipconfigUSE_LLMNR == 1 ) { /* usChar2u16 returns value in host endianness. */ usType = usChar2u16( pucByte ); usClass = usChar2u16( pucByte + 2 ); } #endif /* ipconfigUSE_LLMNR */ /* Skip the type and class fields. */ pucByte += sizeof( uint32_t ); } /* Search through the answers records. */ pxDNSMessageHeader->usAnswers = FreeRTOS_ntohs( pxDNSMessageHeader->usAnswers ); if( ( pxDNSMessageHeader->usFlags & dnsRX_FLAGS_MASK ) == dnsEXPECTED_RX_FLAGS ) { for( x = 0; x < pxDNSMessageHeader->usAnswers; x++ ) { pucByte = prvSkipNameField( pucByte ); /* Is the type field that of an A record? */ if( usChar2u16( pucByte ) == dnsTYPE_A_HOST ) { /* This is the required record. Skip the type, class, and time to live fields, plus the first byte of the data length. */ pucByte += ( sizeof( uint32_t ) + sizeof( uint32_t ) + sizeof( uint8_t ) ); /* Sanity check the data length. */ if( ( size_t ) *pucByte == sizeof( uint32_t ) ) { /* Skip the second byte of the length. */ pucByte++; /* Copy the IP address out of the record. */ memcpy( ( void * ) &ulIPAddress, ( void * ) pucByte, sizeof( uint32_t ) ); #if( ipconfigUSE_DNS_CACHE == 1 ) { prvProcessDNSCache( pcName, &ulIPAddress, pdFALSE ); } #endif /* ipconfigUSE_DNS_CACHE */ #if( ipconfigDNS_USE_CALLBACKS != 0 ) { /* See if any asynchronous call was made to FreeRTOS_gethostbyname_a() */ vDNSDoCallback( ( TickType_t ) pxDNSMessageHeader->usIdentifier, pcName, ulIPAddress ); } #endif /* ipconfigDNS_USE_CALLBACKS != 0 */ } break; } else { /* Skip the type, class and time to live fields. */ pucByte += ( sizeof( uint32_t ) + sizeof( uint32_t ) ); /* Determine the length of the data in the field. */ memcpy( ( void * ) &usDataLength, ( void * ) pucByte, sizeof( uint16_t ) ); usDataLength = FreeRTOS_ntohs( usDataLength ); /* Jump over the data length bytes, and the data itself. */ pucByte += usDataLength + sizeof( uint16_t ); } } } #if( ipconfigUSE_LLMNR == 1 ) else if( ( usQuestions != ( uint16_t )0u ) && ( usType == ( uint16_t )dnsTYPE_A_HOST ) && ( usClass == ( uint16_t )dnsCLASS_IN ) ) { /* If this is not a reply to our DNS request, it might an LLMNR request. */ if( xApplicationDNSQueryHook ( ( pcRequestedName + 1 ) ) ) { int16_t usLength; NetworkBufferDescriptor_t *pxNewBuffer = NULL; NetworkBufferDescriptor_t *pxNetworkBuffer = pxUDPPayloadBuffer_to_NetworkBuffer( pucUDPPayloadBuffer ); LLMNRAnswer_t *pxAnswer; if( ( xBufferAllocFixedSize == pdFALSE ) && ( pxNetworkBuffer != NULL ) ) { BaseType_t xDataLength = pxNetworkBuffer->xDataLength + sizeof( UDPHeader_t ) + sizeof( EthernetHeader_t ) + sizeof( IPHeader_t ); /* The field xDataLength was set to the length of the UDP payload. The answer (reply) will be longer than the request, so the packet must be duplicaed into a bigger buffer */ pxNetworkBuffer->xDataLength = xDataLength; pxNewBuffer = pxDuplicateNetworkBufferWithDescriptor( pxNetworkBuffer, xDataLength + 16 ); if( pxNewBuffer != NULL ) { BaseType_t xOffset1, xOffset2; xOffset1 = ( BaseType_t ) ( pucByte - pucUDPPayloadBuffer ); xOffset2 = ( BaseType_t ) ( ( ( uint8_t * ) pcRequestedName ) - pucUDPPayloadBuffer ); pxNetworkBuffer = pxNewBuffer; pucUDPPayloadBuffer = pxNetworkBuffer->pucEthernetBuffer + ipUDP_PAYLOAD_OFFSET_IPv4; pucByte = pucUDPPayloadBuffer + xOffset1; pcRequestedName = ( char * ) ( pucUDPPayloadBuffer + xOffset2 ); pxDNSMessageHeader = ( DNSMessage_t * ) pucUDPPayloadBuffer; } else { /* Just to indicate that the message may not be answered. */ pxNetworkBuffer = NULL; } } if( pxNetworkBuffer != NULL ) { pxAnswer = (LLMNRAnswer_t *)pucByte; /* Leave 'usIdentifier' and 'usQuestions' untouched. */ vSetField16( pxDNSMessageHeader, DNSMessage_t, usFlags, dnsLLMNR_FLAGS_IS_REPONSE ); /* Set the response flag */ vSetField16( pxDNSMessageHeader, DNSMessage_t, usAnswers, 1 ); /* Provide a single answer */ vSetField16( pxDNSMessageHeader, DNSMessage_t, usAuthorityRRs, 0 ); /* No authority */ vSetField16( pxDNSMessageHeader, DNSMessage_t, usAdditionalRRs, 0 ); /* No additional info */ pxAnswer->ucNameCode = dnsNAME_IS_OFFSET; pxAnswer->ucNameOffset = ( uint8_t )( pcRequestedName - ( char * ) pucUDPPayloadBuffer ); vSetField16( pxAnswer, LLMNRAnswer_t, usType, dnsTYPE_A_HOST ); /* Type A: host */ vSetField16( pxAnswer, LLMNRAnswer_t, usClass, dnsCLASS_IN ); /* 1: Class IN */ vSetField32( pxAnswer, LLMNRAnswer_t, ulTTL, dnsLLMNR_TTL_VALUE ); vSetField16( pxAnswer, LLMNRAnswer_t, usDataLength, 4 ); vSetField32( pxAnswer, LLMNRAnswer_t, ulIPAddress, FreeRTOS_ntohl( *ipLOCAL_IP_ADDRESS_POINTER ) ); usLength = ( int16_t ) ( sizeof( *pxAnswer ) + ( size_t ) ( pucByte - pucUDPPayloadBuffer ) ); prvReplyDNSMessage( pxNetworkBuffer, usLength ); if( pxNewBuffer != NULL ) { vReleaseNetworkBufferAndDescriptor( pxNewBuffer ); } } } } #endif /* ipconfigUSE_LLMNR == 1 */ } return ulIPAddress; }
static void prvTreatNBNS( uint8_t *pucUDPPayloadBuffer, uint32_t ulIPAddress ) { uint16_t usFlags, usType, usClass; uint8_t *pucSource, *pucTarget; uint8_t ucByte; uint8_t ucNBNSName[ 17 ]; usFlags = usChar2u16( pucUDPPayloadBuffer + offsetof( NBNSRequest_t, usFlags ) ); if( ( usFlags & dnsNBNS_FLAGS_OPCODE_MASK ) == dnsNBNS_FLAGS_OPCODE_QUERY ) { usType = usChar2u16( pucUDPPayloadBuffer + offsetof( NBNSRequest_t, usType ) ); usClass = usChar2u16( pucUDPPayloadBuffer + offsetof( NBNSRequest_t, usClass ) ); /* Not used for now */ ( void )usClass; /* For NBNS a name is 16 bytes long, written with capitals only. Make sure that the copy is terminated with a zero. */ pucTarget = ucNBNSName + sizeof(ucNBNSName ) - 2; pucTarget[ 1 ] = '\0'; /* Start with decoding the last 2 bytes. */ pucSource = pucUDPPayloadBuffer + ( offsetof( NBNSRequest_t, ucName ) + ( dnsNBNS_ENCODED_NAME_LENGTH - 2 ) ); for( ;; ) { ucByte = ( uint8_t ) ( ( ( pucSource[ 0 ] - 0x41 ) << 4 ) | ( pucSource[ 1 ] - 0x41 ) ); /* Make sure there are no trailing spaces in the name. */ if( ( ucByte == ' ' ) && ( pucTarget[ 1 ] == '\0' ) ) { ucByte = '\0'; } *pucTarget = ucByte; if( pucTarget == ucNBNSName ) { break; } pucTarget -= 1; pucSource -= 2; } #if( ipconfigUSE_DNS_CACHE == 1 ) { if( ( usFlags & dnsNBNS_FLAGS_RESPONSE ) != 0 ) { /* If this is a response from another device, add the name to the DNS cache */ prvProcessDNSCache( ( char * ) ucNBNSName, &ulIPAddress, pdFALSE ); } } #else { /* Avoid compiler warnings. */ ( void ) ulIPAddress; } #endif /* ipconfigUSE_DNS_CACHE */ if( ( ( usFlags & dnsNBNS_FLAGS_RESPONSE ) == 0 ) && ( usType == dnsNBNS_TYPE_NET_BIOS ) && ( xApplicationDNSQueryHook( ( const char * ) ucNBNSName ) != pdFALSE ) ) { uint16_t usLength; DNSMessage_t *pxMessage; NBNSAnswer_t *pxAnswer; /* Someone is looking for a device with ucNBNSName, prepare a positive reply. */ NetworkBufferDescriptor_t *pxNetworkBuffer = pxUDPPayloadBuffer_to_NetworkBuffer( pucUDPPayloadBuffer ); if( ( xBufferAllocFixedSize == pdFALSE ) && ( pxNetworkBuffer != NULL ) ) { NetworkBufferDescriptor_t *pxNewBuffer; BaseType_t xDataLength = pxNetworkBuffer->xDataLength + sizeof( UDPHeader_t ) + sizeof( EthernetHeader_t ) + sizeof( IPHeader_t ); /* The field xDataLength was set to the length of the UDP payload. The answer (reply) will be longer than the request, so the packet must be duplicated into a bigger buffer */ pxNetworkBuffer->xDataLength = xDataLength; pxNewBuffer = pxDuplicateNetworkBufferWithDescriptor( pxNetworkBuffer, xDataLength + 16 ); if( pxNewBuffer != NULL ) { pucUDPPayloadBuffer = pxNewBuffer->pucEthernetBuffer + sizeof( UDPPacket_t ); pxNetworkBuffer = pxNewBuffer; } else { /* Just prevent that a reply will be sent */ pxNetworkBuffer = NULL; } } /* Should not occur: pucUDPPayloadBuffer is part of a xNetworkBufferDescriptor */ if( pxNetworkBuffer != NULL ) { pxMessage = (DNSMessage_t *)pucUDPPayloadBuffer; /* As the fields in the structures are not word-aligned, we have to copy the values byte-by-byte using macro's vSetField16() and vSetField32() */ vSetField16( pxMessage, DNSMessage_t, usFlags, dnsNBNS_QUERY_RESPONSE_FLAGS ); /* 0x8500 */ vSetField16( pxMessage, DNSMessage_t, usQuestions, 0 ); vSetField16( pxMessage, DNSMessage_t, usAnswers, 1 ); vSetField16( pxMessage, DNSMessage_t, usAuthorityRRs, 0 ); vSetField16( pxMessage, DNSMessage_t, usAdditionalRRs, 0 ); pxAnswer = (NBNSAnswer_t *)( pucUDPPayloadBuffer + offsetof( NBNSRequest_t, usType ) ); vSetField16( pxAnswer, NBNSAnswer_t, usType, usType ); /* Type */ vSetField16( pxAnswer, NBNSAnswer_t, usClass, dnsNBNS_CLASS_IN ); /* Class */ vSetField32( pxAnswer, NBNSAnswer_t, ulTTL, dnsNBNS_TTL_VALUE ); vSetField16( pxAnswer, NBNSAnswer_t, usDataLength, 6 ); /* 6 bytes including the length field */ vSetField16( pxAnswer, NBNSAnswer_t, usNbFlags, dnsNBNS_NAME_FLAGS ); vSetField32( pxAnswer, NBNSAnswer_t, ulIPAddress, FreeRTOS_ntohl( *ipLOCAL_IP_ADDRESS_POINTER ) ); usLength = ( uint16_t ) ( offsetof( NBNSRequest_t, usType ) + sizeof( NBNSAnswer_t ) ); prvReplyDNSMessage( pxNetworkBuffer, usLength ); } } } }
XStatus emacps_send_message(xemacpsif_s *xemacpsif, NetworkBufferDescriptor_t *pxFirstBuffer, int iReleaseAfterSend ) { NetworkBufferDescriptor_t *pxBuffer; int head = xemacpsif->txHead; int tail = xemacpsif->txTail; int iHasSent = 0; uint32_t ulBaseAddress = xemacpsif->emacps.Config.BaseAddress; #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) if( iReleaseAfterSend == pdFALSE ) { NetworkBufferDescriptor_t *pxNewBuffer; pxNewBuffer = pxDuplicateNetworkBufferWithDescriptor( pxFirstBuffer, pxFirstBuffer->xDataLength ); if( pxNewBuffer ) { pxFirstBuffer = pxNewBuffer; iReleaseAfterSend = pdTRUE; } } #endif /* Although it looks like more that 1 message can be sent, this possibility is NOT being used and not yet tested. */ for( pxBuffer = pxFirstBuffer; pxBuffer != NULL; ) { NetworkBufferDescriptor_t *pxNextBuffer; if( ( head == tail ) && ( xemacpsif->txSegments[ head ].flags & XEMACPS_TXBUF_USED_MASK ) == 0 ) { // FreeRTOS_printf( ( "emacps_send_message: All TX buffers full\n" ) ); break; } if( xValidLength( pxBuffer->xDataLength ) ) { /* Just a sanity check, the TX buffer will be assigned permanently. */ #if( ipconfigZERO_COPY_TX_DRIVER == 0 ) if( pxDMA_tx_buffers[ head ] != NULL ) #endif { uint32_t flags = 0; #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) /* Move the message to unbuffered space in RAM. */ pxDMA_tx_buffers[ head ] = ( uint32_t )pxBuffer->pucEthernetBuffer; if( ucIsCachedMemory( pxBuffer->pucEthernetBuffer ) != 0 ) { Xil_DCacheFlushRange( ( unsigned )pxBuffer->pucEthernetBuffer, pxBuffer->xDataLength ); } #else /* Copy the message to unbuffered space in RAM. */ memcpy( pxDMA_tx_buffers[ head ], pxBuffer->pucEthernetBuffer, pxBuffer->xDataLength ); #endif /* Packets will be sent one-by-one, so for each packet the TXBUF_LAST bit will be set. */ flags |= XEMACPS_TXBUF_LAST_MASK; flags |= ( pxBuffer->xDataLength & XEMACPS_TXBUF_LEN_MASK ); if( head == ( ipconfigNIC_N_TX_DESC - 1 ) ) { flags |= XEMACPS_TXBUF_WRAP_MASK; } /* Copy the address of the buffer and set the flags. */ xemacpsif->txSegments[ head ].address = ( uint32_t )pxDMA_tx_buffers[ head ]; xemacpsif->txSegments[ head ].flags = flags; iHasSent = pdTRUE; } if( ++head == ipconfigNIC_N_TX_DESC ) { head = 0; } } /* Make a copy of the next field, before pxBuffer gets deleted */ pxNextBuffer = pxBuffer->pxNextBuffer; #if( ipconfigZERO_COPY_TX_DRIVER == 0 ) if( iReleaseAfterSend != pdFALSE ) { vReleaseNetworkBufferAndDescriptor( pxBuffer ); } #endif pxBuffer = pxNextBuffer; } /* Update the TX-head index. These variable are declared volatile so they will be accessed as little as possible. */ xemacpsif->txHead = head; /* Data Synchronization Barrier */ dsb(); if( iHasSent != pdFALSE ) { /* Make STARTTX high */ uint32_t ulValue = XEmacPs_ReadReg( ulBaseAddress, XEMACPS_NWCTRL_OFFSET); /* Start transmit */ xemacpsif->txBusy = pdTRUE; XEmacPs_WriteReg( ulBaseAddress, XEMACPS_NWCTRL_OFFSET, ( ulValue | XEMACPS_NWCTRL_STARTTX_MASK ) ); } dsb(); return 0; }