Webserver+3d print
Diff: cyclone_tcp/ipv6/mld.c
- Revision:
- 0:8918a71cdbe9
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cyclone_tcp/ipv6/mld.c Sat Feb 04 18:15:49 2017 +0000 @@ -0,0 +1,591 @@ +/** + * @file mld.c + * @brief MLD (Multicast Listener Discovery for IPv6) + * + * @section License + * + * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. + * + * This file is part of CycloneTCP Open. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * @section Description + * + * MLD is used by an IPv6 router to discover the presence of multicast + * listeners on its directly attached links, and to discover specifically + * which multicast addresses are of interest to those neighboring nodes. + * Refer to the following RFCs for complete details: + * - RFC 2710: Multicast Listener Discovery (MLD) for IPv6 + * - RFC 3810: Multicast Listener Discovery Version 2 (MLDv2) for IPv6 + * + * @author Oryx Embedded SARL (www.oryx-embedded.com) + * @version 1.7.6 + **/ + +//Switch to the appropriate trace level +#define TRACE_LEVEL MLD_TRACE_LEVEL + +//Dependencies +#include "core/net.h" +#include "core/ip.h" +#include "ipv6/ipv6.h" +#include "ipv6/icmpv6.h" +#include "ipv6/mld.h" +#include "debug.h" + +//Check TCP/IP stack configuration +#if (IPV6_SUPPORT == ENABLED && MLD_SUPPORT == ENABLED) + +//Tick counter to handle periodic operations +systime_t mldTickCounter; + + +/** + * @brief MLD initialization + * @param[in] interface Underlying network interface + * @return Error code + **/ + +error_t mldInit(NetInterface *interface) +{ + //Successful initialization + return NO_ERROR; +} + + +/** + * @brief Start listening to the address on the interface + * @param[in] interface Underlying network interface + * @param[in] entry IPv6 filter entry identifying the address to listen to + * @return Error code + **/ + +error_t mldStartListening(NetInterface *interface, Ipv6FilterEntry *entry) +{ + //The link-scope all-nodes address (FF02::1) is handled as a special + //case. The host starts in Idle Listener state for that address on + //every interface and never transitions to another state + if(ipv6CompAddr(&entry->addr, &IPV6_LINK_LOCAL_ALL_NODES_ADDR)) + { + //Clear flag + entry->flag = FALSE; + //Enter the Idle Listener state + entry->state = MLD_STATE_IDLE_LISTENER; + } + else + { + //Link is up? + if(interface->linkState) + { + //Send a Multicast Listener Report message for the group on the interface + mldSendListenerReport(interface, &entry->addr); + + //Set flag + entry->flag = TRUE; + //Start timer + entry->timer = osGetSystemTime() + MLD_UNSOLICITED_REPORT_INTERVAL; + //Enter the Delaying Listener state + entry->state = MLD_STATE_DELAYING_LISTENER; + } + //Link is down? + else + { + //Clear flag + entry->flag = FALSE; + //Enter the Idle Listener state + entry->state = MLD_STATE_IDLE_LISTENER; + } + } + + //Successful processing + return NO_ERROR; +} + + +/** + * @brief Stop listening to the address on the interface + * @param[in] interface Underlying network interface + * @param[in] entry IPv6 filter entry identifying the multicast address to leave + * @return Error code + **/ + +error_t mldStopListening(NetInterface *interface, Ipv6FilterEntry *entry) +{ + //Check link state + if(interface->linkState) + { + //Send a Multicast Listener Done message if the flag is set + if(entry->flag) + mldSendListenerDone(interface, &entry->addr); + } + + //Switch to the Non-Listener state + entry->state = MLD_STATE_NON_LISTENER; + + //Successful processing + return NO_ERROR; +} + + +/** + * @brief MLD timer handler + * + * This routine must be periodically called by the TCP/IP stack to + * handle MLD related timers + * + * @param[in] interface Underlying network interface + **/ + +void mldTick(NetInterface *interface) +{ + uint_t i; + systime_t time; + Ipv6FilterEntry *entry; + + //Get current time + time = osGetSystemTime(); + + //Go through the multicast filter table + for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++) + { + //Point to the current entry + entry = &interface->ipv6Context.multicastFilter[i]; + + //Valid entry? + if(entry->refCount > 0) + { + //Delaying Listener state? + if(entry->state == MLD_STATE_DELAYING_LISTENER) + { + //Timer expired? + if(timeCompare(time, entry->timer) >= 0) + { + //Send a Multicast Listener Report message + mldSendListenerReport(interface, &entry->addr); + + //Set flag + entry->flag = TRUE; + //Switch to the Idle Listener state + entry->state = MLD_STATE_IDLE_LISTENER; + } + } + } + } +} + + +/** + * @brief Callback function for link change event + * @param[in] interface Underlying network interface + **/ + +void mldLinkChangeEvent(NetInterface *interface) +{ + uint_t i; + systime_t time; + Ipv6FilterEntry *entry; + + //Get current time + time = osGetSystemTime(); + + //Link up event? + if(interface->linkState) + { + //Go through the multicast filter table + for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++) + { + //Point to the current entry + entry = &interface->ipv6Context.multicastFilter[i]; + + //Valid entry? + if(entry->refCount > 0) + { + //The link-scope all-nodes address (FF02::1) is handled as a special + //case. The host starts in Idle Listener state for that address on + //every interface and never transitions to another state + if(!ipv6CompAddr(&entry->addr, &IPV6_LINK_LOCAL_ALL_NODES_ADDR)) + { + //Send an unsolicited Multicast Listener Report message for that group + mldSendListenerReport(interface, &entry->addr); + + //Set flag + entry->flag = TRUE; + //Start timer + entry->timer = time + MLD_UNSOLICITED_REPORT_INTERVAL; + //Enter the Delaying Listener state + entry->state = MLD_STATE_DELAYING_LISTENER; + } + } + } + } + //Link down event? + else + { + //Go through the multicast filter table + for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++) + { + //Point to the current entry + entry = &interface->ipv6Context.multicastFilter[i]; + + //Valid entry? + if(entry->refCount > 0) + { + //Clear flag + entry->flag = FALSE; + //Enter the Idle Listener state + entry->state = MLD_STATE_IDLE_LISTENER; + } + } + } +} + + +/** + * @brief Process incoming Multicast Listener Query message + * @param[in] interface Underlying network interface + * @param[in] pseudoHeader IPv6 pseudo header + * @param[in] buffer Multi-part buffer containing the incoming MLD message + * @param[in] offset Offset to the first byte of the MLD message + * @param[in] hopLimit Hop Limit field from IPv6 header + **/ + +void mldProcessListenerQuery(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, + const NetBuffer *buffer, size_t offset, uint8_t hopLimit) +{ + uint_t i; + size_t length; + systime_t time; + systime_t maxRespDelay; + MldMessage *message; + Ipv6FilterEntry *entry; + + //Retrieve the length of the MLD message + length = netBufferGetLength(buffer) - offset; + + //The message must be at least 24 octets long + if(length < sizeof(MldMessage)) + return; + + //Point to the beginning of the MLD message + message = netBufferAt(buffer, offset); + //Sanity check + if(message == NULL) + return; + + //Debug message + TRACE_INFO("MLD message received (%" PRIuSIZE " bytes)...\r\n", length); + //Dump message contents for debugging purpose + mldDumpMessage(message); + + //Make sure the source address of the message is a valid link-local address + if(!ipv6IsLinkLocalUnicastAddr(&pseudoHeader->srcAddr)) + return; + + //Check the Hop Limit field + if(hopLimit != MLD_HOP_LIMIT) + return; + + //Get current time + time = osGetSystemTime(); + + //The Max Resp Delay field specifies the maximum time allowed + //before sending a responding report + maxRespDelay = message->maxRespDelay * 10; + + //Go through the multicast filter table + for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++) + { + //Point to the current entry + entry = &interface->ipv6Context.multicastFilter[i]; + + //Valid entry? + if(entry->refCount > 0) + { + //The link-scope all-nodes address (FF02::1) is handled as a special + //case. The host starts in Idle Listener state for that address on + //every interface and never transitions to another state + if(!ipv6CompAddr(&entry->addr, &IPV6_LINK_LOCAL_ALL_NODES_ADDR)) + { + //A General Query is used to learn which multicast addresses have listeners + //on an attached link. A Multicast-Address-Specific Query is used to learn + //if a particular multicast address has any listeners on an attached link + if(ipv6CompAddr(&message->multicastAddr, &IPV6_UNSPECIFIED_ADDR) || + ipv6CompAddr(&message->multicastAddr, &entry->addr)) + { + //Delaying Listener state? + if(entry->state == MLD_STATE_DELAYING_LISTENER) + { + //The timer has not yet expired? + if(timeCompare(time, entry->timer) < 0) + { + //If a timer for the address is already running, it is reset to + //the new random value only if the requested Max Response Delay + //is less than the remaining value of the running timer + if(maxRespDelay < (entry->timer - time)) + { + //Restart delay timer + entry->timer = time + mldRand(maxRespDelay); + } + } + } + //Idle Listener state? + else if(entry->state == MLD_STATE_IDLE_LISTENER) + { + //Switch to the Delaying Listener state + entry->state = MLD_STATE_DELAYING_LISTENER; + //Delay the response by a random amount of time + entry->timer = time + mldRand(maxRespDelay); + } + } + } + } + } +} + + +/** + * @brief Process incoming Multicast Listener Report message + * @param[in] interface Underlying network interface + * @param[in] pseudoHeader IPv6 pseudo header + * @param[in] buffer Multi-part buffer containing the incoming MLD message + * @param[in] offset Offset to the first byte of the MLD message + * @param[in] hopLimit Hop Limit field from IPv6 header + **/ + +void mldProcessListenerReport(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader, + const NetBuffer *buffer, size_t offset, uint8_t hopLimit) +{ + uint_t i; + size_t length; + MldMessage *message; + Ipv6FilterEntry *entry; + + //Retrieve the length of the MLD message + length = netBufferGetLength(buffer) - offset; + + //The message must be at least 24 octets long + if(length < sizeof(MldMessage)) + return; + + //Point to the beginning of the MLD message + message = netBufferAt(buffer, offset); + //Sanity check + if(message == NULL) + return; + + //Debug message + TRACE_INFO("MLD message received (%" PRIuSIZE " bytes)...\r\n", length); + //Dump message contents for debugging purpose + mldDumpMessage(message); + + //Make sure the source address of the message is a valid link-local address + if(!ipv6IsLinkLocalUnicastAddr(&pseudoHeader->srcAddr)) + return; + //Check the Hop Limit field + if(hopLimit != MLD_HOP_LIMIT) + return; + + //Go through the multicast filter table + for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++) + { + //Point to the current entry + entry = &interface->ipv6Context.multicastFilter[i]; + + //Valid entry? + if(entry->refCount > 0) + { + //Report messages are ignored for multicast addresses + //in the Non-Listener or Idle Listener state + if(entry->state == MLD_STATE_DELAYING_LISTENER) + { + //The Multicast Listener Report message matches the current entry? + if(ipv6CompAddr(&message->multicastAddr, &entry->addr)) + { + //Clear flag + entry->flag = FALSE; + //Switch to the Idle Listener state + entry->state = MLD_STATE_IDLE_LISTENER; + } + } + } + } +} + + +/** + * @brief Send Multicast Listener Report message + * @param[in] interface Underlying network interface + * @param[in] ipAddr IPv6 address specifying the multicast address + * @return Error code + **/ + +error_t mldSendListenerReport(NetInterface *interface, Ipv6Addr *ipAddr) +{ + error_t error; + size_t offset; + MldMessage *message; + NetBuffer *buffer; + Ipv6PseudoHeader pseudoHeader; + + //Make sure the specified address is a valid multicast address + if(!ipv6IsMulticastAddr(ipAddr)) + return ERROR_INVALID_ADDRESS; + + //The link-scope all-nodes address (FF02::1) is handled as a special + //case. The host never sends a report for that address + if(ipv6CompAddr(ipAddr, &IPV6_LINK_LOCAL_ALL_NODES_ADDR)) + return ERROR_INVALID_ADDRESS; + + //Allocate a memory buffer to hold a MLD message + buffer = ipAllocBuffer(sizeof(MldMessage), &offset); + //Failed to allocate memory? + if(buffer == NULL) + return ERROR_OUT_OF_MEMORY; + + //Point to the beginning of the MLD message + message = netBufferAt(buffer, offset); + + //Format the Multicast Listener Report message + message->type = ICMPV6_TYPE_MULTICAST_LISTENER_REPORT_V1; + message->code = 0; + message->checksum = 0; + message->maxRespDelay = 0; + message->reserved = 0; + message->multicastAddr = *ipAddr; + + //Format IPv6 pseudo header + pseudoHeader.srcAddr = interface->ipv6Context.addrList[0].addr; + pseudoHeader.destAddr = *ipAddr; + pseudoHeader.length = HTONS(sizeof(MldMessage)); + pseudoHeader.reserved = 0; + pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER; + + //Message checksum calculation + message->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader, + sizeof(Ipv6PseudoHeader), buffer, offset, sizeof(MldMessage)); + + //Debug message + TRACE_INFO("Sending MLD message (%" PRIuSIZE " bytes)...\r\n", sizeof(MldMessage)); + //Dump message contents for debugging purpose + mldDumpMessage(message); + + //The Multicast Listener Report message is sent to the multicast address being reported + error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, MLD_HOP_LIMIT); + + //Free previously allocated memory + netBufferFree(buffer); + //Return status code + return error; +} + + +/** + * @brief Send Multicast Listener Done message + * @param[in] interface Underlying network interface + * @param[in] ipAddr IPv6 address specifying the multicast address being left + * @return Error code + **/ + +error_t mldSendListenerDone(NetInterface *interface, Ipv6Addr *ipAddr) +{ + error_t error; + size_t offset; + MldMessage *message; + NetBuffer *buffer; + Ipv6PseudoHeader pseudoHeader; + + //Make sure the specified address is a valid multicast address + if(!ipv6IsMulticastAddr(ipAddr)) + return ERROR_INVALID_ADDRESS; + + //The link-scope all-nodes address (FF02::1) is handled as a special + //case. The host never sends a report for that address + if(ipv6CompAddr(ipAddr, &IPV6_LINK_LOCAL_ALL_NODES_ADDR)) + return ERROR_INVALID_ADDRESS; + + //Allocate a memory buffer to hold a MLD message + buffer = ipAllocBuffer(sizeof(MldMessage), &offset); + //Failed to allocate memory? + if(buffer == NULL) + return ERROR_OUT_OF_MEMORY; + + //Point to the beginning of the MLD message + message = netBufferAt(buffer, offset); + + //Format the Multicast Listener Done message + message->type = ICMPV6_TYPE_MULTICAST_LISTENER_DONE_V1; + message->code = 0; + message->checksum = 0; + message->maxRespDelay = 0; + message->reserved = 0; + message->multicastAddr = *ipAddr; + + //Format IPv6 pseudo header + pseudoHeader.srcAddr = interface->ipv6Context.addrList[0].addr; + pseudoHeader.destAddr = IPV6_LINK_LOCAL_ALL_ROUTERS_ADDR; + pseudoHeader.length = HTONS(sizeof(MldMessage)); + pseudoHeader.reserved = 0; + pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER; + + //Message checksum calculation + message->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader, + sizeof(Ipv6PseudoHeader), buffer, offset, sizeof(MldMessage)); + + //Debug message + TRACE_INFO("Sending MLD message (%" PRIuSIZE " bytes)...\r\n", sizeof(MldMessage)); + //Dump message contents for debugging purpose + mldDumpMessage(message); + + //The Multicast Listener Done message is sent to the all-routers multicast address + error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, MLD_HOP_LIMIT); + + //Free previously allocated memory + netBufferFree(buffer); + //Return status code + return error; +} + + +/** + * @brief Get a random value in the specified range + * @param[in] max Upper bound + * @return Random value in the specified range + **/ + +uint32_t mldRand(uint32_t max) +{ + //Return a random value in the given range + return netGetRand() % (max + 1); +} + + +/** + * @brief Dump MLD message for debugging purpose + * @param[in] message Pointer to the MLD message + **/ + +void mldDumpMessage(const MldMessage *message) +{ + //Dump MLD message + TRACE_DEBUG(" Type = %" PRIu8 "\r\n", message->type); + TRACE_DEBUG(" Code = %" PRIu8 "\r\n", message->code); + TRACE_DEBUG(" Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum)); + TRACE_DEBUG(" Max Resp Delay = %" PRIu16 "\r\n", message->maxRespDelay); + TRACE_DEBUG(" Multicast Address = %s\r\n", ipv6AddrToString(&message->multicastAddr, NULL)); +} + +#endif +