Beispiel #1
0
error_t ipv4MapMulticastAddrToMac(Ipv4Addr ipAddr, MacAddr *macAddr)
{
   uint8_t *p;

   //Ensure the specified IPv4 address is a valid host group address
   if(!ipv4IsMulticastAddr(ipAddr))
      return ERROR_INVALID_ADDRESS;

   //Cast the address to byte array
   p = (uint8_t *) &ipAddr;

   //An IP host group address is mapped to an Ethernet multicast address
   //by placing the low-order 23-bits of the IP address into the low-order
   //23 bits of the Ethernet multicast address 01-00-5E-00-00-00
   macAddr->b[0] = 0x01;
   macAddr->b[1] = 0x00;
   macAddr->b[2] = 0x5E;
   macAddr->b[3] = p[1] & 0x7F;
   macAddr->b[4] = p[2];
   macAddr->b[5] = p[3];

   //The specified host group address was successfully
   //mapped to a MAC-layer address
   return NO_ERROR;
}
Beispiel #2
0
error_t ipv4LeaveMulticastGroup(NetInterface *interface, Ipv4Addr groupAddr)
{
   uint_t i;
   uint_t j;
   MacAddr macAddr;

   //Ensure the specified IPv4 address is a multicast address
   if(!ipv4IsMulticastAddr(groupAddr))
      return ERROR_INVALID_ADDRESS;

   //Acquire exclusive access to the IPv4 filter table
   osAcquireMutex(&interface->ipv4FilterMutex);

   //Loop through filter table entries
   for(i = 0; i < interface->ipv4FilterSize; i++)
   {
      //Specified IPv4 address found?
      if(interface->ipv4Filter[i].addr == groupAddr)
      {
         //Decrement the reference count
         interface->ipv4Filter[i].refCount--;

         //Remove the entry if the reference count drops to zero
         if(interface->ipv4Filter[i].refCount < 1)
         {
#if (IGMP_SUPPORT == ENABLED)
            //Report group membership termination
            igmpLeaveGroup(interface, &interface->ipv4Filter[i]);
#endif
            //Map the multicast IPv4 address to a MAC-layer address
            ipv4MapMulticastAddrToMac(groupAddr, &macAddr);

#if (ETH_SUPPORT == ENABLED)
            //Drop the corresponding address from the MAC filter table
            ethDropMulticastAddr(interface, &macAddr);
#endif
            //Adjust the size of the IPv4 filter table
            interface->ipv4FilterSize--;

            //Remove the corresponding entry
            for(j = i; j < interface->ipv4FilterSize; j++)
               interface->ipv4Filter[j] = interface->ipv4Filter[j + 1];
         }

         //Release exclusive access to the IPv4 filter table
         osReleaseMutex(&interface->ipv4FilterMutex);
         //No error to report
         return NO_ERROR;
      }
   }

   //Release exclusive access to the IPv4 filter table
   osReleaseMutex(&interface->ipv4FilterMutex);
   //The specified IPv4 address does not exist
   return ERROR_FAILURE;
}
Beispiel #3
0
error_t ipv4CheckDestAddr(NetInterface *interface, Ipv4Addr ipAddr)
{
   uint_t i;

   //Filter out any invalid addresses
   error_t error = ERROR_INVALID_ADDRESS;

   //Broadcast address?
   if(ipv4IsBroadcastAddr(interface, ipAddr))
   {
      //Always accept broadcast address
      error = NO_ERROR;
   }
   //Multicast address?
   else if(ipv4IsMulticastAddr(ipAddr))
   {
      //Acquire exclusive access to the IPv4 filter table
      osAcquireMutex(&interface->ipv4FilterMutex);

      //Loop through the IPv4 filter table
      for(i = 0; i < interface->ipv4FilterSize; i++)
      {
         //Check whether the destination IPv4 address matches
         //a relevant multicast address
         if(interface->ipv4Filter[i].addr == ipAddr)
         {
            //The multicast address is acceptable
            error = NO_ERROR;
            //Stop immediately
            break;
         }
      }

      //Release exclusive access to the IPv4 filter table
      osReleaseMutex(&interface->ipv4FilterMutex);
   }
   //Unicast address?
   else
   {
      //The destination address matches the host address?
      if(interface->ipv4Config.addrState != IPV4_ADDR_STATE_INVALID &&
         interface->ipv4Config.addr == ipAddr)
      {
         //The destination address is acceptable
         error = NO_ERROR;
      }
   }

   //Return status code
   return error;
}
Beispiel #4
0
error_t ipv4SetDefaultGateway(NetInterface *interface, Ipv4Addr addr)
{
   //Check parameters
   if(interface == NULL)
      return ERROR_INVALID_PARAMETER;
   if(ipv4IsMulticastAddr(addr))
      return ERROR_INVALID_ADDRESS;

   //Set up default gateway address
   interface->ipv4Config.defaultGateway = addr;

   //Successful processing
   return NO_ERROR;
}
Beispiel #5
0
error_t ipv4CheckSourceAddr(NetInterface *interface, Ipv4Addr ipAddr)
{
    //Broadcast and multicast addresses must not be used as source
    //address (see RFC 1122 3.2.1.3)
    if(ipv4IsBroadcastAddr(interface, ipAddr) || ipv4IsMulticastAddr(ipAddr))
    {
        //Debug message
        TRACE_WARNING("Wrong source IPv4 address!\r\n");
        //The source address not is acceptable
        return ERROR_INVALID_ADDRESS;
    }

    //The source address is acceptable
    return NO_ERROR;
}
Beispiel #6
0
error_t ipv4SetDnsServer(NetInterface *interface, uint_t num, Ipv4Addr addr)
{
   //Check parameters
   if(interface == NULL)
      return ERROR_INVALID_PARAMETER;
   if(ipv4IsMulticastAddr(addr))
      return ERROR_INVALID_ADDRESS;

   //Set up DNS server address
   if(num < IPV4_MAX_DNS_SERVERS)
      interface->ipv4Config.dnsServer[num] = addr;

   //Successful processing
   return NO_ERROR;
}
Beispiel #7
0
error_t ipv4SetHostAddrEx(NetInterface *interface, Ipv4Addr addr, Ipv4AddrState state)
{
   //Check parameters
   if(interface == NULL)
      return ERROR_INVALID_PARAMETER;
   if(ipv4IsMulticastAddr(addr))
      return ERROR_INVALID_ADDRESS;

   //The address is no more valid
   interface->ipv4Config.addrState = IPV4_ADDR_STATE_INVALID;
   //Clear conflict flag
   interface->ipv4Config.addrConflict = FALSE;

   //Set up host address
   interface->ipv4Config.addr = addr;
   //Set the desired state
   interface->ipv4Config.addrState = state;

   //Successful processing
   return NO_ERROR;
}
Beispiel #8
0
error_t icmpSendErrorMessage(NetInterface *interface, uint8_t type,
                             uint8_t code, uint8_t parameter, const ChunkedBuffer *ipPacket)
{
    error_t error;
    size_t offset;
    size_t length;
    Ipv4Header *ipHeader;
    ChunkedBuffer *icmpMessage;
    IcmpErrorMessage *icmpHeader;
    Ipv4PseudoHeader pseudoHeader;

    //Retrieve the length of the invoking IPv4 packet
    length = chunkedBufferGetLength(ipPacket);

    //Check the length of the IPv4 packet
    if(length < sizeof(Ipv4Header))
        return ERROR_INVALID_LENGTH;

    //Point to the header of the invoking packet
    ipHeader = chunkedBufferAt(ipPacket, 0);
    //Sanity check
    if(!ipHeader) return ERROR_FAILURE;

    //Never respond to a packet destined to a broadcast or a multicast address
    if(ipv4IsBroadcastAddr(interface, ipHeader->destAddr) ||
            ipv4IsMulticastAddr(ipHeader->destAddr))
    {
        //Report an error
        return ERROR_INVALID_ADDRESS;
    }

    //Length of the data that will be returned along with the ICMP header
    length = MIN(length, ipHeader->headerLength * 4 + 8);

    //Allocate a memory buffer to hold the ICMP message
    icmpMessage = ipAllocBuffer(sizeof(IcmpErrorMessage), &offset);
    //Failed to allocate memory?
    if(!icmpMessage) return ERROR_OUT_OF_MEMORY;

    //Point to the ICMP header
    icmpHeader = chunkedBufferAt(icmpMessage, offset);

    //Format ICMP message
    icmpHeader->type = type;
    icmpHeader->code = code;
    icmpHeader->checksum = 0;
    icmpHeader->parameter = parameter;
    icmpHeader->unused = 0;

    //Copy the IP header and the first 8 bytes of the original datagram data
    error = chunkedBufferConcat(icmpMessage, ipPacket, 0, length);
    //Any error to report?
    if(error)
    {
        //Clean up side effects
        chunkedBufferFree(icmpMessage);
        //Exit immediately
        return error;
    }

    //Get the length of the resulting message
    length = chunkedBufferGetLength(icmpMessage) - offset;
    //Message checksum calculation
    icmpHeader->checksum = ipCalcChecksumEx(icmpMessage, offset, length);

    //Format IPv4 pseudo header
    pseudoHeader.srcAddr = ipHeader->destAddr;
    pseudoHeader.destAddr = ipHeader->srcAddr;
    pseudoHeader.reserved = 0;
    pseudoHeader.protocol = IPV4_PROTOCOL_ICMP;
    pseudoHeader.length = htons(length);

    //Debug message
    TRACE_INFO("Sending ICMP Error message (%" PRIuSIZE " bytes)...\r\n", length);
    //Dump message contents for debugging purpose
    icmpDumpErrorMessage(icmpHeader);

    //Send ICMP Error message
    error = ipv4SendDatagram(interface, &pseudoHeader,
                             icmpMessage, offset, IPV4_DEFAULT_TTL);

    //Free previously allocated memory
    chunkedBufferFree(icmpMessage);
    //Return status code
    return error;
}
Beispiel #9
0
void arpProcessReply(NetInterface *interface, ArpPacket *arpReply)
{
   ArpCacheEntry *entry;

   //Debug message
   TRACE_INFO("ARP Reply received...\r\n");

   //Check sender protocol address
   if(arpReply->spa == IPV4_UNSPECIFIED_ADDR)
      return;
   if(ipv4IsMulticastAddr(arpReply->spa))
      return;
   if(ipv4IsBroadcastAddr(interface, arpReply->spa))
      return;

   //Check sender hardware address
   if(macCompAddr(&arpReply->sha, &MAC_UNSPECIFIED_ADDR))
      return;
   if(macCompAddr(&arpReply->sha, &MAC_BROADCAST_ADDR))
      return;

   //Acquire exclusive access to ARP cache
   osMutexAcquire(interface->arpCacheMutex);

   //Search the ARP cache for the specified IPv4 address
   entry = arpFindEntry(interface, arpReply->spa);

   //A matching ARP entry has been found
   if(entry)
   {
      //Check current state
      if(entry->state == ARP_STATE_INCOMPLETE)
      {
         //Record the corresponding MAC address
         entry->macAddr = arpReply->sha;

         //Send all the packets that are pending for transmission
         arpSendQueuedPackets(interface, entry);

         //Save current time
         entry->timestamp = osGetTickCount();
         //The validity of the ARP entry is limited in time
         entry->timeout = ARP_REACHABLE_TIME;
         //Switch to the REACHABLE state
         entry->state = ARP_STATE_REACHABLE;
      }
      else if(entry->state == ARP_STATE_REACHABLE)
      {
         //Different link-layer address than cached?
         if(!macCompAddr(&arpReply->sha, &entry->macAddr))
         {
            //Enter STALE state
            entry->state = ARP_STATE_STALE;
         }
      }
      else if(entry->state == ARP_STATE_PROBE)
      {
         //Record IPv4/MAC address pair
         entry->ipAddr = arpReply->spa;
         entry->macAddr = arpReply->sha;

         //Save current time
         entry->timestamp = osGetTickCount();
         //The validity of the ARP entry is limited in time
         entry->timeout = ARP_REACHABLE_TIME;
         //Switch to the REACHABLE state
         entry->state = ARP_STATE_REACHABLE;
      }
   }

   //Release exclusive access to ARP cache
   osMutexRelease(interface->arpCacheMutex);
}
Beispiel #10
0
void tcpProcessSegment(NetInterface *interface,
   IpPseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset)
{
   uint_t i;
   size_t length;
   Socket *socket;
   Socket *passiveSocket;
   TcpHeader *segment;

   //Total number of segments received, including those received in error
   MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpInSegs, 1);
   MIB2_INC_COUNTER64(mib2Base.tcpGroup.tcpHCInSegs, 1);

   //A TCP implementation must silently discard an incoming
   //segment that is addressed to a broadcast or multicast
   //address (see RFC 1122 4.2.3.10)
#if (IPV4_SUPPORT == ENABLED)
   if(pseudoHeader->length == sizeof(Ipv4PseudoHeader))
   {
      //Ensure the destination address is not a broadcast address
      if(ipv4IsBroadcastAddr(interface, pseudoHeader->ipv4Data.destAddr))
         return;
      //Ensure the destination address is not a multicast address
      if(ipv4IsMulticastAddr(pseudoHeader->ipv4Data.destAddr))
         return;
   }
   else
#endif
#if (IPV6_SUPPORT == ENABLED)
   if(pseudoHeader->length == sizeof(Ipv6PseudoHeader))
   {
      //Ensure the destination address is not a multicast address
      if(ipv6IsMulticastAddr(&pseudoHeader->ipv6Data.destAddr))
         return;
   }
   else
#endif
   {
      //This should never occur...
      return;
   }

   //Retrieve the length of the TCP segment
   length = netBufferGetLength(buffer) - offset;

   //Point to the TCP header
   segment = netBufferAt(buffer, offset);
   //Sanity check
   if(segment == NULL)
      return;

   //Ensure the TCP header is valid
   if(length < sizeof(TcpHeader))
   {
      //Debug message
      TRACE_WARNING("TCP segment length is invalid!\r\n");
      //Total number of segments received in error
      MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpInErrs, 1);
      //Exit immediately
      return;
   }

   //Check header length
   if(segment->dataOffset < 5 || (segment->dataOffset * 4) > length)
   {
      //Debug message
      TRACE_WARNING("TCP header length is invalid!\r\n");
      //Total number of segments received in error
      MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpInErrs, 1);
      //Exit immediately
      return;
   }

   //Verify TCP checksum
   if(ipCalcUpperLayerChecksumEx(pseudoHeader->data,
      pseudoHeader->length, buffer, offset, length) != 0x0000)
   {
      //Debug message
      TRACE_WARNING("Wrong TCP header checksum!\r\n");
      //Total number of segments received in error
      MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpInErrs, 1);
      //Exit immediately
      return;
   }

   //No matching socket in the LISTEN state for the moment
   passiveSocket = NULL;

   //Look through opened sockets
   for(i = 0; i < SOCKET_MAX_COUNT; i++)
   {
      //Point to the current socket
      socket = socketTable + i;

      //TCP socket found?
      if(socket->type != SOCKET_TYPE_STREAM)
         continue;
      //Check whether the socket is bound to a particular interface
      if(socket->interface && socket->interface != interface)
         continue;
      //Check destination port number
      if(socket->localPort != ntohs(segment->destPort))
         continue;

#if (IPV4_SUPPORT == ENABLED)
      //An IPv4 packet was received?
      if(pseudoHeader->length == sizeof(Ipv4PseudoHeader))
      {
         //Destination IP address filtering
         if(socket->localIpAddr.length)
         {
            //An IPv4 address is expected
            if(socket->localIpAddr.length != sizeof(Ipv4Addr))
               continue;
            //Filter out non-matching addresses
            if(socket->localIpAddr.ipv4Addr != pseudoHeader->ipv4Data.destAddr)
               continue;
         }
         //Source IP address filtering
         if(socket->remoteIpAddr.length)
         {
            //An IPv4 address is expected
            if(socket->remoteIpAddr.length != sizeof(Ipv4Addr))
               continue;
            //Filter out non-matching addresses
            if(socket->remoteIpAddr.ipv4Addr != pseudoHeader->ipv4Data.srcAddr)
               continue;
         }
      }
      else
#endif
#if (IPV6_SUPPORT == ENABLED)
      //An IPv6 packet was received?
      if(pseudoHeader->length == sizeof(Ipv6PseudoHeader))
      {
         //Destination IP address filtering
         if(socket->localIpAddr.length)
         {
            //An IPv6 address is expected
            if(socket->localIpAddr.length != sizeof(Ipv6Addr))
               continue;
            //Filter out non-matching addresses
            if(!ipv6CompAddr(&socket->localIpAddr.ipv6Addr, &pseudoHeader->ipv6Data.destAddr))
               continue;
         }
         //Source IP address filtering
         if(socket->remoteIpAddr.length)
         {
            //An IPv6 address is expected
            if(socket->remoteIpAddr.length != sizeof(Ipv6Addr))
               continue;
            //Filter out non-matching addresses
            if(!ipv6CompAddr(&socket->remoteIpAddr.ipv6Addr, &pseudoHeader->ipv6Data.srcAddr))
               continue;
         }
      }
      else
#endif
      //An invalid packet was received?
      {
         //This should never occur...
         continue;
      }

      //Keep track of the first matching socket in the LISTEN state
      if(socket->state == TCP_STATE_LISTEN && !passiveSocket)
         passiveSocket = socket;
      //Source port filtering
      if(socket->remotePort != ntohs(segment->srcPort))
         continue;

      //A matching socket has been found
      break;
   }

   //If no matching socket has been found then try to
   //use the first matching socket in the LISTEN state
   if(i >= SOCKET_MAX_COUNT) socket = passiveSocket;

   //Offset to the first data byte
   offset += segment->dataOffset * 4;
   //Calculate the length of the data
   length -= segment->dataOffset * 4;

   //Debug message
   TRACE_DEBUG("%s: TCP segment received (%" PRIuSIZE " data bytes)...\r\n",
      formatSystemTime(osGetSystemTime(), NULL), length);

   //Dump TCP header contents for debugging purpose
   if(!socket)
      tcpDumpHeader(segment, length, 0, 0);
   else
      tcpDumpHeader(segment, length, socket->irs, socket->iss);

   //Convert from network byte order to host byte order
   segment->srcPort = ntohs(segment->srcPort);
   segment->destPort = ntohs(segment->destPort);
   segment->seqNum = ntohl(segment->seqNum);
   segment->ackNum = ntohl(segment->ackNum);
   segment->window = ntohs(segment->window);
   segment->urgentPointer = ntohs(segment->urgentPointer);

   //Specified port is unreachable?
   if(!socket)
   {
      //An incoming segment not containing a RST causes
      //a reset to be sent in response
      if(!(segment->flags & TCP_FLAG_RST))
         tcpSendResetSegment(interface, pseudoHeader, segment, length);

      //Return immediately
      return;
   }

   //Check current state
   switch(socket->state)
   {
   //Process CLOSED state
   case TCP_STATE_CLOSED:
      //This is the default state that each connection starts in before
      //the process of establishing it begins
      tcpStateClosed(interface, pseudoHeader, segment, length);
      break;
   //Process LISTEN state
   case TCP_STATE_LISTEN:
      //A device (normally a server) is waiting to receive a synchronize (SYN)
      //message from a client. It has not yet sent its own SYN message
      tcpStateListen(socket, interface, pseudoHeader, segment, length);
      break;
   //Process SYN_SENT state
   case TCP_STATE_SYN_SENT:
      //The device (normally a client) has sent a synchronize (SYN) message and
      //is waiting for a matching SYN from the other device (usually a server)
      tcpStateSynSent(socket, segment, length);
      break;
   //Process SYN_RECEIVED state
   case TCP_STATE_SYN_RECEIVED:
      //The device has both received a SYN from its partner and sent its own SYN.
      //It is now waiting for an ACK to its SYN to finish connection setup
      tcpStateSynReceived(socket, segment, buffer, offset, length);
      break;
   //Process ESTABLISHED state
   case TCP_STATE_ESTABLISHED:
      //Data can be exchanged freely once both devices in the connection enter
      //this state. This will continue until the connection is closed
      tcpStateEstablished(socket, segment, buffer, offset, length);
      break;
   //Process CLOSE_WAIT state
   case TCP_STATE_CLOSE_WAIT:
      //The device has received a close request (FIN) from the other device. It
      //must now wait for the application to acknowledge this request and
      //generate a matching request
      tcpStateCloseWait(socket, segment, length);
      break;
   //Process LAST_ACK state
   case TCP_STATE_LAST_ACK:
      //A device that has already received a close request and acknowledged it,
      //has sent its own FIN and is waiting for an ACK to this request
      tcpStateLastAck(socket, segment, length);
      break;
   //Process FIN_WAIT_1 state
   case TCP_STATE_FIN_WAIT_1:
      //A device in this state is waiting for an ACK for a FIN it has sent, or
      //is waiting for a connection termination request from the other device
      tcpStateFinWait1(socket, segment, buffer, offset, length);
      break;
   //Process FIN_WAIT_2 state
   case TCP_STATE_FIN_WAIT_2:
      //A device in this state has received an ACK for its request to terminate the
      //connection and is now waiting for a matching FIN from the other device
      tcpStateFinWait2(socket, segment, buffer, offset, length);
      break;
   //Process CLOSING state
   case TCP_STATE_CLOSING:
      //The device has received a FIN from the other device and sent an ACK for
      //it, but not yet received an ACK for its own FIN message
      tcpStateClosing(socket, segment, length);
      break;
   //Process TIME_WAIT state
   case TCP_STATE_TIME_WAIT:
      //The device has now received a FIN from the other device and acknowledged
      //it, and sent its own FIN and received an ACK for it. We are done, except
      //for waiting to ensure the ACK is received and prevent potential overlap
      //with new connections
      tcpStateTimeWait(socket, segment, length);
      break;
   //Invalid state...
   default:
      //Back to the CLOSED state
      tcpChangeState(socket, TCP_STATE_CLOSED);
      //Silently discard incoming packet
      break;
   }
}
Beispiel #11
0
error_t ipv4JoinMulticastGroup(NetInterface *interface, Ipv4Addr groupAddr)
{
    error_t error;
    uint_t i;
    MacAddr macAddr;

    //Ensure the specified IPv4 address is a multicast address
    if(!ipv4IsMulticastAddr(groupAddr))
        return ERROR_INVALID_ADDRESS;

    //Acquire exclusive access to the IPv4 filter table
    osMutexAcquire(interface->ipv4FilterMutex);

    //Loop through filter table entries
    for(i = 0; i < interface->ipv4FilterSize; i++)
    {
        //Check whether the table already contains the specified IPv4 address
        if(interface->ipv4Filter[i].addr == groupAddr)
        {
            //Increment the reference count
            interface->ipv4Filter[i].refCount++;
            //Release exclusive access to the IPv4 filter table
            osMutexRelease(interface->ipv4FilterMutex);
            //No error to report
            return NO_ERROR;
        }
    }

    //The IPv4 filter table is full ?
    if(i >= IPV4_FILTER_MAX_SIZE)
    {
        //Release exclusive access to the IPv4 filter table
        osMutexRelease(interface->ipv4FilterMutex);
        //A new entry cannot be added
        return ERROR_FAILURE;
    }

    //Map the multicast IPv4 address to a MAC-layer address
    ipv4MapMulticastAddrToMac(groupAddr, &macAddr);
    //Add the corresponding address to the MAC filter table
    error = ethAcceptMulticastAddr(interface, &macAddr);

    //Ensure the MAC filter table was successfully updated
    if(!error)
    {
        //Now we can safely add a new entry to the table
        interface->ipv4Filter[i].addr = groupAddr;
        //Initialize the reference count
        interface->ipv4Filter[i].refCount = 1;
        //Adjust the size of the IPv4 filter table
        interface->ipv4FilterSize++;

#if (IGMP_SUPPORT == ENABLED)
        //Report multicast group membership to the router
        igmpJoinGroup(interface, &interface->ipv4Filter[i]);
#endif
    }

    //Release exclusive access to the IPv4 filter table
    osMutexRelease(interface->ipv4FilterMutex);
    //Return status code
    return error;
}
Beispiel #12
0
error_t ipv4SendPacket(NetInterface *interface, Ipv4PseudoHeader *pseudoHeader,
                       uint16_t fragId, uint16_t fragOffset, ChunkedBuffer *buffer, size_t offset, uint8_t timeToLive)
{
    error_t error;
    size_t length;
    Ipv4Addr destIpAddr = 0;
    MacAddr destMacAddr;
    Ipv4Header *packet;

    //Is there enough space for the IPv4 header?
    if(offset < sizeof(Ipv4Header))
        return ERROR_INVALID_PARAMETER;

    //Make room for the header
    offset -= sizeof(Ipv4Header);
    //Calculate the size of the entire packet, including header and data
    length = chunkedBufferGetLength(buffer) - offset;

    //Point to the IPv4 header
    packet = chunkedBufferAt(buffer, offset);

    //Format IPv4 header
    packet->version = IPV4_VERSION;
    packet->headerLength = 5;
    packet->typeOfService = 0;
    packet->totalLength = htons(length);
    packet->identification = htons(fragId);
    packet->fragmentOffset = htons(fragOffset);
    packet->timeToLive = timeToLive;
    packet->protocol = pseudoHeader->protocol;
    packet->headerChecksum = 0;
    packet->srcAddr = pseudoHeader->srcAddr;
    packet->destAddr = pseudoHeader->destAddr;

    //Calculate IP header checksum
    packet->headerChecksum = ipCalcChecksumEx(buffer, offset, packet->headerLength * 4);

    //Ensure the source address is valid
    error = ipv4CheckSourceAddr(interface, pseudoHeader->srcAddr);
    //Invalid source address?
    if(error) return error;

    //Destination address is the unspecified address?
    if(pseudoHeader->destAddr == IPV4_UNSPECIFIED_ADDR)
    {
        //Destination address is not acceptable
        error = ERROR_INVALID_ADDRESS;
    }
    //Destination address is the loopback address?
    else if(pseudoHeader->destAddr == IPV4_LOOPBACK_ADDR)
    {
        //Not yet implemented...
        error = ERROR_NOT_IMPLEMENTED;
    }
    //Destination address is a broadcast address?
    else if(ipv4IsBroadcastAddr(interface, pseudoHeader->destAddr))
    {
        //Destination IPv4 address
        destIpAddr = pseudoHeader->destAddr;
        //Make use of the broadcast MAC address
        destMacAddr = MAC_BROADCAST_ADDR;
        //No error to report
        error = NO_ERROR;
    }
    //Destination address is a multicast address?
    else if(ipv4IsMulticastAddr(pseudoHeader->destAddr))
    {
        //Destination IPv4 address
        destIpAddr = pseudoHeader->destAddr;
        //Map IPv4 multicast address to MAC-layer multicast address
        error = ipv4MapMulticastAddrToMac(pseudoHeader->destAddr, &destMacAddr);
    }
    //Destination host is in the local subnet?
    else if(ipv4IsInLocalSubnet(interface, pseudoHeader->destAddr))
    {
        //Destination IPv4 address
        destIpAddr = pseudoHeader->destAddr;
        //Resolve host address before sending the packet
        error = arpResolve(interface, pseudoHeader->destAddr, &destMacAddr);
    }
    //Destination host is outside the local subnet?
    else
    {
        //Make sure the default gateway is properly set
        if(interface->ipv4Config.defaultGateway != IPV4_UNSPECIFIED_ADDR)
        {
            //Use the default gateway to forward the packet
            destIpAddr = interface->ipv4Config.defaultGateway;
            //Perform address resolution
            error = arpResolve(interface, interface->ipv4Config.defaultGateway, &destMacAddr);
        }
        else
        {
            //There is no route to the outside world...
            error = ERROR_NO_ROUTE;
        }
    }

    //Successful address resolution?
    if(!error)
    {
        //Debug message
        TRACE_INFO("Sending IPv4 packet (%u bytes)...\r\n", length);
        //Dump IP header contents for debugging purpose
        ipv4DumpHeader(packet);

        //Send Ethernet frame
        error = ethSendFrame(interface, &destMacAddr, buffer, offset, ETH_TYPE_IPV4);
    }
    //Address resolution is in progress?
    else if(error == ERROR_IN_PROGRESS)
    {
        //Debug message
        TRACE_INFO("Enqueuing IPv4 packet (%u bytes)...\r\n", length);
        //Dump IP header contents for debugging purpose
        ipv4DumpHeader(packet);

        //Enqueue packets waiting for address resolution
        error = arpEnqueuePacket(interface, destIpAddr, buffer, offset);
    }
    //Address resolution failed?
    else
    {
        //Debug message
        TRACE_WARNING("Cannot map IPv4 address to Ethernet address!\r\n");
    }

    //Return status code
    return error;
}