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. Both IPv4 and IPv6 headers are shown in Chapter 7 in Figures 7-3 and 7-4. 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.

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:

  • 32-bit source IP address (IP header)

  • 32-bit destination IP address (IP header)

  • 8-bit field zeroed out

  • 8-bit protocol

  • 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.

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.

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

note

The directory IPHDRINC on the companion CD contains a fully functional sample that creates raw UDP packets over IPv4 and IPv6.

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

// 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. See the IPHDRINC //   code sample on the companion CD for its implementation. 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 code sample on the CD has a routine to compute the pseudo-header checksum for both IPv4 and IPv6.

As we mentioned previously, using the header include option for IPv4 is easy because the stack will perform any fragmentation necessary. However, for IPv6 the stack will not, which means if your application needs to send raw data with a payload that exceeds the MTU, it will have to fragment the packets manually before sending them. This is accomplished by including the IPv6 fragmentation header after the IPv6 header but before the remaining payload. To do this, the IPv6 header's next header value will indicate the IPv6 fragmentation header (whose value is 44). The next header value of the IPv6 fragmentation header will then indicate IPPROTO_UDP. Also note that the UDP header occurs only once. The first fragment will contain the IPv6 header, IPv6 fragmentation header, UDP header, and as much of the payload that will fit into the MTU. The subsequent fragments will contain only the IPv6 header, the IPv6 fragmentation header, and the remaining payload. Figure 11-6 illustrates this example. In this case, the MTU is 1500 bytes but a 2000 byte payload is being sent.

Figure 11-6 IPv6 UDP packet with fragmentation

Again, the RAWUDP.CPP sample on the companion CD illustrates sending a fabricated UDP packet for both IPv4 and IPv6. There are two routines of interest: PacketizeIpv4 and PacketizeIpv6. The v4 routine doesn't do anything of interest because we know the stack will fragment the data if required. However, the v6 routine will build the appropriate IPv6 header and fragmentation header for each fragment necessary.



Network Programming for Microsoft Windows
Network Programming for Microsoft Windows (Microsoft Professional Series)
ISBN: 0735605602
EAN: 2147483647
Year: 2001
Pages: 172
Authors: Anthony Jones

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net