void SendArpGeneric(UInt64 destinationEthernetAddress, ArpOperation arpOperation, UInt64 targetPhysicalAddress, UInt32 targetProtocolAddress, Int64 timeoutInMachineTicks) { if (_isDisposed) { return; } lock (_arpFrameBufferLock) { UInt64 physicalAddress = _ethernetInterface.PhysicalAddressAsUInt64; // configure ARP packet /* Op: request (1) or reply (2) */ _arpFrameBuffer[6] = (byte)(((UInt16)arpOperation >> 8) & 0xFF); _arpFrameBuffer[7] = (byte)((UInt16)arpOperation & 0xFF); /* Sender Harwdare Address */ _arpFrameBuffer[8] = (byte)((physicalAddress >> 40) & 0xFF); _arpFrameBuffer[9] = (byte)((physicalAddress >> 32) & 0xFF); _arpFrameBuffer[10] = (byte)((physicalAddress >> 24) & 0xFF); _arpFrameBuffer[11] = (byte)((physicalAddress >> 16) & 0xFF); _arpFrameBuffer[12] = (byte)((physicalAddress >> 8) & 0xFF); _arpFrameBuffer[13] = (byte)(physicalAddress & 0xFF); /* Sender Protocol Address */ _arpFrameBuffer[14] = (byte)((_ipv4ProtocolAddress >> 24) & 0xFF); _arpFrameBuffer[15] = (byte)((_ipv4ProtocolAddress >> 16) & 0xFF); _arpFrameBuffer[16] = (byte)((_ipv4ProtocolAddress >> 8) & 0xFF); _arpFrameBuffer[17] = (byte)(_ipv4ProtocolAddress & 0xFF); /* Target Harwdare Address (if known) */ _arpFrameBuffer[18] = (byte)((targetPhysicalAddress >> 40) & 0xFF); _arpFrameBuffer[19] = (byte)((targetPhysicalAddress >> 32) & 0xFF); _arpFrameBuffer[20] = (byte)((targetPhysicalAddress >> 24) & 0xFF); _arpFrameBuffer[21] = (byte)((targetPhysicalAddress >> 16) & 0xFF); _arpFrameBuffer[22] = (byte)((targetPhysicalAddress >> 8) & 0xFF); _arpFrameBuffer[23] = (byte)(targetPhysicalAddress & 0xFF); /* Target Protocol Address */ _arpFrameBuffer[24] = (byte)((targetProtocolAddress >> 24) & 0xFF); _arpFrameBuffer[25] = (byte)((targetProtocolAddress >> 16) & 0xFF); _arpFrameBuffer[26] = (byte)((targetProtocolAddress >> 8) & 0xFF); _arpFrameBuffer[27] = (byte)(targetProtocolAddress & 0xFF); _bufferArray[0] = _arpFrameBuffer; _ethernetInterface.Send(destinationEthernetAddress, DATA_TYPE_ARP /* dataType: ARP */, _ipv4ProtocolAddress, targetProtocolAddress, 1 /* one buffer in bufferArray */, _bufferArray, _indexArray, _countArray, timeoutInMachineTicks); } }
public void Send(byte protocol, UInt32 srcIPAddress, UInt32 dstIPAddress, byte[][] buffer, int[] offset, int[] count, Int64 timeoutInMachineTicks) { /* if we are receiving more than (MAX_BUFFER_COUNT - 1) buffers, abort; if we need more, we'll have to change our array sizes at top */ if (buffer.Length > MAX_BUFFER_SEGMENT_COUNT - 1) { throw new ArgumentException(); } // determine whether dstIPAddress is a local address or a remote address. UInt64 dstPhysicalAddress; if ((dstIPAddress == _ipv4configIPAddress) || ((dstIPAddress & LOOPBACK_SUBNET_MASK) == (LOOPBACK_IP_ADDRESS & LOOPBACK_SUBNET_MASK))) { // loopback: the destination is ourselves // if the loopback buffer is in use then wait for it to be freed (or until our timeout occurs); if timeout occrs then drop the packet bool loopbackBufferInUse = _loopbackBufferInUse; if (loopbackBufferInUse) { Int32 waitTimeout = (Int32)((timeoutInMachineTicks != Int64.MaxValue) ? System.Math.Max((timeoutInMachineTicks - Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks) / System.TimeSpan.TicksPerMillisecond, 0) : System.Threading.Timeout.Infinite); loopbackBufferInUse = !(_loopbackBufferFreedEvent.WaitOne(waitTimeout, false)); } if (!loopbackBufferInUse) { lock (_loopbackBufferLockObject) { _loopbackProtocol = (ProtocolType)protocol; _loopbackSourceIPAddress = srcIPAddress; _loopbackDestinationIPAddress = dstIPAddress; // if we haven't needed loopback yet, allocate our loopback buffer now. if (_loopbackBuffer == null) { _loopbackBuffer = new byte[LOOPBACK_BUFFER_SIZE]; } int loopbackBufferCount = 0; for (int iBuffer = 0; iBuffer < buffer.Length; iBuffer++) { Array.Copy(buffer[iBuffer], offset[iBuffer], _loopbackBuffer, loopbackBufferCount, count[iBuffer]); loopbackBufferCount += count[iBuffer]; } _loopbackBufferCount = loopbackBufferCount; _loopbackBufferInUse = true; _loopbackBufferFilledEvent.Set(); } } return; } else if (dstIPAddress == 0xFFFFFFFF) { // direct delivery: this destination address is our broadcast address /* get destinationPhysicalAddress of dstIPAddress */ dstPhysicalAddress = _arpResolver.TranslateIPAddressToPhysicalAddress(dstIPAddress, timeoutInMachineTicks); } else if ((dstIPAddress & _ipv4configSubnetMask) == (_ipv4configIPAddress & _ipv4configSubnetMask)) { // direct delivery: this destination address is on our local subnet /* get destinationPhysicalAddress of dstIPAddress */ dstPhysicalAddress = _arpResolver.TranslateIPAddressToPhysicalAddress(dstIPAddress, timeoutInMachineTicks); } else { // indirect delivery; send the frame to our gateway instead /* get destinationPhysicalAddress of dstIPAddress */ dstPhysicalAddress = _arpResolver.TranslateIPAddressToPhysicalAddress(_ipv4configGatewayAddress, timeoutInMachineTicks); } if (dstPhysicalAddress == 0) { throw Utility.NewSocketException(SocketError.HostUnreachable); /* TODO: consider returning a success/fail as bool from this function */ } lock (_ipv4HeaderBufferLockObject) { int headerLength = IPV4_HEADER_MIN_LENGTH; int dataLength = 0; for (int i = 0; i < buffer.Length; i++) { dataLength += count[i]; } // we will send the data in fragments if the total data length exceeds 1500 bytes Int32 fragmentOffset = 0; Int32 fragmentLength; /* NOTE: we send the fragment offsets in reverse order so that the destination host has a chance to create the full buffer size before receiving additional fragments */ if (dataLength > MAX_IPV4_DATA_FRAGMENT_SIZE) { fragmentOffset = dataLength - (dataLength % MAX_IPV4_DATA_FRAGMENT_SIZE); } while (fragmentOffset >= 0) { fragmentLength = System.Math.Min(dataLength - fragmentOffset, MAX_IPV4_DATA_FRAGMENT_SIZE); // populate the header fields _ipv4HeaderBuffer[1] = 0; /* leave the DSField/ECN fields blank */ UInt16 identification = GetNextDatagramID(); _ipv4HeaderBuffer[4] = (byte)((identification >> 8) & 0xFF); _ipv4HeaderBuffer[5] = (byte)(identification & 0xFF); // TODO: populate flags and fragmentation fields, if necessary _ipv4HeaderBuffer[6] = (byte)( (/* (flags << 5) + */ ((fragmentOffset >> 11) & 0xFF)) | ((fragmentOffset + fragmentLength == dataLength) ? 0 : 0x20) /* set MF (More Fragments) bit if this is not the only/last fragment in a datagram */ ); _ipv4HeaderBuffer[7] = (byte)((fragmentOffset >> 3) & 0xFF); // populate the TTL (MaxHopCount) and protocol fields _ipv4HeaderBuffer[8] = DEFAULT_TIME_TO_LIVE; _ipv4HeaderBuffer[9] = protocol; // fill in source and destination addresses _ipv4HeaderBuffer[12] = (byte)((srcIPAddress >> 24) & 0xFF); _ipv4HeaderBuffer[13] = (byte)((srcIPAddress >> 16) & 0xFF); _ipv4HeaderBuffer[14] = (byte)((srcIPAddress >> 8) & 0xFF); _ipv4HeaderBuffer[15] = (byte)(srcIPAddress & 0xFF); _ipv4HeaderBuffer[16] = (byte)((dstIPAddress >> 24) & 0xFF); _ipv4HeaderBuffer[17] = (byte)((dstIPAddress >> 16) & 0xFF); _ipv4HeaderBuffer[18] = (byte)((dstIPAddress >> 8) & 0xFF); _ipv4HeaderBuffer[19] = (byte)(dstIPAddress & 0xFF); /* TODO: populate any datagram options */ // pseudocode: while (options) { AddOptionAt(_upV4HeaderBuffer[20 + offset]); headerLength += 4 }; // insert the length (and header length) _ipv4HeaderBuffer[0] = (byte)((0x04 << 4) /* version: IPv4 */ + (headerLength / 4)) /* Internet Header Length: # of 32-bit words */; _ipv4HeaderBuffer[2] = (byte)(((headerLength + fragmentLength) >> 8) & 0xFF); /* MSB of total datagram length */ _ipv4HeaderBuffer[3] = (byte)((headerLength + fragmentLength) & 0xFF); /* LSB of total datagram length */ // finally calculate the header checksum // for checksum calculation purposes, the checksum field must be empty. _ipv4HeaderBuffer[10] = 0; _ipv4HeaderBuffer[11] = 0; UInt16 checksum = Netduino.IP.Utility.CalculateInternetChecksum(_ipv4HeaderBuffer, 0, headerLength); _ipv4HeaderBuffer[10] = (byte)((checksum >> 8) & 0xFF); _ipv4HeaderBuffer[11] = (byte)(checksum & 0xFF); // queue up our buffer arrays _bufferArray[0] = _ipv4HeaderBuffer; _indexArray[0] = 0; _countArray[0] = headerLength; Int32 totalBufferOffset = 0; Int32 bufferArrayLength = 1; /* we start with index 1, after our IPv4 header */ for (int i = 0; i < buffer.Length; i++) { if (totalBufferOffset + count[i] > fragmentOffset) { // add data from this buffer to our set of downstream buffers _bufferArray[bufferArrayLength] = buffer[i]; _indexArray[bufferArrayLength] = offset[i] + System.Math.Max(0, (fragmentOffset - totalBufferOffset)); _countArray[bufferArrayLength] = System.Math.Min(count[i] - System.Math.Max(0, (fragmentOffset - totalBufferOffset)), fragmentLength - totalBufferOffset); bufferArrayLength++; } else { // we have not yet reached our fragment point; increment our totalBufferOffset and move to the next buffer. } totalBufferOffset += count[i]; // if we have filled our fragment buffer set completely, break out now. if (totalBufferOffset >= fragmentOffset + fragmentLength) { break; } } // send the datagram (or datagram fragment) _ethernetInterface.Send(dstPhysicalAddress, 0x0800 /* dataType: IPV4 */, srcIPAddress, dstIPAddress, bufferArrayLength, _bufferArray, _indexArray, _countArray, timeoutInMachineTicks); fragmentOffset -= MAX_IPV4_DATA_FRAGMENT_SIZE; } } }