Home
小杰的博客 Prev Page Prev Page
?
Main Page
Table of content
Copyright
Addison-Wesley Professional Computing Series
Foreword
Preface
Introduction
Changes from the Second Edition
Using This Book
Source Code and Errata Availability
Acknowledgments
Part 1: Introduction and TCP/IP
Chapter 1. Introduction
1.1 Introduction
1.2 A Simple Daytime Client
1.3 Protocol Independence
1.4 Error Handling: Wrapper Functions
1.5 A Simple Daytime Server
1.6 Roadmap to Client/Server Examples in the Text
1.7 OSI Model
1.8 BSD Networking History
1.9 Test Networks and Hosts
1.10 Unix Standards
1.11 64-Bit Architectures
1.12 Summary
Exercises
Chapter 2. The Transport Layer: TCP, UDP, and SCTP
2.1 Introduction
2.2 The Big Picture
2.3 User Datagram Protocol (UDP)
2.4 Transmission Control Protocol (TCP)
2.5 Stream Control Transmission Protocol (SCTP)
2.6 TCP Connection Establishment and Termination
2.7 TIME_WAIT State
2.8 SCTP Association Establishment and Termination
2.9 Port Numbers
2.10 TCP Port Numbers and Concurrent Servers
2.11 Buffer Sizes and Limitations
2.12 Standard Internet Services
2.13 Protocol Usage by Common Internet Applications
2.14 Summary
Exercises
Part 2: Elementary Sockets
Chapter 3. Sockets Introduction
3.1 Introduction
3.2 Socket Address Structures
3.3 Value-Result Arguments
3.4 Byte Ordering Functions
3.5 Byte Manipulation Functions
3.6 'inet_aton', 'inet_addr', and 'inet_ntoa' Functions
3.7 'inet_pton' and 'inet_ntop' Functions
3.8 'sock_ntop' and Related Functions
3.9 'readn', 'writen', and 'readline' Functions
3.10 Summary
Exercises
Chapter 4. Elementary TCP Sockets
4.1 Introduction
4.2 'socket' Function
4.3 'connect' Function
4.4 'bind' Function
4.5 'listen' Function
4.6 'accept' Function
4.7 'fork' and 'exec' Functions
4.8 Concurrent Servers
4.9 'close' Function
4.10 'getsockname' and 'getpeername' Functions
4.11 Summary
Exercises
Chapter 5. TCP Client/Server Example
5.1 Introduction
5.2 TCP Echo Server: 'main' Function
5.3 TCP Echo Server: 'str_echo' Function
5.4 TCP Echo Client: 'main' Function
5.5 TCP Echo Client: 'str_cli' Function
5.6 Normal Startup
5.7 Normal Termination
5.8 POSIX Signal Handling
5.9 Handling 'SIGCHLD' Signals
5.10 'wait' and 'waitpid' Functions
5.11 Connection Abort before 'accept' Returns
5.12 Termination of Server Process
5.13 'SIGPIPE' Signal
5.14 Crashing of Server Host
5.15 Crashing and Rebooting of Server Host
5.16 Shutdown of Server Host
5.17 Summary of TCP Example
5.18 Data Format
5.19 Summary
Exercises
Chapter 6. I/O Multiplexing: The 'select' and 'poll' Functions
6.1 Introduction
6.2 I/O Models
6.3 'select' Function
6.4 'str_cli' Function (Revisited)
6.5 Batch Input and Buffering
6.6 'shutdown' Function
6.7 'str_cli' Function (Revisited Again)
6.8 TCP Echo Server (Revisited)
6.9 'pselect' Function
6.10 'poll' Function
6.11 TCP Echo Server (Revisited Again)
6.12 Summary
Exercises
Chapter 7. Socket Options
7.1 Introduction
7.2 'getsockopt' and 'setsockopt' Functions
7.3 Checking if an Option Is Supported and Obtaining the Default
7.4 Socket States
7.5 Generic Socket Options
7.6 IPv4 Socket Options
7.7 ICMPv6 Socket Option
7.8 IPv6 Socket Options
7.9 TCP Socket Options
7.10 SCTP Socket Options
7.11 'fcntl' Function
7.12 Summary
Exercises
Chapter 8. Elementary UDP Sockets
8.1 Introduction
8.2 'recvfrom' and 'sendto' Functions
8.3 UDP Echo Server: 'main' Function
8.4 UDP Echo Server: 'dg_echo' Function
8.5 UDP Echo Client: 'main' Function
8.6 UDP Echo Client: 'dg_cli' Function
8.7 Lost Datagrams
8.8 Verifying Received Response
8.9 Server Not Running
8.10 Summary of UDP Example
8.11 'connect' Function with UDP
8.12 'dg_cli' Function (Revisited)
8.13 Lack of Flow Control with UDP
8.14 Determining Outgoing Interface with UDP
8.15 TCP and UDP Echo Server Using 'select'
8.16 Summary
Exercises
Chapter 9. Elementary SCTP Sockets
9.1 Introduction
9.2 Interface Models
9.3 'sctp_bindx' Function
9.4 'sctp_connectx' Function
9.5 'sctp_getpaddrs' Function
9.6 'sctp_freepaddrs' Function
9.7 'sctp_getladdrs' Function
9.8 'sctp_freeladdrs' Function
9.9 'sctp_sendmsg' Function
9.10 'sctp_recvmsg' Function
9.11 'sctp_opt_info' Function
9.12 'sctp_peeloff' Function
9.13 'shutdown' Function
9.14 Notifications
9.15 Summary
Exercises
Chapter 10. SCTP Client/Server Example
10.1 Introduction
10.2 SCTP One-to-Many-Style Streaming Echo Server: 'main' Function
10.3 SCTP One-to-Many-Style Streaming Echo Client: 'main' Function
10.4 SCTP Streaming Echo Client: 'str_cli' Function
10.5 Exploring Head-of-Line Blocking
10.6 Controlling the Number of Streams
10.7 Controlling Termination
10.8 Summary
Exercises
Chapter 11. Name and Address Conversions
11.1 Introduction
11.2 Domain Name System (DNS)
11.3 'gethostbyname' Function
11.4 'gethostbyaddr' Function
11.5 'getservbyname' and 'getservbyport' Functions
11.6 'getaddrinfo' Function
11.7 'gai_strerror' Function
11.8 'freeaddrinfo' Function
11.9 'getaddrinfo' Function: IPv6
11.10 'getaddrinfo' Function: Examples
11.11 'host_serv' Function
11.12 'tcp_connect' Function
11.13 'tcp_listen' Function
11.14 'udp_client' Function
11.15 'udp_connect' Function
11.16 'udp_server' Function
11.17 'getnameinfo' Function
11.18 Re-entrant Functions
11.19 'gethostbyname_r' and 'gethostbyaddr_r' Functions
11.20 Obsolete IPv6 Address Lookup Functions
11.21 Other Networking Information
11.22 Summary
Exercises
Part 3: Advanced Sockets
Chapter 12. IPv4 and IPv6 Interoperability
12.1 Introduction
12.2 IPv4 Client, IPv6 Server
12.3 IPv6 Client, IPv4 Server
12.4 IPv6 Address-Testing Macros
12.5 Source Code Portability
12.6 Summary
Exercises
Chapter 13. Daemon Processes and the 'inetd' Superserver
13.1 Introduction
13.2 'syslogd' Daemon
13.3 'syslog' Function
13.4 'daemon_init' Function
13.5 'inetd' Daemon
13.6 'daemon_inetd' Function
13.7 Summary
Exercises
Chapter 14. Advanced I/O Functions
14.1 Introduction
14.2 Socket Timeouts
14.3 'recv' and 'send' Functions
14.4 'readv' and 'writev' Functions
14.5 'recvmsg' and 'sendmsg' Functions
14.6 Ancillary Data
14.7 How Much Data Is Queued?
14.8 Sockets and Standard I/O
14.9 Advanced Polling
14.10 Summary
Exercises
Chapter 15. Unix Domain Protocols
15.1 Introduction
15.2 Unix Domain Socket Address Structure
15.3 'socketpair' Function
15.4 Socket Functions
15.5 Unix Domain Stream Client/Server
15.6 Unix Domain Datagram Client/Server
15.7 Passing Descriptors
15.8 Receiving Sender Credentials
15.9 Summary
Exercises
Chapter 16. Nonblocking I/O
16.1 Introduction
16.2 Nonblocking Reads and Writes: 'str_cli' Function (Revisited)
16.3 Nonblocking 'connect'
16.4 Nonblocking 'connect:' Daytime Client
16.5 Nonblocking 'connect:' Web Client
16.6 Nonblocking 'accept'
16.7 Summary
Exercises
Chapter 17. 'ioctl' Operations
17.1 Introduction
17.2 'ioctl' Function
17.3 Socket Operations
17.4 File Operations
17.5 Interface Configuration
17.6 'get_ifi_info' Function
17.7 Interface Operations
17.8 ARP Cache Operations
17.9 Routing Table Operations
17.10 Summary
Exercises
Chapter 18. Routing Sockets
18.1 Introduction
18.2 Datalink Socket Address Structure
18.3 Reading and Writing
18.4 'sysctl' Operations
18.5 'get_ifi_info' Function (Revisited)
18.6 Interface Name and Index Functions
18.7 Summary
Exercises
Chapter 19. Key Management Sockets
19.1 Introduction
19.2 Reading and Writing
19.3 Dumping the Security Association Database (SADB)
19.4 Creating a Static Security Association (SA)
19.5 Dynamically Maintaining SAs
19.6 Summary
Exercises
Chapter 20. Broadcasting
20.1 Introduction
20.2 Broadcast Addresses
20.3 Unicast versus Broadcast
20.4 'dg_cli' Function Using Broadcasting
20.5 Race Conditions
20.6 Summary
Exercises
Chapter 21. Multicasting
21.1 Introduction
21.2 Multicast Addresses
21.3 Multicasting versus Broadcasting on a LAN
21.4 Multicasting on a WAN
21.5 Source-Specific Multicast
21.6 Multicast Socket Options
21.7 'mcast_join' and Related Functions
21.8 'dg_cli' Function Using Multicasting
21.9 Receiving IP Multicast Infrastructure Session Announcements
21.10 Sending and Receiving
21.11 Simple Network Time Protocol (SNTP)
21.12 Summary
Exercises
Chapter 22. Advanced UDP Sockets
22.1 Introduction
22.2 Receiving Flags, Destination IP Address, and Interface Index
22.3 Datagram Truncation
22.4 When to Use UDP Instead of TCP
22.5 Adding Reliability to a UDP Application
22.6 Binding Interface Addresses
22.7 Concurrent UDP Servers
22.8 IPv6 Packet Information
22.9 IPv6 Path MTU Control
22.10 Summary
Exercises
Chapter 23. Advanced SCTP Sockets
23.1 Introduction
23.2 An Autoclosing One-to-Many-Style Server
23.3 Partial Delivery
23.4 Notifications
23.5 Unordered Data
23.6 Binding a Subset of Addresses
23.7 Determining Peer and Local Address Information
23.8 Finding an Association ID Given an IP Address
23.9 Heartbeating and Address Failure
23.10 Peeling Off an Association
23.11 Controlling Timing
23.12 When to Use SCTP Instead of TCP
23.13 Summary
Exercises
Chapter 24. Out-of-Band Data
24.1 Introduction
24.2 TCP Out-of-Band Data
24.3 'sockatmark' Function
24.4 TCP Out-of-Band Data Recap
24.5 Summary
Exercises
Chapter 25. Signal-Driven I/O
25.1 Introduction
25.2 Signal-Driven I/O for Sockets
25.3 UDP Echo Server Using 'SIGIO'
25.4 Summary
Exercises
Chapter 26. Threads
26.1 Introduction
26.2 Basic Thread Functions: Creation and Termination
26.3 'str_cli' Function Using Threads
26.4 TCP Echo Server Using Threads
26.5 Thread-Specific Data
26.6 Web Client and Simultaneous Connections (Continued)
26.7 Mutexes: Mutual Exclusion
26.8 Condition Variables
26.9 Web Client and Simultaneous Connections (Continued)
26.10 Summary
Exercises
Chapter 27. IP Options
27.1 Introduction
27.2 IPv4 Options
27.3 IPv4 Source Route Options
27.4 IPv6 Extension Headers
27.5 IPv6 Hop-by-Hop Options and Destination Options
27.6 IPv6 Routing Header
27.7 IPv6 Sticky Options
27.8 Historical IPv6 Advanced API
27.9 Summary
Exercises
Chapter 28. Raw Sockets
28.1 Introduction
28.2 Raw Socket Creation
28.3 Raw Socket Output
28.4 Raw Socket Input
28.5 'ping' Program
28.6 'traceroute' Program
28.7 An ICMP Message Daemon
28.8 Summary
Exercises
Chapter 29. Datalink Access
29.1 Introduction
29.2 BSD Packet Filter (BPF)
29.3 Datalink Provider Interface (DLPI)
29.4 Linux: 'SOCK_PACKET' and 'PF_PACKET'
29.5 'libpcap': Packet Capture Library
29.6 'libnet': Packet Creation and Injection Library
29.7 Examining the UDP Checksum Field
29.8 Summary
Exercises
Chapter 30. Client/Server Design Alternatives
30.1 Introduction
30.2 TCP Client Alternatives
30.3 TCP Test Client
30.4 TCP Iterative Server
30.5 TCP Concurrent Server, One Child per Client
30.6 TCP Preforked Server, No Locking Around 'accept'
30.7 TCP Preforked Server, File Locking Around 'accept'
30.8 TCP Preforked Server, Thread Locking Around 'accept'
30.9 TCP Preforked Server, Descriptor Passing
30.10 TCP Concurrent Server, One Thread per Client
30.11 TCP Prethreaded Server, per-Thread 'accept'
30.12 TCP Prethreaded Server, Main Thread 'accept'
30.13 Summary
Exercises
Chapter 31. Streams
31.1 Introduction
31.2 Overview
31.3 'getmsg' and 'putmsg' Functions
31.4 'getpmsg' and 'putpmsg' Functions
31.5 'ioctl' Function
31.6 Transport Provider Interface (TPI)
31.7 Summary
Exercises
Appendix A. IPv4, IPv6, ICMPv4, and ICMPv6
A.1 Introduction
A.2 IPv4 Header
A.3 IPv6 Header
A.4 IPv4 Addresses
A.5 IPv6 Addresses
A.6 Internet Control Message Protocols (ICMPv4 and ICMPv6)
Appendix B. Virtual Networks
B.1 Introduction
B.2 The MBone
B.3 The 6bone
B.4 IPv6 Transition: 6to4
Appendix C. Debugging Techniques
C.1 System Call Tracing
C.2 Standard Internet Services
C.3 'sock' Program
C.4 Small Test Programs
C.5 'tcpdump' Program
C.6 'netstat' Program
C.7 'lsof' Program
Appendix D. Miscellaneous Source Code
D.1 'unp.h' Header
D.2 'config.h' Header
D.3 Standard Error Functions
Appendix E. Solutions to Selected Exercises
Chapter 1
Chapter 2
Chapter 3
Chapter 4
Chapter 5
Chapter 6
Chapter 7
Chapter 8
Chapter 9
Chapter 10
Chapter 11
Chapter 12
Chapter 13
Chapter 14
Chapter 15
Chapter 16
Chapter 17
Chapter 18
Chapter 20
Chapter 21
Chapter 22
Chapter 24
Chapter 25
Chapter 26
Chapter 27
Chapter 28
Chapter 29
Chapter 30
Chapter 31
Bibliography
?
[ Team LiB ] Previous Section Next Section

28.6 traceroute Program

In this section, we will develop our own version of the traceroute program. Like the ping program we developed in the previous section, we will develop and present our own version, instead of presenting the publicly available version. We do this because we need a version that supports both IPv4 and IPv6, and we do not want to be distracted with lots of options that are not germane to our discussion of network programming.

traceroute lets us determine the path that IP datagrams follow from our host to some other destination. Its operation is simple and Chapter 8 of TCPv1 covers it in detail with numerous examples of its usage. traceroute uses the IPv4 TTL field or the IPv6 hop limit field and two ICMP messages. It starts by sending a UDP datagram to the destination with a TTL (or hop limit) of 1. This datagram causes the first-hop router to return an ICMP "time exceeded in transit" error. The TTL is then increased by one and another UDP datagram is sent, which locates the next router in the path. When the UDP datagram reaches the final destination, the goal is to have that host return an ICMP "port unreachable" error. This is done by sending the UDP datagram to a random port that is (hopefully) not in use on that host.

Early versions of traceroute were able to set the TTL field in the IPv4 header only by setting the IP_HDRINCL socket option and then building their own IPv4 header. Current systems, however, provide an IP_TTL socket option that lets us specify the TTL to use for outgoing datagrams. (This socket option was introduced with the 4.3BSD Reno release.) It is easier to set this socket option than to build a complete IPv4 header (although we will show how to build IPv4 and UDP headers in Section 29.7). The IPv6 IPV6_UNICAST_HOPS socket option lets us control the hop limit field for IPv6 datagrams.

Figure 28.17 shows our trace.h header, which all our program files include.

1鈥?1 We include the standard IPv4 headers that define the IPv4, ICMPv4, and UDP structures and constants. The rec structure defines the data portion of the UDP datagram that we send, but we will see that we never need to examine this data. It is sent mainly for debugging purposes.

Define proto structure

32鈥?3 As with our ping program in the previous section, we handle the protocol differences between IPv4 and IPv6 by defining a proto structure that contains function pointers, pointers to socket address structures, and other constants that differ between the two IP versions. The global pr will be set to point to one of these structures that is initialized for either IPv4 or IPv6, after the destination address is processed by the main function (since the destination address is what specifies whether we use IPv4 or IPv6).

Include IPv6 headers

44鈥?7 We include the headers that define the IPv6 and ICMPv6 structures and constants.

Figure 28.17 trace.h header.

traceroute/trace.h

 1 #include     "unp.h"
 2 #include    <netinet/in_systm.h>
 3 #include    <netinet/ip.h>
 4 #include    <netinet/ip_icmp.h>
 5 #include    <netinet/udp.h>

 6 #define BUFSIZE      1500

 7 struct rec {                        /* of outgoing UDP data */
 8     u_short rec_seq;                /* sequence number */
 9     u_short rec_ttl;                /* TTL packet left with */
10     struct timeval rec_tv;          /* time packet left */
11 };

12              /* globals */
13 char     recvbuf [BUFSIZE];
14 char     sendbuf [BUFSIZE];

15 int     datalen;                    /* # bytes of data following ICMP header */
16 char   *host;
17 u_short sport, dport;
18 int     nsent;                      /* add 1 for each sendto () */
19 pid_t   pid;                        /* our PID */
20 int     probe, nprobes;
21 int     sendfd, recvfd;             /* send on UDP sock, read on raw ICMP sock */
22 int     ttl, max_ttl;
23 int     verbose;

24             /* function prototypes */
25 const char *icmpcode_v4 (int);
26 const char *icmpcode_v6 (int);
27 int     recv_v4 (int, struct timeval *);
28 int     recv_v6 (int, struct timeval *);
29 void    sig_alrm (int);
30 void    traceloop (void);
31 void    tv_sub (struct timeval *, struct timeval *);

32 struct proto {
33     const char *(*icmpcode) (int);
34     int     (*recv) (int, struct timeval *);
35     struct sockaddr *sasend;    /* sockaddr{} for send, from getaddrinfo */
36     struct sockaddr *sarecv;    /* sockaddr{} for receiving */
37     struct sockaddr *salast;    /* last sockaddr{} for receiving */
38     struct sockaddr *sabind;    /* sockaddr{} for binding source port */
39     socklen_t salen;            /* length of sockaddr{}s */
40     int     icmpproto;          /* IPPROTO_xxx value for ICMP */
41     int     ttllevel;           /* setsockopt () level to set TTL */
42     int     ttloptname;         /* setsockopt () name to set TTL */
43 } *pr;

44 #ifdef IPV6

45 #include     <netinet/ip6.h>
46 #include     <netinet/icmp6.h>

47 #endif

The main function is shown in Figure 28.18 (p. 759). It processes the command-line arguments, initializes the pr pointer for either IPv4 or IPv6, and calls our traceloop function.

Define proto structures

2鈥? We define the two proto structures, one for IPv4 and one for IPv6, although the pointers to the socket address structures are not allocated until the end of this function.

Set defaults

10鈥?3 The maximum TTL or hop limit that the program uses defaults to 30, although we provide the -m command-line option to let the user change this. For each TTL, we send three probe packets, but this could be changed with another command-line option. The initial destination port is 32768+666, which will be incremented by one each time we send a UDP datagram. We hope that these ports are not in use on the destination host when the datagrams finally reach the destination, but there is no guarantee.

Process command-line arguments

19鈥?7 The -v command-line option causes most received ICMP messages to be printed.

Process hostname or IP address argument and finish initialization

38鈥?8 The destination hostname or IP address is processed by our host_serv function, returning a pointer to an addrinfo structure. Depending on the type of returned address, IPv4 or IPv6, we finish initializing the proto structure, store the pointer in the pr global, and allocate additional socket address structures of the correct size.

59 The function traceloop, shown in Figure 28.19, sends the datagrams and reads the returned ICMP messages. This is the main loop of the program.

Figure 28.18 main function for traceroute program.

traceroute/main.c

 1 #include     "trace.h"

 2 struct proto proto_v4 = { icmpcode_v4, recv_v4, NULL, NULL, NULL, NULL, 0,
 3     IPPROTO_ICMP, IPPROTO_IP, IP_TTL
 4 };

 5 #ifdef IPV6
 6 struct proto proto_v6 = { icmpcode_v6, recv_v6, NULL, NULL, NULL, NULL, 0,
 7     IPPROTO_ICMPV6, IPPROTO_IPV6, IPV6_UNICAST_HOPS
 8 };
 9 #endif

10 int     datalen = sizeof (struct rec); /* defaults */
11 int     max_ttl = 30;
12 int     nprobes = 3;
13 u_short dport = 32768 + 666;

14 int
15 main(int argc, char **argv)
16 {
17     int     c;
18     struct addrinfo *ai;
19     char   *h;

20     opterr = 0;                  /* don't want getopt () writing to stderr */
21     while ( (c = getopt (argc, argv, "m:v")) != -1) {
22         switch (c) {
23         case 'm':
24             if ( (max_ttl = atoi (optarg)) <= 1)
25                 err_quit ("invalid -m value");
26             break;

27         case 'v':
28             verbose++;
29             break;

30         case '?':
31             err_quit ("unrecognized option: %c", c);
32         }
33     }

34     if (optind != argc - 1)
35         err_quit ("usage: traceroute [ -m <maxttl> -v ] <hostname>");
36     host = argv [optind];

37     pid = getpid();
38     Signal (SIGALRM, sig_alrm);

39     ai = Host_serv (host, NULL, 0, 0);

40     h = Sock_ntop_host (ai->ai_addr, ai->ai_addrlen);
41     printf ("traceroute to %s (%s) : %d hops max, %d data bytes\n",
42             ai->ai_canonname ? ai->ai_canonname : h, h, max_ttl, datalen);

43     /* initialize according to protocol */
44     if (ai->ai_family == AF_INET) {
45         pr = &proto_v4;
46 #ifdef  IPV6
47     } else if (ai->ai_family == AF_INET6) {
48         pr = &proto_v6;
49         if (IN6_IS_ADDR_V4MAPPED
50             (&(((struct sockaddr_in6 *) ai->ai_addr)->sin6_addr)))
51             err_quit ("cannot traceroute IPv4-mapped IPv6 address");
52 #endif
53     } else
54         err_quit ("unknown address family %d", ai->ai_family);

55     pr->sasend = ai->ai_addr; /* contains destination address */
56     pr->sarecv = Calloc (1, ai->ai_addrlen);
57     pr->salast = Calloc (1, ai->ai_addrlen);
58     pr->sabind = Calloc (1, ai->ai_addrlen);
59     pr->salen = ai->ai_addrlen;

60     traceloop();

61     exit (0);
62 }

We next examine our function traceloop, shown in Figure 28.19 (p. 761).

Create raw socket

9鈥?0 We need two sockets: a raw socket on which we read all returned ICMP messages and a UDP socket on which we send the probe packets with the increasing TTLs. After creating the raw socket, we reset our effective user ID to our real user ID since we no longer require superuser privileges.

Set ICMPv6 receive filter

11鈥?0 If we are tracing the route to an IPv6 address and the -v command-line option was not specified, install a filter that blocks all ICMP message types except for the ones we expect: "time exceeded" or "destination unreachable." This reduces the number of packets received on the socket.

Create UDP socket and bind source port

21鈥?5 We bind a source port to the UDP socket that is used for sending, using the low-order 16 bits of our PID with the high-order bit set to 1. Since it is possible for multiple copies of the traceroute program to be running at any given time, we need a way to determine if a received ICMP message was generated in response to one of our datagrams, or in response to a datagram sent by another copy of the program. We use the source port in the UDP header to identify the sending process because the returned ICMP message is required to include the UDP header from the datagram that caused the ICMP error.

Establish signal handler for SIGALRM

26 We establish our function sig_alrm as the signal handler for SIGALRM because each time we send a UDP datagram, we wait three seconds for an ICMP message before sending the next probe.

Figure 28.19 traceloop function: main processing loop.

traceroute/traceloop.c

 1 #include     "trace.h"

 2 void
 3 traceloop(void)
 4 {
 5     int     seq, code, done;
 6     double  rtt;
 7     struct rec *rec;
 8     struct timeval tvrecv;

 9     recvfd = Socket (pr->sasend->sa_family, SOCK_RAW, pr->icmpproto);
10     setuid (getuid());           /* don't need special permissions anymore */

11 #ifdef  IPV6
12     if (pr->sasend->sa_family == AF_INET6 && verbose == 0) {
13         struct icmp6_filter myfilt;
14         ICMP6_FILTER_SETBLOCKALL (&myfilt);
15         ICMP6_FILTER_SETPASS (ICMP6_TIME_EXCEEDED, &myfilt);
16         ICMP6_FILTER_SETPASS (ICMP6_DST_UNREACH, &myfilt);
17         setsockopt (recvfd, IPPROTO_IPV6, ICMP6_FILTER,
18                    &myfilt, sizeof (myfilt));
19     }
20 #endif

21     sendfd = Socket (pr->sasend->sa_family, SOCK_DGRAM, 0);

22     pr->sabind->sa_family = pr->sasend->sa_family;
23     sport = (getpid() & 0xffff) | 0x8000; /* our source UDP port # */
24     sock_set_port (pr->sabind, pr->salen; htons (sport));
25     Bind (sendfd, pr->sabind, pr->salen);

26     sig_alrm (SIGALRM);

27     seq = 0;
28     done = 0;
29     for (ttl = 1; ttl <= max_ttl && done == 0; ttl++) {
30          Setsockopt (sendfd, pr->ttllevel, pr->ttloptname, &ttl, sizeof (int));
31          bzero (pr->salast, pr->salen);

32          printf ("%2d ", ttl);
33          fflush (stdout);

34          for (probe = 0; probe < nprobes; probe++) {
35               rec = (struct rec *) sendbuf;
36               rec->rec_seq = ++seq;
37               rec->rec_ttl = ttl;
38               Gettimeofday (&rec->rec_tv, NULL);

39               sock_set_port (pr->sasend, pr->salen, htons (dport + seq));
40               Sendto (sendfd, sendbuf, datalen, 0, pr->sasend, pr->salen),

41               if ( (code = (*pr->recv) (seq, &tvrecv)) == -3)
42                   printf (" *"); /* timeout, no reply */
43               else {
44                   char     str [NI_MAXHOST];

45                   if (sock_cmp_addr (pr->sarecv, pr->salast, pr->salen) != 0) {

46                   if (getnameinfo (pr->sarecv, pr->salen, str, sizeof (str),
47                                    NULL, 0, 0) == 0)
48                       printf (" %s (%s)", str,
49                               Sock_ntop_host (pr->sarecv, pr->salen));
50                  else
51                       printf (" %s", Sock_ntop_host (pr->sarecv, pr->salen));
52                  memcpy (pr->salast, pr->sarecv, pr->salen);
53          }
54          tv_sub (&tvrecv, &rec->rec_tv);
55          rtt = tvrecv.tv_sec * 1000.0 + tvrecv.tv_usec / 1000.0;
56          printf (" %.3f ms", rtt);

57          if (code == -1) /* port unreachable; at destination */
58              done++;
59          else if (code >= 0)
60              printf (" (ICMP %s)", (*pr->icmpcode) (code));
61        }
62        fflush (stdout);
63     }
64     printf ("\n");
65   }
66 }

Main loop; set TTL or hop limit and send three probes

27鈥?8 The main loop of the function is a double nested for loop. The outer loop starts the TTL or hop limit at 1, and increases it by 1, while the inner loop sends three probes (UDP datagrams) to the destination. Each time the TTL changes, we call setsockopt to set the new value using either the IP_TTL or IPV6_UNICAST_HOPS socket option.

Each time around the outer loop, we initialize the socket address structure pointed to by salast to 0. This structure will be compared to the socket address structure returned by recvfrom when the ICMP message is read, and if the two structures are different, the IP address from the new structure will be printed. Using this technique, the IP address corresponding to the first probe for each TTL is printed, and should the IP address change for a given value of the TTL (say a route changes while we are running the program), the new IP address will then be printed.

Set destination port and send UDP datagram

39鈥?0 Each time a probe packet is sent, the destination port in the sasend socket address structure is changed by calling our sock_set_port function. The reason for changing the port for each probe is that when we reach the final destination, all three probes are sent to a different port, and hopefully at least one of the ports is not in use. sendto sends the UDP datagram.

Read ICMP message

41鈥?2 One of our functions, recv_v4 or recv_v6, calls recvfrom to read and process the returned ICMP messages. These two functions return -3 if a timeout occurs (telling us to send another probe if we haven't sent three for this TTL), -2 if an ICMP "time exceeded in transit" error is received, -1 if an ICMP "port unreachable" error is received (which means we have reached the final destination), or the non-negative ICMP code if some other ICMP destination unreachable error is received.

Print reply

43鈥?3 As we mentioned earlier, if this is the first reply for a given TTL, or if the IP address of the node sending the ICMP message has changed for this TTL, we print the hostname and IP address, or just the IP address (if the call to getnameinfo doesn't return the hostname). The RTT is calculated as the time difference from when we sent the probe to the time when the ICMP message was returned and printed.

Our recv_v4 function is showin in Figure 28.20.

Figure 28.20 recv_v4 function: reads and processes ICMPv4 messages.

traceroute/recv_v4.c

 1 #include     "trace.h"

 2 extern int gotalarm;

 3 /*
 4  * Return: -3 on timeout
 5  *         -2 on ICMP time exceeded in transit (caller keeps going)
 6  *         -1 on ICMP port unreachable (caller is done)
 7  *       >= 0 return value is some other ICMP unreachable code
 8  */

 9 int
10 recv_v4(int seq, struct timeval *tv)
11 {
12     int     hlen1, hlen2, icmplen, ret;
13     socklen_t len;
14     ssize_t n;
15     struct ip *ip, *hip;
16     struct icmp *icmp;
17     struct udphdr *udp;

18     gotalarm = 0;
19     alarm(3);
20     for ( ; ; ) {
21         if (gotalarm)
22             return (-3);        /* alarm expried */
23         len = pr->salen;
24         n = recvfrom (recvfd, recvbuf, sizeof (recvbuf), 0, pr->sarecv, &len);
25         if (n < 0) {
26             if (errno == EINTR)
27                 continue;
28             else
29                  err_sys ("recvfrom error");
30          }

31          ip = (struct ip *) recvbuf; /* start of IP header */
32          hlen1 = ip->ip_hl << 2; /* length of IP header */

33          icmp = (struct icmp *) (recvbuf + hlen1); /* start of ICMP header */
34          if ( (icmplen = n - hlen1) < 8)
35              continue;     /* not enough to look at ICMP header */

36          if (icmp->icmp_type == ICMP_TIMXCEED &&
37              icmp->icmp_code == ICMP_TIMXCEED_INTRANS) {
38              if (icmplen < 8 + sizeof (struct ip))
39              continue;         /* not enough data to look at inner IP */

40          hip = (struct ip *) (recvbuf + hlen1 + 8);
41          hlen2 = hip->ip_hl << 2;
42          if (icmplen < 8 + hlen2 + 4)
43              continue;        /* not enough data to look at UDP ports */

44          udp = (struct udphdr *) (recvbuf + hlen1 + 8 + hlen2);
45          if (hip->ip_p == IPPROTO_UDP &&
46              udp->uh_sport == htons (sport) &&
47              udp->uh_dport == htons (dport + seq)) {
48              ret = -2;        /* we hit an intermediate router */
49              break;
50          }

51     } else if (icmp->icmp_type == ICMP_UNREACH) {
52          if (icmplen <8 + sizeof (struct ip))
53               continue;       /* not enough data to look at inner IP */

54          hip = (struct ip *) (recvbuf + hlen1 + 8);
55          hlen2 = hip->ip_hl << 2;
56          if (icmplen < 8 + hlen2 + 4)
57              continue;       /* not enough data to look at UDP ports */

58          udp = (struct udphdr *) (recvbuf + hlen1 + 8 + hlen2);
59          if (hip->ip_p == IPPROTO_UDP &&
60              udp->uh_sport == htons (sport) &&
61              udp->uh_dport == htons (dport + seq)) {
62              if (icmp->icmp_code == ICMP_UNREACH_PORT)
63                  ret = -1;    /* have reached destination */
64              else
65                  ret = icmp->icmp_code; /* 0, 1, 2, ... */
66              break;
67          }
68     }
69     if (verbose) {
70         printf (" (from %s: type = %d, code = %d)\n",
71                 Sock_ntop_host (pr->sarecv, pr->salen),
72                 icmp->icmp_type, icmp->icmp_code);
73     }
74     /* Some other ICMP error, recvfrom() again */
75     }
76     alarm(0);                   /* don't leave alarm running */
77     Gettimeofday (tv, NULL);    /* get time of packet arrival */
78     return (ret);
79 }

Set alarm and read each ICMP message

19鈥?0 An alarm is set for three seconds in the future and the function enters a loop that calls recvfrom, reading each ICMPv4 message returned on the raw socket.

This function avoids the race condition we described in Section 20.5 by using a global flag.

Get pointer to ICMP header

31鈥?5 ip points to the beginning of the IPv4 header (recall that a read on a raw socket always returns the IP header), and icmp points to the beginning of the ICMP header. Figure 28.21 shows the various headers, pointers, and lengths used by the code.

Figure 28.21. Headers, pointers, and lengths in processing ICMPv4 error.

graphics/28fig21.gif

Process ICMP "time exceeded in transit" message

36鈥?0 If the ICMP message is a "time exceeded in transit" message, it is possibly a reply to one of our probes. hip points to the IPv4 header that is returned in the ICMP message following the 8-byte ICMP header. udp points to the UDP header that follows. If the ICMP message was generated by a UDP datagram and if the source and destination ports of that datagram are the values we sent, then this is a reply to our probe from an intermediate router.

Process ICMP "port unreachable" message

51鈥?8 If the ICMP message is "destination unreachable," then we look at the UDP header returned in the ICMP message to see if the message is a response to our probe. If so, and if the ICMP code is "port unreachable," we return -1 as we have reached the final destination. If the ICMP message is from one of our probes but it is not a "port unreachable," then that ICMP code value is returned. A common example of this is a firewall returning some other unreachable code for the destination host we are probing.

Handle other ICMP messages

69鈥?3 All other ICMP messages are printed if the -v flag was specified.

The next function, recv_v6, is shown in Figure 28.24 (p.767) and is the IPv6 equivalent to the previously described function. This function is nearly identical to recv_v4 except for the different constant names and the different structure member names. Also, the IPv6 header is not part of the data received on an IPv6 raw socket; the data starts with the ICMPv6 header. Figure 28.22 shows the various headers, pointers, and lengths used by the code.

Figure 28.22. Headers, pointers, and lengths in processing ICMPv6 error.

graphics/28fig22.gif

We define two functions, icmpcode_v4 and icmpcode_v6, that can be called from the bottom of the traceloop function to print a description string corresponding to an ICMP "destination unreachable" error. Figure 28.25 (p.767) shows just the IPv6 function. The IPv4 function is similar, albeit longer, as there are more ICMPv4 "destination unreachable" codes (Figure A.15).

The final function in our traceroute program is our SIGALRM handler, the sig_alrm function shown in Figure 28.23. All this function does is return, causing an error return of EINTR from the recvfrom in either recv_v4 or recv_v6.

Figure 28.23 sig_alrm function.

traceroute/sig_alrm.c

1 #include     "trace.h"

2 int     gotalarm;

3 void
4 sig_alrm(int signo)
5 {
6     gotalarm = 1:         /* set flag to note that alarm occurred */
7     return;               /* and interrupt the recvfrom() */
8  }
Figure 28.24 recv_v6 function: reads and processes ICMPv6 messages.

traceroute/recv_v6.c

 1 #include     "trace.h"

 2 extern int gotalarm;

 3 /*
 4  * Return: -3 on timeout
 5  *         -2 on ICMP time exceeded in transit (caller keeps going)
 6  *         -1 on ICMP port unreachable (caller is done)
 7  *       >= 0 return value is some other ICMP unreachable code
 8  */

 9 int
10 recv_v6 (int seq, struct timeval *tv)
11 {
12 #ifdef IPV6
13     int     hlen2, icmp6len, ret;
14     ssize_t n;
15     socklen_t len;
16     struct ip6_hdr *hip6;
17     struct icmp6_hdr *icmp6;
18     struct udphdr *udp;

19     gotalarm = 0;
20     alarm(3);
21     for ( ; ; ) {
22         if (gotalarm)
23             return (-3);         /* alarm expired */
24         len = pr->salen;
25         n = recvfrom (recvfd, recvbuf, sizeof (recvbuf), 0, pr->sarecv, &len);
26         if (n < 0) {
27             if (errno == EINTR)
28                 continue;
29             else
30                 err_sys ("recvfrom error");
31          }

32          icmp6 = (struct icmp6_hdr *) recvbuf; /* ICMP header */
33          if ( (icmp6len = n) < 8)
34                continue;         /* not enough to look at ICMP header */

35          if (icmp6->icmp6_type == ICMP6_TIME_EXCEEDED &&
36              icmp6->icmp6_code == ICMP6_TIME_EXCEED_TRANSIT) {
37              if (icmp6len < 8 + sizeof (struct ip6_hdr) + 4)
38                  continue;       /* not enough data to look at inner header */

39              hip6 = (struct ip6_hdr *) (recvbuf + 8);
40              hlen2 = sizeof (struct ip6_hdr);
41              udp = (struct udphdr *) (recvbuf + 8 + hlen2);
42              if (hip6->ip6_nxt == IPPROTO_UDP &&
43                  udp->uh_sport == htons (sport) &&
44                  udp->uh_dport == htons (dport + seq))
45                  ret = -2;     /* we hit an intermediate router */
46              break;

47          } else if (icmp6->icmp6_type == ICMP6_DST_UNREACH) {
48              if (icmp6len <8 + sizeof (struct ip6_hdr) + 4)
49                  continue;       /* not enough data to look at inner header */
50          hip6 = (struct ip6_hdr *) (recvbuf + 8);
51          hlen2 = sizeof (struct ip6_hdr);
52          udp = (struct udphdr *) (recvbuf + 8 + hlen2);
53          if (hip6->ip6_nxt == IPPROTO_UDP &&
54              udp->uh_sport == htons (sport) &&
55              udp->uh_dport == htons (dport + seq)) {
56              if (icmp6->icmp6_code == ICMP6_DST_UNREACH_NOPORT)
57                  ret = -1; /* have reached destination */
58              else
59                  ret = icmp6->icmp6_code; /* 0, 1, 2, ... */
60              break;
61          }
62     } else if (verbose) {
63          printf (" (from %s: type = %d, code = %d)\n",
64                  Sock_ntop_host (pr->sarecv, pr->salen),
65                  icmp6->icmp6_type, icmp6->icmp6_code);
66          }
67          /* Some other ICMP error, recvfrom () again */
68     }
69     alarm (0);                   /* don't leave alarm running */
70     Gettimeofday (tv, NULL);     /* get time of packet arrival */
71     return (ret);
72 #endif
73 }
Figure 28.25 Return the string corresponding to an ICMPv6 unreachable code.

traceroute/icmpcode_v6.c

 1 #include     "trace.h"

 2 const char *
 3 icmpcode_v6 (int code)
 4 {
 5 #ifdef IPV6
 6     static char errbuf [100];
 7     switch (code) {
 8     case ICMP6_DST_UNREACH_NOROUTE:
 9         return ("no route to host");
10     case ICMP6_DST_UNREACH_ADMIN:
11         return ("administratively prohibited");
12     case ICMP6_DST_UNREACH_NOTNEIGHBOR:
13         return ("not a neighbor");
14     case ICMP6_DST_UNREACH_ADDR:
15         return ("address unreachable");
16     case ICMP6_DST_UNREACH_NOPORT:
17         return ("port unreachable");
18     default:
19          sprintf (errbuf, "[unknown code %d]", code);
20          return errbuf;
21     }
22 #endif
23 }

Example

We first show an example using IPv4.


freebsd % traceroute www.unpbook.com
traceroute to www.unpbook.com (206.168.112.219): 30 hops max, 24 data bytes
 1  12.106.32.1 (12.106.32.1) 0.799 ms 0.719 ms 0.540 ms
 2  12.124.47.113 (12.124.47.113) 1.758 ms 1.760 ms 1.839 ms
 3  gbr2-p27.sffca.ip.att.net (12.123.195.38) 2.744 ms 2.575 ms 2.648 ms
 4  tbr2-p012701.sffca.ip.att.net (12.122.11.85) 3.770 ms 3.689 ms 3.848 ms
 5  gbr3-p50.dvmco.ip.att.net (12.122.2.66) 26.202 ms 26.242 ms 26.102 ms
 6  gbr2-p20.dvmco.ip.att.net (12.122.5.26) 26.255 ms 26.194 ms 26.470 ms
 7  gar2-p370.dvmco.ip.att.net (12.123.36.141) 26.443 ms 26.310 ms 26.427 ms
 8  att-46.den.internap.ip.att.net (12.124.158.58) 26.962 ms 27.130 ms
                                                                    27.279 ms
 9  border10.ge3-0-bbnet2.den.pnap.net (216.52.40.79) 27.285 ms 27.293 ms
                                                                    26.860 ms
10  coop-2.border10.den.pnap.net (216.52.42.118) 28.721 ms 28.991 ms
                                                                   30.077 ms
11  199.45.130.33 (199.45.130.33) 29.095 ms 29.055 ms 29.378 ms
12  border-to-141-netrack.boulder.co.coop.net (207.174.144.178) 30.875 ms
                                                           29.747 ms 30.142 ms
13  linux.unpbook.com (206.168.112.219) 31.713 ms 31.573 ms 33.952 ms

We have wrapped the long lines for a more readable output.

Here is an example using IPv6.


freebsd % traceroute www.kame.net
traceroute to orange.kame.net (2001:200:0:4819:203:47ff:fea5:3085):
            30 hops max, 24 data bytes
 1  3ffe:b80:3:9adl::1 (3ffe:b80:3:9adl::1) 107.437 ms 99.341 ms 103.477 ms
 2  Viagenie-gw.int.ipv6.ascc.net (2001:288:3b0::55)
            105.129 ms 89.418 ms 90.016 ms
3  gw-Viagenie.int.ipv6.ascc.net (2001:288:3b0::54)
             302.300 ms 291.580 ms 289.839 ms
 4  c7513-gw.int.ipv6.ascc.net (2001:288:3b0::c)
            296.088 ms 298.600 ms 292.196 ms
 5  m160-c7513.int.ipv6.ascc.net (2001:288:3b0::1e)
            296.266 ms 314.878 ms 302.429 ms
 6  m20jp-m160tw.int.ipv6.ascc.net (2001:288:3b0::1b)
            327.637 ms 326.897 ms 347.062 ms
 7  hitachil.otemachi.wide.ad.jp (2001:200:0:1800::9c4:2)
            420.140 ms 426.592 ms 422.756 ms
 8  pc3.yagami.wide.ad.jp (2001:200:0:1c04::1000:2000)
            415.471 ms 418.308 ms 461.654 ms
 9  gr2000.k2c.wide.ad.jp (2001:200:0:8002::2000:1)
            416.581 ms 422.430 ms 427.692 ms
10  2001:200:0:4819:203:47ff:fea5:3085 (2001:200:0:4819:203:47ff:fea5:3085)
            417.169 ms 434.674 ms 424.037 ms

We have wrapped the long lines for a more readable output.

[ Team LiB ] Previous Section Next Section
Converted from CHM to HTML with chm2web Pro 2.85 (unicode)