< Traceroute Program Example | RAW Socket Main | UDP RAW Socket Example >


 

 

Raw Sockets 11 Part 4

 

 

What do we have in this chapter 11 part 4?

  1. Using IP Header Include Option

 

Using IP Header Include Option

 

The one limitation of raw sockets is that you can work only with certain protocols that are already defined, such as ICMP and IGMP. You cannot create a raw socket with IPPROTO_UDP and manipulate the UDP header; likewise with TCP. To manipulate the IP header as well as either the TCP or UDP header (or any other protocol encapsulated in IP), you must use the IP_HDRINCL socket option with a raw socket. For IPv6, the option is IPV6_HDRINCL. This option allows you to build your own IP header as well as other protocols' headers.

In addition to manipulating well-known protocols such as UDP, using raw sockets with the header include option allows you to implement your own protocol scheme that is encapsulated in IP. This is done by creating a raw socket and using the IPPROTO_RAW value as the protocol. This allows you to set the protocol field in the IP header manually and build your own custom protocol header. However, in this section we will take a look at how to build your own UDP packets so that you can gain a good understanding of the steps involved. Once you understand how to manipulate the UDP header, creating your own protocol header or manipulating other protocols encapsulated in IP is fairly trivial.

Before getting into the details of using the header include option, you need to know one important difference between using this option with IPv4 and IPv6. For IPv4, the stack still verifies some fields within the supplied IPv4 header. For example, the IPv4 identification field is set by the stack and the stack will fragment the packet if necessary. That is, if you create a raw IPv4 packet and set IP_HDRINCL and send a packet larger than the MTU size, the stack will fragment the data into multiple packets for you. For IPv6, if the IPV6_HDRINCL option is set, it is your responsibility to compute all the headers and fields necessary. If you submit a send larger than the MTU size, your application must create the IPv6 fragment headers and compute the offsets correctly; otherwise, the IPv6 stack will drop the packet without sending it.

When you use the header include option, you are required to fill in the IP header yourself for every send call, as well as the headers of any other protocols wrapped within. The UDP header is quite a bit simpler than the IP header. It is only 8 bytes long and contains only four fields, as shown in Figure 11-3. The first two fields are the source and destination port numbers. They are 16 bits each. The third field is the UDP length, which is the length, in bytes, of the UDP header and data. The fourth field is the checksum, which we will discuss shortly. The last part of the UDP packet is the data.

 

Using IP Header Include Option: The UDP header format

 

Figure 11-3 UDP header format

 

Because UDP is an unreliable protocol, calculating the checksum is optional. Unlike the IPv4 checksum, which covers only the IPv4 header, the UDP checksum covers the data and also includes part of the IPv4 header. The additional fields required to calculate the UDP checksum are known as a pseudo-header. The IPv4 UDP pseudo-header is composed of the following items:

 

  1. 32-bit source IP address (IP header).
  2. 32-bit destination IP address (IP header).
  3. 8-bit field zeroed out.
  4. 8-bit protocol.
  5. 16-bit UDP length.

 

Added to these items are the UDP header and data. The method of calculating the checksum is the 16-bit one's complement sum. Because the data can be an odd number of bytes, it might be necessary to pad a zero byte to the end of the data to calculate the checksum. This pad field is not transmitted as part of the data. Figure 11-4 illustrates all of the fields required for the checksum calculation. The first three 32-bit words make up the UDP pseudo-header. The UDP header and its data follows that. Notice that because the checksum is calculated on 16-bit values, the data might need to be padded with a zero byte.

 

Using IP Header Include Option: The IPv4 pseudo-header with UDP packet and data

 

Figure 11-4 IPv4 pseudo-header with UDP packet and data

 

For IPv6, you have already seen how to calculate the IPv6 pseudo-header as is required to calculate the checksum for ICMPv6 packets. The calculation is the same for UDP with the IPv6 pseudo-header coming first and is followed by the UDP header and payload (zero padded to the next 16-bit boundary if necessary). The IPv6 pseudo-header is shown in Figure 11-5.

 

----------------------------------------------------------------

Using IP Header Include Option: The IPv6 pseudo-header with UDP packet and data

 

Figure 11-5 IPv6 pseudo-header with UDP packet and data

 

The following code snippet shows how to build an IPv4 and UDP header:

 

// Define the ICMP header

typedef struct icmp_hdr

{

    unsigned char   icmp_type;

    unsigned char   icmp_code;

    unsigned short  icmp_checksum;

    unsigned short  icmp_id;

    unsigned short  icmp_sequence;

    unsigned long   icmp_timestamp;

} ICMP_HDR, *PICMP_HDR, FAR *LPICMP_HDR;

 

ICMP_HDR *icmp=NULL;

SOCKET        s;

SOCKADDR_STORAGE dest;

char     buf[sizeof(ICMP_HDR) + 32];

 

// IPv4 header

typedef struct ip_hdr

{

    unsigned char  ip_verlen;        // 4-bit IPv4 version 4-bit header length (in 32-bit words)

    unsigned char  ip_tos;           // IP type of service

    unsigned short ip_totallength;   // Total length

    unsigned short ip_id;            // Unique identifier

    unsigned short ip_offset;        // Fragment offset field

    unsigned char  ip_ttl;           // Time to live

    unsigned char  ip_protocol;      // Protocol(TCP,UDP etc)

    unsigned short ip_checksum;      // IP checksum

    unsigned int   ip_srcaddr;       // Source address

    unsigned int   ip_destaddr;      // Source address

} IPV4_HDR, *PIPV4_HDR, FAR * LPIPV4_HDR;

 

// Define the UDP header

typedef struct udp_hdr

{

    unsigned short src_portno;       // Source port no.

    unsigned short dst_portno;       // Dest. port no.

    unsigned short udp_length;       // Udp packet length

    unsigned short udp_checksum;     // Udp checksum (optional)

} UDP_HDR, *PUDP_HDR;

 

SOCKET    s;

char      buf[MAX_BUFFER], // large enough buffer

     *data=NULL;

IPV4_HDR *v4hdr=NULL;

UDP_HDR  *udphdr=NULL;

USHORT   sourceport=5000, Destport=5001;

int      payload=512,  // size of UDP data

         optval;

SOCKADDR_STORAGE dest;

 

// Initialize the IPv4 header

v4hdr = (IPV4_HDR *)buf;

v4hdr->ip_verlen = (4 << 4) │ (sizeof(IPV4_HDR) / sizeof(ULONG));

v4hdr->ip_tos    = 0;

v4hdr->ip_totallength = htons(sizeof(IPV4_HDR) + sizeof(UDP_HDR) +  payload);

v4hdr->ip_id     = 0;

v4hdr->ip_offset = 0;

v4hdr->ip_ttl    = 8;    // Time-to-live is eight

v4hdr->ip_protocol = IPPROTO_UDP;

v4hdr->ip_checksum = 0;

v4hdr->ip_srcaddr  = inet_addr("1.2.3.4");

v4hdr->ip_destaddr = inet_addr("157.32.159.101");

// Calculate checksum for IPv4 header

//   The checksum() function computes the 16-bit one's

//   complement on the specified buffer.

v4hdr->ip_checksum = checksum(v4hdr, sizeof(IPV4_HDR));

 

// Initialize the UDP header

udphdr = (UDP_HDR *)&buf[sizeof(IPV4_HDR)];

udphdr->src_portno = htons(sourceport);

udphdr->dst_portno = htons(destport);

udphdr->udp_length = htons(sizeof(UDP_HDR) + payload);

udphdr->udp_checksum = 0;

 

// Initialize the UDP payload to something

data = &buf[sizeof(IPV4_HDR) + sizeof(UDP_HDR)];

memset(data, '^', payload);

 

 

 

 

// Calculate the IPv4 and UDP pseudo-header checksum - this routine

// extracts all the necessary fields from the headers and calculates

// the checksum over it. See the iphdrinc sample for the implementation

//    of Ipv4PseudoHeaderChecksum().

udphdr->udp_checksum = Ipv4PseudoHeaderChecksum(v4hdr, udphdr, data, sizeof(IPV4_HDR) + sizeof(UDP_HDR) + payload);

 

// Create the raw UDP socket

s = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);

 

// Set the header include option

optval = 1;

setsockopt(s, IPPROTO_IP, IP_HDRINCL, (char *)&optval, sizeof(optval));

 

// Send the data

((SOCKADDR_IN *)&dest)->sin_family = AF_INET;

((SOCKADDR_IN *)&dest)->sin_port   = htons(destport);

((SOCKADDR_IN *)&dest)->sin_addr.s_addr = inet_addr("157.32.159.101");

 

sendto(s, buf, sizeof(IPV4_HDR) + sizeof(UDP_HDR) + payload, 0, (SOCKADDR *)&dest, sizeof(dest));

 

This code is straightforward and easy to follow. The IPv4 header is initialized with valid entries. In this case, a bogus source IPv4 address is used (1.2.3.4) but a valid destination address is supplied. Also, we set the TTL value to 8. Lastly, the checksum is calculated for the IPv4 header only. After the IPv4 header is the UDP header, as indicated by the ip_protocol field of the IPv4 header being set to IPPROTO_UDP. For that header, the source and destination ports are set in addition to the length of the UDP header and its payload. The last piece is to compute the pseudo-header checksum, which isn't shown but is an easy computation. The necessary fields are extracted out of the various headers after which the checksum can be computed.

The following program example creates raw UDP packets over IPv4 and IPv6. This sample also has a routine to compute the pseudo-header checksum for both IPv4 and IPv6.

 

 

 


< Traceroute Program Example | RAW Socket Main | UDP RAW Socket Example >