Raw Sockets

éÓÔÏÞÎÉË: Anthony Jones and Jim Ohlund "Windows Network Programming" - published by Microsoft Press, 1999

Raw Sockets

A raw socket is a socket that allows access to the underlying transport protocol. This chapter is dedicated to illustrating how raw sockets can be used to simulate IP utilities, such as Traceroute and Ping. Raw sockets can also be used to actually manipulate the IP header information. This chapter is concerned only with the IP protocol; we will not address raw sockets with any other protocol, as most protocols (except ATM) do not support raw sockets at all. All raw sockets are created using the SOCK_RAW socket type and are currently supported only under Winsock 2. Therefore, neither Microsoft Windows CE nor Windows 95 (without the Winsock 2 update) can utilize raw sockets.

Additionally, using raw sockets requires substantial knowledge of the underlying protocol structure, which is generally not the focus of this book. In this chapter, we will discuss the Internet Control Message Protocol (ICMP), the Internet Group Management Protocol (IGMP), and the User Datagram Protocol (UDP). ICMP is used by the Ping utility, which can detect whether a route to a host is valid and whether the host machine is responding. Developers often need a programmatic method of determining whether a machine is alive and reachable. IGMP is used by IP multicasting to advertise multicast group membership to routers. Recently support was added to most Win32 platforms to support IGMP version 2. However, in some cases you might want to send your own IGMP packets to drop group membership. We will examine the UDP protocol in conjunction with the IP_HDRINCL socket option as an example of how to send your own IGMP packets. For all three of these protocols, we will cover only the aspects necessary to fully explain the code in this chapter and in the example programs. For more detailed information, consult W. Richard Stevens's book on IP, TCP/IP Illustrated Vol. 1 (Addison-Wesley, 1994).

Raw Socket Creation

The first step in using raw sockets is creating the socket. You can use either socket or WSASocket. Note that typically, no catalog entry in Winsock for IP has the SOCK_RAW socket type. However, this does not prevent you from creating this type of socket. It just means that you cannot create a raw socket using a WSAPROTOCOL_INFO structure. Refer back to Chapter 5 for information on enumerating protocol entries with the WSAEnumProtocols function and the WSAPROTOCOL_INFO structure. You must specify the SOCK_RAW flag yourself in socket creation. The following code snippet illustrates the creation of a raw socket using ICMP as the underlying IP protocol.

SOCKETššššs;
    
sš=šsocket(AF_INET,šSOCK_RAW,šIPPROTO_ICMP);

//šOr

sš=šWSASocket(AF_INET,šSOCK_RAW,šIPPROTO_ICMP,šNULL,š0,
š  ššššWSA_FLAG_OVERLAPPED);
  
ifš(sš==šINVALID_SOCKET)
{
  šššš//šSocketšcreationšfailed  
}

Because raw sockets offer the ability to manipulate the underlying transport, they can be used for malicious purposes and are a security issue in Windows NT. Therefore, only members of the Administrators group can create sockets of type SOCK_RAW. Windows 95 and Windows 98 do not impose any kind of limitation.

To work around this problem in Windows NT, you can disable the security check on raw sockets by creating the following registry variable and setting its value to the integer 1 as a DWORD type.

HKEY_LOCAL_MACHINE\System\CurrentControlSet
  šššš\Services\Afd\Parameters\DisableRawSecurity

After the registry change, you need to reboot the machine.

In the above code example, we used the ICMP protocol, but you can also use IGMP, UDP, IP, or raw IP using the flags IPPROTO_IGMP, IPPROTO_UDP, IPPROTO_IP, or IPPROTO_RAW, respectively. However, be aware of one limitation: On Windows NT 4, Windows 98, and Windows 95 (with Winsock 2), you can use only IGMP and ICMP when creating raw sockets. The protocol flags IPPROTO_UDP, IPPROTO_IP, and IPPROTO_RAW require the use of the socket option IP_HDRINCL, which is not supported on those platforms. Windows 2000 does, however, support IP_HDRINCL, so it is possible to manipulate the IP header itself (IPPROTO_RAW), the TCP header (IPPROTO_TCP), and the UDP header (IPPROTO_UDP).

Once the raw socket is created with the appropriate protocol flags, you can use the socket handle in send and receive calls. When creating raw sockets, the IP header will be included in the data returned upon any receive, regardless of whether the IP_HDRINCL option is set.

Internet Control Message Protocol

ICMP is used as a means of messaging between hosts. Most ICMP messages relate to errors that occur in communication between hosts; the remaining ICMP messages are used to query hosts. The ICMP protocol uses IP addressing because it is a protocol encapsulated within an IP datagram. Figure 13-1 illustrates the fields of an ICMP message. The ICMP message is wrapped in an IP header.

The first field is the ICMP message type, which can be classified as either a query or an error. The code field further defines the type of query or message. The checksum field is the 16-bit one's complement sum of the ICMP header. Finally, the ICMP contents depend on the ICMP type and code. Table 13-1 lists the various types and codes.

When an ICMP error message is generated, the message always contains the IP header and the first 8 bytes of the IP datagram that caused the ICMP error to occur. This allows the host receiving the ICMP error to associate the message with one particular protocol and process associated with that error. In our case, Ping relies on the echo request and echo reply ICMP queries rather than on error messages. Hosts generate ICMP messages in response to problems with TCP or UDP; ICMP doesn't have many applications beyond that. In the next section, we will discuss how to use the ICMP protocol with a raw socket to generate a Ping request by using the echo request and echo reply messages. If you require additional information about ICMP errors or the other types of ICMP queries, consult more in-depth sources, such as Stevens's TCP/IP Illustrated Vol. 1.

Table 13-1. ICMP message types

Type Query/Error (Error Type) Code Description
0 Query 0 Echo reply
3 Error: Destination unreachable 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Network unreachable
Host unreachable
Protocol unreachable
Port unreachable
Fragmentation needed, but the Don't Fragment bit has been set
Source route failed
Destination network unknown
Destination host unknown
Source host isolated (obsolete)
Destination network administratively prohibited
Destination host administratively prohibited
Network unreachable for TOS
Host unreachable for TOS
Communication administratively prohibited by filtering
Host precedence violation
Precedence cutoff in effect
4 Error 0 Source quench
5 Error: Redirect 0
1
2
3
Redirect for network
Redirect for host
Redirect for TOS and network
Redirect for TOS and host
8 Query 0 Echo request
9 Query 0 Router advertisement
10 Query 0 Router solicitation
11 Error: Time exceeded 0
1
TTL equals 0 during transit
TTL equals 0 during reassembly
12 Error: Parameter problem 0 IP header bad
Required option missing
13 Query 0 Time stamp request
14 Query 0 Time stamp reply
15 Query 0 Information request
16 Query 0 Information reply
17 Query 0 Address mask request
18 Query 0 Address mask reply

Ping Example

A ping is often used to determine whether a particular host is alive and reachable through the network. By generating an ICMP echo request and directing it to the host you are interested in, you can determine whether you can successfully reach that machine. Of course, this does not guarantee that a socket client will be able to connect to a process on that host (a process on the remote server might not be listening, for example); it just means that the network layer of the remote host is responding to network events. Essentially, the Ping example performs the following steps:

  1. Creates a socket of type SOCK_RAW and protocol IPPROTO_ICMP
  2. Creates and initializes the ICMP header
  3. Calls sendto or WSASendto to send the ICMP request to the remote host
  4. Calls recvfrom or WSARecvfrom to receive any ICMP responses

When you send the ICMP echo request, the remote machine intercepts the ICMP query and generates an echo reply message back to you. If for some reason the host is not reachable, the appropriate ICMP error message-such as destination host unreachable-will be returned by a router somewhere along the path to the intended recipient. If the physical network connection to the host is good but the remote host is either down or not responding to network events, you need to perform your own timeout to determine this. The Ping.c example in Figure 13-2 illustrates how to create a socket capable of sending and receiving ICMP packets, as well as how to use the IP_OPTIONS socket option to implement the record route option.

One noticeable feature of the Ping example is its use of the IP_OPTIONS socket option. We use the record route IP option so that when our ICMP packet hits a router, its IP address is added into the IP option header at the location indicated by the offset field in the IP option header. This offset is also incremented by 4 each time a router adds its address. The increment value is based on the fact that an IP version 4 address is 4 bytes in length. This book does not address any IP version 6 concerns, as no current Windows platforms support this yet. Once you receive the echo reply, decode the option header and print the IP addresses and host names of the routers visited. See Chapter 9 for more information on the other types of IP options available.

Traceroute

Another valuable IP networking tool is the Traceroute utility. This allows you to determine the IP addresses of the routers that are traversed in order to reach a certain host on the network. With Ping, using the record route option in the IP option header also allows you to determine the IP addresses of intermediary routers, but Ping is limited to only 9 hops-the maximum space allocated for addresses in the option header. A hop occurs whenever an IP datagram must pass through a router in order to traverse multiple physical networks. For routes with more than 9 hops, use Traceroute.

The idea behind Traceroute is to send a UDP packet to the destination and incrementally change the IP time-to-live (TTL) value. Initially, the TTL value is 1, which means the UDP packet will reach the first router, where the TTL will expire. The expiration will cause the router to generate an ICMP time-exceeded packet. Then the initial TTL value increases by 1, so this time the UDP packet gets one router farther and an ICMP time-exceeded packet is sent from that router. Collecting each of the ICMP messages gives you a clear path of the IP addresses traversed in order to reach the endpoint. Once the TTL is incremented enough so that packets actually reach the endpoint in question, an ICMP port-unreachable message is most likely returned, as no process on the recipient is waiting for this message.

Traceroute is a useful utility because it gives you a lot of information about the route to a particular host, which is often a concern when you use multicasting or when you experience routing problems. Fewer applications need to perform a Traceroute programmatically than a ping, but certain tasks might require Traceroute-like capabilities.

Two methods can be used to implement the Traceroute program. First you can use UDP packets and send datagrams, incrementally changing the TTL. Each time the TTL expires, an ICMP message will be returned to you. This method requires one socket of the UDP protocol to send the messages and another socket of the ICMP protocol to read the returned messages. The UDP socket is a normal UDP socket, as you saw in Chapter 7. The ICMP socket is of type SOCK_RAW and protocol IPPROTO_ICMP. The TTL of the UDP socket needs to be manipulated via the IP_TTL socket option. Alternatively, you can create a UDP socket and use the IP_HDRINCL option (discussed later in this chapter) to set the TTL manually within the IP header, but this is quite a lot of work.

The other method is simply to send ICMP packets to the destination, also incrementally changing the TTL. This also results in ICMP error messages being returned when the TTL expires. This method resembles the Ping example in that it requires only one socket (of the ICMP protocol). Under the sample code folder on the companion CD, you will find a Traceroute example using ICMP packets named Traceroute.c. We won't include the whole example in this chapter, as it is similar in design to the Ping example.