< Adding C# ICMP For Ipv4 & IGMP For Ipv4 Header Definition Classes 4 | Main | C++ Raw UDP Socket Program Example >

 


 

Chapter 8 Part 17:

Client (and Server) Sockets Communication

 

 

What do we have in this chapter 8 Part 17?

  1. Creating Protocols Header Definition Class (C#) 5

 

Creating Protocols Header Definition Class (C#) 5

 

Next add the ICMP for Ipv6 header definition class.

 

class Icmpv6Header : ProtocolHeader

    {

        private byte icmpType;

        private byte icmpCode;

        private ushort icmpChecksum;

 

        public Ipv6Header ipv6Header;

 

        // Common values for the ICMPv6 type and code fields

        static public byte Icmpv6EchoRequestType = 128;      // ICMPv6 echo request type

        static public byte Icmpv6EchoRequestCode = 0;         // ICMPv6 echo request code

        static public byte Icmpv6EchoReplyType = 129;          // ICMPv6 echo reply type

        static public byte Icmpv6EchoReplyCode = 0;             // ICMPv6 echo reply code

        static public int Icmpv6HeaderLength = 4;                    // ICMPv6 header length

 

        /// <summary>

        /// Simple constructor for the ICMPv6 protocol header.

        /// </summary>

        public Icmpv6Header() : base()

        {

            icmpType = 0;

            icmpCode = 0;

            icmpChecksum = 0;

        }

 

        /// <summary>

        /// Constructor for the ICMPv6 header which also takes a reference to the

        /// encompassing IPv6 header. This is necessary since the IPv6 protocol

        /// defines a pseudo header checksum which requires the checksum to be

        /// calculated over fields in the ICMPv6 header and payload as well as

        /// fields from the IPv6 packet.

        /// </summary>

        /// <param name="packetHeader">Reference to the Ipv6Header object encompassing the ICMPv6 packet</param>

        public Icmpv6Header(Ipv6Header packetHeader) : base()

        {

            icmpType = 0;

            icmpCode = 0;

            icmpChecksum = 0;

            ipv6Header = packetHeader;

        }

 

        /// <summary>

        /// Sets the ICMPv6 message type.

        /// </summary>

        public byte Type

        {

            get

            {

                return icmpType;

            }

            set

            {

                icmpType = value;

            }

        }

 

        /// <summary>

        /// Sets the ICMPv6 code type.

        /// </summary>

        public byte Code

        {

            get

            {

                return icmpCode;

            }

            set

            {

                icmpCode = value;

            }

        }

 

        /// <summary>

        /// The ICMPv6 checksum value. This value is computed over the ICMPv6 header, payload,

        /// and the IPv6 header as well.

        /// </summary>

        public ushort Checksum

        {

            get

            {

                return (ushort)IPAddress.NetworkToHostOrder((short)icmpChecksum);

            }

            set

            {

                icmpChecksum = (ushort)IPAddress.HostToNetworkOrder((short)value);

            }

        }

 

        /// <summary>

        /// This routine creates an instance of the Icmpv6Header class from a byte

        /// array that is a received IGMP packet. This is useful when a packet

        /// is received from the network and the header object needs to be

        /// constructed from those values.

        /// </summary>

        /// <param name="icmpv6Packet">Byte array containing the binary ICMPv6 header</param>

        /// <param name="bytesCopied">Number of bytes used in header</param>

        /// <returns>Returns the Icmpv6Header object created from the byte array</returns>

        static public Icmpv6Header Create(byte[ ] icmpv6Packet, ref int bytesCopied)

        {

            Icmpv6Header icmpv6Header = new Icmpv6Header();

            int offset = 0;

 

            // Verify buffer is large enough to contain an ICMPv6 header

            if (icmpv6Packet.Length < Icmpv6Header.Icmpv6HeaderLength)

                return null;

 

            icmpv6Header.icmpType = icmpv6Packet[offset++];

            icmpv6Header.icmpCode = icmpv6Packet[offset++];

            icmpv6Header.icmpChecksum = BitConverter.ToUInt16(icmpv6Packet, offset);

            bytesCopied = Icmpv6Header.Icmpv6HeaderLength;

            return icmpv6Header;

        }

 

        /// <summary>

        /// This routine builds the ICMPv6 packet and payload into a byte array.

        /// It also computes the IPv6 pseudo header checksum that appears in the

        /// ICMPv6 packet.

        /// </summary>

        /// <param name="payLoad">A byte array representing the ICMPv6 payload</param>

        /// <returns>A byte array of the ICMPv6 packet and payload</returns>

        public override byte[ ] GetProtocolPacketBytes(byte[ ] payLoad)

        {

            byte[ ] icmpv6Packet, pseudoHeader, byteValue;

            int offset = 0, payLoadLength;

 

            // Build the ICMPv6 packet first since its required in the pseudo header calculation

            icmpv6Packet = new byte[Icmpv6HeaderLength + payLoad.Length];

 

            offset = 0;

            icmpv6Packet[offset++] = icmpType;

            icmpv6Packet[offset++] = icmpCode;

            icmpv6Packet[offset++] = 0;

            icmpv6Packet[offset++] = 0;

 

            // Copy the payload into the build ICMPv6 packet

            Array.Copy(payLoad, 0, icmpv6Packet, offset, payLoad.Length);

 

            // Now build the pseudo header

            pseudoHeader = new byte[40 + icmpv6Packet.Length];

 

            offset = 0;

 

            byteValue = ipv6Header.SourceAddress.GetAddressBytes();

            Array.Copy(byteValue, 0, pseudoHeader, offset, byteValue.Length);

            offset += byteValue.Length;

 

            byteValue = ipv6Header.DestinationAddress.GetAddressBytes();

            Array.Copy(byteValue, 0, pseudoHeader, offset, byteValue.Length);

            offset += byteValue.Length;

 

            // Packet total length

            payLoadLength = IPAddress.HostToNetworkOrder(Icmpv6HeaderLength + payLoad.Length);

 

            byteValue = BitConverter.GetBytes(payLoadLength);

            Array.Copy(byteValue, 0, pseudoHeader, offset, byteValue.Length);

            offset += byteValue.Length;

 

            // 3 bytes of zero padding

            pseudoHeader[offset++] = (byte)0;

            pseudoHeader[offset++] = (byte)0;

            pseudoHeader[offset++] = (byte)0;

            pseudoHeader[offset++] = (byte)ipv6Header.NextHeader;

 

            // Next is the icmpv6 header and its payload

            Array.Copy(icmpv6Packet, 0, pseudoHeader, offset, icmpv6Packet.Length);

            offset += icmpv6Packet.Length;

 

            // Compute checksum on pseudo header

            Checksum = ComputeChecksum(pseudoHeader);

 

            // Go back and put the checksum value into the marshalled byte array

            byteValue = BitConverter.GetBytes(icmpChecksum);

            Array.Copy(byteValue, 0, icmpv6Packet, 2, byteValue.Length);

            return icmpv6Packet;

        }

    }

 

 

 

Next, add the ICMP for Ipv6 echo request class.

 

    /// <summary>

    /// Class representing the ICMPv6 echo request header. Since the ICMPv6 protocol is

    /// used for a variety of different functions other than "ping", this header is

    /// broken out from the base ICMPv6 header that is common across all of its functions

    /// (such as Multicast Listener Discovery, Neighbor Discovery, etc.).

    /// </summary>

    class Icmpv6EchoRequest : ProtocolHeader

    {

        private ushort echoId;

        private ushort echoSequence;

 

        static public int Icmpv6EchoRequestLength = 4;

 

        /// <summary>

        /// Simple constructor for the ICMPv6 echo request header

        /// </summary>

        public Icmpv6EchoRequest() : base()

        {

            echoId = 0;

            echoSequence = 0;

        }

 

        /// <summary>

        /// Gets and sets the ID field. Also performs the necessary byte order conversion.

        /// </summary>

        public ushort Id

        {

            get

            {

                return (ushort)IPAddress.NetworkToHostOrder((short)echoId);

            }

            set

            {

                echoId = (ushort)IPAddress.HostToNetworkOrder((short)value);

            }

        }

 

        /// <summary>

        /// Gets and sets the echo sequence field. Also performs the necessary byte order conversion.

        /// </summary>

        public ushort Sequence

        {

            get

            {

                return (ushort)IPAddress.NetworkToHostOrder((short)echoSequence);

            }

            set

            {

                echoSequence = (ushort)IPAddress.HostToNetworkOrder((short)value);

            }

        }

 

        /// <summary>

        /// This routine creates an instance of the Icmpv6EchoRequest class from a byte

        /// array that is a received IGMP packet. This is useful when a packet

        /// is received from the network and the header object needs to be

        /// constructed from those values.

        /// </summary>

        /// <param name="echoData">Byte array containing the binary ICMPv6 echo request header</param>

        /// <param name="bytesCopied">Number of bytes used in header</param>

        /// <returns>Returns the Icmpv6EchoRequest object created from the byte array</returns>

        static public Icmpv6EchoRequest Create(byte[ ] echoData, ref int bytesCopied)

        {

            Icmpv6EchoRequest icmpv6EchoRequestHeader = new Icmpv6EchoRequest();

 

            // Verify buffer is large enough

            if (echoData.Length < Icmpv6EchoRequest.Icmpv6EchoRequestLength)

                return null;

 

            // Properties are stored in network byte order so just grab the bytes

            //    from the buffer

            icmpv6EchoRequestHeader.echoId = BitConverter.ToUInt16(echoData, 0);

            icmpv6EchoRequestHeader.echoSequence = BitConverter.ToUInt16(echoData, 2);

            bytesCopied = Icmpv6EchoRequest.Icmpv6EchoRequestLength;

            return icmpv6EchoRequestHeader;

        }

 

        /// <summary>

        /// This method builds the byte array representation of the ICMPv6 echo request header

        /// as it would appear on the wire.

        /// </summary>

        /// <param name="payLoad">Payload to appear after the ICMPv6 echo request header</param>

        /// <returns>Returns the byte array representing the packet and payload</returns>

        public override byte[ ] GetProtocolPacketBytes(byte[] payLoad)

        {

            byte[ ] icmpv6EchoRequestHeader = new byte[Icmpv6EchoRequestLength + payLoad.Length], byteValue;

            int offset = 0;

 

            byteValue = BitConverter.GetBytes(echoId);

            Array.Copy(byteValue, 0, icmpv6EchoRequestHeader, offset, byteValue.Length);

            offset += byteValue.Length;

            byteValue = BitConverter.GetBytes(echoSequence);

            Array.Copy(byteValue, 0, icmpv6EchoRequestHeader, offset, byteValue.Length);

            offset += byteValue.Length;

            Array.Copy(payLoad, 0, icmpv6EchoRequestHeader, offset, payLoad.Length);

 

            return icmpv6EchoRequestHeader;

        }

    }

 

Next, add the UDP header class definition.

 

class UdpHeader : ProtocolHeader

    {

        private ushort srcPort;

        private ushort destPort;

        private ushort udpLength;

        private ushort udpChecksum;

 

        public Ipv6Header ipv6PacketHeader;

        public Ipv4Header ipv4PacketHeader;

 

        static public int UdpHeaderLength = 8;

 

        /// <summary>

        /// Simple constructor for the UDP header.

        /// </summary>

        public UdpHeader() : base()

        {

            srcPort = 0;

            destPort = 0;

            udpLength = 0;

            udpChecksum = 0;

 

            ipv6PacketHeader = null;

            ipv4PacketHeader = null;

        }

 

        /// <summary>

        /// Gets and sets the destination port. Performs the necessary byte order conversion.

        /// </summary>

        public ushort SourcePort

        {

            get

            {

                return (ushort)IPAddress.NetworkToHostOrder((short)srcPort);

            }

            set

            {

                srcPort = (ushort)IPAddress.HostToNetworkOrder((short)value);

            }

        }

 

        /// <summary>

        /// Gets and sets the destination port. Performs the necessary byte order conversion.

        /// </summary>

        public ushort DestinationPort

        {

            get

            {

                return (ushort)IPAddress.NetworkToHostOrder((short)destPort);

            }

            set

            {

                destPort = (ushort)IPAddress.HostToNetworkOrder((short)value);

            }

        }

 

        /// <summary>

        /// Gets and sets the UDP payload length. This is the length of the payload

        /// plus the size of the UDP header itself.

        /// </summary>

        public ushort Length

        {

            get

            {

                return (ushort)IPAddress.NetworkToHostOrder((short)udpLength);

            }

            set

            {

                udpLength = (ushort)IPAddress.HostToNetworkOrder((short)value);

            }

        }

 

        /// <summary>

        /// Gets and sets the checksum value. It performs the necessary byte order conversion.

        /// </summary>

        public ushort Checksum

        {

            get

            {

                return (ushort)IPAddress.NetworkToHostOrder((short)udpChecksum);

            }

            set

            {

                udpChecksum = (ushort)IPAddress.HostToNetworkOrder((short)value);

            }

        }

 

        /// <summary>

        ///

        /// </summary>

        /// <param name="udpData"></param>

        /// <param name="bytesCopied"></param>

        /// <returns></returns>

        static public UdpHeader Create(byte[] udpData, ref int bytesCopied)

        {

            UdpHeader udpPacketHeader = new UdpHeader();

 

            udpPacketHeader.srcPort = BitConverter.ToUInt16(udpData, 0);

            udpPacketHeader.destPort = BitConverter.ToUInt16(udpData, 2);

            udpPacketHeader.udpLength = BitConverter.ToUInt16(udpData, 4);

            udpPacketHeader.udpChecksum = BitConverter.ToUInt16(udpData, 6);

 

            return udpPacketHeader;

        }

 

        /// <summary>

        /// This method builds the byte array representation of the UDP header as it would appear

        /// on the wire. To do this it must build the IPv4 or IPv6 pseudo header in order to

        /// calculate the checksum on the packet. This requires knowledge of the IPv4 or IPv6 header

        /// so one of these must be set before a UDP packet can be set.

        ///

        /// The IPv4 pseudo header consists of:

        ///   4-byte source IP address

        ///   4-byte destination address

        ///   1-byte zero field

        ///   1-byte protocol field

        ///   2-byte UDP length

        ///   2-byte source port

        ///   2-byte destination port

        ///   2-byte UDP packet length

        ///   2-byte UDP checksum (zero)

        ///   UDP payload (padded to the next 16-bit boundary)

        /// The IPv6 pseudo header consists of:

        ///   16-byte source address

        ///   16-byte destination address

        ///   4-byte payload length

        ///   3-byte zero pad

        ///   1-byte protocol value

        ///   2-byte source port

        ///   2-byte destination port

        ///   2-byte UDP length

        ///   2-byte UDP checksum (zero)

        ///   UDP payload (padded to the next 16-bit boundary)

        /// </summary>

        /// <param name="payLoad">Payload that follows the UDP header</param>

        /// <returns></returns>

        public override byte[ ] GetProtocolPacketBytes(byte[ ] payLoad)

        {

            byte[ ] udpPacket = new byte[UdpHeaderLength + payLoad.Length], pseudoHeader = null, byteValue = null;

            int offset = 0;

 

            // Build the UDP packet first

            byteValue = BitConverter.GetBytes(srcPort);

            Array.Copy(byteValue, 0, udpPacket, offset, byteValue.Length);

            offset += byteValue.Length;

 

            byteValue = BitConverter.GetBytes(destPort);

            Array.Copy(byteValue, 0, udpPacket, offset, byteValue.Length);

            offset += byteValue.Length;

 

            byteValue = BitConverter.GetBytes(udpLength);

            Array.Copy(byteValue, 0, udpPacket, offset, byteValue.Length);

            offset += byteValue.Length;

 

            udpPacket[offset++] = 0;      // Checksum is initially zero

            udpPacket[offset++] = 0;

 

            // Copy payload to end of packet

            Array.Copy(payLoad, 0, udpPacket, offset, payLoad.Length);

 

            if (ipv4PacketHeader != null)

            {

                pseudoHeader = new byte[UdpHeaderLength + 12 + payLoad.Length];

 

                // Build the IPv4 pseudo header

                offset = 0;

 

                // Source address

                byteValue = ipv4PacketHeader.SourceAddress.GetAddressBytes();

                Array.Copy(byteValue, 0, pseudoHeader, offset, byteValue.Length);

                offset += byteValue.Length;

 

                // Destination address

                byteValue = ipv4PacketHeader.DestinationAddress.GetAddressBytes();

                Array.Copy(byteValue, 0, pseudoHeader, offset, byteValue.Length);

                offset += byteValue.Length;

 

                // 1 byte zero pad plus next header protocol value

                pseudoHeader[offset++] = 0;

                pseudoHeader[offset++] = ipv4PacketHeader.Protocol;

 

                // Packet length

                byteValue = BitConverter.GetBytes(udpLength);

                Array.Copy(byteValue, 0, pseudoHeader, offset, byteValue.Length);

                offset += byteValue.Length;

 

                // Copy the UDP packet to the end of this

                Array.Copy(udpPacket, 0, pseudoHeader, offset, udpPacket.Length);

            }

            else if (ipv6PacketHeader != null)

            {

                uint ipv6PayloadLength;

 

                pseudoHeader = new byte[UdpHeaderLength + 40 + payLoad.Length];

 

                // Build the IPv6 pseudo header

                offset = 0;

 

                // Source address

                byteValue = ipv6PacketHeader.SourceAddress.GetAddressBytes();

                Array.Copy(byteValue, 0, pseudoHeader, offset, byteValue.Length);

                offset += byteValue.Length;

 

                // Destination address

                byteValue = ipv6PacketHeader.DestinationAddress.GetAddressBytes();

                Array.Copy(byteValue, 0, pseudoHeader, offset, byteValue.Length);

                offset += byteValue.Length;

 

                ipv6PayloadLength = (uint)IPAddress.HostToNetworkOrder((int)(payLoad.Length + UdpHeaderLength));

 

                // Packet payload: ICMPv6 headers plus payload

                byteValue = BitConverter.GetBytes(ipv6PayloadLength);

                Array.Copy(byteValue, 0, pseudoHeader, offset, byteValue.Length);

                offset += byteValue.Length;

 

                // 3 bytes zero pad plus next header protocol value

                pseudoHeader[offset++] = 0;

                pseudoHeader[offset++] = 0;

                pseudoHeader[offset++] = 0;

                pseudoHeader[offset++] = ipv6PacketHeader.NextHeader;

 

                // Copy the UDP packet to the end of this

                Array.Copy(udpPacket, 0, pseudoHeader, offset, udpPacket.Length);

            }

 

            if (pseudoHeader != null)

            {

                Checksum = ComputeChecksum(pseudoHeader);

            }

 

            // Put checksum back into packet

            byteValue = BitConverter.GetBytes(udpChecksum);

            Array.Copy(byteValue, 0, udpPacket, 6, byteValue.Length);

 

            return udpPacket;

        }

    }

 

Build the project and make sure there is no error.

 

Creating Protocols Header Definition Class (C#) - building the class library project  

 

Creating Protocols Header Definition Class (C#) - making sure there is no error in the build process

 

You should see the generated DLL under the Debug folder that ready to be used.

 

Creating Protocols Header Definition Class (C#) - the generated DLL file ready for use

 

We will use the Protocol.cs source file or the generated DLL for other projects later.

 

 

 


 

< Adding C# ICMP For Ipv4 & IGMP For Ipv4 Header Definition Classes 4 | Main | C++ Raw UDP Socket Program Example >