void udpEchoTask(void *param) { error_t error; uint_t length; uint16_t port; IpAddr ipAddr; EchoServiceContext *context; //Get a pointer to the context context = (EchoServiceContext *) param; //Main loop while(1) { //Wait for an incoming datagram error = socketReceiveFrom(context->socket, &ipAddr, &port, context->buffer, ECHO_BUFFER_SIZE, &length, 0); //Any datagram received? if(!error) { //Debug message TRACE_INFO("Echo service: %u bytes received from %s port %u\r\n", length, ipAddrToString(&ipAddr, NULL), port); //Send the data back to the source error = socketSendTo(context->socket, &ipAddr, port, context->buffer, length, NULL, 0); } } }
void udpChargenTask(void *param) { error_t error; size_t i; size_t k; size_t n; size_t length; uint16_t port; IpAddr ipAddr; ChargenServiceContext *context; //Get a pointer to the context context = (ChargenServiceContext *) param; //Main loop while(1) { //Wait for an incoming datagram error = socketReceiveFrom(context->socket, &ipAddr, &port, context->buffer, CHARGEN_BUFFER_SIZE, &n, 0); //Any datagram received? if(!error) { //When a datagram is received, an answering datagram is sent //containing a random number (between 0 and 512) of characters length = tcpIpStackGetRand() % 513; //Reset line counter n = 0; //Format output data for(i = 0; i < length; i += 74) { //Calculate the length of the current line k = min(length - i, 74); //Copy character pattern memcpy(context->buffer + i, pattern + n, k); //End each line with carriage return and line feed if(k == 74) { context->buffer[i + 72] = '\r'; context->buffer[i + 73] = '\n'; } //Increment line counter if(++n >= 95) n = 0; } //Send data to the remote host error = socketSendTo(context->socket, &ipAddr, port, context->buffer, length, &n, 0); //Debug message TRACE_INFO("Chargen service: %" PRIuSIZE " bytes sent to %s port %" PRIu16 "\r\n", n, ipAddrToString(&ipAddr, NULL), port); } } }
void udpDiscardTask(void *param) { error_t error; size_t length; uint16_t port; IpAddr ipAddr; DiscardServiceContext *context; //Get a pointer to the context context = (DiscardServiceContext *) param; //Main loop while(1) { //Wait for an incoming datagram error = socketReceiveFrom(context->socket, &ipAddr, &port, context->buffer, DISCARD_BUFFER_SIZE, &length, 0); //Any datagram received? if(!error) { //Debug message TRACE_INFO("Discard service: %" PRIuSIZE " bytes received from %s port %" PRIu16 "\r\n", length, ipAddrToString(&ipAddr, NULL), port); //Throw away any received datagram... } } }
int_t recvfrom(int_t s, void *data, int_t size, int_t flags, sockaddr *addr, int_t *addrlen) { error_t error; size_t received; uint16_t port; IpAddr ipAddr; Socket *socket; //Make sure the socket descriptor is valid if(s < 0 || s >= SOCKET_MAX_COUNT) { socketError(NULL, ERROR_INVALID_SOCKET); return SOCKET_ERROR; } //Point to the socket structure socket = &socketTable[s]; //Receive data error = socketReceiveFrom(socket, &ipAddr, &port, data, size, &received, flags << 8); //Any error to report? if(error) { socketError(socket, error); return SOCKET_ERROR; } //The address is optional if(addr != NULL && addrlen != NULL) { #if (IPV4_SUPPORT == ENABLED) //IPv4 address? if(ipAddr.length == sizeof(Ipv4Addr) && *addrlen >= sizeof(sockaddr_in)) { //Point to the IPv4 address information sockaddr_in *sa = (sockaddr_in *) addr; //Set address family and port number sa->sin_family = AF_INET; sa->sin_port = htons(port); //Copy IPv4 address sa->sin_addr.s_addr = ipAddr.ipv4Addr; //Return the actual length of the address *addrlen = sizeof(sockaddr_in); } else #endif #if (IPV6_SUPPORT == ENABLED) //IPv6 address? if(ipAddr.length == sizeof(Ipv6Addr) && *addrlen >= sizeof(sockaddr_in6)) { //Point to the IPv6 address information sockaddr_in6 *sa = (sockaddr_in6 *) addr; //Set address family and port number sa->sin6_family = AF_INET6; sa->sin6_port = htons(port); //Copy IPv6 address ipv6CopyAddr(sa->sin6_addr.s6_addr, &ipAddr.ipv6Addr); //Return the actual length of the address *addrlen = sizeof(sockaddr_in6); } else #endif //Invalid address? { //Report an error socketError(socket, ERROR_INVALID_PARAMETER); return SOCKET_ERROR; } } //Return the number of bytes received return received; }
error_t dhcpv6ForwardRelayReplyMessage(Dhcpv6RelayCtx *context) { error_t error; uint_t i; uint32_t interfaceId; size_t inputMessageLen; size_t outputMessageLen; Dhcpv6RelayMessage *inputMessage; Dhcpv6Message *outputMessage; Dhcpv6Option *option; IpAddr ipAddr; uint16_t port; //Point to the buffer where to store the incoming DHCPv6 message inputMessage = (Dhcpv6RelayMessage *) context->buffer; //Read incoming message error = socketReceiveFrom(context->serverSocket, &ipAddr, &port, inputMessage, DHCPV6_MAX_MSG_SIZE, &inputMessageLen, 0); //Any error to report? if(error) return error; //Debug message TRACE_INFO("\r\nDHCPv6 message received on network-facing interface %s (%" PRIuSIZE " bytes)...\r\n", context->serverInterface->name, inputMessageLen); //Dump the contents of the message for debugging purpose dhcpv6DumpMessage(inputMessage, inputMessageLen); //Check the length of the DHCPv6 message if(inputMessageLen < sizeof(Dhcpv6RelayMessage)) return ERROR_INVALID_MESSAGE; //Inspect the message type and only forward Relay-Reply messages. //Other DHCPv6 message types must be silently discarded if(inputMessage->msgType != DHCPV6_MSG_TYPE_RELAY_REPL) return ERROR_INVALID_MESSAGE; //Get the length of the Options field inputMessageLen -= sizeof(Dhcpv6Message); //Check whether an Interface ID option is included in the Relay-Reply option = dhcpv6GetOption(inputMessage->options, inputMessageLen, DHCPV6_OPTION_INTERFACE_ID); //Failed to retrieve specified option? if(!option || ntohs(option->length) != sizeof(interfaceId)) return ERROR_INVALID_MESSAGE; //Read the Interface ID option contents memcpy(&interfaceId, option->value, sizeof(interfaceId)); //Convert the 32-bit integer from network byte order interfaceId = ntohl(interfaceId); //The Relay-Reply message must include a Relay Message option option = dhcpv6GetOption(inputMessage->options, inputMessageLen, DHCPV6_OPTION_RELAY_MSG); //Failed to retrieve specified option? if(!option || ntohs(option->length) < sizeof(Dhcpv6Message)) return ERROR_INVALID_MESSAGE; //Extract the message from the Relay Message option outputMessage = (Dhcpv6Message *) option->value; //Save the length of the message outputMessageLen = ntohs(option->length); //Loop through client-facing interfaces for(i = 0; i < context->clientInterfaceCount; i++) { //Check whether the current interface matches the Interface ID option if(context->clientInterface[i]->id == interfaceId) { //Debug message TRACE_INFO("Forwarding DHCPv6 message on client-facing interface %s (%" PRIuSIZE " bytes)...\r\n", context->clientInterface[i]->name, outputMessageLen); //Dump the contents of the message for debugging purpose dhcpv6DumpMessage(outputMessage, outputMessageLen); //Copy the peer-address into the destination IP address ipAddr.length = sizeof(Ipv6Addr); ipAddr.ipv6Addr = inputMessage->peerAddress; //Select the relevant port number to use if(outputMessage->msgType == DHCPV6_MSG_TYPE_RELAY_REPL) port = DHCPV6_SERVER_PORT; else port = DHCPV6_CLIENT_PORT; //Relay the DHCPv6 message to the client on the link //identified by the Interface ID option return socketSendTo(context->clientSocket[i], &ipAddr, port, outputMessage, outputMessageLen, NULL, 0); } } //Unknown interface identifier... return ERROR_INVALID_OPTION; }
error_t dhcpv6ForwardClientMessage(Dhcpv6RelayCtx *context, uint_t index) { error_t error; uint32_t interfaceId; size_t inputMessageLen; size_t outputMessageLen; Dhcpv6RelayMessage *inputMessage; Dhcpv6RelayMessage *outputMessage; Dhcpv6Option *option; IpAddr ipAddr; //Point to the buffer where to store the incoming DHCPv6 message inputMessage = (Dhcpv6RelayMessage *) (context->buffer + DHCPV6_RELAY_FORW_OVERHEAD); //Message that will be forwarded by the DHCPv6 relay agent outputMessage = (Dhcpv6RelayMessage *) context->buffer; //Read incoming message error = socketReceiveFrom(context->clientSocket[index], &ipAddr, NULL, inputMessage, DHCPV6_MAX_MSG_SIZE - DHCPV6_RELAY_FORW_OVERHEAD, &inputMessageLen, 0); //Any error to report? if(error) return error; //Debug message TRACE_INFO("\r\nDHCPv6 message received on client-facing interface %s (%" PRIuSIZE " bytes)...\r\n", context->clientInterface[index]->name, inputMessageLen); //Dump the contents of the message for debugging purpose dhcpv6DumpMessage(inputMessage, inputMessageLen); //The source address must be a valid IPv6 address if(ipAddr.length != sizeof(Ipv6Addr)) return ERROR_INVALID_ADDRESS; //Check the length of the DHCPv6 message if(inputMessageLen < sizeof(Dhcpv6Message)) return ERROR_INVALID_MESSAGE; //When the relay agent receives a valid message to be relayed, //it constructs a new Relay-Forward message outputMessage->msgType = DHCPV6_MSG_TYPE_RELAY_FORW; //Inspect the message type switch(inputMessage->msgType) { //Message received from a client? case DHCPV6_MSG_TYPE_SOLICIT: case DHCPV6_MSG_TYPE_REQUEST: case DHCPV6_MSG_TYPE_CONFIRM: case DHCPV6_MSG_TYPE_RENEW: case DHCPV6_MSG_TYPE_REBIND: case DHCPV6_MSG_TYPE_RELEASE: case DHCPV6_MSG_TYPE_DECLINE: case DHCPV6_MSG_TYPE_INFO_REQUEST: //If the relay agent received the message to be relayed from a client //the hop-count in the Relay-Forward message is set to 0 outputMessage->hopCount = 0; //Continue processing break; //Message received from another relay agent? case DHCPV6_MSG_TYPE_RELAY_FORW: //If the message received by the relay agent is a Relay-Forward message //and the hop-count in the message is greater than or equal to 32, the //relay agent discards the received message if(inputMessage->hopCount >= DHCPV6_HOP_COUNT_LIMIT) return ERROR_INVALID_MESSAGE; //Set the hop-count field to the value of the hop-count field in //the received message incremented by 1 outputMessage->hopCount = inputMessage->hopCount + 1; //Continue processing break; //Message received from a server? default: //Discard ADVERTISE, REPLY, RECONFIGURE and RELAY-REPL messages return ERROR_INVALID_MESSAGE; } //Set the link-address field to the unspecified address outputMessage->linkAddress = IPV6_UNSPECIFIED_ADDR; //Copy the source address from the header of the IP datagram in //which the message was received to the peer-address field outputMessage->peerAddress = ipAddr.ipv6Addr; //Size of the Relay-Forward message outputMessageLen = sizeof(Dhcpv6RelayMessage); //Get the interface identifier interfaceId = context->clientInterface[index]->id; //Convert the 32-bit integer to network byte order interfaceId = htonl(interfaceId); //If the relay agent cannot use the address in the link-address field //to identify the interface through which the response to the client //will be relayed, the relay agent must include an Interface ID option dhcpv6AddOption(outputMessage, &outputMessageLen, DHCPV6_OPTION_INTERFACE_ID, &interfaceId, sizeof(interfaceId)); //Copy the received DHCPv6 message into a Relay Message option option = dhcpv6AddOption(outputMessage, &outputMessageLen, DHCPV6_OPTION_RELAY_MSG, NULL, 0); //Set the appropriate length of the option option->length = htons(inputMessageLen); //Adjust the length of the Relay-Forward message outputMessageLen += inputMessageLen; //Debug message TRACE_INFO("Forwarding DHCPv6 message on network-facing interface %s (%" PRIuSIZE " bytes)...\r\n", context->serverInterface->name, outputMessageLen); //Dump the contents of the message for debugging purpose dhcpv6DumpMessage(outputMessage, outputMessageLen); //Destination address to be used when relaying the client message ipAddr.length = sizeof(Ipv6Addr); ipAddr.ipv6Addr = context->serverAddress; //Relay the client message to the server return socketSendTo(context->serverSocket, &ipAddr, DHCPV6_SERVER_PORT, outputMessage, outputMessageLen, NULL, 0); }