From 4c17d895d13ff17d2511d13a4136f9a16e4a6498 Mon Sep 17 00:00:00 2001 From: pbldp <> Date: Thu, 31 May 2007 18:51:47 +0000 Subject: [PATCH] Import contribution for Programming C-API, minor fixes --- .../Peter-Bieringer/Linux+IPv6-HOWTO.lyx | 9064 ++++++++++++++++- .../Peter-Bieringer/Linux+IPv6-HOWTO.sgml | 2200 +++- 2 files changed, 11074 insertions(+), 190 deletions(-) diff --git a/LDP/users/Peter-Bieringer/Linux+IPv6-HOWTO.lyx b/LDP/users/Peter-Bieringer/Linux+IPv6-HOWTO.lyx index 0121a7ca..e78b148d 100644 --- a/LDP/users/Peter-Bieringer/Linux+IPv6-HOWTO.lyx +++ b/LDP/users/Peter-Bieringer/Linux+IPv6-HOWTO.lyx @@ -71,8 +71,8 @@ status inlined \begin_layout Standard - Release 0.52wip 2007-04-16 - PB + 0.60 2007-05-31 PB + \end_layout \end_inset @@ -86,8 +86,8 @@ status inlined \begin_layout Standard - Release 0.51 2006-11-08 PB + 0.51 2006-11-08 PB + \end_layout \end_inset @@ -194,7 +194,11 @@ The Linux IPv6 HOWTO, a guide how to configure and use IPv6 on Linux systems. \end_layout \begin_layout Standard -Copyright (C) 2001-2007 Peter Bieringer +Copyright +\lang ngerman +© +\lang english + 2001-2007 Peter Bieringer \newline \end_layout @@ -1129,7 +1133,9 @@ Experience with networking theory You should know about layers, protocols, addresses, cables, plugs, etc. If you are new to this field, here is one good starting point for you: -\begin_inset LatexCommand \url[linuxports/howto/intro_to_networking]{http://www.linuxports.com/howto/intro_to_networking/} +\lang ngerman + +\begin_inset LatexCommand \url[http://www.rigacci.org/docs/biblio/online/intro_to_networking/book1.htm]{http://www.rigacci.org/docs/biblio/online/intro_to_networking/book1.htm} \end_inset @@ -4814,7 +4820,8 @@ Usage: \end_layout \begin_layout Code -# /sbin/route -A inet6 del / [dev ] +# /sbin/route -A inet6 del / gw [dev + ] \end_layout \begin_layout Standard @@ -4898,7 +4905,7 @@ Usage: \end_layout \begin_layout Code -# /sbin/route -A inet6 add / dev +# /sbin/route -A inet6 add / dev \end_layout \begin_layout Standard @@ -13511,16 +13518,11 @@ Programming \end_inset -Programming (using API) +Programming using C-API \end_layout \begin_layout Standard -I have no experience in IPv6 programming, perhaps this chapter will be filled - by others or moved away to another HOWTO. -\end_layout - -\begin_layout Standard -More Information can be found here: +Related RFCs: \end_layout \begin_layout Itemize @@ -13539,27 +13541,8970 @@ More Information can be found here: \end_layout -\begin_layout Itemize - -\lang ngerman -\begin_inset LatexCommand \url[Porting applications to IPv6 HowTo]{http://jungla.dit.upm.es/~ecastro/IPv6-web/ipv6.html} +\begin_layout Standard +Following contents of this section is contributed by John Wenker, Sr. + Software Engineer Performance Technologies San Diego, CA USA +\begin_inset LatexCommand \url[http://www.pt.com/]{http://www.pt.com/} \end_inset - by Eva M. - Castro -\end_layout - -\begin_layout Section -Languages -\end_layout - -\begin_layout Subsection -C +. \end_layout \begin_layout Standard -(please contribute) +\begin_inset VSpace defskip +\end_inset + + +\end_layout + +\begin_layout Standard +This section describes how to write IPv6 client-server applications under + the Linux operating system. + First thing's first, and credit must be given where it is due. + The information contained in this section is derived from Chapters 2 through + 4 of IPv6 Network Programming by Jun-ichiro itojun Hagino (ISBN 1-55558-318-0). + The reader is encouraged to consult that book for more detailed information. + It describes how to convert IPv4 applications to be IPv6 compatible in + a protocol-independent way, and describes some of the common +\bar under +problems +\bar default + encountered during the conversion along with suggested solutions. + At the time of this writing, this is the only book of which the author + is aware that specifically addresses how to program IPv6 applications [since + writing this section, the author has also become aware of the Porting applicati +ons to IPv6 HowTo by Eva M. + Castro at +\begin_inset LatexCommand \url[ Since writing this HowTo, the author has also become aware of the Porting applications to IPv6 HowTo by Eva M. Castro at http://jungla.dit.upm.es/~ecastro/IPv6-web/ipv6.html]{ Since writing this HowTo, the author has also become aware of the Porting applications to IPv6 HowTo by Eva M. Castro at http://jungla.dit.upm.es/~ecastro/IPv6-web/ipv6.html} + +\end_inset + +]. + Unfortunately, of the almost 360 pages in the book, maybe 60 are actually + useful (the chapters mentioned). + Nevertheless, without the guidance of that book, the author would have + been unable to perform his job duties or compose this HowTo. + While most (but certainly not all) of the information in the Hagino book + is available via the Linux 'man' pages, application programmers will save + a significant amount of time and frustration by reading the indicated chapters + of the book rather than searching through the 'man' pages and online documentat +ion. +\end_layout + +\begin_layout Standard +Other than the Hagino book, any other information presented in this HowTo + was obtained through trial and error. + Some items or explanations may not be entirely +\begin_inset Quotes sld +\end_inset + +correct +\begin_inset Quotes srd +\end_inset + + in the grand IPv6 scheme, but seem to work in practical application. +\end_layout + +\begin_layout Standard +The discussion that follows assumes the reader is already experienced with + the traditional TCP/IP socket API. + For more information on traditional socket programming, the Internetworking + with TCP/IP series of textbooks by Comer & Stevens is hard to beat, specificall +y Volume III: Client-Server Programming and Applications, Linux/POSIX Sockets + Version (ISBN 0-13-032071-4). + This HowTo also assumes that the reader has had at least a bare basic introduct +ion to IPv6 and in particular the addressing scheme for network addresses + (see Section 2.3). +\end_layout + +\begin_layout Subsection +Address Structures +\end_layout + +\begin_layout Standard +This section provides a brief overview of the structures provided in the + socket API to represent network addresses (or more specifically transport + endpoints) when using the Internet protocols in a client-server application. +\end_layout + +\begin_layout Subsubsection +IPv4 sockaddr_in +\end_layout + +\begin_layout Standard +In IPv4, network addresses are 32 bits long and define a network node. + Addresses are written in dotted decimal notation, such as 192.0.2.1, where + each number represents eight bits of the address. + Such an IPv4 address is represented by the +\family typewriter +struct sockaddr_in +\family default + data type, which is defined in +\family typewriter + +\family default +. +\end_layout + +\begin_layout Code +struct sockaddr_in +\end_layout + +\begin_layout Code +{ +\end_layout + +\begin_layout Code + sa_family_t sin_family; +\end_layout + +\begin_layout Code + in_port_t sin_port; +\end_layout + +\begin_layout Code + struct in_addr sin_addr; +\end_layout + +\begin_layout Code + /* Plus some padding for alignment */ +\end_layout + +\begin_layout Code +}; +\end_layout + +\begin_layout Standard +The +\family typewriter +sin_family +\family default + component indicates the address family. + For IPv4 addresses, this is always set to +\family typewriter +AF_INET +\family default +. + The +\family typewriter +sin_addr +\family default + field contains the 32-bit network address (in network byte order). + Finally, the +\family typewriter +sin_port +\family default + component represents the transport layer port number (in network byte order). + Readers should already be familiar with this structure, as this is the + standard IPv4 address structure. +\end_layout + +\begin_layout Subsubsection +IPv6 sockaddr_in6 +\end_layout + +\begin_layout Standard +The biggest feature of IPv6 is its increased address space. + Instead of 32-bit network addresses, IPv6 allots 128 bits to an address. + Addresses are written in colon-hex notation of the form fe80::2c0:8cff:fe01:234 +5, where each hex number separated by colons represents 16 bits of the address. + Two consecutive colons indicate a string of consecutive zeros for brevity, + and at most only one double-colon may appear in the address. + IPv6 addresses are represented by the +\family typewriter +struct sockaddr_in6 +\family default + data type, also defined in +\family typewriter + +\family default +. +\end_layout + +\begin_layout Code +struct sockaddr_in6 +\end_layout + +\begin_layout Code +{ +\end_layout + +\begin_layout Code + sa_family_t sin6_family; +\end_layout + +\begin_layout Code + in_port_t sin6_port; +\end_layout + +\begin_layout Code + uint32_t sin6_flowinfo; +\end_layout + +\begin_layout Code + struct in6_addr sin6_addr; +\end_layout + +\begin_layout Code + uint32_t sin6_scope_id; +\end_layout + +\begin_layout Code +}; +\end_layout + +\begin_layout Standard +The +\family typewriter +sin6_family +\family default +, +\family typewriter +sin6_port +\family default +, and +\family typewriter +sin6_addr +\family default + components of the structure have the same meaning as the corresponding + fields in the +\family typewriter +sockaddr_in +\family default + structure. + However, the +\family typewriter +sin6_family +\family default + member is set to +\family typewriter +AF_INET6 +\family default + for IPv6 addresses, and the +\family typewriter +sin6_addr +\family default + field holds a 128-bit address instead of only 32 bits. +\end_layout + +\begin_layout Standard +The +\family typewriter +sin6_flowinfo +\family default + field is used for flow control, but is not yet standardized and can be + ignored. +\end_layout + +\begin_layout Standard +The +\family typewriter +sin6_scope_id +\family default + field has an odd use, and it seems (at least to this naïve author) that + the IPv6 designers took a huge step backwards when devising this. + Apparently, 128-bit IPv6 network addresses are not unique. + For example, it is possible to have two hosts, on separate networks, with + the same link-local address (see Figure 1). + In order to pass information to a specific host, more than just the network + address is required; the scope identifier must also be specified. + In Linux, the network interface name is used for the scope identifier (e.g. + +\begin_inset Quotes sld +\end_inset + +eth0 +\begin_inset Quotes srd +\end_inset + +) [be warned that the scope identifier is implementation dependent!]. + Use the +\family typewriter +ifconfig(1M) +\family default + command to display a list of active network interfaces. +\end_layout + +\begin_layout Standard +A colon-hex network address can be augmented with the scope identifier to + produce a "scoped address +\begin_inset Quotes srd +\end_inset + +. + The percent sign ('%') is used to delimit the network address from the + scope identifier. + For example, +\family typewriter +fe80::1%eth0 +\family default + is a scoped IPv6 address where +\family typewriter +fe80::1 +\family default + represents the 128-bit network address and +\family typewriter +eth0 +\family default + is the network interface (i.e. + the scope identifier). + Thus, if a host resides on two networks, such as Host B in example below, + the user now has to know which path to take in order to get to a particular + host. + In Figure 1, Host B addresses Host A using the scoped address +\family typewriter +fe80::1%eth0 +\family default +, while Host C is addressed with +\family typewriter +fe80::1%eth1 +\family default +. +\end_layout + +\begin_layout Code +Host A (fe80::1) ---- eth0 ---- Host B ---- eth1 ---- Host C (fe80::1) +\end_layout + +\begin_layout Standard +Getting back to the +\family typewriter +sockaddr_in6 +\family default + structure, its +\family typewriter +sin6_scope_id +\family default + field contains the index of the network interface on which a host may be + found. + Server applications will have this field set automatically by the socket + API when they accept a connection or receive a datagram. + For client applications, if a scoped address is passed as the node parameter + to +\family typewriter +getaddrinfo(3) +\family default + (described later in this HowTo), then the +\family typewriter +sin6_scope_id +\family default + field will be filled in correctly by the system upon return from the function; + if a scoped address is not supplied, then the sin6_scope_id field must + be explicitly set by the client software prior to attempting to communicate + with the remote server. + The +\family typewriter +if_nametoindex(3) +\family default + function is used to translate a network interface name into its corresponding + index. + It is declared in +\family typewriter + +\family default +. +\end_layout + +\begin_layout Subsubsection +Generic Addresses +\end_layout + +\begin_layout Standard +As any programmer familiar with the traditional TCP/IP socket API knows, + several socket functions deal with "generic" pointers. + For example, a pointer to a generic +\family typewriter +struct sockaddr +\family default + data type is passed as a parameter to some socket functions (such as +\family typewriter +connect(2) +\family default + or +\family typewriter +bind(2) +\family default +) rather than a pointer to a specific address type. + Be careful\SpecialChar \ldots{} + the +\family typewriter +sockaddr_in6 +\family default + structure is larger than the generic +\family typewriter +sockaddr +\family default + structure! Thus, if your program +\bar under +receives +\bar default + a generic address whose actual type is unknown (e.g. + it could be an IPv4 address structure or an IPv6 address structure), you + must supply sufficient storage to hold the entire address. + The +\family typewriter +struct sockaddr_storage +\family default + data type is defined in +\family typewriter + +\family default + for this purpose [do not #include this file directly within an application; + use +\family typewriter + +\family default + as usual, and +\family typewriter + +\family default + will be implicitly included]. +\end_layout + +\begin_layout Standard +For example, consider the +\family typewriter +recvfrom(2) +\family default + system call, which is used to receive a message from a remote peer. + Its function prototype is: +\end_layout + +\begin_layout Code +ssize_t recvfrom( int s, +\end_layout + +\begin_layout Code + void *buf, +\end_layout + +\begin_layout Code + size_t len, +\end_layout + +\begin_layout Code + int flags, +\end_layout + +\begin_layout Code + struct sockaddr *from, +\end_layout + +\begin_layout Code + socklen_t *fromlen ); +\end_layout + +\begin_layout Standard +The from parameter points to a generic +\family typewriter +sockaddr +\family default + structure. + If data can be received from an IPv6 peer on the socket referenced by +\family typewriter +s +\family default +, then +\family typewriter +from +\family default + should point to a data type of +\family typewriter +struct sockaddr_storage +\family default +, as in the following dummy example: +\end_layout + +\begin_layout Code +/* +\end_layout + +\begin_layout Code +** Read a message from a remote peer, and return a buffer pointer to +\end_layout + +\begin_layout Code +** the caller. +\end_layout + +\begin_layout Code +** +\end_layout + +\begin_layout Code +** 's' is the file descriptor for the socket. +\end_layout + +\begin_layout Code +*/ +\end_layout + +\begin_layout Code +char *rcvMsg( int s ) +\end_layout + +\begin_layout Code +{ +\end_layout + +\begin_layout Code + static char bfr[ 1025 ]; /* Where the msg is stored. + */ +\end_layout + +\begin_layout Code + ssize_t count; +\end_layout + +\begin_layout Code + struct sockaddr_storage ss; /* Where the peer adr goes. + */ +\end_layout + +\begin_layout Code + socklen_t sslen; +\end_layout + +\begin_layout Code + sslen = sizeof( ss ); +\end_layout + +\begin_layout Code + count = recvfrom( s, +\end_layout + +\begin_layout Code + bfr, +\end_layout + +\begin_layout Code + sizeof( bfr ) - 1, +\end_layout + +\begin_layout Code + 0, +\end_layout + +\begin_layout Code + (struct sockaddr*) &ss, +\end_layout + +\begin_layout Code + &sslen ); +\end_layout + +\begin_layout Code + bfr[ count ] = ' +\backslash +0'; /* Null-terminates the message. + */ +\end_layout + +\begin_layout Code + return bfr; +\end_layout + +\begin_layout Code +} /* End rcvMsg() */ +\end_layout + +\begin_layout Standard +As seen in the above example, +\family typewriter +ss +\family default + (a +\family typewriter +struct sockaddr_storage +\family default + data object) is used to receive the peer address information, but it's + address is typecast to a generic +\family typewriter +struct sockaddr* +\family default + pointer in the call to +\family typewriter +recvfrom(2) +\family default +. + +\end_layout + +\begin_layout Subsection +Lookup Functions +\end_layout + +\begin_layout Standard +Traditionally, hostname and service name resolution were performed by functions + such as +\family typewriter +gethostbyname(3) +\family default + and +\family typewriter +getservbyname(3) +\family default +. + These traditional lookup functions are still available, but they are not + forward compatible to IPv6. + Instead, the IPv6 socket API provides new lookup functions that consolidate + the functionality of several traditional functions. + These new lookup functions are also backward compatible with IPv4, so a + programmer can use the same translation algorithm in an application for + both the IPv4 and IPv6 protocols. + This is an important feature, because obviously a global IPv6 infrastructure + isn't going to be put in place overnight. + Thus, during the transition period from IPv4 to IPv6, client-server application +s should be designed with the flexibility to handle both protocols simultaneousl +y. + The example programs at the end of this chapter do just that. +\end_layout + +\begin_layout Standard +The primary lookup function in the new socket API is +\family typewriter +getaddrinfo(3) +\family default +. + Its prototype is as follows. + +\end_layout + +\begin_layout Code +int getaddrinfo( const char *node, +\end_layout + +\begin_layout Code + const char *service, +\end_layout + +\begin_layout Code + const struct addrinfo *hints, +\end_layout + +\begin_layout Code + struct addrinfo **res ); +\end_layout + +\begin_layout Standard +The node parameter is a pointer to the hostname or IP address being translated. + The referenced string can be a hostname, IPv4 dotted decimal address, or + IPv6 colon-hex address (possibly scoped). + The +\family typewriter +service +\family default + parameter is a pointer to the transport layer's service name or port number. + It can be specified as a name found in +\family typewriter +/etc/services +\family default + or a decimal number. + +\family typewriter +getaddrinfo(3) +\family default + resolves the host/service combination and returns a +\bar under +list +\bar default + of address records; a pointer to the list is placed in the location pointed + at by +\family typewriter +res +\family default +. + For example, suppose a host can be identified by both an IPv4 and IPv6 + address, and that the indicated service has both a TCP entry and UDP entry + in +\family typewriter +/etc/services +\family default +. + In such a scenario, it is not inconceivable that four address records are + returned; one for TCP/IPv6, one for UDP/IPv6, one for TCP/IPv4, and one + for UDP/IPv4. +\end_layout + +\begin_layout Standard +The definition for +\family typewriter +struct addrinfo +\family default + is found in +\family typewriter + +\family default + (as is the declaration for getaddrinfo(3) and the other functions described + in this section). + The structure has the following format: +\end_layout + +\begin_layout Code +struct addrinfo +\end_layout + +\begin_layout Code +{ +\end_layout + +\begin_layout Code + int ai_flags; +\end_layout + +\begin_layout Code + int ai_family; +\end_layout + +\begin_layout Code + int ai_socktype; +\end_layout + +\begin_layout Code + int ai_protocol; +\end_layout + +\begin_layout Code + socklen_t ai_addrlen; +\end_layout + +\begin_layout Code + struct sockaddr *ai_addr; +\end_layout + +\begin_layout Code + char *ai_canonname; +\end_layout + +\begin_layout Code + struct addrinfo *ai_next; +\end_layout + +\begin_layout Code +}; +\end_layout + +\begin_layout Standard +Consult the 'man' page for +\family typewriter +getaddrinfo(3) +\family default + for detailed information about the various fields; this HowTo only describes + a subset of them, and only to the extent necessary for normal IPv6 programming. +\end_layout + +\begin_layout Standard +The +\family typewriter +ai_family +\family default +, +\family typewriter +ai_socktype +\family default +, and +\family typewriter +ai_protocol +\family default + fields have the exact same meaning as the parameters to the +\family typewriter +socket(2) +\family default + system call. + The +\family typewriter +ai_family +\family default + field indicates the +\bar under +protocol +\bar default + family (not the address family) associated with the record, and will be + +\family typewriter +PF_INET6 +\family default + for IPv6 or +\family typewriter +PF_INET +\family default + for IPv4. + The +\family typewriter +ai_socktype +\family default + parameter indicates the type of socket to which the record corresponds; + +\family typewriter +SOCK_STREAM +\family default + for a reliable connection-oriented byte-stream or +\family typewriter +SOCK_DGRAM +\family default + for connectionless communication. + The +\family typewriter +ai_protocol +\family default + field specifies the underlying transport protocol for the record. +\end_layout + +\begin_layout Standard +The +\family typewriter +ai_addr +\family default + field points to a generic +\family typewriter +struct sockaddr +\family default + object. + Depending on the value in the +\family typewriter +ai_family +\family default + field, it will point to either a +\family typewriter +struct sockaddr_in +\family default + ( +\family typewriter +PF_INET +\family default +) or a +\family typewriter +struct sockaddr_in6 +\family default + ( +\family typewriter +PF_INET6 +\family default +). + The +\family typewriter +ai_addrlen +\family default + field contains the size of the object pointed at by the +\family typewriter +ai_addr +\family default + field. +\end_layout + +\begin_layout Standard +As mentioned, +\family typewriter +getaddrinfo(3) +\family default + returns a list of address records. + The +\family typewriter +ai_next +\family default + field points to the next record in the list. +\end_layout + +\begin_layout Standard +The +\family typewriter +hints +\family default + parameter to +\family typewriter +getaddrinfo(3) +\family default + is also of type +\family typewriter +struct addrinfo +\family default + and acts as a filter for the address records returned in +\family typewriter +res +\family default +. + If +\family typewriter +hints +\family default + is +\family typewriter +NULL +\family default +, all matching records are returned; but if +\family typewriter +hints +\family default + is non- +\family typewriter +NULL +\family default +, the referenced structure gives "hints" to +\family typewriter +getaddrinfo(3) +\family default + about which records to return. + Only the +\family typewriter +ai_flags +\family default +, +\family typewriter +ai_family +\family default +, +\family typewriter +ai_socktype +\family default +, and +\family typewriter +ai_protocol +\family default + fields are significant in the +\family typewriter +hints +\family default + structure, and all other fields should be set to zero. +\end_layout + +\begin_layout Standard +Programs can use +\family typewriter +hints->ai_family +\family default + to specify the protocol family. + For example, if it is set to +\family typewriter +PF_INET6 +\family default +, then only IPv6 address records are returned. + Likewise, setting +\family typewriter +hints->ai_family +\family default + to +\family typewriter +PF_INET +\family default + results in only IPv4 address records being returned. + If an application wants both IPv4 and IPv6 records, the field should be + set to +\family typewriter +PF_UNSPEC +\family default +. +\end_layout + +\begin_layout Standard +The +\family typewriter +hints->socktype +\family default + field can be set to +\family typewriter +SOCK_STREAM +\family default + to return only records that correspond to connection-oriented byte streams, + +\family typewriter +SOCK_DGRAM +\family default + to return only records corresponding to connectionless communication, or + +\family typewriter +0 +\family default + to return both. +\end_layout + +\begin_layout Standard +For the Internet protocols, there is only one protocol associated with connectio +n-oriented sockets (TCP) and one protocol associated with connectionless + sockets (UDP), so setting +\family typewriter +hints->ai_socktype +\family default + to +\family typewriter +SOCK_STREAM +\family default + or +\family typewriter +SOCK_DGRAM +\family default + is the same as saying, "Give me only TCP records," or "Give me only UDP + records," respectively. + With that in mind, the +\family typewriter +hints->ai_protocol +\family default + field isn't really that important with the Internet protocols, and pretty + much mirrors the +\family typewriter +hints->ai_socktype +\family default + field. + Nevertheless, +\family typewriter +hints->ai_protocol +\family default + can be set to +\family typewriter +IPPROTO_TCP +\family default + to return only TCP records, +\family typewriter +IPPROTO_UDP +\family default + to return only UDP records, or +\family typewriter +0 +\family default + for both. +\end_layout + +\begin_layout Standard +The +\family typewriter +node +\family default + or +\family typewriter +service +\family default + parameter to +\family typewriter +gethostbyname(3) +\family default + can be +\family typewriter +NULL +\family default +, but not both. + If +\family typewriter +node +\family default + is +\family typewriter +NULL +\family default +, then the +\family typewriter +ai_flags field +\family default + of the +\family typewriter +hints +\family default + parameter specifies how the network address in a returned record is set + (i.e. + the +\family typewriter +sin_addr +\family default + or +\family typewriter +sin6_addr +\family default + field of the object pointed at by the +\family typewriter +ai_addr +\family default + component in a returned record). + If the +\family typewriter +AI_PASSIVE +\family default + flag is set in +\family typewriter +hints +\family default +, then the returned network addresses are left unresolved (all zeros). + This is how server applications would use +\family typewriter +getaddrinfo(3) +\family default +. + If the flag is not set, then the address is set to the local loopback address + (::1 for IPv6 or 127.0.0.1 for IPv4). + This is one way a client application can specify that the target server + is running on the same machine as the client. + If the +\family typewriter +service +\family default + parameter is +\family typewriter +NULL +\family default +, the port number in the returned address records remains unresolved. +\end_layout + +\begin_layout Standard +The +\family typewriter +getaddrinfo(3) +\family default + function returns zero on success, or an error code. + In the case of an error, the +\family typewriter +gai_strerror(3) +\family default + function is used to obtain a character pointer to an error message correspondin +g to the error code, just like +\family typewriter +strerror(3) +\family default + does in the standard 'C' library. +\end_layout + +\begin_layout Standard +Once the address list is no longer needed, it must be freed by the application. + This is done with the +\family typewriter +freeaddrinfo(3) +\family default + function. +\end_layout + +\begin_layout Standard +The last function that will be mentioned in this section is +\family typewriter +getnameinfo(3) +\family default +. + This function is the inverse of +\family typewriter +getaddrinfo(3) +\family default +; it is used to create a string representation of the hostname and service + from a generic +\family typewriter +struct sockaddr +\family default + data object. + It has the following prototype. + +\end_layout + +\begin_layout Code +int getnameinfo( const struct sockaddr *sa, +\end_layout + +\begin_layout Code + socklen_t salen, +\end_layout + +\begin_layout Code + char *host, +\end_layout + +\begin_layout Code + size_t hostlen, +\end_layout + +\begin_layout Code + char *serv, +\end_layout + +\begin_layout Code + size_t servlen, +\end_layout + +\begin_layout Code + int flags ); +\end_layout + +\begin_layout Standard +The +\family typewriter +sa +\family default + parameter points to the address structure in question, and +\family typewriter +salen +\family default + contains its size. + The +\family typewriter +host +\family default + parameter points to a buffer where the null-terminated hostname string + is placed, and the hostlen parameter is the size of that buffer. + If there is no hostname that corresponds to the address, then the network + address (dotted decimal or colon-hex) is placed in +\family typewriter +host +\family default +. + Likewise, the +\family typewriter +serv +\family default + parameter points to a buffer where the null-terminated service name string + (or port number) is placed, and the +\family typewriter +servlen +\family default + parameter is the size of that buffer. + The +\family typewriter +flags +\family default + parameter modifies the function's behavior; in particular, the +\family typewriter +NI_NUMERICHOST +\family default + flag indicates that the converted hostname should always be formatted in + numeric form (i.e. + dotted decimal or colon-hex), and the +\family typewriter +NI_NUMERICSERV +\family default + flag indicates that the converted service should always be in numeric form + (i.e. + the port number). +\end_layout + +\begin_layout Standard +The symbols +\family typewriter +NI_MAXHOST +\family default + and +\family typewriter +NI_MAXSERV +\family default + are available to applications and represent the maximum size of any converted + hostname or service name, respectively. + Use these when declaring output buffers for +\family typewriter +getnameinfo(3) +\family default +. +\end_layout + +\begin_layout Subsection +Quirks Encountered +\end_layout + +\begin_layout Standard +Before jumping into the programming examples, there are several quirks in + IPv6 of which the reader should be aware. + The more significant ones (in addition to the non-uniqueness of IPv6 network + addresses already discussed) are described in the paragraphs below. + +\end_layout + +\begin_layout Subsubsection +IPv4 Mapped Addresses +\end_layout + +\begin_layout Standard +For security reasons that this author won't pretend to understand, "IPv4 + mapped addresses" should not be allowed in IPv6-capable server applications. + To put it in terms that everyone can understand, this simply means that + a server should not accept IPv4 traffic on an IPv6 socket (an otherwise + legal operation). + An IPv4 mapped address is a mixed-format address of the form: +\end_layout + +\begin_layout Code +::ffff:192.0.2.1 +\end_layout + +\begin_layout Standard +where the first portion is in IPv6 colon-hex format and the last portion + is in IPv4 dotted decimal notation. + The dotted decimal IPv4 address is the actual network address, but it is + being mapped into an IPv6 compatible format. +\end_layout + +\begin_layout Standard +To prevent IPv4 mapped addresses from being accepted on an IPv6 socket, + server applications must explicitly set the +\family typewriter +IPV6_V6ONLY +\family default + socket option on all IPv6 sockets created [the Hagino book implies that + this is only a concern with server applications. + However, it has been observed during testing that if a client application + uses an IPv4 mapped address to specify the target server, and the target + server has IPv4 mapped addresses disabled, the connection still completes + regardless. + On the server side, the connection endpoint is an IPv4 socket as desired; + but on the client side, the connection endpoint is an IPv6 socket. + Setting the IPV6_V6ONLY socket option on the client side as well as the + server side prevents any connection from being established at all.]. + There's only one problem. + Apparently, +\family typewriter +IPV6_V6ONLY +\family default + isn't defined on all systems [or at least it wasn't in 2005 when the Hagino + book was written]. + The server example at the end of this chapter provides a method for handling + this problem. +\end_layout + +\begin_layout Standard +If IPv4 traffic cannot be handled on IPv6 sockets, then that implies that + server applications must open both an IPv4 and IPv6 socket for a particular + network service if it wants to handle requests from either protocol. + This goes back to the flexibility issue mentioned earlier. + If +\family typewriter +getaddrinfo(3) +\family default + returns multiple address records, then server applications should traverse + the list and open a passive socket for each address provided. + +\end_layout + +\begin_layout Subsubsection +Cannot Specify the Scope Identifier in /etc/hosts +\end_layout + +\begin_layout Standard +It is possible to assign a hostname to an IPv6 network address in +\family typewriter +/etc/hosts +\family default +. + For example, the following is an excerpt from the +\family typewriter +/etc/hosts +\family default + file on the author's development system. + +\end_layout + +\begin_layout Code + ::1 localhost +\end_layout + +\begin_layout Code + 127.0.0.1 localhost +\end_layout + +\begin_layout Code + fe80::2c0:8cff:fe01:2345 pt141 +\end_layout + +\begin_layout Code + 192.0.2.1 pt141 +\end_layout + +\begin_layout Standard +The "localhost" and "pt141" hostnames can be translated to either an IPv4 + or IPv6 network address. + So, for example, if "pt141" is passed as the node parameter to +\family typewriter +getaddrinfo(3) +\family default +, the function returns both an IPv4 and IPv6 address record for the host + (assuming the behavior hasn't been modified by the hints parameter). + Unfortunately, a scoped address cannot be used in +\family typewriter +/etc/hosts +\family default +. + Doing so results in +\family typewriter +getaddrinfo(3) +\family default + returning only the IPv4 record. +\end_layout + +\begin_layout Subsubsection +Client & Server Residing on the Same Machine +\end_layout + +\begin_layout Standard +Suppose a machine has the IPv4 address 192.0.2.1. + A client application running on that machine can connect to a server applicatio +n on the same machine by using either the local loopback address (127.0.0.1) + or the network address (192.0.2.1) as the target server. + Much to this author's surprise (and dismay), it turns out that an IPv6 + client application cannot connect to a server application on the same machine + if it uses the network address of that machine as the target; it must use + the local loopback address (::1). + +\end_layout + +\begin_layout Subsection +Putting It All Together (A Client-Server Programming Example) +\end_layout + +\begin_layout Standard +Now it's time to put everything discussed thus far together into a sample + client-server application. + The remainder of this section is devoted to a remote time-of-day application + (the 'daytime' Internet service) [I noticed that Ms. + Castro used a 'daytime' example in her +\emph on +Porting applications to IPv6 HowTo +\emph default +. + For the record, the source code presented here is original, developed from + scratch, and any similarity between it and any other publicly available + 'daytime' example is purely coincidental.]. + The source code presented in this section was developed and tested on a + RedHat Linux release using the 2.6 kernel (2.6.9 to be specific). + Readers may use the source code freely, so long as proper credit is attributed; + but of course the standard disclaimer must be given first: +\end_layout + +\begin_layout Quotation +Although the sample source code is believed to be free of errors, the author + makes no guarantees as to its reliability, especially considering that + some error paths were intentionally omitted for brevity. + Use it at your own risk! +\end_layout + +\begin_layout Standard +When you get right down to it, there really aren't that many differences + between IPv4 and IPv6 applications. + The trick is to code IPv6 applications in a protocol-independent manner, + such that they can handle both IPv4 and IPv6 simultaneously and transparently. + This sample application does just that. + The only protocol-dependent code in the example occurs when printing network + addresses in verbose mode; but only +\bar under +after +\bar default + the +\family typewriter +ai_family field +\family default + in the +\family typewriter +addrinfo +\family default + structure has been checked, so the programs know exactly what type of address + they're handling at the time. +\end_layout + +\begin_layout Subsubsection +'Daytime' Server Code +\end_layout + +\begin_layout Standard +The server code is found in file tod6d.c (time-of-day IPv6 daemon). + Once built, the server may be started using the following command syntax + (assuming tod6d is the executable file): +\end_layout + +\begin_layout Code +tod6d [-v] [service] +\end_layout + +\begin_layout Standard +ARGUMENTS: +\end_layout + +\begin_layout Description +service The service (or well-known port) on which to listen. + Default is "daytime". +\end_layout + +\begin_layout Standard +OPTIONS: +\end_layout + +\begin_layout Description +-v Turn on verbose mode. +\end_layout + +\begin_layout Standard +The server handles both TCP and UDP requests on the network. + The server source code contained in tod6d.c follows: +\end_layout + +\begin_layout Code +/****************************************************************************** +\end_layout + +\begin_layout Code +* File: tod6d.c +\end_layout + +\begin_layout Code +* Description: Contains source code for an IPv6-capable 'daytime' server. +\end_layout + +\begin_layout Code +* Author: John Wenker, Sr. + Software Engineer, +\end_layout + +\begin_layout Code +* Performance Technologies, San Diego, USA +\end_layout + +\begin_layout Code +******************************************************************************/ +\end_layout + +\begin_layout Code +/* +\end_layout + +\begin_layout Code +** System header files. +\end_layout + +\begin_layout Code +*/ +\end_layout + +\begin_layout Code +#include /* errno declaration & error codes. + */ +\end_layout + +\begin_layout Code +#include /* getaddrinfo(3) et al. + */ +\end_layout + +\begin_layout Code +#include /* sockaddr_in & sockaddr_in6 definition. + */ +\end_layout + +\begin_layout Code +#include /* printf(3) et al. + */ +\end_layout + +\begin_layout Code +#include /* exit(2). + */ +\end_layout + +\begin_layout Code +#include /* String manipulation & memory functions. + */ +\end_layout + +\begin_layout Code +#include /* poll(2) and related definitions. + */ +\end_layout + +\begin_layout Code +#include /* Socket functions (socket(2), bind(2), etc). + */ +\end_layout + +\begin_layout Code +#include /* time(2) & ctime(3). + */ +\end_layout + +\begin_layout Code +#include /* getopt(3), read(2), etc. + */ +\end_layout + +\begin_layout Code +/* +\end_layout + +\begin_layout Code +** Constants. +\end_layout + +\begin_layout Code +*/ +\end_layout + +\begin_layout Code +#define DFLT_SERVICE "daytime" /* Default service name. + */ +\end_layout + +\begin_layout Code +#define INVALID_DESC -1 /* Invalid file descriptor. + */ +\end_layout + +\begin_layout Code +#define MAXCONNQLEN 3 /* Max nbr of connection requests to queue. + */ +\end_layout + +\begin_layout Code +#define MAXTCPSCKTS 2 /* One TCP socket for IPv4 & one for IPv6. + */ +\end_layout + +\begin_layout Code +#define MAXUDPSCKTS 2 /* One UDP socket for IPv4 & one for IPv6. + */ +\end_layout + +\begin_layout Code +#define VALIDOPTS "v" /* Valid command options. + */ +\end_layout + +\begin_layout Code +/* +\end_layout + +\begin_layout Code +** Simple boolean type definition. +\end_layout + +\begin_layout Code +*/ +\end_layout + +\begin_layout Code +typedef enum { false = 0, true } boolean; +\end_layout + +\begin_layout Code +/* +\end_layout + +\begin_layout Code +** Prototypes for internal helper functions. +\end_layout + +\begin_layout Code +*/ +\end_layout + +\begin_layout Code +static int openSckt( const char *service, +\end_layout + +\begin_layout Code + const char *protocol, +\end_layout + +\begin_layout Code + int desc[ ], +\end_layout + +\begin_layout Code + size_t *descSize ); +\end_layout + +\begin_layout Code +static void tod( int tSckt[ ], +\end_layout + +\begin_layout Code + size_t tScktSize, +\end_layout + +\begin_layout Code + int uSckt[ ], +\end_layout + +\begin_layout Code + size_t uScktSize ); +\end_layout + +\begin_layout Code +/* +\end_layout + +\begin_layout Code +** Global (within this file only) data objects. +\end_layout + +\begin_layout Code +*/ +\end_layout + +\begin_layout Code +static char hostBfr[ NI_MAXHOST ]; /* For use w/getnameinfo(3). + */ +\end_layout + +\begin_layout Code +static const char *pgmName; /* Program name w/o dir prefix. + */ +\end_layout + +\begin_layout Code +static char servBfr[ NI_MAXSERV ]; /* For use w/getnameinfo(3). + */ +\end_layout + +\begin_layout Code +static boolean verbose = false; /* Verbose mode indication. + */ +\end_layout + +\begin_layout Code +/* +\end_layout + +\begin_layout Code +** Usage macro for command syntax violations. +\end_layout + +\begin_layout Code +*/ +\end_layout + +\begin_layout Code +#define USAGE +\backslash + +\end_layout + +\begin_layout Code + { +\backslash + +\end_layout + +\begin_layout Code + fprintf( stderr, +\backslash + +\end_layout + +\begin_layout Code + "Usage: %s [-v] [service] +\backslash +n", +\backslash + +\end_layout + +\begin_layout Code + pgmName ); +\backslash + +\end_layout + +\begin_layout Code + exit( 127 ); +\backslash + +\end_layout + +\begin_layout Code + } /* End USAGE macro. + */ +\end_layout + +\begin_layout Code +/* +\end_layout + +\begin_layout Code +** Macro to terminate the program if a system call error occurs. + The system +\end_layout + +\begin_layout Code +** call must be one of the usual type that returns -1 on error. + This macro is +\end_layout + +\begin_layout Code +** a modified version of a macro authored by Dr. + V. + Vinge, SDSU Dept. + of +\end_layout + +\begin_layout Code +** Computer Science (retired)... + best professor I ever had. + I hear he writes +\end_layout + +\begin_layout Code +** great science fiction in addition to robust code, too. +\end_layout + +\begin_layout Code +*/ +\end_layout + +\begin_layout Code +#define CHK(expr) +\backslash + +\end_layout + +\begin_layout Code + do +\backslash + +\end_layout + +\begin_layout Code + { +\backslash + +\end_layout + +\begin_layout Code + if ( (expr) == -1 ) +\backslash + +\end_layout + +\begin_layout Code + { +\backslash + +\end_layout + +\begin_layout Code + fprintf( stderr, +\backslash + +\end_layout + +\begin_layout Code + "%s (line %d): System call ERROR - %s. +\backslash +n", +\backslash + +\end_layout + +\begin_layout Code + pgmName, +\backslash + +\end_layout + +\begin_layout Code + __LINE__, +\backslash + +\end_layout + +\begin_layout Code + strerror( errno ) ); +\backslash + +\end_layout + +\begin_layout Code + exit( 1 ); +\backslash + +\end_layout + +\begin_layout Code + } /* End IF system call failed. + */ +\backslash + +\end_layout + +\begin_layout Code + } while ( false ) +\end_layout + +\begin_layout Code +/****************************************************************************** +\end_layout + +\begin_layout Code +* Function: main +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Description: +\end_layout + +\begin_layout Code +* Set up a time-of-day server and handle network requests. + This server +\end_layout + +\begin_layout Code +* handles both TCP and UDP requests. +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Parameters: +\end_layout + +\begin_layout Code +* The usual argc and argv parameters to a main() function. +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Return Value: +\end_layout + +\begin_layout Code +* This is a daemon program and never returns. + However, in the degenerate +\end_layout + +\begin_layout Code +* case where no sockets are created, the function returns zero. +\end_layout + +\begin_layout Code +******************************************************************************/ +\end_layout + +\begin_layout Code +int main( int argc, +\end_layout + +\begin_layout Code + char *argv[ ] ) +\end_layout + +\begin_layout Code +{ +\end_layout + +\begin_layout Code + int opt; +\end_layout + +\begin_layout Code + const char *service = DFLT_SERVICE; +\end_layout + +\begin_layout Code + int tSckt[ MAXTCPSCKTS ]; /* Array of TCP socket descriptors. + */ +\end_layout + +\begin_layout Code + size_t tScktSize = MAXTCPSCKTS; /* Size of uSckt (# of elements). + */ +\end_layout + +\begin_layout Code + int uSckt[ MAXUDPSCKTS ]; /* Array of UDP socket descriptors. + */ +\end_layout + +\begin_layout Code + size_t uScktSize = MAXUDPSCKTS; /* Size of uSckt (# of elements). + */ +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Set the program name (w/o directory prefix). +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + pgmName = strrchr( argv[ 0 ], '/' ); +\end_layout + +\begin_layout Code + pgmName = pgmName == NULL ? argv[ 0 ] : pgmName + 1; +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Process command options. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + opterr = 0; /* Turns off "invalid option" error messages. + */ +\end_layout + +\begin_layout Code + while ( ( opt = getopt( argc, argv, VALIDOPTS ) ) >= 0 ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + switch ( opt ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + case 'v': /* Verbose mode. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + verbose = true; +\end_layout + +\begin_layout Code + break; +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + default: +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + USAGE; +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + } /* End SWITCH on command option. + */ +\end_layout + +\begin_layout Code + } /* End WHILE processing options. + */ +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Process command line arguments. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + switch ( argc - optind ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + case 0: break; +\end_layout + +\begin_layout Code + case 1: service = argv[ optind ]; break; +\end_layout + +\begin_layout Code + default: USAGE; +\end_layout + +\begin_layout Code + } /* End SWITCH on number of command line arguments. + */ +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Open both a TCP and UDP socket, for both IPv4 & IPv6, on which to + receive +\end_layout + +\begin_layout Code + ** service requests. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + if ( ( openSckt( service, "tcp", tSckt, &tScktSize ) < 0 ) || +\end_layout + +\begin_layout Code + ( openSckt( service, "udp", uSckt, &uScktSize ) < 0 ) ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + exit( 1 ); +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Run the time-of-day server. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + if ( ( tScktSize > 0 ) || ( uScktSize > 0 ) ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + tod( tSckt, /* tod() never returns. + */ +\end_layout + +\begin_layout Code + tScktSize, +\end_layout + +\begin_layout Code + uSckt, +\end_layout + +\begin_layout Code + uScktSize ); +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Since tod() never returns, execution only gets here if no sockets + were +\end_layout + +\begin_layout Code + ** created. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + if ( verbose ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "%s: No sockets opened... + terminating. +\backslash +n", +\end_layout + +\begin_layout Code + pgmName ); +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + return 0; +\end_layout + +\begin_layout Code +} /* End main() */ +\end_layout + +\begin_layout Code +/****************************************************************************** +\end_layout + +\begin_layout Code +* Function: openSckt +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Description: +\end_layout + +\begin_layout Code +* Open passive (server) sockets for the indicated inet service & protocol. +\end_layout + +\begin_layout Code +* Notice in the last sentence that "sockets" is plural. + During the interim +\end_layout + +\begin_layout Code +* transition period while everyone is switching over to IPv6, the server +\end_layout + +\begin_layout Code +* application has to open two sockets on which to listen for connections... +\end_layout + +\begin_layout Code +* one for IPv4 traffic and one for IPv6 traffic. +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Parameters: +\end_layout + +\begin_layout Code +* service - Pointer to a character string representing the well-known + port +\end_layout + +\begin_layout Code +* on which to listen (can be a service name or a decimal number). +\end_layout + +\begin_layout Code +* protocol - Pointer to a character string representing the transport + layer +\end_layout + +\begin_layout Code +* protocol (only "tcp" or "udp" are valid). +\end_layout + +\begin_layout Code +* desc - Pointer to an array into which the socket descriptors are +\end_layout + +\begin_layout Code +* placed when opened. +\end_layout + +\begin_layout Code +* descSize - This is a value-result parameter. + On input, it contains the +\end_layout + +\begin_layout Code +* max number of descriptors that can be put into 'desc' (i.e. + the +\end_layout + +\begin_layout Code +* number of elements in the array). + Upon return, it will contain +\end_layout + +\begin_layout Code +* the number of descriptors actually opened. + Any unused slots in +\end_layout + +\begin_layout Code +* 'desc' are set to INVALID_DESC. +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Return Value: +\end_layout + +\begin_layout Code +* 0 on success, -1 on error. +\end_layout + +\begin_layout Code +******************************************************************************/ +\end_layout + +\begin_layout Code +static int openSckt( const char *service, +\end_layout + +\begin_layout Code + const char *protocol, +\end_layout + +\begin_layout Code + int desc[ ], +\end_layout + +\begin_layout Code + size_t *descSize ) +\end_layout + +\begin_layout Code +{ +\end_layout + +\begin_layout Code + struct addrinfo *ai; +\end_layout + +\begin_layout Code + int aiErr; +\end_layout + +\begin_layout Code + struct addrinfo *aiHead; +\end_layout + +\begin_layout Code + struct addrinfo hints = { .ai_flags = AI_PASSIVE, /* Server mode. + */ +\end_layout + +\begin_layout Code + .ai_family = PF_UNSPEC }; /* IPv4 or IPv6. + */ +\end_layout + +\begin_layout Code + size_t maxDescs = *descSize; +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Initialize output parameters. + When the loop completes, *descSize is 0. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + while ( *descSize > 0 ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + desc[ --( *descSize ) ] = INVALID_DESC; +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Check which protocol is selected (only TCP and UDP are valid). +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + if ( strcmp( protocol, "tcp" ) == 0 ) /* TCP protocol. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + hints.ai_socktype = SOCK_STREAM; +\end_layout + +\begin_layout Code + hints.ai_protocol = IPPROTO_TCP; +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + else if ( strcmp( protocol, "udp" ) == 0 ) /* UDP protocol. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + hints.ai_socktype = SOCK_DGRAM; +\end_layout + +\begin_layout Code + hints.ai_protocol = IPPROTO_UDP; +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + else /* Invalid protocol. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "%s (line %d): ERROR - Unknown transport " +\end_layout + +\begin_layout Code + "layer protocol +\backslash +"%s +\backslash +". +\backslash +n", +\end_layout + +\begin_layout Code + pgmName, +\end_layout + +\begin_layout Code + __LINE__, +\end_layout + +\begin_layout Code + protocol ); +\end_layout + +\begin_layout Code + return -1; +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Look up the service's well-known port number. + Notice that NULL is being +\end_layout + +\begin_layout Code + ** passed for the 'node' parameter, and that the AI_PASSIVE flag is set + in +\end_layout + +\begin_layout Code + ** 'hints'. + Thus, the program is requesting passive address information. +\end_layout + +\begin_layout Code + ** The network address is initialized to :: (all zeros) for IPv6 records, + or +\end_layout + +\begin_layout Code + ** 0.0.0.0 for IPv4 records. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + if ( ( aiErr = getaddrinfo( NULL, +\end_layout + +\begin_layout Code + service, +\end_layout + +\begin_layout Code + &hints, +\end_layout + +\begin_layout Code + &aiHead ) ) != 0 ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "%s (line %d): ERROR - %s. +\backslash +n", +\end_layout + +\begin_layout Code + pgmName, +\end_layout + +\begin_layout Code + __LINE__, +\end_layout + +\begin_layout Code + gai_strerror( aiErr ) ); +\end_layout + +\begin_layout Code + return -1; +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** For each of the address records returned, attempt to set up a passive +\end_layout + +\begin_layout Code + ** socket. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + for ( ai = aiHead; +\end_layout + +\begin_layout Code + ( ai != NULL ) && ( *descSize < maxDescs ); +\end_layout + +\begin_layout Code + ai = ai->ai_next ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + if ( verbose ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Display the current address info. + Start with the protocol- +\end_layout + +\begin_layout Code + ** independent fields first. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "Setting up a passive socket based on the " +\end_layout + +\begin_layout Code + "following address info: +\backslash +n" +\end_layout + +\begin_layout Code + " ai_flags = 0x%02X +\backslash +n" +\end_layout + +\begin_layout Code + " ai_family = %d (PF_INET = %d, PF_INET6 = %d) +\backslash +n" +\end_layout + +\begin_layout Code + " ai_socktype = %d (SOCK_STREAM = %d, SOCK_DGRAM = + %d) +\backslash +n" +\end_layout + +\begin_layout Code + " ai_protocol = %d (IPPROTO_TCP = %d, IPPROTO_UDP = + %d) +\backslash +n" +\end_layout + +\begin_layout Code + " ai_addrlen = %d (sockaddr_in = %d, " +\end_layout + +\begin_layout Code + "sockaddr_in6 = %d) +\backslash +n", +\end_layout + +\begin_layout Code + ai->ai_flags, +\end_layout + +\begin_layout Code + ai->ai_family, +\end_layout + +\begin_layout Code + PF_INET, +\end_layout + +\begin_layout Code + PF_INET6, +\end_layout + +\begin_layout Code + ai->ai_socktype, +\end_layout + +\begin_layout Code + SOCK_STREAM, +\end_layout + +\begin_layout Code + SOCK_DGRAM, +\end_layout + +\begin_layout Code + ai->ai_protocol, +\end_layout + +\begin_layout Code + IPPROTO_TCP, +\end_layout + +\begin_layout Code + IPPROTO_UDP, +\end_layout + +\begin_layout Code + ai->ai_addrlen, +\end_layout + +\begin_layout Code + sizeof( struct sockaddr_in ), +\end_layout + +\begin_layout Code + sizeof( struct sockaddr_in6 ) ); +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Now display the protocol-specific formatted socket address. + Note +\end_layout + +\begin_layout Code + ** that the program is requesting that getnameinfo(3) convert the +\end_layout + +\begin_layout Code + ** host & service into numeric strings. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + getnameinfo( ai->ai_addr, +\end_layout + +\begin_layout Code + ai->ai_addrlen, +\end_layout + +\begin_layout Code + hostBfr, +\end_layout + +\begin_layout Code + sizeof( hostBfr ), +\end_layout + +\begin_layout Code + servBfr, +\end_layout + +\begin_layout Code + sizeof( servBfr ), +\end_layout + +\begin_layout Code + NI_NUMERICHOST | NI_NUMERICSERV ); +\end_layout + +\begin_layout Code + switch ( ai->ai_family ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + case PF_INET: /* IPv4 address record. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + struct sockaddr_in *p = (struct sockaddr_in*) ai->ai_addr; +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + " ai_addr = sin_family: %d (AF_INET = %d, + " +\end_layout + +\begin_layout Code + "AF_INET6 = %d) +\backslash +n" +\end_layout + +\begin_layout Code + " sin_addr: %s +\backslash +n" +\end_layout + +\begin_layout Code + " sin_port: %s +\backslash +n", +\end_layout + +\begin_layout Code + p->sin_family, +\end_layout + +\begin_layout Code + AF_INET, +\end_layout + +\begin_layout Code + AF_INET6, +\end_layout + +\begin_layout Code + hostBfr, +\end_layout + +\begin_layout Code + servBfr ); +\end_layout + +\begin_layout Code + break; +\end_layout + +\begin_layout Code + } /* End CASE of IPv4. + */ +\end_layout + +\begin_layout Code + case PF_INET6: /* IPv6 address record. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + struct sockaddr_in6 *p = (struct sockaddr_in6*) ai->ai_addr; +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + " ai_addr = sin6_family: %d (AF_INET = + %d, " +\end_layout + +\begin_layout Code + "AF_INET6 = %d) +\backslash +n" +\end_layout + +\begin_layout Code + " sin6_addr: %s +\backslash +n" +\end_layout + +\begin_layout Code + " sin6_port: %s +\backslash +n" +\end_layout + +\begin_layout Code + " sin6_flowinfo: %d +\backslash +n" +\end_layout + +\begin_layout Code + " sin6_scope_id: %d +\backslash +n", +\end_layout + +\begin_layout Code + p->sin6_family, +\end_layout + +\begin_layout Code + AF_INET, +\end_layout + +\begin_layout Code + AF_INET6, +\end_layout + +\begin_layout Code + hostBfr, +\end_layout + +\begin_layout Code + servBfr, +\end_layout + +\begin_layout Code + p->sin6_flowinfo, +\end_layout + +\begin_layout Code + p->sin6_scope_id ); +\end_layout + +\begin_layout Code + break; +\end_layout + +\begin_layout Code + } /* End CASE of IPv6. + */ +\end_layout + +\begin_layout Code + default: /* Can never get here, but just for completeness. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "%s (line %d): ERROR - Unknown protocol family (%d). +\backslash +n", +\end_layout + +\begin_layout Code + pgmName, +\end_layout + +\begin_layout Code + __LINE__, +\end_layout + +\begin_layout Code + ai->ai_family ); +\end_layout + +\begin_layout Code + freeaddrinfo( aiHead ); +\end_layout + +\begin_layout Code + return -1; +\end_layout + +\begin_layout Code + } /* End DEFAULT case (unknown protocol family). + */ +\end_layout + +\begin_layout Code + } /* End SWITCH on protocol family. + */ +\end_layout + +\begin_layout Code + } /* End IF verbose mode. + */ +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Create a socket using the info in the addrinfo structure. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + CHK( desc[ *descSize ] = socket( ai->ai_family, +\end_layout + +\begin_layout Code + ai->ai_socktype, +\end_layout + +\begin_layout Code + ai->ai_protocol ) ); +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Here is the code that prevents "IPv4 mapped addresses", as discussed +\end_layout + +\begin_layout Code + ** in Section 22.1.3.1. + If an IPv6 socket was just created, then set the +\end_layout + +\begin_layout Code + ** IPV6_V6ONLY socket option. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + if ( ai->ai_family == PF_INET6 ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code +#if defined( IPV6_V6ONLY ) +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Disable IPv4 mapped addresses. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + int v6Only = 1; +\end_layout + +\begin_layout Code + CHK( setsockopt( desc[ *descSize ], +\end_layout + +\begin_layout Code + IPPROTO_IPV6, +\end_layout + +\begin_layout Code + IPV6_V6ONLY, +\end_layout + +\begin_layout Code + &v6Only, +\end_layout + +\begin_layout Code + sizeof( v6Only ) ) ); +\end_layout + +\begin_layout Code +#else +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** IPV6_V6ONLY is not defined, so the socket option can't be set + and +\end_layout + +\begin_layout Code + ** thus IPv4 mapped addresses can't be disabled. + Print a warning +\end_layout + +\begin_layout Code + ** message and close the socket. + Design note: If the +\end_layout + +\begin_layout Code + ** #if...#else...#endif construct were removed, then this program +\end_layout + +\begin_layout Code + ** would not compile (because IPV6_V6ONLY isn't defined). + That's an +\end_layout + +\begin_layout Code + ** acceptable approach; IPv4 mapped addresses are certainly disabled +\end_layout + +\begin_layout Code + ** if the program can't build! However, since this program is + also +\end_layout + +\begin_layout Code + ** designed to work for IPv4 sockets as well as IPv6, I decided + to +\end_layout + +\begin_layout Code + ** allow the program to compile when IPV6_V6ONLY is not defined, + and +\end_layout + +\begin_layout Code + ** turn it into a run-time warning rather than a compile-time error. +\end_layout + +\begin_layout Code + ** IPv4 mapped addresses are still disabled because _all_ IPv6 + traffic +\end_layout + +\begin_layout Code + ** is disabled (all IPv6 sockets are closed here), but at least + this +\end_layout + +\begin_layout Code + ** way the server can still service IPv4 network traffic. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "%s (line %d): WARNING - Cannot set IPV6_V6ONLY socket + " +\end_layout + +\begin_layout Code + "option. + Closing IPv6 %s socket. +\backslash +n", +\end_layout + +\begin_layout Code + pgmName, +\end_layout + +\begin_layout Code + __LINE__, +\end_layout + +\begin_layout Code + ai->ai_protocol == IPPROTO_TCP ? "TCP" : "UDP" ); +\end_layout + +\begin_layout Code + CHK( close( desc[ *descSize ] ) ); +\end_layout + +\begin_layout Code + continue; /* Go to top of FOR loop w/o updating *descSize! */ +\end_layout + +\begin_layout Code +#endif /* IPV6_V6ONLY */ +\end_layout + +\begin_layout Code + } /* End IF this is an IPv6 socket. + */ +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Bind the socket. + Again, the info from the addrinfo structure is used. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + CHK( bind( desc[ *descSize ], +\end_layout + +\begin_layout Code + ai->ai_addr, +\end_layout + +\begin_layout Code + ai->ai_addrlen ) ); +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** If this is a TCP socket, put the socket into passive listening + mode +\end_layout + +\begin_layout Code + ** (listen is only valid on connection-oriented sockets). +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + if ( ai->ai_socktype == SOCK_STREAM ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + CHK( listen( desc[ *descSize ], +\end_layout + +\begin_layout Code + MAXCONNQLEN ) ); +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Socket set up okay. + Bump index to next descriptor array element. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + *descSize += 1; +\end_layout + +\begin_layout Code + } /* End FOR each address info structure returned. + */ +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Dummy check for unused address records. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + if ( verbose && ( ai != NULL ) ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "%s (line %d): WARNING - Some address records were " +\end_layout + +\begin_layout Code + "not processed due to insufficient array space. +\backslash +n", +\end_layout + +\begin_layout Code + pgmName, +\end_layout + +\begin_layout Code + __LINE__ ); +\end_layout + +\begin_layout Code + } /* End IF verbose and some address records remain unprocessed. + */ +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Clean up. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + freeaddrinfo( aiHead ); +\end_layout + +\begin_layout Code + return 0; +\end_layout + +\begin_layout Code +} /* End openSckt() */ +\end_layout + +\begin_layout Code +/****************************************************************************** +\end_layout + +\begin_layout Code +* Function: tod +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Description: +\end_layout + +\begin_layout Code +* Listen on a set of sockets and send the current time-of-day to any +\end_layout + +\begin_layout Code +* clients. + This function never returns. +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Parameters: +\end_layout + +\begin_layout Code +* tSckt - Array of TCP socket descriptors on which to listen. +\end_layout + +\begin_layout Code +* tScktSize - Size of the tSckt array (nbr of elements). +\end_layout + +\begin_layout Code +* uSckt - Array of UDP socket descriptors on which to listen. +\end_layout + +\begin_layout Code +* uScktSize - Size of the uSckt array (nbr of elements). +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Return Value: None. +\end_layout + +\begin_layout Code +******************************************************************************/ +\end_layout + +\begin_layout Code +static void tod( int tSckt[ ], +\end_layout + +\begin_layout Code + size_t tScktSize, +\end_layout + +\begin_layout Code + int uSckt[ ], +\end_layout + +\begin_layout Code + size_t uScktSize ) +\end_layout + +\begin_layout Code +{ +\end_layout + +\begin_layout Code + char bfr[ 256 ]; +\end_layout + +\begin_layout Code + ssize_t count; +\end_layout + +\begin_layout Code + struct pollfd *desc; +\end_layout + +\begin_layout Code + size_t descSize = tScktSize + uScktSize; +\end_layout + +\begin_layout Code + int idx; +\end_layout + +\begin_layout Code + int newSckt; +\end_layout + +\begin_layout Code + struct sockaddr *sadr; +\end_layout + +\begin_layout Code + socklen_t sadrLen; +\end_layout + +\begin_layout Code + struct sockaddr_storage sockStor; +\end_layout + +\begin_layout Code + int status; +\end_layout + +\begin_layout Code + size_t timeLen; +\end_layout + +\begin_layout Code + char *timeStr; +\end_layout + +\begin_layout Code + time_t timeVal; +\end_layout + +\begin_layout Code + ssize_t wBytes; +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Allocate memory for the poll(2) array. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + desc = malloc( descSize * sizeof( struct pollfd ) ); +\end_layout + +\begin_layout Code + if ( desc == NULL ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "%s (line %d): ERROR - %s. +\backslash +n", +\end_layout + +\begin_layout Code + pgmName, +\end_layout + +\begin_layout Code + __LINE__, +\end_layout + +\begin_layout Code + strerror( ENOMEM ) ); +\end_layout + +\begin_layout Code + exit( 1 ); +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Initialize the poll(2) array. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + for ( idx = 0; idx < descSize; idx++ ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + desc[ idx ].fd = idx < tScktSize ? tSckt[ idx ] +\end_layout + +\begin_layout Code + : uSckt[ idx - tScktSize ]; +\end_layout + +\begin_layout Code + desc[ idx ].events = POLLIN; +\end_layout + +\begin_layout Code + desc[ idx ].revents = 0; +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Main time-of-day server loop. + Handles both TCP & UDP requests. + This is +\end_layout + +\begin_layout Code + ** an interative server, and all requests are handled directly within + the +\end_layout + +\begin_layout Code + ** main loop. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + while ( true ) /* Do forever. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Wait for activity on one of the sockets. + The DO..WHILE construct is +\end_layout + +\begin_layout Code + ** used to restart the system call in the event the process is +\end_layout + +\begin_layout Code + ** interrupted by a signal. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + do +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + status = poll( desc, +\end_layout + +\begin_layout Code + descSize, +\end_layout + +\begin_layout Code + -1 /* Wait indefinitely for input. + */ ); +\end_layout + +\begin_layout Code + } while ( ( status < 0 ) && ( errno == EINTR ) ); +\end_layout + +\begin_layout Code + CHK( status ); /* Check for a bona fide system call error. + */ +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Get the current time. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + timeVal = time( NULL ); +\end_layout + +\begin_layout Code + timeStr = ctime( &timeVal ); +\end_layout + +\begin_layout Code + timeLen = strlen( timeStr ); +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Indicate that there is new network activity. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + if ( verbose ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + char *s = malloc( timeLen+1 ); +\end_layout + +\begin_layout Code + strcpy( s, timeStr ); +\end_layout + +\begin_layout Code + s[ timeLen-1 ] = ' +\backslash +0'; /* Overwrite ' +\backslash +n' in date string. + */ +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "%s: New network activity on %s. +\backslash +n", +\end_layout + +\begin_layout Code + pgmName, +\end_layout + +\begin_layout Code + s ); +\end_layout + +\begin_layout Code + free( s ); +\end_layout + +\begin_layout Code + } /* End IF verbose. + */ +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Process sockets with input available. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + for ( idx = 0; idx < descSize; idx++ ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + switch ( desc[ idx ].revents ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + case 0: /* No activity on this socket; try the next. + */ +\end_layout + +\begin_layout Code + continue; +\end_layout + +\begin_layout Code + case POLLIN: /* Network activity. + Go process it. + */ +\end_layout + +\begin_layout Code + break; +\end_layout + +\begin_layout Code + default: /* Invalid poll events. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "%s (line %d): ERROR - Invalid poll event (0x%02X). +\backslash +n", +\end_layout + +\begin_layout Code + pgmName, +\end_layout + +\begin_layout Code + __LINE__, +\end_layout + +\begin_layout Code + desc[ idx ].revents ); +\end_layout + +\begin_layout Code + exit( 1 ); +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + } /* End SWITCH on returned poll events. + */ +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Determine if this is a TCP request or UDP request. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + if ( idx < tScktSize ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** TCP connection requested. + Accept it. + Notice the use of +\end_layout + +\begin_layout Code + ** the sockaddr_storage data type. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + sadrLen = sizeof( sockStor ); +\end_layout + +\begin_layout Code + sadr = (struct sockaddr*) &sockStor; +\end_layout + +\begin_layout Code + CHK( newSckt = accept( desc[ idx ].fd, +\end_layout + +\begin_layout Code + sadr, +\end_layout + +\begin_layout Code + &sadrLen ) ); +\end_layout + +\begin_layout Code + CHK( shutdown( newSckt, /* Server never recv's anything. + */ +\end_layout + +\begin_layout Code + SHUT_RD ) ); +\end_layout + +\begin_layout Code + if ( verbose ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Display the socket address of the remote client. + Begin with +\end_layout + +\begin_layout Code + ** the address-independent fields. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "Sockaddr info for new TCP client: +\backslash +n" +\end_layout + +\begin_layout Code + " sa_family = %d (AF_INET = %d, AF_INET6 = %d) +\backslash +n" +\end_layout + +\begin_layout Code + " addr len = %d (sockaddr_in = %d, " +\end_layout + +\begin_layout Code + "sockaddr_in6 = %d) +\backslash +n", +\end_layout + +\begin_layout Code + sadr->sa_family, +\end_layout + +\begin_layout Code + AF_INET, +\end_layout + +\begin_layout Code + AF_INET6, +\end_layout + +\begin_layout Code + sadrLen, +\end_layout + +\begin_layout Code + sizeof( struct sockaddr_in ), +\end_layout + +\begin_layout Code + sizeof( struct sockaddr_in6 ) ); +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Display the address-specific fields. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + getnameinfo( sadr, +\end_layout + +\begin_layout Code + sadrLen, +\end_layout + +\begin_layout Code + hostBfr, +\end_layout + +\begin_layout Code + sizeof( hostBfr ), +\end_layout + +\begin_layout Code + servBfr, +\end_layout + +\begin_layout Code + sizeof( servBfr ), +\end_layout + +\begin_layout Code + NI_NUMERICHOST | NI_NUMERICSERV ); +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Notice that we're switching on an address family now, + not a +\end_layout + +\begin_layout Code + ** protocol family. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + switch ( sadr->sa_family ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + case AF_INET: /* IPv4 address. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + struct sockaddr_in *p = (struct sockaddr_in*) sadr; +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + " sin_addr = sin_family: %d +\backslash +n" +\end_layout + +\begin_layout Code + " sin_addr: %s +\backslash +n" +\end_layout + +\begin_layout Code + " sin_port: %s +\backslash +n", +\end_layout + +\begin_layout Code + p->sin_family, +\end_layout + +\begin_layout Code + hostBfr, +\end_layout + +\begin_layout Code + servBfr ); +\end_layout + +\begin_layout Code + break; +\end_layout + +\begin_layout Code + } /* End CASE of IPv4. + */ +\end_layout + +\begin_layout Code + case AF_INET6: /* IPv6 address. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + struct sockaddr_in6 *p = (struct sockaddr_in6*) sadr; +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + " sin6_addr = sin6_family: %d +\backslash +n" +\end_layout + +\begin_layout Code + " sin6_addr: %s +\backslash +n" +\end_layout + +\begin_layout Code + " sin6_port: %s +\backslash +n" +\end_layout + +\begin_layout Code + " sin6_flowinfo: %d +\backslash +n" +\end_layout + +\begin_layout Code + " sin6_scope_id: %d +\backslash +n", +\end_layout + +\begin_layout Code + p->sin6_family, +\end_layout + +\begin_layout Code + hostBfr, +\end_layout + +\begin_layout Code + servBfr, +\end_layout + +\begin_layout Code + p->sin6_flowinfo, +\end_layout + +\begin_layout Code + p->sin6_scope_id ); +\end_layout + +\begin_layout Code + break; +\end_layout + +\begin_layout Code + } /* End CASE of IPv6. + */ +\end_layout + +\begin_layout Code + default: /* Can never get here, but for completeness. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "%s (line %d): ERROR - Unknown address " +\end_layout + +\begin_layout Code + "family (%d). +\backslash +n", +\end_layout + +\begin_layout Code + pgmName, +\end_layout + +\begin_layout Code + __LINE__, +\end_layout + +\begin_layout Code + sadr->sa_family ); +\end_layout + +\begin_layout Code + break; +\end_layout + +\begin_layout Code + } /* End DEFAULT case (unknown address family). + */ +\end_layout + +\begin_layout Code + } /* End SWITCH on address family. + */ +\end_layout + +\begin_layout Code + } /* End IF verbose mode. + */ +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Send the TOD to the client. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + wBytes = timeLen; +\end_layout + +\begin_layout Code + while ( wBytes > 0 ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + do +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + count = write( newSckt, +\end_layout + +\begin_layout Code + timeStr, +\end_layout + +\begin_layout Code + wBytes ); +\end_layout + +\begin_layout Code + } while ( ( count < 0 ) && ( errno == EINTR ) ); +\end_layout + +\begin_layout Code + CHK( count ); /* Check for a bona fide error. + */ +\end_layout + +\begin_layout Code + wBytes -= count; +\end_layout + +\begin_layout Code + } /* End WHILE there is data to send. + */ +\end_layout + +\begin_layout Code + CHK( close( newSckt ) ); +\end_layout + +\begin_layout Code + } /* End IF this was a TCP connection request. + */ +\end_layout + +\begin_layout Code + else +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** This is a UDP socket, and a datagram is available. + The funny +\end_layout + +\begin_layout Code + ** thing about UDP requests is that this server doesn't require + any +\end_layout + +\begin_layout Code + ** client input; but it can't send the TOD unless it knows a + client +\end_layout + +\begin_layout Code + ** wants the data, and the only way that can occur with UDP + is if +\end_layout + +\begin_layout Code + ** the server receives a datagram from the client. + Thus, the +\end_layout + +\begin_layout Code + ** server must receive _something_, but the content of the datagram +\end_layout + +\begin_layout Code + ** is irrelevant. + Read in the datagram. + Again note the use of +\end_layout + +\begin_layout Code + ** sockaddr_storage to receive the address. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + sadrLen = sizeof( sockStor ); +\end_layout + +\begin_layout Code + sadr = (struct sockaddr*) &sockStor; +\end_layout + +\begin_layout Code + CHK( count = recvfrom( desc[ idx ].fd, +\end_layout + +\begin_layout Code + bfr, +\end_layout + +\begin_layout Code + sizeof( bfr ), +\end_layout + +\begin_layout Code + 0, +\end_layout + +\begin_layout Code + sadr, +\end_layout + +\begin_layout Code + &sadrLen ) ); +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Display whatever was received on stdout. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + if ( verbose ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + ssize_t rBytes = count; +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "%s: UDP datagram received (%d bytes). +\backslash +n", +\end_layout + +\begin_layout Code + pgmName, +\end_layout + +\begin_layout Code + count ); +\end_layout + +\begin_layout Code + while ( count > 0 ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + fputc( bfr[ rBytes - count-- ], +\end_layout + +\begin_layout Code + stdout ); +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + if ( bfr[ rBytes-1 ] != ' +\backslash +n' ) +\end_layout + +\begin_layout Code + fputc( ' +\backslash +n', stdout ); /* Newline also flushes stdout. + */ +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Display the socket address of the remote client. + Address- +\end_layout + +\begin_layout Code + ** independent fields first. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "Remote client's sockaddr info: +\backslash +n" +\end_layout + +\begin_layout Code + " sa_family = %d (AF_INET = %d, AF_INET6 = %d) +\backslash +n" +\end_layout + +\begin_layout Code + " addr len = %d (sockaddr_in = %d, " +\end_layout + +\begin_layout Code + "sockaddr_in6 = %d) +\backslash +n", +\end_layout + +\begin_layout Code + sadr->sa_family, +\end_layout + +\begin_layout Code + AF_INET, +\end_layout + +\begin_layout Code + AF_INET6, +\end_layout + +\begin_layout Code + sadrLen, +\end_layout + +\begin_layout Code + sizeof( struct sockaddr_in ), +\end_layout + +\begin_layout Code + sizeof( struct sockaddr_in6 ) ); +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Display the address-specific information. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + getnameinfo( sadr, +\end_layout + +\begin_layout Code + sadrLen, +\end_layout + +\begin_layout Code + hostBfr, +\end_layout + +\begin_layout Code + sizeof( hostBfr ), +\end_layout + +\begin_layout Code + servBfr, +\end_layout + +\begin_layout Code + sizeof( servBfr ), +\end_layout + +\begin_layout Code + NI_NUMERICHOST | NI_NUMERICSERV ); +\end_layout + +\begin_layout Code + switch ( sadr->sa_family ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + case AF_INET: /* IPv4 address. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + struct sockaddr_in *p = (struct sockaddr_in*) sadr; +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + " sin_addr = sin_family: %d +\backslash +n" +\end_layout + +\begin_layout Code + " sin_addr: %s +\backslash +n" +\end_layout + +\begin_layout Code + " sin_port: %s +\backslash +n", +\end_layout + +\begin_layout Code + p->sin_family, +\end_layout + +\begin_layout Code + hostBfr, +\end_layout + +\begin_layout Code + servBfr ); +\end_layout + +\begin_layout Code + break; +\end_layout + +\begin_layout Code + } /* End CASE of IPv4 address. + */ +\end_layout + +\begin_layout Code + case AF_INET6: /* IPv6 address. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + struct sockaddr_in6 *p = (struct sockaddr_in6*) sadr; +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + " sin6_addr = sin6_family: %d +\backslash +n" +\end_layout + +\begin_layout Code + " sin6_addr: %s +\backslash +n" +\end_layout + +\begin_layout Code + " sin6_port: %s +\backslash +n" +\end_layout + +\begin_layout Code + " sin6_flowinfo: %d +\backslash +n" +\end_layout + +\begin_layout Code + " sin6_scope_id: %d +\backslash +n", +\end_layout + +\begin_layout Code + p->sin6_family, +\end_layout + +\begin_layout Code + hostBfr, +\end_layout + +\begin_layout Code + servBfr, +\end_layout + +\begin_layout Code + p->sin6_flowinfo, +\end_layout + +\begin_layout Code + p->sin6_scope_id ); +\end_layout + +\begin_layout Code + break; +\end_layout + +\begin_layout Code + } /* End CASE of IPv6 address. + */ +\end_layout + +\begin_layout Code + default: /* Can never get here, but for completeness. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "%s (line %d): ERROR - Unknown address " +\end_layout + +\begin_layout Code + "family (%d). +\backslash +n", +\end_layout + +\begin_layout Code + pgmName, +\end_layout + +\begin_layout Code + __LINE__, +\end_layout + +\begin_layout Code + sadr->sa_family ); +\end_layout + +\begin_layout Code + break; +\end_layout + +\begin_layout Code + } /* End DEFAULT case (unknown address family). + */ +\end_layout + +\begin_layout Code + } /* End SWITCH on address family. + */ +\end_layout + +\begin_layout Code + } /* End IF verbose mode. + */ +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Send the time-of-day to the client. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + wBytes = timeLen; +\end_layout + +\begin_layout Code + while ( wBytes > 0 ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + do +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + count = sendto( desc[ idx ].fd, +\end_layout + +\begin_layout Code + timeStr, +\end_layout + +\begin_layout Code + wBytes, +\end_layout + +\begin_layout Code + 0, +\end_layout + +\begin_layout Code + sadr, /* Address & address length + */ +\end_layout + +\begin_layout Code + sadrLen ); /* received in recvfrom(). + */ +\end_layout + +\begin_layout Code + } while ( ( count < 0 ) && ( errno == EINTR ) ); +\end_layout + +\begin_layout Code + CHK( count ); /* Check for a bona fide error. + */ +\end_layout + +\begin_layout Code + wBytes -= count; +\end_layout + +\begin_layout Code + } /* End WHILE there is data to send. + */ +\end_layout + +\begin_layout Code + } /* End ELSE a UDP datagram is available. + */ +\end_layout + +\begin_layout Code + desc[ idx ].revents = 0; /* Clear the returned poll events. + */ +\end_layout + +\begin_layout Code + } /* End FOR each socket descriptor. + */ +\end_layout + +\begin_layout Code + } /* End WHILE forever. + */ +\end_layout + +\begin_layout Code +} /* End tod() */ +\end_layout + +\begin_layout Subsubsection +'Daytime' TCP Client Code +\end_layout + +\begin_layout Standard +The TCP client code is found in file tod6tc.c (time-of-day IPv6 TCP client). + Once built, the TCP client may be started using the following command syntax + (assuming tod6tc is the executable file): +\end_layout + +\begin_layout Code +tod6tc [-v] [-s scope_id] [host [service]] +\end_layout + +\begin_layout Standard +ARGUMENTS: +\end_layout + +\begin_layout Description +host The hostname or IP address (dotted decimal or colon-hex) of the remote + host providing the service. + Default is "localhost". +\end_layout + +\begin_layout Description +service The TCP service (or well-known port number) to which a connection + attempt is made. + Default is "daytime". +\end_layout + +\begin_layout Standard +OPTIONS: +\end_layout + +\begin_layout Description +-s This option is only meaningful for IPv6 addresses, and is used to set + the scope identifier (i.e. + the network interface on which to establish the connection). + Default is "eth0". + If host is a scoped address, this option is ignored. +\end_layout + +\begin_layout Description +-v Turn on verbose mode. +\end_layout + +\begin_layout Standard +The TCP client source code contained in tod6tc.c follows: +\end_layout + +\begin_layout Code +/****************************************************************************** +\end_layout + +\begin_layout Code +* File: tod6tc.c +\end_layout + +\begin_layout Code +* Description: Contains source code for an IPv6-capable 'daytime' TCP client. +\end_layout + +\begin_layout Code +* Author: John Wenker, Sr. + Software Engineer +\end_layout + +\begin_layout Code +* Performance Technologies, San Diego, USA +\end_layout + +\begin_layout Code +******************************************************************************/ +\end_layout + +\begin_layout Code +/* +\end_layout + +\begin_layout Code +** System header files. +\end_layout + +\begin_layout Code +*/ +\end_layout + +\begin_layout Code +#include /* errno declaration and error codes. + */ +\end_layout + +\begin_layout Code +#include /* if_nametoindex(3). + */ +\end_layout + +\begin_layout Code +#include /* getaddrinfo(3) and associated definitions. + */ +\end_layout + +\begin_layout Code +#include /* sockaddr_in and sockaddr_in6 definitions. + */ +\end_layout + +\begin_layout Code +#include /* printf(3) et al. + */ +\end_layout + +\begin_layout Code +#include /* exit(2). + */ +\end_layout + +\begin_layout Code +#include /* String manipulation and memory functions. + */ +\end_layout + +\begin_layout Code +#include /* Socket functions (socket(2), connect(2), etc). + */ +\end_layout + +\begin_layout Code +#include /* getopt(3), read(2), etc. + */ +\end_layout + +\begin_layout Code +/* +\end_layout + +\begin_layout Code +** Constants & macros. +\end_layout + +\begin_layout Code +*/ +\end_layout + +\begin_layout Code +#define DFLT_HOST "localhost" /* Default server name. + */ +\end_layout + +\begin_layout Code +#define DFLT_SCOPE_ID "eth0" /* Default scope identifier. + */ +\end_layout + +\begin_layout Code +#define DFLT_SERVICE "daytime" /* Default service name. + */ +\end_layout + +\begin_layout Code +#define INVALID_DESC -1 /* Invalid file (socket) descriptor. + */ +\end_layout + +\begin_layout Code +#define MAXBFRSIZE 256 /* Max bfr sz to read remote TOD. + */ +\end_layout + +\begin_layout Code +#define VALIDOPTS "s:v" /* Valid command options. + */ +\end_layout + +\begin_layout Code +/* +\end_layout + +\begin_layout Code +** Type definitions (for convenience). +\end_layout + +\begin_layout Code +*/ +\end_layout + +\begin_layout Code +typedef enum { false = 0, true } boolean; +\end_layout + +\begin_layout Code +typedef struct sockaddr_in sockaddr_in_t; +\end_layout + +\begin_layout Code +typedef struct sockaddr_in6 sockaddr_in6_t; +\end_layout + +\begin_layout Code +/* +\end_layout + +\begin_layout Code +** Prototypes for internal helper functions. +\end_layout + +\begin_layout Code +*/ +\end_layout + +\begin_layout Code +static int openSckt( const char *host, +\end_layout + +\begin_layout Code + const char *service, +\end_layout + +\begin_layout Code + unsigned int scopeId ); +\end_layout + +\begin_layout Code +static void tod( int sckt ); +\end_layout + +\begin_layout Code +/* +\end_layout + +\begin_layout Code +** Global (within this file only) data objects. +\end_layout + +\begin_layout Code +*/ +\end_layout + +\begin_layout Code +static const char *pgmName; /* Program name (w/o directory). + */ +\end_layout + +\begin_layout Code +static boolean verbose = false; /* Verbose mode. + */ +\end_layout + +\begin_layout Code +/* +\end_layout + +\begin_layout Code +** Usage macro. +\end_layout + +\begin_layout Code +*/ +\end_layout + +\begin_layout Code +#define USAGE + +\backslash + +\end_layout + +\begin_layout Code + { + +\backslash + +\end_layout + +\begin_layout Code + fprintf( stderr, + +\backslash + +\end_layout + +\begin_layout Code + "Usage: %s [-v] [-s scope_id] [host [service]] +\backslash +n", +\backslash + +\end_layout + +\begin_layout Code + pgmName ); + +\backslash + +\end_layout + +\begin_layout Code + exit( 127 ); + +\backslash + +\end_layout + +\begin_layout Code + } /* End USAGE macro. + */ +\end_layout + +\begin_layout Code +/* +\end_layout + +\begin_layout Code +** This "macro" (even though it's really a function) is loosely based on + the +\end_layout + +\begin_layout Code +** CHK() macro by Dr. + V. + Vinge (see server code). + The status parameter is +\end_layout + +\begin_layout Code +** a boolean expression indicating the return code from one of the usual + system +\end_layout + +\begin_layout Code +** calls that returns -1 on error. + If a system call error occurred, an alert +\end_layout + +\begin_layout Code +** is written to stderr. + It returns a boolean value indicating success/failure +\end_layout + +\begin_layout Code +** of the system call. +\end_layout + +\begin_layout Code +** +\end_layout + +\begin_layout Code +** Example: if ( !SYSCALL( "write", +\end_layout + +\begin_layout Code +** count = write( fd, bfr, size ) ) ) +\end_layout + +\begin_layout Code +** { +\end_layout + +\begin_layout Code +** // Error processing... + but SYSCALL() will have already taken +\end_layout + +\begin_layout Code +** // care of dumping an error alert to stderr. +\end_layout + +\begin_layout Code +** } +\end_layout + +\begin_layout Code +*/ +\end_layout + +\begin_layout Code +static __inline boolean SYSCALL( const char *syscallName, +\end_layout + +\begin_layout Code + int lineNbr, +\end_layout + +\begin_layout Code + int status ) +\end_layout + +\begin_layout Code +{ +\end_layout + +\begin_layout Code + if ( ( status == -1 ) && verbose ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "%s (line %d): System call failed ('%s') - %s. +\backslash +n", +\end_layout + +\begin_layout Code + pgmName, +\end_layout + +\begin_layout Code + lineNbr, +\end_layout + +\begin_layout Code + syscallName, +\end_layout + +\begin_layout Code + strerror( errno ) ); +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + return status != -1; /* True if the system call was successful. + */ +\end_layout + +\begin_layout Code +} /* End SYSCALL() */ +\end_layout + +\begin_layout Code +/****************************************************************************** +\end_layout + +\begin_layout Code +* Function: main +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Description: +\end_layout + +\begin_layout Code +* Connect to a remote time-of-day service and write the remote host's + TOD to +\end_layout + +\begin_layout Code +* stdout. +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Parameters: +\end_layout + +\begin_layout Code +* The usual argc & argv parameters to a main() program. +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Return Value: +\end_layout + +\begin_layout Code +* This function always returns zero. +\end_layout + +\begin_layout Code +******************************************************************************/ +\end_layout + +\begin_layout Code +int main( int argc, +\end_layout + +\begin_layout Code + char *argv[ ] ) +\end_layout + +\begin_layout Code +{ +\end_layout + +\begin_layout Code + const char *host = DFLT_HOST; +\end_layout + +\begin_layout Code + int opt; +\end_layout + +\begin_layout Code + int sckt; +\end_layout + +\begin_layout Code + unsigned int scopeId = if_nametoindex( DFLT_SCOPE_ID ); +\end_layout + +\begin_layout Code + const char *service = DFLT_SERVICE; +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Determine the program name (w/o directory prefix). +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + pgmName = (const char*) strrchr( argv[ 0 ], '/' ); +\end_layout + +\begin_layout Code + pgmName = pgmName == NULL ? argv[ 0 ] : pgmName+1; +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Process command line options. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + opterr = 0; /* Turns off "invalid option" error messages. + */ +\end_layout + +\begin_layout Code + while ( ( opt = getopt( argc, argv, VALIDOPTS ) ) != -1 ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + switch ( opt ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + case 's': /* Scope identifier (IPv6 kluge). + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + scopeId = if_nametoindex( optarg ); +\end_layout + +\begin_layout Code + if ( scopeId == 0 ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "%s: Unknown network interface (%s). +\backslash +n", +\end_layout + +\begin_layout Code + pgmName, +\end_layout + +\begin_layout Code + optarg ); +\end_layout + +\begin_layout Code + USAGE; +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + break; +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + case 'v': /* Verbose mode. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + verbose = true; +\end_layout + +\begin_layout Code + break; +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + default: +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + USAGE; +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + } /* End SWITCH on command option. + */ +\end_layout + +\begin_layout Code + } /* End WHILE processing command options. + */ +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Process command arguments. + At the end of the above loop, optind is the +\end_layout + +\begin_layout Code + ** index of the first NON-option argv element. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + switch ( argc - optind ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + case 2: /* Both host & service are specified on the command line. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + service = argv[ optind + 1 ]; +\end_layout + +\begin_layout Code + /***** Fall through *****/ +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + case 1: /* Host is specified on the command line. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + host = argv[ optind ]; +\end_layout + +\begin_layout Code + /***** Fall through *****/ +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + case 0: /* Use default host & service. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + break; +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + default: +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + USAGE; +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + } /* End SWITCH on number of command arguments. + */ +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Open a connection to the indicated host/service. +\end_layout + +\begin_layout Code + ** +\end_layout + +\begin_layout Code + ** Note that if all three of the following conditions are met, then the +\end_layout + +\begin_layout Code + ** scope identifier remains unresolved at this point. +\end_layout + +\begin_layout Code + ** 1) The default network interface is unknown for some reason. +\end_layout + +\begin_layout Code + ** 2) The -s option was not used on the command line. +\end_layout + +\begin_layout Code + ** 3) An IPv6 "scoped address" was not specified for the hostname + on the +\end_layout + +\begin_layout Code + ** command line. +\end_layout + +\begin_layout Code + ** If the above three conditions are met, then only an IPv4 socket can + be +\end_layout + +\begin_layout Code + ** opened (connect(2) fails without the scope ID properly set for IPv6 +\end_layout + +\begin_layout Code + ** sockets). +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + if ( ( sckt = openSckt( host, +\end_layout + +\begin_layout Code + service, +\end_layout + +\begin_layout Code + scopeId ) ) == INVALID_DESC ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "%s: Sorry... + a connection could not be established. +\backslash +n", +\end_layout + +\begin_layout Code + pgmName ); +\end_layout + +\begin_layout Code + exit( 1 ); +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Get the remote time-of-day. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + tod( sckt ); +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Close the connection and terminate. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + (void) SYSCALL( "close", +\end_layout + +\begin_layout Code + __LINE__, +\end_layout + +\begin_layout Code + close( sckt ) ); +\end_layout + +\begin_layout Code + return 0; +\end_layout + +\begin_layout Code +} /* End main() */ +\end_layout + +\begin_layout Code +/****************************************************************************** +\end_layout + +\begin_layout Code +* Function: openSckt +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Description: +\end_layout + +\begin_layout Code +* Sets up a TCP connection to a remote server. + Getaddrinfo(3) is used to +\end_layout + +\begin_layout Code +* perform lookup functions and can return multiple address records (i.e. + a +\end_layout + +\begin_layout Code +* list of 'struct addrinfo' records). + This function traverses the list and +\end_layout + +\begin_layout Code +* tries to establish a connection to the remote server. + The function ends +\end_layout + +\begin_layout Code +* when either a connection has been established or all records in the + list +\end_layout + +\begin_layout Code +* have been processed. +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Parameters: +\end_layout + +\begin_layout Code +* host - A pointer to a character string representing the hostname + or IP +\end_layout + +\begin_layout Code +* address (IPv4 or IPv6) of the remote server. +\end_layout + +\begin_layout Code +* service - A pointer to a character string representing the service + name or +\end_layout + +\begin_layout Code +* well-known port number. +\end_layout + +\begin_layout Code +* scopeId - For IPv6 sockets only. + This is the index corresponding to the +\end_layout + +\begin_layout Code +* network interface on which to set up the connection. + This +\end_layout + +\begin_layout Code +* parameter is ignored for IPv4 sockets or when an IPv6 "scoped +\end_layout + +\begin_layout Code +* address" is specified in 'host' (i.e. + where the colon-hex +\end_layout + +\begin_layout Code +* network address is augmented with the scope ID). +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Return Value: +\end_layout + +\begin_layout Code +* Returns the socket descriptor for the connection, or INVALID_DESC if + all +\end_layout + +\begin_layout Code +* address records have been processed and a connection could not be +\end_layout + +\begin_layout Code +* established. +\end_layout + +\begin_layout Code +******************************************************************************/ +\end_layout + +\begin_layout Code +static int openSckt( const char *host, +\end_layout + +\begin_layout Code + const char *service, +\end_layout + +\begin_layout Code + unsigned int scopeId ) +\end_layout + +\begin_layout Code +{ +\end_layout + +\begin_layout Code + struct addrinfo *ai; +\end_layout + +\begin_layout Code + int aiErr; +\end_layout + +\begin_layout Code + struct addrinfo *aiHead; +\end_layout + +\begin_layout Code + struct addrinfo hints; +\end_layout + +\begin_layout Code + sockaddr_in6_t *pSadrIn6; +\end_layout + +\begin_layout Code + int sckt; +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Initialize the 'hints' structure for getaddrinfo(3). +\end_layout + +\begin_layout Code + ** +\end_layout + +\begin_layout Code + ** Notice that the 'ai_family' field is set to PF_UNSPEC, indicating + to +\end_layout + +\begin_layout Code + ** return both IPv4 and IPv6 address records for the host/service. + Most of +\end_layout + +\begin_layout Code + ** the time, the user isn't going to care whether an IPv4 connection + or an +\end_layout + +\begin_layout Code + ** IPv6 connection is established; the user simply wants to exchange + data +\end_layout + +\begin_layout Code + ** with the remote host and doesn't care how it's done. + Sometimes, however, +\end_layout + +\begin_layout Code + ** the user might want to explicitly specify the type of underlying socket. +\end_layout + +\begin_layout Code + ** It is left as an exercise for the motivated reader to add a command + line +\end_layout + +\begin_layout Code + ** option allowing the user to specify the IP protocol, and then process + the +\end_layout + +\begin_layout Code + ** list of addresses accordingly (it's not that difficult). +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + memset( &hints, 0, sizeof( hints ) ); +\end_layout + +\begin_layout Code + hints.ai_family = PF_UNSPEC; /* IPv4 or IPv6 records (don't care). + */ +\end_layout + +\begin_layout Code + hints.ai_socktype = SOCK_STREAM; /* Connection-oriented byte stream. + */ +\end_layout + +\begin_layout Code + hints.ai_protocol = IPPROTO_TCP; /* TCP transport layer protocol only. + */ +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Look up the host/service information. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + if ( ( aiErr = getaddrinfo( host, +\end_layout + +\begin_layout Code + service, +\end_layout + +\begin_layout Code + &hints, +\end_layout + +\begin_layout Code + &aiHead ) ) != 0 ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "%s (line %d): ERROR - %s. +\backslash +n", +\end_layout + +\begin_layout Code + pgmName, +\end_layout + +\begin_layout Code + __LINE__, +\end_layout + +\begin_layout Code + gai_strerror( aiErr ) ); +\end_layout + +\begin_layout Code + return INVALID_DESC; +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Go through the list and try to open a connection. + Continue until either +\end_layout + +\begin_layout Code + ** a connection is established or the entire list is exhausted. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + for ( ai = aiHead, sckt = INVALID_DESC; +\end_layout + +\begin_layout Code + ( ai != NULL ) && ( sckt == INVALID_DESC ); +\end_layout + +\begin_layout Code + ai = ai->ai_next ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** IPv6 kluge. + Make sure the scope ID is set. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + if ( ai->ai_family == PF_INET6 ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + pSadrIn6 = (sockaddr_in6_t*) ai->ai_addr; +\end_layout + +\begin_layout Code + if ( pSadrIn6->sin6_scope_id == 0 ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + pSadrIn6->sin6_scope_id = scopeId; +\end_layout + +\begin_layout Code + } /* End IF the scope ID wasn't set. + */ +\end_layout + +\begin_layout Code + } /* End IPv6 kluge. + */ +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Display the address info for the remote host. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + if ( verbose ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Temporary character string buffers for host & service. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + char hostBfr[ NI_MAXHOST ]; +\end_layout + +\begin_layout Code + char servBfr[ NI_MAXSERV ]; +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Display the address information just fetched. + Start with the +\end_layout + +\begin_layout Code + ** common (protocol-independent) stuff first. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "Address info: +\backslash +n" +\end_layout + +\begin_layout Code + " ai_flags = 0x%02X +\backslash +n" +\end_layout + +\begin_layout Code + " ai_family = %d (PF_INET = %d, PF_INET6 = %d) +\backslash +n" +\end_layout + +\begin_layout Code + " ai_socktype = %d (SOCK_STREAM = %d, SOCK_DGRAM = + %d) +\backslash +n" +\end_layout + +\begin_layout Code + " ai_protocol = %d (IPPROTO_TCP = %d, IPPROTO_UDP = + %d) +\backslash +n" +\end_layout + +\begin_layout Code + " ai_addrlen = %d (sockaddr_in = %d, " +\end_layout + +\begin_layout Code + "sockaddr_in6 = %d) +\backslash +n", +\end_layout + +\begin_layout Code + ai->ai_flags, +\end_layout + +\begin_layout Code + ai->ai_family, +\end_layout + +\begin_layout Code + PF_INET, +\end_layout + +\begin_layout Code + PF_INET6, +\end_layout + +\begin_layout Code + ai->ai_socktype, +\end_layout + +\begin_layout Code + SOCK_STREAM, +\end_layout + +\begin_layout Code + SOCK_DGRAM, +\end_layout + +\begin_layout Code + ai->ai_protocol, +\end_layout + +\begin_layout Code + IPPROTO_TCP, +\end_layout + +\begin_layout Code + IPPROTO_UDP, +\end_layout + +\begin_layout Code + ai->ai_addrlen, +\end_layout + +\begin_layout Code + sizeof( struct sockaddr_in ), +\end_layout + +\begin_layout Code + sizeof( struct sockaddr_in6 ) ); +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Display the protocol-specific formatted address. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + getnameinfo( ai->ai_addr, +\end_layout + +\begin_layout Code + ai->ai_addrlen, +\end_layout + +\begin_layout Code + hostBfr, +\end_layout + +\begin_layout Code + sizeof( hostBfr ), +\end_layout + +\begin_layout Code + servBfr, +\end_layout + +\begin_layout Code + sizeof( servBfr ), +\end_layout + +\begin_layout Code + NI_NUMERICHOST | NI_NUMERICSERV ); +\end_layout + +\begin_layout Code + switch ( ai->ai_family ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + case PF_INET: /* IPv4 address record. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + sockaddr_in_t *pSadrIn = (sockaddr_in_t*) ai->ai_addr; +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + " ai_addr = sin_family: %d (AF_INET = %d, + " +\end_layout + +\begin_layout Code + "AF_INET6 = %d) +\backslash +n" +\end_layout + +\begin_layout Code + " sin_addr: %s +\backslash +n" +\end_layout + +\begin_layout Code + " sin_port: %s +\backslash +n", +\end_layout + +\begin_layout Code + pSadrIn->sin_family, +\end_layout + +\begin_layout Code + AF_INET, +\end_layout + +\begin_layout Code + AF_INET6, +\end_layout + +\begin_layout Code + hostBfr, +\end_layout + +\begin_layout Code + servBfr ); +\end_layout + +\begin_layout Code + break; +\end_layout + +\begin_layout Code + } /* End CASE of IPv4 record. + */ +\end_layout + +\begin_layout Code + case PF_INET6: /* IPv6 address record. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + pSadrIn6 = (sockaddr_in6_t*) ai->ai_addr; +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + " ai_addr = sin6_family: %d (AF_INET = + %d, " +\end_layout + +\begin_layout Code + "AF_INET6 = %d) +\backslash +n" +\end_layout + +\begin_layout Code + " sin6_addr: %s +\backslash +n" +\end_layout + +\begin_layout Code + " sin6_port: %s +\backslash +n" +\end_layout + +\begin_layout Code + " sin6_flowinfo: %d +\backslash +n" +\end_layout + +\begin_layout Code + " sin6_scope_id: %d +\backslash +n", +\end_layout + +\begin_layout Code + pSadrIn6->sin6_family, +\end_layout + +\begin_layout Code + AF_INET, +\end_layout + +\begin_layout Code + AF_INET6, +\end_layout + +\begin_layout Code + hostBfr, +\end_layout + +\begin_layout Code + servBfr, +\end_layout + +\begin_layout Code + pSadrIn6->sin6_flowinfo, +\end_layout + +\begin_layout Code + pSadrIn6->sin6_scope_id ); +\end_layout + +\begin_layout Code + break; +\end_layout + +\begin_layout Code + } /* End CASE of IPv6 record. + */ +\end_layout + +\begin_layout Code + default: /* Can never get here, but just for completeness. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "%s (line %d): ERROR - Unknown protocol family (%d). +\backslash +n", +\end_layout + +\begin_layout Code + pgmName, +\end_layout + +\begin_layout Code + __LINE__, +\end_layout + +\begin_layout Code + ai->ai_family ); +\end_layout + +\begin_layout Code + break; +\end_layout + +\begin_layout Code + } /* End DEFAULT case (unknown protocol family). + */ +\end_layout + +\begin_layout Code + } /* End SWITCH on protocol family. + */ +\end_layout + +\begin_layout Code + } /* End IF verbose mode. + */ +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Create a socket. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + if ( !SYSCALL( "socket", +\end_layout + +\begin_layout Code + __LINE__, +\end_layout + +\begin_layout Code + sckt = socket( ai->ai_family, +\end_layout + +\begin_layout Code + ai->ai_socktype, +\end_layout + +\begin_layout Code + ai->ai_protocol ) ) ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + sckt = INVALID_DESC; +\end_layout + +\begin_layout Code + continue; /* Try the next address record in the list. + */ +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Connect to the remote host. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + if ( !SYSCALL( "connect", +\end_layout + +\begin_layout Code + __LINE__, +\end_layout + +\begin_layout Code + connect( sckt, +\end_layout + +\begin_layout Code + ai->ai_addr, +\end_layout + +\begin_layout Code + ai->ai_addrlen ) ) ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + (void) close( sckt ); /* Could use SYSCALL() again here, but + why? */ +\end_layout + +\begin_layout Code + sckt = INVALID_DESC; +\end_layout + +\begin_layout Code + continue; /* Try the next address record in the list. + */ +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + } /* End FOR each address record returned by getaddrinfo(3). + */ +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Clean up & return. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + freeaddrinfo( aiHead ); +\end_layout + +\begin_layout Code + return sckt; +\end_layout + +\begin_layout Code +} /* End openSckt() */ +\end_layout + +\begin_layout Code +/****************************************************************************** +\end_layout + +\begin_layout Code +* Function: tod +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Description: +\end_layout + +\begin_layout Code +* Receive the time-of-day from the remote server and write it to stdout. +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Parameters: +\end_layout + +\begin_layout Code +* sckt - The socket descriptor for the connection. +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Return Value: None. +\end_layout + +\begin_layout Code +******************************************************************************/ +\end_layout + +\begin_layout Code +static void tod( int sckt ) +\end_layout + +\begin_layout Code +{ +\end_layout + +\begin_layout Code + char bfr[ MAXBFRSIZE+1 ]; +\end_layout + +\begin_layout Code + int inBytes; +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** The client never sends anything, so shut down the write side of the +\end_layout + +\begin_layout Code + ** connection. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + if ( !SYSCALL( "shutdown", +\end_layout + +\begin_layout Code + __LINE__, +\end_layout + +\begin_layout Code + shutdown( sckt, SHUT_WR ) ) ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + return; +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Read the time-of-day from the remote host. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + do +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + if ( !SYSCALL( "read", +\end_layout + +\begin_layout Code + __LINE__, +\end_layout + +\begin_layout Code + inBytes = read( sckt, +\end_layout + +\begin_layout Code + bfr, +\end_layout + +\begin_layout Code + MAXBFRSIZE ) ) ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + return; +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + bfr[ inBytes ] = ' +\backslash +0'; /* Null-terminate the received string. + */ +\end_layout + +\begin_layout Code + fputs( bfr, stdout ); /* Null string if EOF (inBytes == 0). + */ +\end_layout + +\begin_layout Code + } while ( inBytes > 0 ); +\end_layout + +\begin_layout Code + fflush( stdout ); +\end_layout + +\begin_layout Code +} /* End tod() */ +\end_layout + +\begin_layout Subsubsection +'Daytime' UDP Client Code +\end_layout + +\begin_layout Standard +The UDP client code is found in file tod6uc.c (time-of-day IPv6 UDP client). + It is almost an exact duplicate of the TCP client (and in fact was derived + from it), but is included in this HowTo for completeness. + Once built, the UDP client may be started using the following command syntax + (assuming tod6uc is the executable file): +\end_layout + +\begin_layout Code +tod6uc [-v] [-s scope_id] [host [service]] +\end_layout + +\begin_layout Standard +ARGUMENTS: +\end_layout + +\begin_layout Description +host The hostname or IP address (dotted decimal or colon-hex) of the remote + host providing the service. + Default is "localhost". +\end_layout + +\begin_layout Description +service The UDP service (or well-known port number) to which datagrams are + sent. + Default is "daytime". +\end_layout + +\begin_layout Standard +OPTIONS: +\end_layout + +\begin_layout Description +-s This option is only meaningful for IPv6 addresses, and is used to set + the scope identifier (i.e. + the network interface on which to exchange datagrams). + Default is "eth0". + If host is a scoped address, this option is ignored. +\end_layout + +\begin_layout Description +-v Turn on verbose mode. +\end_layout + +\begin_layout Standard +The UDP client source code contained in tod6uc.c follows: +\end_layout + +\begin_layout Code +/****************************************************************************** +\end_layout + +\begin_layout Code +* File: tod6uc.c +\end_layout + +\begin_layout Code +* Description: Contains source code for an IPv6-capable 'daytime' UDP client. +\end_layout + +\begin_layout Code +* Author: John Wenker, Sr. + Software Engineer +\end_layout + +\begin_layout Code +* Performance Technologies, San Diego, USA +\end_layout + +\begin_layout Code +******************************************************************************/ +\end_layout + +\begin_layout Code +/* +\end_layout + +\begin_layout Code +** System header files. +\end_layout + +\begin_layout Code +*/ +\end_layout + +\begin_layout Code +#include /* errno declaration and error codes. + */ +\end_layout + +\begin_layout Code +#include /* if_nametoindex(3). + */ +\end_layout + +\begin_layout Code +#include /* getaddrinfo(3) and associated definitions. + */ +\end_layout + +\begin_layout Code +#include /* sockaddr_in and sockaddr_in6 definitions. + */ +\end_layout + +\begin_layout Code +#include /* printf(3) et al. + */ +\end_layout + +\begin_layout Code +#include /* exit(2). + */ +\end_layout + +\begin_layout Code +#include /* String manipulation and memory functions. + */ +\end_layout + +\begin_layout Code +#include /* Socket functions (socket(2), connect(2), etc). + */ +\end_layout + +\begin_layout Code +#include /* getopt(3), recvfrom(2), sendto(2), etc. + */ +\end_layout + +\begin_layout Code +/* +\end_layout + +\begin_layout Code +** Constants & macros. +\end_layout + +\begin_layout Code +*/ +\end_layout + +\begin_layout Code +#define DFLT_HOST "localhost" /* Default server name. + */ +\end_layout + +\begin_layout Code +#define DFLT_SCOPE_ID "eth0" /* Default scope identifier. + */ +\end_layout + +\begin_layout Code +#define DFLT_SERVICE "daytime" /* Default service name. + */ +\end_layout + +\begin_layout Code +#define INVALID_DESC -1 /* Invalid file (socket) descriptor. + */ +\end_layout + +\begin_layout Code +#define MAXBFRSIZE 256 /* Max bfr sz to read remote TOD. + */ +\end_layout + +\begin_layout Code +#define VALIDOPTS "s:v" /* Valid command options. + */ +\end_layout + +\begin_layout Code +/* +\end_layout + +\begin_layout Code +** Type definitions (for convenience). +\end_layout + +\begin_layout Code +*/ +\end_layout + +\begin_layout Code +typedef enum { false = 0, true } boolean; +\end_layout + +\begin_layout Code +typedef struct sockaddr_in sockaddr_in_t; +\end_layout + +\begin_layout Code +typedef struct sockaddr_in6 sockaddr_in6_t; +\end_layout + +\begin_layout Code +/* +\end_layout + +\begin_layout Code +** Prototypes for internal helper functions. +\end_layout + +\begin_layout Code +*/ +\end_layout + +\begin_layout Code +static int openSckt( const char *host, +\end_layout + +\begin_layout Code + const char *service, +\end_layout + +\begin_layout Code + unsigned int scopeId ); +\end_layout + +\begin_layout Code +static void tod( int sckt ); +\end_layout + +\begin_layout Code +/* +\end_layout + +\begin_layout Code +** Global (within this file only) data objects. +\end_layout + +\begin_layout Code +*/ +\end_layout + +\begin_layout Code +static const char *pgmName; /* Program name (w/o directory). + */ +\end_layout + +\begin_layout Code +static boolean verbose = false; /* Verbose mode. + */ +\end_layout + +\begin_layout Code +/* +\end_layout + +\begin_layout Code +** Usage macro. +\end_layout + +\begin_layout Code +*/ +\end_layout + +\begin_layout Code +#define USAGE + +\backslash + +\end_layout + +\begin_layout Code + { + +\backslash + +\end_layout + +\begin_layout Code + fprintf( stderr, + +\backslash + +\end_layout + +\begin_layout Code + "Usage: %s [-v] [-s scope_id] [host [service]] +\backslash +n", +\backslash + +\end_layout + +\begin_layout Code + pgmName ); + +\backslash + +\end_layout + +\begin_layout Code + exit( 127 ); + +\backslash + +\end_layout + +\begin_layout Code + } /* End USAGE macro. + */ +\end_layout + +\begin_layout Code +/* +\end_layout + +\begin_layout Code +** This "macro" (even though it's really a function) is loosely based on + the +\end_layout + +\begin_layout Code +** CHK() macro by Dr. + V. + Vinge (see server code). + The status parameter is +\end_layout + +\begin_layout Code +** a boolean expression indicating the return code from one of the usual + system +\end_layout + +\begin_layout Code +** calls that returns -1 on error. + If a system call error occurred, an alert +\end_layout + +\begin_layout Code +** is written to stderr. + It returns a boolean value indicating success/failure +\end_layout + +\begin_layout Code +** of the system call. +\end_layout + +\begin_layout Code +** +\end_layout + +\begin_layout Code +** Example: if ( !SYSCALL( "write", +\end_layout + +\begin_layout Code +** count = write( fd, bfr, size ) ) ) +\end_layout + +\begin_layout Code +** { +\end_layout + +\begin_layout Code +** // Error processing... + but SYSCALL() will have already taken +\end_layout + +\begin_layout Code +** // care of dumping an error alert to stderr. +\end_layout + +\begin_layout Code +** } +\end_layout + +\begin_layout Code +*/ +\end_layout + +\begin_layout Code +static __inline boolean SYSCALL( const char *syscallName, +\end_layout + +\begin_layout Code + int lineNbr, +\end_layout + +\begin_layout Code + int status ) +\end_layout + +\begin_layout Code +{ +\end_layout + +\begin_layout Code + if ( ( status == -1 ) && verbose ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "%s (line %d): System call failed ('%s') - %s. +\backslash +n", +\end_layout + +\begin_layout Code + pgmName, +\end_layout + +\begin_layout Code + lineNbr, +\end_layout + +\begin_layout Code + syscallName, +\end_layout + +\begin_layout Code + strerror( errno ) ); +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + return status != -1; /* True if the system call was successful. + */ +\end_layout + +\begin_layout Code +} /* End SYSCALL() */ +\end_layout + +\begin_layout Code +/****************************************************************************** +\end_layout + +\begin_layout Code +* Function: main +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Description: +\end_layout + +\begin_layout Code +* Connect to a remote time-of-day service and write the remote host's + TOD to +\end_layout + +\begin_layout Code +* stdout. +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Parameters: +\end_layout + +\begin_layout Code +* The usual argc & argv parameters to a main() program. +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Return Value: +\end_layout + +\begin_layout Code +* This function always returns zero. +\end_layout + +\begin_layout Code +******************************************************************************/ +\end_layout + +\begin_layout Code +int main( int argc, +\end_layout + +\begin_layout Code + char *argv[ ] ) +\end_layout + +\begin_layout Code +{ +\end_layout + +\begin_layout Code + const char *host = DFLT_HOST; +\end_layout + +\begin_layout Code + int opt; +\end_layout + +\begin_layout Code + int sckt; +\end_layout + +\begin_layout Code + unsigned int scopeId = if_nametoindex( DFLT_SCOPE_ID ); +\end_layout + +\begin_layout Code + const char *service = DFLT_SERVICE; +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Determine the program name (w/o directory prefix). +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + pgmName = (const char*) strrchr( argv[ 0 ], '/' ); +\end_layout + +\begin_layout Code + pgmName = pgmName == NULL ? argv[ 0 ] : pgmName+1; +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Process command line options. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + opterr = 0; /* Turns off "invalid option" error messages. + */ +\end_layout + +\begin_layout Code + while ( ( opt = getopt( argc, argv, VALIDOPTS ) ) != -1 ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + switch ( opt ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + case 's': /* Scope identifier (IPv6 kluge). + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + scopeId = if_nametoindex( optarg ); +\end_layout + +\begin_layout Code + if ( scopeId == 0 ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "%s: Unknown network interface (%s). +\backslash +n", +\end_layout + +\begin_layout Code + pgmName, +\end_layout + +\begin_layout Code + optarg ); +\end_layout + +\begin_layout Code + USAGE; +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + break; +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + case 'v': /* Verbose mode. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + verbose = true; +\end_layout + +\begin_layout Code + break; +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + default: +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + USAGE; +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + } /* End SWITCH on command option. + */ +\end_layout + +\begin_layout Code + } /* End WHILE processing command options. + */ +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Process command arguments. + At the end of the above loop, optind is the +\end_layout + +\begin_layout Code + ** index of the first NON-option argv element. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + switch ( argc - optind ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + case 2: /* Both host & service are specified on the command line. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + service = argv[ optind + 1 ]; +\end_layout + +\begin_layout Code + /***** Fall through *****/ +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + case 1: /* Host is specified on the command line. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + host = argv[ optind ]; +\end_layout + +\begin_layout Code + /***** Fall through *****/ +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + case 0: /* Use default host & service. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + break; +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + default: +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + USAGE; +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + } /* End SWITCH on number of command arguments. + */ +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Open a connection to the indicated host/service. +\end_layout + +\begin_layout Code + ** +\end_layout + +\begin_layout Code + ** Note that if all three of the following conditions are met, then the +\end_layout + +\begin_layout Code + ** scope identifier remains unresolved at this point. +\end_layout + +\begin_layout Code + ** 1) The default network interface is unknown for some reason. +\end_layout + +\begin_layout Code + ** 2) The -s option was not used on the command line. +\end_layout + +\begin_layout Code + ** 3) An IPv6 "scoped address" was not specified for the hostname + on the +\end_layout + +\begin_layout Code + ** command line. +\end_layout + +\begin_layout Code + ** If the above three conditions are met, then only an IPv4 socket can + be +\end_layout + +\begin_layout Code + ** opened (connect(2) fails without the scope ID properly set for IPv6 +\end_layout + +\begin_layout Code + ** sockets). +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + if ( ( sckt = openSckt( host, +\end_layout + +\begin_layout Code + service, +\end_layout + +\begin_layout Code + scopeId ) ) == INVALID_DESC ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "%s: Sorry... + a connectionless socket could " +\end_layout + +\begin_layout Code + "not be set up. +\backslash +n", +\end_layout + +\begin_layout Code + pgmName ); +\end_layout + +\begin_layout Code + exit( 1 ); +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Get the remote time-of-day. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + tod( sckt ); +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Close the connection and terminate. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + (void) SYSCALL( "close", +\end_layout + +\begin_layout Code + __LINE__, +\end_layout + +\begin_layout Code + close( sckt ) ); +\end_layout + +\begin_layout Code + return 0; +\end_layout + +\begin_layout Code +} /* End main() */ +\end_layout + +\begin_layout Code +/****************************************************************************** +\end_layout + +\begin_layout Code +* Function: openSckt +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Description: +\end_layout + +\begin_layout Code +* Sets up a UDP socket to a remote server. + Getaddrinfo(3) is used to +\end_layout + +\begin_layout Code +* perform lookup functions and can return multiple address records (i.e. + a +\end_layout + +\begin_layout Code +* list of 'struct addrinfo' records). + This function traverses the list and +\end_layout + +\begin_layout Code +* tries to establish a connection to the remote server. + The function ends +\end_layout + +\begin_layout Code +* when either a connection has been established or all records in the + list +\end_layout + +\begin_layout Code +* have been processed. +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Parameters: +\end_layout + +\begin_layout Code +* host - A pointer to a character string representing the hostname + or IP +\end_layout + +\begin_layout Code +* address (IPv4 or IPv6) of the remote server. +\end_layout + +\begin_layout Code +* service - A pointer to a character string representing the service + name or +\end_layout + +\begin_layout Code +* well-known port number. +\end_layout + +\begin_layout Code +* scopeId - For IPv6 sockets only. + This is the index corresponding to the +\end_layout + +\begin_layout Code +* network interface on which to exchange datagrams. + This +\end_layout + +\begin_layout Code +* parameter is ignored for IPv4 sockets or when an IPv6 "scoped +\end_layout + +\begin_layout Code +* address" is specified in 'host' (i.e. + where the colon-hex +\end_layout + +\begin_layout Code +* network address is augmented with the scope ID). +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Return Value: +\end_layout + +\begin_layout Code +* Returns the socket descriptor for the connection, or INVALID_DESC if + all +\end_layout + +\begin_layout Code +* address records have been processed and a socket could not be initialized. +\end_layout + +\begin_layout Code +******************************************************************************/ +\end_layout + +\begin_layout Code +static int openSckt( const char *host, +\end_layout + +\begin_layout Code + const char *service, +\end_layout + +\begin_layout Code + unsigned int scopeId ) +\end_layout + +\begin_layout Code +{ +\end_layout + +\begin_layout Code + struct addrinfo *ai; +\end_layout + +\begin_layout Code + int aiErr; +\end_layout + +\begin_layout Code + struct addrinfo *aiHead; +\end_layout + +\begin_layout Code + struct addrinfo hints; +\end_layout + +\begin_layout Code + sockaddr_in6_t *pSadrIn6; +\end_layout + +\begin_layout Code + int sckt; +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Initialize the 'hints' structure for getaddrinfo(3). +\end_layout + +\begin_layout Code + ** +\end_layout + +\begin_layout Code + ** Notice that the 'ai_family' field is set to PF_UNSPEC, indicating + to +\end_layout + +\begin_layout Code + ** return both IPv4 and IPv6 address records for the host/service. + Most of +\end_layout + +\begin_layout Code + ** the time, the user isn't going to care whether an IPv4 connection + or an +\end_layout + +\begin_layout Code + ** IPv6 connection is established; the user simply wants to exchange + data +\end_layout + +\begin_layout Code + ** with the remote host and doesn't care how it's done. + Sometimes, however, +\end_layout + +\begin_layout Code + ** the user might want to explicitly specify the type of underlying socket. +\end_layout + +\begin_layout Code + ** It is left as an exercise for the motivated reader to add a command + line +\end_layout + +\begin_layout Code + ** option allowing the user to specify the IP protocol, and then process + the +\end_layout + +\begin_layout Code + ** list of addresses accordingly (it's not that difficult). +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + memset( &hints, 0, sizeof( hints ) ); +\end_layout + +\begin_layout Code + hints.ai_family = PF_UNSPEC; /* IPv4 or IPv6 records (don't care). + */ +\end_layout + +\begin_layout Code + hints.ai_socktype = SOCK_DGRAM; /* Connectionless communication. + */ +\end_layout + +\begin_layout Code + hints.ai_protocol = IPPROTO_UDP; /* UDP transport layer protocol only. + */ +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Look up the host/service information. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + if ( ( aiErr = getaddrinfo( host, +\end_layout + +\begin_layout Code + service, +\end_layout + +\begin_layout Code + &hints, +\end_layout + +\begin_layout Code + &aiHead ) ) != 0 ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "%s (line %d): ERROR - %s. +\backslash +n", +\end_layout + +\begin_layout Code + pgmName, +\end_layout + +\begin_layout Code + __LINE__, +\end_layout + +\begin_layout Code + gai_strerror( aiErr ) ); +\end_layout + +\begin_layout Code + return INVALID_DESC; +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Go through the list and try to open a connection. + Continue until either +\end_layout + +\begin_layout Code + ** a connection is established or the entire list is exhausted. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + for ( ai = aiHead, sckt = INVALID_DESC; +\end_layout + +\begin_layout Code + ( ai != NULL ) && ( sckt == INVALID_DESC ); +\end_layout + +\begin_layout Code + ai = ai->ai_next ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** IPv6 kluge. + Make sure the scope ID is set. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + if ( ai->ai_family == PF_INET6 ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + pSadrIn6 = (sockaddr_in6_t*) ai->ai_addr; +\end_layout + +\begin_layout Code + if ( pSadrIn6->sin6_scope_id == 0 ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + pSadrIn6->sin6_scope_id = scopeId; +\end_layout + +\begin_layout Code + } /* End IF the scope ID wasn't set. + */ +\end_layout + +\begin_layout Code + } /* End IPv6 kluge. + */ +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Display the address info for the remote host. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + if ( verbose ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Temporary character string buffers for host & service. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + char hostBfr[ NI_MAXHOST ]; +\end_layout + +\begin_layout Code + char servBfr[ NI_MAXSERV ]; +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Display the address information just fetched. + Start with the +\end_layout + +\begin_layout Code + ** common (protocol-independent) stuff first. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "Address info: +\backslash +n" +\end_layout + +\begin_layout Code + " ai_flags = 0x%02X +\backslash +n" +\end_layout + +\begin_layout Code + " ai_family = %d (PF_INET = %d, PF_INET6 = %d) +\backslash +n" +\end_layout + +\begin_layout Code + " ai_socktype = %d (SOCK_STREAM = %d, SOCK_DGRAM = + %d) +\backslash +n" +\end_layout + +\begin_layout Code + " ai_protocol = %d (IPPROTO_TCP = %d, IPPROTO_UDP = + %d) +\backslash +n" +\end_layout + +\begin_layout Code + " ai_addrlen = %d (sockaddr_in = %d, " +\end_layout + +\begin_layout Code + "sockaddr_in6 = %d) +\backslash +n", +\end_layout + +\begin_layout Code + ai->ai_flags, +\end_layout + +\begin_layout Code + ai->ai_family, +\end_layout + +\begin_layout Code + PF_INET, +\end_layout + +\begin_layout Code + PF_INET6, +\end_layout + +\begin_layout Code + ai->ai_socktype, +\end_layout + +\begin_layout Code + SOCK_STREAM, +\end_layout + +\begin_layout Code + SOCK_DGRAM, +\end_layout + +\begin_layout Code + ai->ai_protocol, +\end_layout + +\begin_layout Code + IPPROTO_TCP, +\end_layout + +\begin_layout Code + IPPROTO_UDP, +\end_layout + +\begin_layout Code + ai->ai_addrlen, +\end_layout + +\begin_layout Code + sizeof( struct sockaddr_in ), +\end_layout + +\begin_layout Code + sizeof( struct sockaddr_in6 ) ); +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Display the protocol-specific formatted address. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + getnameinfo( ai->ai_addr, +\end_layout + +\begin_layout Code + ai->ai_addrlen, +\end_layout + +\begin_layout Code + hostBfr, +\end_layout + +\begin_layout Code + sizeof( hostBfr ), +\end_layout + +\begin_layout Code + servBfr, +\end_layout + +\begin_layout Code + sizeof( servBfr ), +\end_layout + +\begin_layout Code + NI_NUMERICHOST | NI_NUMERICSERV ); +\end_layout + +\begin_layout Code + switch ( ai->ai_family ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + case PF_INET: /* IPv4 address record. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + sockaddr_in_t *pSadrIn = (sockaddr_in_t*) ai->ai_addr; +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + " ai_addr = sin_family: %d (AF_INET = %d, + " +\end_layout + +\begin_layout Code + "AF_INET6 = %d) +\backslash +n" +\end_layout + +\begin_layout Code + " sin_addr: %s +\backslash +n" +\end_layout + +\begin_layout Code + " sin_port: %s +\backslash +n", +\end_layout + +\begin_layout Code + pSadrIn->sin_family, +\end_layout + +\begin_layout Code + AF_INET, +\end_layout + +\begin_layout Code + AF_INET6, +\end_layout + +\begin_layout Code + hostBfr, +\end_layout + +\begin_layout Code + servBfr ); +\end_layout + +\begin_layout Code + break; +\end_layout + +\begin_layout Code + } /* End CASE of IPv4 record. + */ +\end_layout + +\begin_layout Code + case PF_INET6: /* IPv6 address record. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + pSadrIn6 = (sockaddr_in6_t*) ai->ai_addr; +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + " ai_addr = sin6_family: %d (AF_INET = + %d, " +\end_layout + +\begin_layout Code + "AF_INET6 = %d) +\backslash +n" +\end_layout + +\begin_layout Code + " sin6_addr: %s +\backslash +n" +\end_layout + +\begin_layout Code + " sin6_port: %s +\backslash +n" +\end_layout + +\begin_layout Code + " sin6_flowinfo: %d +\backslash +n" +\end_layout + +\begin_layout Code + " sin6_scope_id: %d +\backslash +n", +\end_layout + +\begin_layout Code + pSadrIn6->sin6_family, +\end_layout + +\begin_layout Code + AF_INET, +\end_layout + +\begin_layout Code + AF_INET6, +\end_layout + +\begin_layout Code + hostBfr, +\end_layout + +\begin_layout Code + servBfr, +\end_layout + +\begin_layout Code + pSadrIn6->sin6_flowinfo, +\end_layout + +\begin_layout Code + pSadrIn6->sin6_scope_id ); +\end_layout + +\begin_layout Code + break; +\end_layout + +\begin_layout Code + } /* End CASE of IPv6 record. + */ +\end_layout + +\begin_layout Code + default: /* Can never get here, but just for completeness. + */ +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + fprintf( stderr, +\end_layout + +\begin_layout Code + "%s (line %d): ERROR - Unknown protocol family (%d). +\backslash +n", +\end_layout + +\begin_layout Code + pgmName, +\end_layout + +\begin_layout Code + __LINE__, +\end_layout + +\begin_layout Code + ai->ai_family ); +\end_layout + +\begin_layout Code + break; +\end_layout + +\begin_layout Code + } /* End DEFAULT case (unknown protocol family). + */ +\end_layout + +\begin_layout Code + } /* End SWITCH on protocol family. + */ +\end_layout + +\begin_layout Code + } /* End IF verbose mode. + */ +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Create a socket. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + if ( !SYSCALL( "socket", +\end_layout + +\begin_layout Code + __LINE__, +\end_layout + +\begin_layout Code + sckt = socket( ai->ai_family, +\end_layout + +\begin_layout Code + ai->ai_socktype, +\end_layout + +\begin_layout Code + ai->ai_protocol ) ) ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + sckt = INVALID_DESC; +\end_layout + +\begin_layout Code + continue; /* Try the next address record in the list. + */ +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Set the target destination for the remote host on this socket. + That +\end_layout + +\begin_layout Code + ** is, this socket only communicates with the specified host. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + if ( !SYSCALL( "connect", +\end_layout + +\begin_layout Code + __LINE__, +\end_layout + +\begin_layout Code + connect( sckt, +\end_layout + +\begin_layout Code + ai->ai_addr, +\end_layout + +\begin_layout Code + ai->ai_addrlen ) ) ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + (void) close( sckt ); /* Could use SYSCALL() again here, but + why? */ +\end_layout + +\begin_layout Code + sckt = INVALID_DESC; +\end_layout + +\begin_layout Code + continue; /* Try the next address record in the list. + */ +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + } /* End FOR each address record returned by getaddrinfo(3). + */ +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Clean up & return. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + freeaddrinfo( aiHead ); +\end_layout + +\begin_layout Code + return sckt; +\end_layout + +\begin_layout Code +} /* End openSckt() */ +\end_layout + +\begin_layout Code +/****************************************************************************** +\end_layout + +\begin_layout Code +* Function: tod +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Description: +\end_layout + +\begin_layout Code +* Receive the time-of-day from the remote server and write it to stdout. +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Parameters: +\end_layout + +\begin_layout Code +* sckt - The socket descriptor for the connection. +\end_layout + +\begin_layout Code +* +\end_layout + +\begin_layout Code +* Return Value: None. +\end_layout + +\begin_layout Code +******************************************************************************/ +\end_layout + +\begin_layout Code +static void tod( int sckt ) +\end_layout + +\begin_layout Code +{ +\end_layout + +\begin_layout Code + char bfr[ MAXBFRSIZE+1 ]; +\end_layout + +\begin_layout Code + int inBytes; +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Send a datagram to the server to wake it up. + The content isn't +\end_layout + +\begin_layout Code + ** important, but something must be sent to let it know we want the TOD. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + if ( !SYSCALL( "write", +\end_layout + +\begin_layout Code + __LINE__, +\end_layout + +\begin_layout Code + write( sckt, "Are you there?", 14 ) ) ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + return; +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + /* +\end_layout + +\begin_layout Code + ** Read the time-of-day from the remote host. +\end_layout + +\begin_layout Code + */ +\end_layout + +\begin_layout Code + if ( !SYSCALL( "read", +\end_layout + +\begin_layout Code + __LINE__, +\end_layout + +\begin_layout Code + inBytes = read( sckt, +\end_layout + +\begin_layout Code + bfr, +\end_layout + +\begin_layout Code + MAXBFRSIZE ) ) ) +\end_layout + +\begin_layout Code + { +\end_layout + +\begin_layout Code + return; +\end_layout + +\begin_layout Code + } +\end_layout + +\begin_layout Code + bfr[ inBytes ] = ' +\backslash +0'; /* Null-terminate the received string. + */ +\end_layout + +\begin_layout Code + fputs( bfr, stdout ); /* Null string if EOF (inBytes == 0). + */ +\end_layout + +\begin_layout Code + fflush( stdout ); +\end_layout + +\begin_layout Code +} /* End tod() */ +\end_layout + +\begin_layout Section +Other programming languages \end_layout \begin_layout Subsection @@ -13567,7 +22512,8 @@ JAVA \end_layout \begin_layout Standard -Sun Java version 1.4 and 1.5 (5.0) are IPv6 enabled, see +Sun Java versions since 1.4 are IPv6 enabled, see e.g. + \begin_inset LatexCommand \url[Inet6Address (1.5/5.0)]{http://java.sun.com/j2se/1.5.0/docs/api/java/net/Inet6Address.html} \end_inset @@ -13590,6 +22536,42 @@ Networking IPv6 User Guide for JDK/JRE . \end_layout +\begin_layout Subsection +Perl +\end_layout + +\begin_layout Standard +As of May 2007 it's not known that the Perl core itself already supports + IPv6. + It can be added by using following modules: +\end_layout + +\begin_layout Itemize +\begin_inset LatexCommand \url[Socket6]{http://search.cpan.org/~umemoto/Socket6/} + +\end_inset + + +\end_layout + +\begin_layout Standard +Anyway, some other modules exist for/with IPv6 support (e.g. + Net::IP), search for +\begin_inset Quotes sld +\end_inset + +IPv6 +\begin_inset Quotes srd +\end_inset + + on +\begin_inset LatexCommand \url[http://search.cpan.org/]{http://search.cpan.org/} + +\end_inset + +. +\end_layout + \begin_layout Chapter \begin_inset LatexCommand \label{chapter-interoperability} @@ -17123,7 +26105,12 @@ Releases 0.x \end_layout \begin_layout Description -0.52wip 2007-04-16/PB: update firewalling chapter, improve document for proper +0.60 2007-05-29/PB: import major contribution to Programming using C-API + written by John Wenker, minor fixes +\end_layout + +\begin_layout Description +0.52 2007-05-23/PB: update firewalling chapter, improve document for proper SGML validation, minor bugfixes \end_layout @@ -17679,6 +26666,11 @@ Benjamin Thery : For contribution of updated mobility section \end_layout +\begin_layout Itemize +John Wenker : major contribution to Programming using + C-API +\end_layout + \begin_layout Subsection Other credits \end_layout @@ -17902,6 +26894,14 @@ Bryan Vukich: Reporting a broken URL Daniele Masini: reporting a broken iptables example \end_layout +\begin_layout Itemize +Yao Zhao: reporting a bug in IPv6 route remove description +\end_layout + +\begin_layout Itemize +Aaron Kunde: reporting a broken URL and a content related bug +\end_layout + \begin_layout Section The End \end_layout diff --git a/LDP/users/Peter-Bieringer/Linux+IPv6-HOWTO.sgml b/LDP/users/Peter-Bieringer/Linux+IPv6-HOWTO.sgml index 3f391f13..ccc7d3e8 100644 --- a/LDP/users/Peter-Bieringer/Linux+IPv6-HOWTO.sgml +++ b/LDP/users/Peter-Bieringer/Linux+IPv6-HOWTO.sgml @@ -1,44 +1,29 @@ ]> - - - + Linux IPv6 HOWTO (en) - - - -Peter -Bieringer -
pb at bieringer dot de
-
+PeterBieringer
pb at bieringer dot de
- Release 0.51 2006-11-08 PB - Release 0.50.2 2006-10-25 PB - Release 0.50.1 2006-09-22 PB - Release 0.50 2006-08-24 PB - Release 0.49 2005-10-03 PB + 0.60 2007-05-31 PB + 0.51 2006-11-08 PB The goal of the Linux IPv6 HOWTO is to answer both basic and advanced questions about IPv6 on the Linux operating system. This HOWTO will provide the reader with enough information to install, configure, and use IPv6 applications on Linux machines. Intermediate releases of this HOWTO are available at mirrors.bieringer.de or mirrors.deepspace6.net. See also revision history for changes.
<!-- anchor id="chapter-general" -->General - -CVS-ID: $Id$ - - Information about available translations you will find in section Translations. <!-- anchor id="general-copright" -->Copyright, license and others Copyright -Written and Copyright (C) 2001-2006 by Peter Bieringer +Written and Copyright (C) 2001-2007 by Peter Bieringer License This Linux IPv6 HOWTO is published under GNU GPL version 2: @@ -47,7 +32,7 @@ The Linux IPv6 HOWTO, a guide how to configure and use IPv6 on Linux systems. -Copyright (C) 2001-2006 Peter Bieringer +Copyright © 2001-2007 Peter Bieringer This documentation is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. @@ -56,16 +41,17 @@ Internet/IPv6 history of the author -1993: I got in contact with the Internet using console based e-mail and news client (e.g. look for "e91abier" on groups.google.com, that's me).1996: I got a request for designing a course on IPv6, including a workshop with the Linux operating system.1997: Started writing a guide on how to install, configure and use IPv6 on Linux systems, called IPv6 & Linux - HowTo (see IPv6 & Linux - HowTo/History for more information).2001: Started writing this new Linux IPv6 HOWTO. +1993: I got in contact with the Internet using console based e-mail and news client (e.g. look for “e91abier” on groups.google.com, that's me).1996: I got a request for designing a course on IPv6, including a workshop with the Linux operating system.1997: Started writing a guide on how to install, configure and use IPv6 on Linux systems, called IPv6 & Linux - HowTo (see IPv6 & Linux - HowTo/History for more information).2001: Started writing this new Linux IPv6 HOWTO. Contact The author can be contacted via e-mail at <pb at bieringer dot de> and also via his homepage. He's currently living in Munich [northern part of Schwabing] / Bavaria / Germany (south) / Europe (middle) / Earth (surface/mainland). <!-- anchor id="general-category" -->Category -This HOWTO should be listed in category "Networking/Protocols". +This HOWTO should be listed in category “Networking/Protocols”. Version, History and To-Do Version The current version is shown at the beginning of the document. +CVS information: CVS-ID: $Id$ For other available versions/translations see also http://www.bieringer.de/linux/IPv6/. History @@ -84,7 +70,8 @@ 2004-03-12: Italian translation is available 2004-06-18: Greek translation is in progress 2004-08-29: Spanish translation is still NOT in progress -2005-07-25: Turkish translation is availble +2005-07-25: Turkish translation is availble +2007-03-28: Portuguese-Brazil is in progress Full history See revision history at the end of this document. To-Do @@ -112,17 +99,17 @@ Greek On 2004-06-18 Nikolaos Tsarmpopoulos <ntsarb at uth dot gr> send me a note that he planned to translate the HowTo into Greek. Turkish -On 2005-07-18 Necdet Yucel <nyucel at comu dot edu dot tr> send me a note that a Turkish translation is available. It's a snapshot translation and can be found at http://www.belgeler.org/howto/ipv6-howto.html. +On 2005-07-18 Necdet Yucel <nyucel at comu dot edu dot tr> send me a note that a Turkish translation is available. It's a snapshot translation and can be found at http://www.belgeler.org/howto/ipv6-howto.html. +Portuguese-Brazil +On 2007-03-28 Claudemir da Luz <claudemir dot daluz at virtuallink dot com dot br> send me a note that he planned to translate the HowTo in Portuguese-Brazil. Technical <!-- anchor id="general-original-source" -->Original source of this HOWTO -This HOWTO is currently written with LyX version 1.4.2 on a Fedora Core 5 system with template SGML (DocBook book). It's available on TLDP-CVS / users / Peter-Bieringer for contribution. +This HOWTO is currently written with LyX version 1.4.4 on a Fedora Core 6 system with template SGML/XML (DocBook book). It's available on TLDP-CVS / users / Peter-Bieringer for contribution. Code line wrapping -Code line wrapping is done using selfmade utility "lyxcodelinewrapper.pl", you can get it from CVS for your own usage: TLDP-CVS / users / Peter-Bieringer +Code line wrapping is done using selfmade utility “lyxcodelinewrapper.pl”, you can get it from CVS for your own usage: TLDP-CVS / users / Peter-Bieringer SGML generation -SGML is generated using export function in LyX. -Also some fixes are have to be made to create proper SGML code (see also here for the Perl programs TLDP-CVS / users / Peter-Bieringer): -Export of LyX table does not create proper "colspan" tags - tool for fixing: "sgmllyxtabletagfix.pl" (fixed since LyX 1.2.0)LyX sometimes uses special left/right entities for quotes instead the normal one, which will still exist in generated HTML. Some browsers don't parse this very well (known: Opera 6 TP 2 or Konqueror) - tool for fixing: "sgmllyxquotefix.pl" +SGML/XML is generated using export function in LyX. On-line references to the HTML version of this HOWTO (linking/anchors) Master index page @@ -136,12 +123,8 @@ Including this, there are three (3) HOWTO documents available. Apologies, if that is too many ;-) Linux IPv6 FAQ/HOWTO (outdated) The first IPv6 related document was written by Eric Osborne and called Linux IPv6 FAQ/HOWTO (please use it only for historical issues). Latest version was 3.2.1 released July, 14 1997. -Please help: if someone knows the date of birth of this HOWTO, please send me an e-mail (information will be needed in "history"). +Please help: if someone knows the date of birth of this HOWTO, please send me an e-mail (information will be needed in “history”). IPv6 & Linux - HowTo (maintained) - -This HOWTO is really named "HowTo" - - There exists a second version called IPv6 & Linux - HowTo written by me (Peter Bieringer) in pure HTML. It was born April 1997 and the first English version was published in June 1997. I will continue to maintain it, but it will slowly fade (but not full) in favour of the Linux IPv6 HOWTO you are currently reading. Linux IPv6 HOWTO (this document) Because the IPv6 & Linux - HowTo is written in pure HTML it's not really compatible with the The Linux Documentation Project (TLDP). I (Peter Bieringer) got a request in late November 2001 to rewrite the IPv6 & Linux - HowTo in SGML. However, because of the discontinuation of that HOWTO (Future of IPv6 & Linux - HowTo), and as IPv6 is becoming more and more standard, I decided to write a new document covering basic and advanced issues which will remain important over the next few years. More dynamic and some advanced content will be still found further on in the second HOWTO (IPv6 & Linux - HowTo). @@ -149,14 +132,14 @@ Network related -Base 10Well known decimal number system, represent any value with digit 0-9.Base 16Usually used in lower and higher programming languages, known also as hexadecimal number system, represent any value with digit 0-9 and char A-F (case insensitive).Base 85Representation of a value with 85 different digits/chars, this can lead to shorter strings but never seen in the wild.BitSmallest storage unit, on/true (1) or off/false (0)ByteMostly a collection of 8 (but not really a must - see older computer systems) bitsDeviceHere, hardware of network connection, see also NICDual homed hostA dual homed host is a node with two network (physical or virtual) interfaces on two different links, but does not forward any packets between the interfaces.HostGenerally a single homed host on a link. Normally it has only one active network interface, e.g. Ethernet or (not and) PPP.InterfaceMostly same as "device", see also NICIP HeaderHeader of an IP packet (each network packet has a header, kind of is depending on network layer)LinkA link is a layer 2 network packet transport medium, examples are Ethernet, Token Ring, PPP, SLIP, ATM, ISDN, Frame Relay,...NodeA node is a host or a router.OctetA collection of 8 real bits, today also similar to "byte".PortInformation for the TCP/UDP dispatcher (layer 4) to transport information to upper layersProtocolEach network layer contains mostly a protocol field to make life easier on dispatching transported information to upper layer, seen in layer 2 (MAC) and 3 (IP)RouterA router is a node with two or more network (physical or virtual) interfaces, capable of forwarding packets between the interfaces.SocketAn IP socket is defined by source and destination IP addresses and Ports and (binding) StackNetwork related a collection of layersSubnetmaskIP networks uses bit masks to separate local networks from remote onesTunnelA tunnel is typically a point-to-point connection over which packets are exchanged which carry the data of another protocol, e.g. an IPv6-in-IPv4 tunnel. +Base 10Well known decimal number system, represent any value with digit 0-9.Base 16Usually used in lower and higher programming languages, known also as hexadecimal number system, represent any value with digit 0-9 and char A-F (case insensitive).Base 85Representation of a value with 85 different digits/chars, this can lead to shorter strings but never seen in the wild.BitSmallest storage unit, on/true (1) or off/false (0)ByteMostly a collection of 8 (but not really a must - see older computer systems) bitsDeviceHere, hardware of network connection, see also NICDual homed hostA dual homed host is a node with two network (physical or virtual) interfaces on two different links, but does not forward any packets between the interfaces.HostGenerally a single homed host on a link. Normally it has only one active network interface, e.g. Ethernet or (not and) PPP.InterfaceMostly same as “device”, see also NICIP HeaderHeader of an IP packet (each network packet has a header, kind of is depending on network layer)LinkA link is a layer 2 network packet transport medium, examples are Ethernet, Token Ring, PPP, SLIP, ATM, ISDN, Frame Relay,...NodeA node is a host or a router.OctetA collection of 8 real bits, today also similar to “byte”.PortInformation for the TCP/UDP dispatcher (layer 4) to transport information to upper layersProtocolEach network layer contains mostly a protocol field to make life easier on dispatching transported information to upper layer, seen in layer 2 (MAC) and 3 (IP)RouterA router is a node with two or more network (physical or virtual) interfaces, capable of forwarding packets between the interfaces.SocketAn IP socket is defined by source and destination IP addresses and Ports and (binding) StackNetwork related a collection of layersSubnetmaskIP networks uses bit masks to separate local networks from remote onesTunnelA tunnel is typically a point-to-point connection over which packets are exchanged which carry the data of another protocol, e.g. an IPv6-in-IPv4 tunnel. <!-- anchor id="Glossar" -->Shortcuts ACLAccess Control ListAPIApplication Programming InterfaceASICApplication Specified Integrated CircuitBSDBerkeley Software DistributionCAN-BusController Area Network Bus (physical bus system)ISPInternet Service ProviderKAMEProject - a joint effort of six companies in Japan to provide a free IPv6 and IPsec (for both IPv4 and IPv6) stack for BSD variants to the world www.kame.netLIRLocal Internet RegistryNICNetwork Interface CardRFCRequest For Comments - set of technical and organizational notes about the InternetUSAGIUniverSAl playGround for Ipv6 Project - works to deliver the production quality IPv6 protocol stack for the Linux system. Document related Long code line wrapping signal char -The special character "¬" is used for signaling that this code line is wrapped for better viewing in PDF and PS files. +The special character “¬” is used for signaling that this code line is wrapped for better viewing in PDF and PS files. Placeholders In generic examples you will sometimes find the following: @@ -176,7 +159,7 @@ Experience with Unix tools You should be familiar with the major Unix tools e.g. grep, awk, find, ... , and know about their most commonly used command-line options. Experience with networking theory -You should know about layers, protocols, addresses, cables, plugs, etc. If you are new to this field, here is one good starting point for you: linuxports/howto/intro_to_networking +You should know about layers, protocols, addresses, cables, plugs, etc. If you are new to this field, here is one good starting point for you: http://www.rigacci.org/docs/biblio/online/intro_to_networking/book1.htm Experience with IPv4 configuration You should definitely have some experience in IPv4 configuration, otherwise it will be hard for you to understand what is really going on. Experience with the Domain Name System (DNS) @@ -222,7 +205,7 @@ What do IPv6 addresses look like? As previously mentioned, IPv6 addresses are 128 bits long. This number of bits generates very high decimal numbers with up to 39 digits: Such numbers are not really addresses that can be memorized. Also the IPv6 address schema is bitwise orientated (just like IPv4, but that's not often recognized). Therefore a better notation of such big numbers is hexadecimal. In hexadecimal, 4 bits (also known as "nibble") are represented by a digit or character from 0-9 and a-f (10-15). This format reduces the length of the IPv6 address to 32 characters. +]]>Such numbers are not really addresses that can be memorized. Also the IPv6 address schema is bitwise orientated (just like IPv4, but that's not often recognized). Therefore a better notation of such big numbers is hexadecimal. In hexadecimal, 4 bits (also known as “nibble”) are represented by a digit or character from 0-9 and a-f (10-15). This format reduces the length of the IPv6 address to 32 characters. This representation is still not very convenient (possible mix-up or loss of single hexadecimal digits), so the designers of IPv6 chose a hexadecimal format with a colon as separator after each block of 16 bits. In addition, the leading "0x" (a signifier for hexadecimal values used in programming languages) is removed: For simplifications, leading zeros of each 16 bit block can be omitted: ¬ 2001:db8:100:f101:210:a4ff:fee3:9566 -]]>One sequence of 16 bit blocks containing only zeroes can be replaced with "::". But not more than one at a time, otherwise it is no longer a unique representation. +]]>One sequence of 16 bit blocks containing only zeroes can be replaced with “::“. But not more than one at a time, otherwise it is no longer a unique representation. 2001:db8:100:f101::1 ]]>The biggest reduction is seen by the IPv6 localhost address: ::1 @@ -249,7 +232,7 @@ IPv6 addresses: why such a high number of bits? During the design of IPv4, people thought that 32 bits were enough for the world. Looking back into the past, 32 bits were enough until now and will perhaps be enough for another few years. However, 32 bits are not enough to provide each network device with a global address in the future. Think about mobile phones, cars (including electronic devices on its CAN-bus), toasters, refrigerators, light switches, and so on... So designers have chosen 128 bits, 4 times more in length than in IPv4 today. -The usable size is smaller than it may appear however. This is because in the currently defined address schema, 64 bits are used for interface identifiers. The other 64 bits are used for routing. Assuming the current strict levels of aggregation (/48, /32, ...), it is still possible to "run out" of space, but hopefully not in the near future. +The usable size is smaller than it may appear however. This is because in the currently defined address schema, 64 bits are used for interface identifiers. The other 64 bits are used for routing. Assuming the current strict levels of aggregation (/48, /32, ...), it is still possible to “run out” of space, but hopefully not in the near future. See also for more information RFC 1715 / The H Ratio for Address Assignment Efficiency and RFC 3194 / The Host-Density Ratio for Address Assignment Efficiency. IPv6 addresses: why so small a number of bits on a new design? While, there are (possibly) some people (only know about Jim Fleming...) on the Internet who are thinking about IPv8 and IPv16, their design is far away from acceptance and implementation. In the meantime 128 bits was the best choice regarding header overhead and data transport. Consider the minimum Maximum Transfer Unit (MTU) in IPv4 (576 octets) and in IPv6 (1280 octets), the header length in IPv4 is 20 octets (minimum, can increase to 60 octets with IPv4 options) and in IPv6 is 48 octets (fixed). This is 3.4 % of MTU in IPv4 and 3.8 % of MTU in IPv6. This means the header overhead is almost equal. More bits for addresses would require bigger headers and therefore more overhead. Also, consider the maximum MTU on normal links (like Ethernet today): it's 1500 octets (in special cases: 9k octets using Jumbo frames). Ultimately, it wouldn't be a proper design if 10 % or 20 % of transported data in a Layer-3 packet were used for addresses and not for payload. @@ -261,13 +244,13 @@ Addresses without a special prefix Localhost address -This is a special address for the loopback interface, similiar to IPv4 with its "127.0.0.1". With IPv6, the localhost address is: +This is a special address for the loopback interface, similiar to IPv4 with its “127.0.0.1”. With IPv6, the localhost address is: or compressed: Packets with this address as source or destination should never leave the sending host. Unspecified address -This is a special address like "any" or "0.0.0.0" in IPv4 . For IPv6 it's: +This is a special address like “any” or “0.0.0.0” in IPv4 . For IPv6 it's: or: Now lets take a look at the different types of prefixes (and therefore address types): Link local address type These are special addresses which will only be valid on a link of an interface. Using this address as destination the packet would never pass through a router. It's used for link communications such as: -anyone else here on this link?anyone here with a special address (e.g. looking for a router)?They begin with ( where "x" is any hex character, normally "0") +anyone else here on this link?anyone here with a special address (e.g. looking for a router)?They begin with ( where “x” is any hex character, normally “0”) fe8x: <- currently the only one in use. fe9x: feax: @@ -310,7 +293,7 @@ feex: fefx: (where "x" is any hex character, normally "0") +]]>(where “x” is any hex character, normally “0”) This address type is now deprecated RFC 3879 / Deprecating Site Local Addresses, but for a test in a lab, such addresses are still a good choice in my humble opinion. Unique Local IPv6 Unicast Addresses Because the original defined site local addresses are not unique, this can lead to major problems, if two former independend networks would be connected later (overlapping of subnets). This and other issues lead to a new address type named RFC 4193 / Unique Local IPv6 Unicast Addresses. @@ -327,7 +310,7 @@ It begins with (x are hex characters) 2xxx: 3xxx: -Note: the prefix "aggregatable" is thrown away in current drafts. +Note: the prefix “aggregatable” is thrown away in current drafts. There are some further subtypes defined, see below: 6bone test addresses These were the first global addresses which were defined and in use. They all start with @@ -373,7 +356,7 @@ Because IPv6 is now in production, this prefix is no longer be delegated and rem Special multicast address used as destination address in neighborhood discovery, because unlike in IPv4, ARP no longer exists in IPv6. An example of this address looks like Used prefix shows that this is a link-local multicast address. The suffix is generated from the destination address. In this example, a packet should be sent to address "fe80::1234", but the network stack doesn't know the current layer 2 MAC address. It replaces the upper 104 bits with "ff02:0:0:0:0:1:ff00::/104" and leaves the lower 24 bits untouched. This address is now used `on-link' to find the corresponding node which has to send a reply containing its layer 2 MAC address. +]]>Used prefix shows that this is a link-local multicast address. The suffix is generated from the destination address. In this example, a packet should be sent to address “fe80::1234”, but the network stack doesn't know the current layer 2 MAC address. It replaces the upper 104 bits with “ff02:0:0:0:0:1:ff00::/104” and leaves the lower 24 bits untouched. This address is now used `on-link' to find the corresponding node which has to send a reply containing its layer 2 MAC address. Anycast addresses Anycast addresses are special addresses and are used to cover things like nearest DNS server, nearest DHCP server, or similar dynamic groups. Addresses are taken out of the unicast address space (aggregatable global or site-local at the moment). The anycast mechanism (client view) will be handled by dynamic routing protocols. Note: Anycast addresses cannot be used as source addresses, they are only used as destination addresses. @@ -401,9 +384,9 @@ Because IPv6 is now in production, this prefix is no longer be delegated and rem Manually set For servers it's probably easier to remember simpler addresses, this can also be accommodated. It is possible to assign an additional IPv6 address to an interface, e.g. For manual suffixes like "::1" shown in the above example it's required that the 7th most significant bit is set to 0 (the universal/local bit of the automatically generated identifier). Also some other (otherwise unchosen ) bit combinations are reserved for anycast addresses, too. +]]>For manual suffixes like “::1” shown in the above example it's required that the 7th most significant bit is set to 0 (the universal/local bit of the automatically generated identifier). Also some other (otherwise unchosen ) bit combinations are reserved for anycast addresses, too. Prefix lengths for routing -In the early design phase it was planned to use a fully hierarchical routing approach to reduce the size of the routing tables maximally. The reasoning behind this approach were the number of current IPv4 routing entries in core routers (> 104 thousand in May 2001), reducing the need of memory in hardware routers (ASIC "Application Specified Integrated Circuit" driven) to hold the routing table and increase speed (fewer entries hopefully result in faster lookups). +In the early design phase it was planned to use a fully hierarchical routing approach to reduce the size of the routing tables maximally. The reasoning behind this approach were the number of current IPv4 routing entries in core routers (> 104 thousand in May 2001), reducing the need of memory in hardware routers (ASIC “Application Specified Integrated Circuit” driven) to hold the routing table and increase speed (fewer entries hopefully result in faster lookups). Todays view is that routing will be mostly hierarchically designed for networks with only one service provider. With more than one ISP connections, this is not possible, and subject to an issue named multi-homing (infos on multi-homing: drafts-ietf-multi6-*,IPv6 Multihoming Solutions). Prefix lengths (also known as "netmasks") Similar to IPv4, the routable network path for routing to take place. Because standard netmask notation for 128 bits doesn't look nice, designers employed the IPv4 Classless Inter Domain Routing (CIDR, RFC 1519 / Classless Inter-Domain Routing) scheme, which specifies the number of bits of the IP address to be used for routing. It is also called the "slash" notation. @@ -461,10 +444,10 @@ Because IPv6 is now in production, this prefix is no longer be delegated and rem IPv6-ready network devices Not all existing network devices have already (or ever) the capability to transport IPv6 packets. A current status can be found at IPv6+Linux-status-kernel.html#transport. A major issue is that because of the network layer structure of kernel implementation an IPv6 packet isn't really recognized by it's IP header number (6 instead of 4). It's recognized by the protocol number of the Layer 2 transport protocol. Therefore any transport protocol which doesn't use such protocol number cannot dispatch IPv6 packets. Note: the packet is still transported over the link, but on receivers side, the dispatching won't work (you can see this e.g. using tcpdump). -Currently known never "IPv6 capable links" +Currently known never “IPv6 capable links” Serial Line IP (SLIP, RFC 1055 / SLIP), should be better called now to SLIPv4, device named: slXParallel Line IP (PLIP), same like SLIP, device names: plipXISDN with encapsulation rawip, device names: isdnX -Currently known "not supported IPv6 capable links" +Currently known “not supported IPv6 capable links” ISDN with encapsulation syncppp, device names: ipppX (design issue of the ipppd, will be merged into more general PPP layer in kernel series 2.5.x) IPv6-ready network configuration tools @@ -550,7 +533,7 @@ Resume: pmtu 1280 On Linux, tcpdump is the major tool for packet capturing. Below you find some examples. IPv6 support is normally built-in in current releases of version 3.6. tcpdump uses expressions for filtering packets to minimize the noise: icmp6: filters native ICMPv6 trafficip6: filters native IPv6 traffic (including ICMPv6)proto ipv6: filters tunneled IPv6-in-IPv4 trafficnot port ssh: to suppress displaying SSH packets for running tcpdump in a remote SSH sessionAlso some command line options are very useful to catch and print more information in a packet, mostly interesting for digging into ICMPv6 packets: -"-s 512": increase the snap length during capturing of a packet to 512 bytes"-vv": really verbose output"-n": don't resolve addresses to names, useful if reverse DNS resolving isn't working proper +“-s 512”: increase the snap length during capturing of a packet to 512 bytes“-vv”: really verbose output“-n”: don't resolve addresses to names, useful if reverse DNS resolving isn't working proper IPv6 ping to 2001:0db8:100:f101::1 native over a local link If the telnet client don't understand the IPv6 address and says something like "cannot resolve hostname", then it's not IPv6-enabled. +]]>If the telnet client don't understand the IPv6 address and says something like “cannot resolve hostname”, then it's not IPv6-enabled. IPv6-ready ssh clients openssh @@ -612,7 +595,7 @@ Connection closed by foreign host. $ ssh -6 ::1 If your ssh client doesn't understand the option "-6" then it's not IPv6-enabled, like most ssh version 1 packages. +]]>If your ssh client doesn't understand the option “-6” then it's not IPv6-enabled, like most ssh version 1 packages. ssh.com SSH.com's SSH client and server is also IPv6 aware now and is free for all Linux and FreeBSD machine regardless if used for personal or commercial use. IPv6-ready web browsers @@ -631,11 +614,11 @@ Connection closed by foreign host. Q: Cannot ping6 to link-local addresses Error message: "connect: Invalid argument" Kernel doesn't know, which physical or virtual link you want to use to send such ICMPv6 packets. Therefore it displays this error message. -Solution: Specify interface like: "ping6 -I eth0 fe80::2e0:18ff:fe90:9205", see also program ping6 usage. +Solution: Specify interface like: “ping6 -I eth0 fe80::2e0:18ff:fe90:9205”, see also program ping6 usage. Q: Cannot ping6 or traceroute6 as normal user -Error message: "icmp socket: Operation not permitted" -These utilities create special ICMPv6 packets and send them out. This is done by using raw sockets in the kernel. But raw sockets can only be used by the "root" user. Therefore normal users get such error message. -Solution: If it's really needed that all users should be able to use these utilities, you can add the "suid" bit using "chmod u+s /path/to/program", see also program ping6 usage. If not all users should be able to, you can change the group of the program to e.g. "wheel", add these power users to this group and remove the execution bit for other users using "chmod o-rwx /path/to/program". Or configure "sudo" to enable your security policy. +Error message: “icmp socket: Operation not permitted +These utilities create special ICMPv6 packets and send them out. This is done by using raw sockets in the kernel. But raw sockets can only be used by the “root” user. Therefore normal users get such error message. +Solution: If it's really needed that all users should be able to use these utilities, you can add the “suid” bit using ”chmod u+s /path/to/program”, see also program ping6 usage. If not all users should be able to, you can change the group of the program to e.g. “wheel”, add these power users to this group and remove the execution bit for other users using “chmod o-rwx /path/to/program”. Or configure “sudo” to enable your security policy. <!-- anchor id="chapter-configuration-interface" -->Configuring interfaces Different network devices @@ -657,7 +640,7 @@ Connection closed by foreign host. SLIP + PLIP Like mentioned earlier, this interfaces don't support IPv6 transport (sending is OK, but dispatching on receiving don't work). Ether-tap device -Ether-tap devices are IPv6-enabled and also stateless configured. For use, the module "ethertap" has to be loaded before. +Ether-tap devices are IPv6-enabled and also stateless configured. For use, the module “ethertap” has to be loaded before. tun devices Currently not tested by me. ATM @@ -799,7 +782,7 @@ ff00::/8 :: UA 256 0 0 eth0 <- Interface route for all multicast ]]> Using "route" Usage: -/ [dev ] +/ gw [dev ] ]]>Example for removing upper added route again: @@ -811,10 +794,10 @@ ff00::/8 :: UA 256 0 0 eth0 <- Interface route for all multicast ¬ metric 1 ]]>Example: Metric "1" is used here to be compatible with the metric used by route, because the default metric on using "ip" is "1024". +]]>Metric “1” is used here to be compatible with the metric used by route, because the default metric on using “ip” is “1024”. Using "route" Usage: -/ dev +/ dev ]]>Example: @@ -838,24 +821,24 @@ ff00::/8 :: UA 256 0 0 eth0 <- Interface route for all multicast One idea of IPv6 was a hierachical routing, therefore only less routing entries are needed in routers. There are some issues in current Linux kernels: Clients (not routing any packet!) -Client can setup a default route like prefix "::/0", they also learn such route on autoconfiguration e.g. using radvd on the link like following example shows: +Client can setup a default route like prefix “::/0”, they also learn such route on autoconfiguration e.g. using radvd on the link like following example shows: Routers in case of packet forwarding -Older Linux kernel (at least <= 2.4.17) don't support default routes. You can set them up, but the route lookup fails when a packet should be forwarded (normal intention of a router). If you're still using such older kernel, "default routing" can be setup using the currently used global address prefix "2000::/3". +Older Linux kernel (at least <= 2.4.17) don't support default routes. You can set them up, but the route lookup fails when a packet should be forwarded (normal intention of a router). If you're still using such older kernel, “default routing” can be setup using the currently used global address prefix “2000::/3”. Note: take care about default routing without address filtering on edge routers. Otherwise unwanted multicast or site-local traffic can leave the edge. <!-- anchor id="chapter-Neighbor-Discovery" -->Neighbor Discovery -Neighbor discovery was the IPv6 successor for the ARP (Address Resolution Protocol) in IPv4. You can retrieve information about the current neighbors, in addition you can set and delete entries. The kernel keeps tracking of successful neighbor detection (like ARP in IPv4). You can dig into the learnt table using "ip". -Displaying neighbors using "ip" +Neighbor discovery was the IPv6 successor for the ARP (Address Resolution Protocol) in IPv4. You can retrieve information about the current neighbors, in addition you can set and delete entries. The kernel keeps tracking of successful neighbor detection (like ARP in IPv4). You can dig into the learnt table using “ip”. +Displaying neighbors using “ip” With following command you can display the learnt or configured IPv6 neighbors ] ]]>The following example shows one neighbor, which is a reachable router -Manipulating neighbors table using "ip" +Manipulating neighbors table using “ip” Manually add an entry With following command you are able to manually add an entry @@ -870,7 +853,7 @@ fe80::201:23ff:fe45:6789 dev eth0 lladdr 00:01:23:45:67:89 router nud reachable More advanced settings -The tool "ip" is less documentated, but very strong. See online "help" for more: +The tool “ip” is less documentated, but very strong. See online “help” for more: FP and TLA together (16 bits) have the value 0x2002. V4ADDR is the node's global unique IPv4 address (in hexadecimal notation). SLA is the subnet identifier (65536 local subnets possible) and are usable to represent your local network structure. -For gateways, such prefix is generated by normally using SLA "0000" and suffix "::1" (not a must, can be an arbitrary one with local-scope) and assigned to the 6to4 tunnel interface. Note that Microsoft Windows uses V4ADDR also for suffix. +For gateways, such prefix is generated by normally using SLA “0000” and suffix “::1” (not a must, can be an arbitrary one with local-scope) and assigned to the 6to4 tunnel interface. Note that Microsoft Windows uses V4ADDR also for suffix. 6to4 upstream tunneling -The node has to know to which foreign tunnel endpoint its in IPv4 packed IPv6 packets should be send to. In "early" days of 6to4 tunneling, dedicated upstream accepting routers were defined. See NSayer's 6to4 information for a list of routers. +The node has to know to which foreign tunnel endpoint its in IPv4 packed IPv6 packets should be send to. In “early” days of 6to4 tunneling, dedicated upstream accepting routers were defined. See NSayer's 6to4 information for a list of routers. Nowadays, 6to4 upstream routers can be found auto-magically using the anycast address 192.88.99.1. In the background routing protocols handle this, see RFC 3068 / An Anycast Prefix for 6to4 Relay Routers for details. 6to4 downstream tunneling The downstream (6bone -> your 6to4 enabled node) is not really fix and can vary from foreign host which originated packets were send to. There exist two possibilities: @@ -930,7 +913,7 @@ ff00::/8 :: UA 256 0 0 sit0 ]]> <!-- anchor id="conf-ipv6-in-ipv4-point-to-point-tunnels" -->Setup of point-to-point tunnel There are 3 possibilities to add or remove point-to-point tunnels. -A good additional information about tunnel setup using "ip" is Configuring tunnels with iproute2 (article) (Mirror). +A good additional information about tunnel setup using “ip” is Configuring tunnels with iproute2 (article) (Mirror). Add point-to-point tunnels Using "ip" @@ -1038,7 +1021,7 @@ ff00::/8 :: UA 256 0 0 sit0 the generated 6to4 prefix will be Local 6to4 gateways should (but it's not a must, you can choose an arbitrary suffix with local-scope, if you feel better) always assigned the suffix "::1", therefore your local 6to4 address will be +]]>Local 6to4 gateways should (but it's not a must, you can choose an arbitrary suffix with local-scope, if you feel better) always assigned the suffix “::1”, therefore your local 6to4 address will be Use e.g. following for automatic generation: /16 dev tun6to4 ]]>Add (default) route to the global IPv6 network using the all-6to4-routers IPv4 anycast address It was reported that some versions of "ip" (e.g. SuSE Linux 9.0) don't support IPv4-compatible IPv6 addresses for gateways, in this case the related IPv6 address has to be used: +]]>It was reported that some versions of “ip” (e.g. SuSE Linux 9.0) don't support IPv4-compatible IPv6 addresses for gateways, in this case the related IPv6 address has to be used: # /sbin/ip -6 route add 2000::/3 via 2002:c058:6301::1 dev tun6to4 metric 1 -Using "ifconfig" and "route" and generic tunnel device "sit0" (deprecated) +Using "ifconfig" and "route" and generic tunnel device “sit0” (deprecated) This is now deprecated because using the generic tunnel device sit0 doesn't let specify filtering per device. Bring generic tunnel interface sit0 up Remove created tunnel device -Using "ifconfig" and "route" and generic tunnel device "sit0" (deprecated) +Using “ifconfig” and “route” and generic tunnel device “sit0” (deprecated) Remove (default) route through the 6to4 tunnel interface Remove local 6to4 address to interface @@ -1087,11 +1070,11 @@ ff00::/8 :: UA 256 0 0 sit0 This will be filled in the future. At the moment, such tunnels are more used in test environments but it looks like that support is missing currently for Linux (03/2004). More information in the meantime: RFC 2473 / Generic Packet Tunneling in IPv6 Specification <!-- anchor id="chapter-kernel-settings" -->Kernel settings in /proc-filesystem -Note: the source of this section is mostly the file "ip-sysctl.txt" which is included in current kernel sources in directory "Documentation/networking". Credits to Pekka Savola for maintaining the IPv6-related part in this file. Also some text is more or less copied & pasted into this document. +Note: the source of this section is mostly the file “ip-sysctl.txt” which is included in current kernel sources in directory “Documentation/networking”. Credits to Pekka Savola for maintaining the IPv6-related part in this file. Also some text is more or less copied & pasted into this document. How to access the /proc-filesystem -Using "cat" and "echo" -Using "cat" and "echo" is the simplest way to access the /proc filesystem, but some requirements are needed for that +Using “cat” and “echo” +Using “cat” and “echo” is the simplest way to access the /proc filesystem, but some requirements are needed for that The /proc-filesystem had to be enabled in kernel, means on compiling following switch has to be set @@ -1101,17 +1084,17 @@ none on /proc type proc (rw) ]]> You need read and sometimes also write access (normally root only) to the /proc-filesystemNormally, only entries in /proc/sys/* are writable, the others are readonly and for information retrieving only. Retrieving a value -The value of an entry can be retrieved using "cat": +The value of an entry can be retrieved using “cat”: Setting a value -A new value can be set (if entry is writable) using "echo": +A new value can be set (if entry is writable) using “echo”: /proc/sys/net/ipv6/conf/all/forwarding ]]> -Using "sysctl" -Using the "sysctl" program to access the kernel switches is a modern method today. You can use it also, if the /proc-filesystem isn't mounted. But you have only access to /proc/sys/*! -The program "sysctl" is included in package "procps" (on Red Hat Linux systems). +Using “sysctl” +Using the “sysctl” program to access the kernel switches is a modern method today. You can use it also, if the /proc-filesystem isn't mounted. But you have only access to /proc/sys/*! +The program “sysctl” is included in package “procps” (on Red Hat Linux systems). The sysctl-interface had to be enabled in kernel, means on compiling following switch has to be set @@ -1124,24 +1107,24 @@ net.ipv6.conf.all.forwarding = 0 A new value can be set (if entry is writable): Note: Don't use spaces around the "=" on setting values. Also on multiple values per line, quote them like e.g. +]]>Note: Don't use spaces around the “=” on setting values. Also on multiple values per line, quote them like e.g. # sysctl -w net.ipv4.ip_local_port_range="32768 61000" Additionals -Note: There are sysctl versions in the wild which displaying "/" instead of the "." +Note: There are sysctl versions in the wild which displaying “/” instead of the “.” For more details take a look into sysctl's manpage. -Hint: for digging fast into the settings, use the option "-a" (display all entries) in conjunction with "grep". +Hint: for digging fast into the settings, use the option “-a” (display all entries) in conjunction with “grep”. Values found in /proc-filesystems There are several formats seen in /proc-filesystem: -BOOLEAN: simple a "0" (false) or a "1" (true)INTEGER: an integer value, can be unsigned, toomore sophisticated lines with several values: sometimes a header line is displayed also, if not, have a look into the kernel source to retrieve information about the meaning of each value... +BOOLEAN: simple a “0” (false) or a “1” (true)INTEGER: an integer value, can be unsigned, toomore sophisticated lines with several values: sometimes a header line is displayed also, if not, have a look into the kernel source to retrieve information about the meaning of each value... <!-- anchor id="proc-sys-net-ipv6." -->Entries in /proc/sys/net/ipv6/ conf/default/* Change the interface-specific default settings. conf/all/* Change all the interface-specific settings. -Exception: "conf/all/forwarding" has a different meaning here +Exception: “conf/all/forwarding” has a different meaning here conf/all/forwarding Type: BOOLEANThis enables global IPv6 forwarding between all interfaces. @@ -1159,7 +1142,7 @@ net.ipv6.conf.all.forwarding = 1 Type: BOOLEANFunctional default: enabled if local forwarding is disabled. disabled if local forwarding is enabled.Accept Redirects sent by an IPv6 router. autoconf -Type: BOOLEANDefault: TRUEConfigure link-local addresses (see also Addresstypes) using L2 hardware addresses. E.g. this generates automagically an address like "fe80::201:23ff:fe45:6789" on an interface with a L2-MAC address. +Type: BOOLEANDefault: TRUEConfigure link-local addresses (see also Addresstypes) using L2 hardware addresses. E.g. this generates automagically an address like “fe80::201:23ff:fe45:6789” on an interface with a L2-MAC address. dad_transmits Type: INTEGERDefault: 1The amount of Duplicate Address Detection probes to send. @@ -1282,20 +1265,20 @@ net.ipv6.conf.all.forwarding = 1 others Unknown, but probably not used by IPv6. <!-- anchor id="proc-net" -->IPv6-related entries in /proc/net/ -In /proc/net there are several read-only entries available. You cannot retrieve information using "sysctl" here, so use e.g. "cat". +In /proc/net there are several read-only entries available. You cannot retrieve information using “sysctl” here, so use e.g. “cat”. if_inet6 -Type: One line per addresss containing multiple valuesHere all configured IPv6 addresses are shown in a special format. The example displays for loopback interface only. The meaning is shown below (see "net/ipv6/addrconf.c" for more). +Type: One line per addresss containing multiple valuesHere all configured IPv6 addresses are shown in a special format. The example displays for loopback interface only. The meaning is shown below (see “net/ipv6/addrconf.c” for more). -IPv6 address displayed in 32 hexadecimal chars without colons as separatorNetlink device number (interface index) in hexadecimal (see "ip addr" , too)Prefix length in hexadecimalScope value (see kernel source " include/net/ipv6.h" and "net/ipv6/addrconf.c" for more)Interface flags (see "include/linux/rtnetlink.h" and "net/ipv6/addrconf.c" for more)Device name +IPv6 address displayed in 32 hexadecimal chars without colons as separatorNetlink device number (interface index) in hexadecimal (see “ip addr” , too)Prefix length in hexadecimalScope value (see kernel source “ include/net/ipv6.h” and “net/ipv6/addrconf.c” for more)Interface flags (see “include/linux/rtnetlink.h” and “net/ipv6/addrconf.c” for more)Device name ipv6_route -Type: One line per route containing multiple valuesHere all configured IPv6 routes are shown in a special format. The example displays for loopback interface only. The meaning is shown below (see "net/ipv6/route.c" for more). +Type: One line per route containing multiple valuesHere all configured IPv6 routes are shown in a special format. The example displays for loopback interface only. The meaning is shown below (see “net/ipv6/route.c” for more). Server socket binding -Using "netstat" for server socket binding check -It's always interesting which server sockets are currently active on a node. Using "netstat" is a short way to get such information: +Using “netstat” for server socket binding check +It's always interesting which server sockets are currently active on a node. Using “netstat” is a short way to get such information: Used options: -nlptu Example: Router with link-local address "fe80::212:34ff:fe12:3450" send an advertisement to the all-node-on-link multicast address "ff02::1" containing two prefixes "2002:0102:0304:1::/64" (lifetime 30 s) and "2001:0db8:0:1::/64" (lifetime 2592000 s) including its own layer 2 MAC address "0:12:34:12:34:50". +]]>Router with link-local address “fe80::212:34ff:fe12:3450” send an advertisement to the all-node-on-link multicast address “ff02::1” containing two prefixes “2002:0102:0304:1::/64” (lifetime 30 s) and “2001:0db8:0:1::/64” (lifetime 2592000 s) including its own layer 2 MAC address “0:12:34:12:34:50”. Router solicitation ff02::2: icmp6: router solicitation ¬ (src lladdr: 0:12:34:12:34:56) (len 16, hlim 255) -]]>Node with link-local address "fe80::212:34ff:fe12:3456" and layer 2 MAC address "0:12:34:12:34:56" is looking for a router on-link, therefore sending this solicitation to the all-router-on-link multicast address "ff02::2". +]]>Node with link-local address “fe80::212:34ff:fe12:3456” and layer 2 MAC address “0:12:34:12:34:56” is looking for a router on-link, therefore sending this solicitation to the all-router-on-link multicast address “ff02::2”. Neighbor discovery Neighbor discovery solicitation for duplicate address detection -Following packets are sent by a node with layer 2 MAC address "0:12:34:12:34:56" during autoconfiguration to check whether a potential address is already used by another node on the link sending this to the solicited-node link-local multicast address. -Node wants to configure its link-local address "fe80::212:34ff:fe12:3456", checks for duplicate now +Following packets are sent by a node with layer 2 MAC address “0:12:34:12:34:56” during autoconfiguration to check whether a potential address is already used by another node on the link sending this to the solicited-node link-local multicast address. +Node wants to configure its link-local address “fe80::212:34ff:fe12:3456”, checks for duplicate now ff02::1:ff12:3456: icmp6: neighbor sol: who has ¬ fe80::212:34ff:fe12:3456(src lladdr: 0:12:34:12:34:56) (len 32, hlim 255) ]]> -Node wants to configure its global address "2002:0102:0304:1:212:34ff:fe12:3456" (after receiving advertisement shown above), checks for duplicate now +Node wants to configure its global address “2002:0102:0304:1:212:34ff:fe12:3456” (after receiving advertisement shown above), checks for duplicate now ff02::1:ff12:3456: icmp6: neighbor sol: who has ¬ 2002:0102:0304:1:212:34ff:fe12:3456(src lladdr: 0:12:34:12:34:56) (len 32, ¬ hlim 255) ]]> -Node wants to configure its global address "2001:0db8:0:1:212:34ff:fe12:3456" (after receiving advertisement shown above), checks for duplicate now +Node wants to configure its global address “2001:0db8:0:1:212:34ff:fe12:3456” (after receiving advertisement shown above), checks for duplicate now ff02::1:ff12:3456: icmp6: neighbor sol: who has ¬ 2001:0db8:0:1:212:34ff:fe12:3456(src lladdr: 0:12:34:12:34:56) (len 32, hlim ¬ 255) ]]> Neighbor discovery solicitation for looking for host or gateway -Node wants to send packages to "2001:0db8:0:1::10" but has no layer 2 MAC address to send packet, so send solicitation now +Node wants to send packages to “2001:0db8:0:1::10” but has no layer 2 MAC address to send packet, so send solicitation now ff02::1:ff00:10: icmp6: ¬ neighbor sol: who has 2001:0db8:0:1::10(src lladdr: 0:e0:18:90:92:5) (len 32, ¬ hlim 255) ]]> -Node looks for "fe80::10" now +Node looks for “fe80::10” now ff02::1:ff00:10: icmp6: neighbor ¬ sol: who has fe80::10(src lladdr: 0:e0:18:90:92:5) (len 32, hlim 255) ]]> <!-- anchor id="chapter-support-persistent-configuration" -->Support for persistent IPv6 configuration in Linux distributions Some Linux distribution contain already support of a persistent IPv6 configuration using existing or new configuration and script files and some hook in the IPv4 script files. -Red Hat Linux and "clones" +Red Hat Linux and “clones” Since starting writing the IPv6 & Linux - HowTo it was my intention to enable a persistent IPv6 configuration which catch most of the wished cases like host-only, router-only, dual-homed-host, router with second stub network, normal tunnels, 6to4 tunnels, and so on. Nowadays there exists a set of configuration and script files which do the job very well (never heard about real problems, but I don't know how many use the set). Because this configuration and script files are extended from time to time, they got their own homepage: initscripts-ipv6 homepage (Mirror). Because I began my IPv6 experience using a Red Hat Linux 5.0 clone, my IPv6 development systems are mostly Red Hat Linux based now, it's kind a logic that the scripts are developed for this kind of distribution (so called historic issue). Also it was very easy to extend some configuration files, create new ones and create some simple hook for calling IPv6 setup during IPv4 setup. Fortunately, in Red Hat Linux since 7.1 a snapshot of my IPv6 scripts is included, this was and is still further on assisted by Pekka Savola. -Mandrake since version 8.0 also includes an IPv6-enabled initscript package, but a minor bug still prevents usage ("ifconfig" misses "inet6" before "add"). +Mandrake since version 8.0 also includes an IPv6-enabled initscript package, but a minor bug still prevents usage (“ifconfig” misses “inet6” before “add”). Test for IPv6 support of network configuration scripts You can test, whether your Linux distribution contain support for persistent IPv6 configuration using my set. Following script library should exist: -If result is "off", then enable IPv6 networking by editing /etc/sysconfig/network, add following new line +If result is “off”, then enable IPv6 networking by editing /etc/sysconfig/network, add following new line Reboot or restart networking using @@ -1571,7 +1554,7 @@ In versions 8.x they completly change their configuration setup. Native IPv6 firewalling is only supported in kernel versions 2.4+. In older 2.2- you can only filter IPv6-in-IPv4 by protocol 41. Attention: no warranty that described rules or examples can really protect your system! Audit your ruleset after installation, see for more. -Note also that the USAGI project is currently working on finishing the connection tracking for IPv6! This will make ruleset easier and more secure in the future! +Since kernel version 2.6.20 IPv6 connection tracking is fully working (and does not break IPv4 NAT anymore like versions before) More information Netfilter projectmaillist archive of netfilter usersmaillist archive of netfilter developersUnofficial status informations @@ -1686,6 +1669,10 @@ Extensions found: IPv6:owner IPv6:limit IPv6:mac IPv6:multiport +Enable connection tracking +Since kernel version 2.6.20 IPv6 connection tracking is well supported and should be used instead of using stateless filter rules. + Allow ICMPv6 Using older kernels (unpatched kernel 2.4.5 and iptables-1.2.2) no type can be specified Accept incoming ICMPv6 through tunnels @@ -1707,9 +1694,9 @@ Extensions found: IPv6:owner IPv6:limit IPv6:mac IPv6:multiport -Allow response packets (at the moment IPv6 connection tracking isn't in mainstream netfilter6 implemented) +Allow response packets (no longer needed if connection tracking is used!) Enable tunneled IPv6-in-IPv4 To accept tunneled IPv6-in-IPv4 packets, you have to insert rules in your IPv4 firewall setup relating to such packets, for example @@ -1719,11 +1706,11 @@ Extensions found: IPv6:owner IPv6:limit IPv6:mac IPv6:multiport Allow outgoing IPv6-in-IPv4 to interface ppp0 If you have only a static tunnel, you can specify the IPv4 addresses, too, like -Accept incoming IPv6-in-IPv4 on interface ppp0 from tunnel endpoint 1.2.3.4 -Accept incoming IPv6-in-IPv4 on interface ppp0 from tunnel endpoint 192.0.2.2 + Allow outgoing IPv6-in-IPv4 to interface ppp0 to tunnel endpoint 1.2.3.4 - Protection against incoming TCP connection requests VERY RECOMMENDED! For security issues you should really insert a rule which blocks incoming TCP connection requests. Adapt "-i" option, if other interface names are in use! @@ -1741,8 +1728,60 @@ Extensions found: IPv6:owner IPv6:limit IPv6:mac IPv6:multiport Block incoming UDP packets which cannot be responses of forwarded requests of hosts behind this router -Demonstration example -Following lines show a more sophisticated setup as an example. Happy netfilter6 ruleset creation.... +Examples + +Simple example for Fedora Core +Following lines show a simple firewall configuration for Fedora Core 6 (since kernel version 2.6.20). It was modfied from the default one (generated by system-config-firewall) for supporting connection tracking and return the proper ICMPv6 code for rejects. Incoming SSH (port 22) connections are allowed. +For completeness also the IPv4 configuration is shown here: +Usage: +Create/modify the configuration filesActivate IPv4 & IPv6 firewalling + +Enable automatic start after reboot + +Sophisticated example +Following lines show a more sophisticated but still stateless filter setup as an example. Happy netfilter6 ruleset creation.... +]]> <!-- anchor id="chapter-security" -->Security Node security @@ -1912,13 +1951,13 @@ Nmap run completed -- 1 IP address (1 host up) scanned in 0.525 seconds Current versions (as time of writing 2.6.9 and upper) support native IPsec for IPv4 and IPv6. Implementation was helped by the USAGI project. Automatic key exchange (IKE) -IPsec requires a key exchange of a secret. This is mostly done automatically by so called IKE daemons. They also handle the authentication of the peers, either by a common known secret (so called "pre-shared secret") or by RSA keys (which can also be used from X.509 certificates). +IPsec requires a key exchange of a secret. This is mostly done automatically by so called IKE daemons. They also handle the authentication of the peers, either by a common known secret (so called “pre-shared secret”) or by RSA keys (which can also be used from X.509 certificates). Currently, two different IKE daemons are available for Linux, which totally differ in configuration and usage. -I prefer "pluto" from the *S/WAN implementation because of the easier and one-config-only setup. -IKE daemon "racoon" -The IKE daemon "racoon" is taken from the KAME project and ported to Linux. Modern Linux distributions contain this daemon in the package "ipsec-tools". Two executables are required for a proper IPsec setup. Take a look on Linux Advanced Routing & Traffic Control HOWTO / IPSEC, too. -Manipulation of the IPsec SA/SP database with the tool "setkey" -"setkey" is important to define the security policy (SP) for the kernel. +I prefer “pluto” from the *S/WAN implementation because of the easier and one-config-only setup. +IKE daemon “racoon” +The IKE daemon “racoon” is taken from the KAME project and ported to Linux. Modern Linux distributions contain this daemon in the package “ipsec-tools”. Two executables are required for a proper IPsec setup. Take a look on Linux Advanced Routing & Traffic Control HOWTO / IPSEC, too. +Manipulation of the IPsec SA/SP database with the tool “setkey” +“setkey” is important to define the security policy (SP) for the kernel. File: /etc/racoon/setkey.sh Example for an end-to-end encrypted connection in transport mode For the other peer, you have to replace "in" with "out". -Configuration of the IKE daemon "racoon" -"racoon" requires a configuration file for proper execution. It includes the related settings to the security policy, which should be set up previously using "setkey". +]]>For the other peer, you have to replace “in” with “out”. +Configuration of the IKE daemon “racoon” +“racoon” requires a configuration file for proper execution. It includes the related settings to the security policy, which should be set up previously using “setkey”. File: /etc/racoon/racoon.conf -Running IPsec with IKE daemon "racoon" +Running IPsec with IKE daemon “racoon” At least the daemon needs to be started. For the first time, use debug and foreground mode. The following example shows a successful IKE phase 1 (ISAKMP-SA) and 2 (IPsec-SA) negotiation: 2001:db8:1:1::1 spi=253935531(0xf22bfab) 2005-01-01 20:31:10: INFO: IPsec-SA established: ¬ ESP/Tunnel 2001:db8:1:1::1->2001:db8:2:2::2 spi=175002564(0xa6e53c4) -]]>Each direction got its own IPsec-SA (like defined in the IPsec standard). With "tcpdump" on the related interface, you will see as result of an IPv6 ping: +]]>Each direction got its own IPsec-SA (like defined in the IPsec standard). With “tcpdump” on the related interface, you will see as result of an IPv6 ping: 2001:db8:2:2::2: ESP(spi=0x0a6e53c4,seq=0x3) 20:35:55.537522 2001:db8:2:2::2 > 2001:db8:1:1::1: ESP(spi=0x0f22bfab,seq=0x3) ]]>As expected, the negotiated SPIs are being used here. -And using "setkey", current active parameters are shown: +And using “setkey”, current active parameters are shown: -IKE daemon "pluto" -The IKE daemon "pluto" is included in distributions of the *S/WAN projects. *S/WAN project starts at the beginning as FreeS/WAN. Unfortunately, the FreeS/WAN project stopped further development in 2004. Because of the slow pace of development in the past, two spin-offs started: strongSwan and Openswan. Today, readily installable packages are available for at least Openswan (included in Fedora Core 3). -A major difference to "racoon", only one configuration file is required. Also, an initscript exists for automatic setup after booting. -Configuration of the IKE daemon "pluto" +IKE daemon “pluto” +The IKE daemon “pluto” is included in distributions of the *S/WAN projects. *S/WAN project starts at the beginning as FreeS/WAN. Unfortunately, the FreeS/WAN project stopped further development in 2004. Because of the slow pace of development in the past, two spin-offs started: strongSwan and Openswan. Today, readily installable packages are available for at least Openswan (included in Fedora Core 3). +A major difference to “racoon”, only one configuration file is required. Also, an initscript exists for automatic setup after booting. +Configuration of the IKE daemon “pluto” The configuration is very similar to the IPv4 one, only one important option is necessary. File: /etc/ipsec.conf File: /etc/ipsec.secrets -Running IPsec with IKE daemon "pluto" +Running IPsec with IKE daemon “pluto” If installation of Openswan was successfully, an initscript should exist for starting IPsec, simply run (on each peer): Afterwards, start this connection on one peer. If you saw the line "IPsec SA established", all worked fine. +]]>Afterwards, start this connection on one peer. If you saw the line “IPsec SA established”, all worked fine. 0xa98b7710 <0xa51e1f22} -]]>Because *S/WAN and setkey/racoon do use the same IPsec implementation in Linux 2.6.x kernel, "setkey" can be used here too to show current active parameters: +]]>Because *S/WAN and setkey/racoon do use the same IPsec implementation in Linux 2.6.x kernel, “setkey” can be used here too to show current active parameters: Additional informations: -On Linux Kernel 2.6.x you can get the policy and status of IPsec also using "ip": +On Linux Kernel 2.6.x you can get the policy and status of IPsec also using “ip”: <!-- anchor id="chapter-qos" -->Quality of Service (QoS) -IPv6 supports QoS with use of Flow Labels and Traffic Classes. This can be controlled using "tc" (contained in package "iproute"). +IPv6 supports QoS with use of Flow Labels and Traffic Classes. This can be controlled using “tc” (contained in package “iproute”). Additional infos: RFC 3697 / IPv6 Flow Label Specificationmore to be filled... <!-- anchor id="chapter-hints-daemons" -->Hints for IPv6-enabled daemons @@ -2282,7 +2321,7 @@ tcp 0 0 2001:0db8:100::2:80 :::* LISTEN 12345/httpd2 ]]>For simple tests use the telnet example already shown. Additional notes -Apache2 supports a method called "sendfile" to speedup serving data. Some NIC drivers also support offline checksumming. In some cases, this can lead to connection problems and invalid TCP checksums. In this cases, disable "sendfile" either by recompiling using configure option "--without-sendfile" or by using the "EnableSendfile off" directive in configuration file. +Apache2 supports a method called “sendfile” to speedup serving data. Some NIC drivers also support offline checksumming. In some cases, this can lead to connection problems and invalid TCP checksums. In this cases, disable “sendfile” either by recompiling using configure option “--without-sendfile” or by using the "EnableSendfile off" directive in configuration file. <!-- anchor id="hints-daemons-radvd" -->Router Advertisement Daemon (radvd) The router advertisement daemon is very useful on a LAN, if clients should be auto-configured. The daemon itself should run on the Linux default IPv6 gateway router (it's not required that this is also the default IPv4 gateway, so pay attention who on your LAN is sending router advertisements). You can specify some information and flags which should be contained in the advertisement. Common used are @@ -2334,7 +2373,7 @@ tcp 0 0 2001:0db8:100::2:80 :::* LISTEN 12345/httpd2 # /sbin/ip -6 route add 2002:0102:0304:f101::/64 dev eth0 metric 1 This route needs to be replaced every time the prefix changes, which is the case after a new IPv4 address was assigned to the dial-up interface. Debugging -A program called "radvdump" can help you looking into sent or received advertisements. Simple to use: +A program called “radvdump” can help you looking into sent or received advertisements. Simple to use: ]]>That's all. <!-- anchor id="hints-daemons-others" -->Other daemons -Nowadays it's mostly simple, look for either a command line option or a configuration value to enable IPv6 listening. See manual page of the daemon or check related FAQs. It can happen that you can bind a daemon only to the IPv6-"any"-address (::) and not to bind to a dedicated IPv6 address, because the lack of support (depends on that what the programmer has implemented so far...). +Nowadays it's mostly simple, look for either a command line option or a configuration value to enable IPv6 listening. See manual page of the daemon or check related FAQs. It can happen that you can bind a daemon only to the IPv6-“any”-address (::) and not to bind to a dedicated IPv6 address, because the lack of support (depends on that what the programmer has implemented so far...). <!-- anchor id="chapter-programming" -->Programming -<!-- anchor id="chapter-section-using-API" --><!-- anchor id="chapter-programming-using-API" -->Programming (using API) -I have no experience in IPv6 programming, perhaps this chapter will be filled by others or moved away to another HOWTO. -More Information can be found here: -RFC 3493 / Basic Socket Interface Extensions for IPv6RFC 3542 / Advanced Sockets Application Program Interface (API) for IPv6Porting applications to IPv6 HowTo by Eva M. Castro -Languages +<!-- anchor id="chapter-section-using-API" --><!-- anchor id="chapter-programming-using-API" -->Programming using C-API +Related RFCs: +RFC 3493 / Basic Socket Interface Extensions for IPv6RFC 3542 / Advanced Sockets Application Program Interface (API) for IPv6Following contents of this section is contributed by John Wenker, Sr. Software Engineer Performance Technologies San Diego, CA USA http://www.pt.com/. + + +This section describes how to write IPv6 client-server applications under the Linux operating system. First thing's first, and credit must be given where it is due. The information contained in this section is derived from Chapters 2 through 4 of IPv6 Network Programming by Jun-ichiro itojun Hagino (ISBN 1-55558-318-0). The reader is encouraged to consult that book for more detailed information. It describes how to convert IPv4 applications to be IPv6 compatible in a protocol-independent way, and describes some of the common problems encountered during the conversion along with suggested solutions. At the time of this writing, this is the only book of which the author is aware that specifically addresses how to program IPv6 applications [since writing this section, the author has also become aware of the Porting applications to IPv6 HowTo by Eva M. Castro at Since writing this HowTo, the author has also become aware of the Porting applications to IPv6 HowTo by Eva M. Castro at http://jungla.dit.upm.es/~ecastro/IPv6-web/ipv6.html]. Unfortunately, of the almost 360 pages in the book, maybe 60 are actually useful (the chapters mentioned). Nevertheless, without the guidance of that book, the author would have been unable to perform his job duties or compose this HowTo. While most (but certainly not all) of the information in the Hagino book is available via the Linux 'man' pages, application programmers will save a significant amount of time and frustration by reading the indicated chapters of the book rather than searching through the 'man' pages and online documentation. +Other than the Hagino book, any other information presented in this HowTo was obtained through trial and error. Some items or explanations may not be entirely “correct” in the grand IPv6 scheme, but seem to work in practical application. +The discussion that follows assumes the reader is already experienced with the traditional TCP/IP socket API. For more information on traditional socket programming, the Internetworking with TCP/IP series of textbooks by Comer & Stevens is hard to beat, specifically Volume III: Client-Server Programming and Applications, Linux/POSIX Sockets Version (ISBN 0-13-032071-4). This HowTo also assumes that the reader has had at least a bare basic introduction to IPv6 and in particular the addressing scheme for network addresses (see Section 2.3). +Address Structures +This section provides a brief overview of the structures provided in the socket API to represent network addresses (or more specifically transport endpoints) when using the Internet protocols in a client-server application. +IPv4 sockaddr_in +In IPv4, network addresses are 32 bits long and define a network node. Addresses are written in dotted decimal notation, such as 192.0.2.1, where each number represents eight bits of the address. Such an IPv4 address is represented by the struct sockaddr_in data type, which is defined in <netinet/in.h>. +The sin_family component indicates the address family. For IPv4 addresses, this is always set to AF_INET. The sin_addr field contains the 32-bit network address (in network byte order). Finally, the sin_port component represents the transport layer port number (in network byte order). Readers should already be familiar with this structure, as this is the standard IPv4 address structure. +IPv6 sockaddr_in6 +The biggest feature of IPv6 is its increased address space. Instead of 32-bit network addresses, IPv6 allots 128 bits to an address. Addresses are written in colon-hex notation of the form fe80::2c0:8cff:fe01:2345, where each hex number separated by colons represents 16 bits of the address. Two consecutive colons indicate a string of consecutive zeros for brevity, and at most only one double-colon may appear in the address. IPv6 addresses are represented by the struct sockaddr_in6 data type, also defined in <netinet/in.h>. +The sin6_family, sin6_port, and sin6_addr components of the structure have the same meaning as the corresponding fields in the sockaddr_in structure. However, the sin6_family member is set to AF_INET6 for IPv6 addresses, and the sin6_addr field holds a 128-bit address instead of only 32 bits. +The sin6_flowinfo field is used for flow control, but is not yet standardized and can be ignored. +The sin6_scope_id field has an odd use, and it seems (at least to this naïve author) that the IPv6 designers took a huge step backwards when devising this. Apparently, 128-bit IPv6 network addresses are not unique. For example, it is possible to have two hosts, on separate networks, with the same link-local address (see Figure 1). In order to pass information to a specific host, more than just the network address is required; the scope identifier must also be specified. In Linux, the network interface name is used for the scope identifier (e.g. “eth0”) [be warned that the scope identifier is implementation dependent!]. Use the ifconfig(1M) command to display a list of active network interfaces. +A colon-hex network address can be augmented with the scope identifier to produce a "scoped address”. The percent sign ('%') is used to delimit the network address from the scope identifier. For example, fe80::1%eth0 is a scoped IPv6 address where fe80::1 represents the 128-bit network address and eth0 is the network interface (i.e. the scope identifier). Thus, if a host resides on two networks, such as Host B in example below, the user now has to know which path to take in order to get to a particular host. In Figure 1, Host B addresses Host A using the scoped address fe80::1%eth0, while Host C is addressed with fe80::1%eth1. +Getting back to the sockaddr_in6 structure, its sin6_scope_id field contains the index of the network interface on which a host may be found. Server applications will have this field set automatically by the socket API when they accept a connection or receive a datagram. For client applications, if a scoped address is passed as the node parameter to getaddrinfo(3) (described later in this HowTo), then the sin6_scope_id field will be filled in correctly by the system upon return from the function; if a scoped address is not supplied, then the sin6_scope_id field must be explicitly set by the client software prior to attempting to communicate with the remote server. The if_nametoindex(3) function is used to translate a network interface name into its corresponding index. It is declared in <net/if.h>. +Generic Addresses +As any programmer familiar with the traditional TCP/IP socket API knows, several socket functions deal with "generic" pointers. For example, a pointer to a generic struct sockaddr data type is passed as a parameter to some socket functions (such as connect(2) or bind(2)) rather than a pointer to a specific address type. Be careful... the sockaddr_in6 structure is larger than the generic sockaddr structure! Thus, if your program receives a generic address whose actual type is unknown (e.g. it could be an IPv4 address structure or an IPv6 address structure), you must supply sufficient storage to hold the entire address. The struct sockaddr_storage data type is defined in <bits/socket.h> for this purpose [do not #include this file directly within an application; use <sys/socket.h> as usual, and <bits/socket.h> will be implicitly included]. +For example, consider the recvfrom(2) system call, which is used to receive a message from a remote peer. Its function prototype is: +The from parameter points to a generic sockaddr structure. If data can be received from an IPv6 peer on the socket referenced by s, then from should point to a data type of struct sockaddr_storage, as in the following dummy example: +As seen in the above example, ss (a struct sockaddr_storage data object) is used to receive the peer address information, but it's address is typecast to a generic struct sockaddr* pointer in the call to recvfrom(2). +Lookup Functions +Traditionally, hostname and service name resolution were performed by functions such as gethostbyname(3) and getservbyname(3). These traditional lookup functions are still available, but they are not forward compatible to IPv6. Instead, the IPv6 socket API provides new lookup functions that consolidate the functionality of several traditional functions. These new lookup functions are also backward compatible with IPv4, so a programmer can use the same translation algorithm in an application for both the IPv4 and IPv6 protocols. This is an important feature, because obviously a global IPv6 infrastructure isn't going to be put in place overnight. Thus, during the transition period from IPv4 to IPv6, client-server applications should be designed with the flexibility to handle both protocols simultaneously. The example programs at the end of this chapter do just that. +The primary lookup function in the new socket API is getaddrinfo(3). Its prototype is as follows. +The node parameter is a pointer to the hostname or IP address being translated. The referenced string can be a hostname, IPv4 dotted decimal address, or IPv6 colon-hex address (possibly scoped). The service parameter is a pointer to the transport layer's service name or port number. It can be specified as a name found in /etc/services or a decimal number. getaddrinfo(3) resolves the host/service combination and returns a list of address records; a pointer to the list is placed in the location pointed at by res. For example, suppose a host can be identified by both an IPv4 and IPv6 address, and that the indicated service has both a TCP entry and UDP entry in /etc/services. In such a scenario, it is not inconceivable that four address records are returned; one for TCP/IPv6, one for UDP/IPv6, one for TCP/IPv4, and one for UDP/IPv4. +The definition for struct addrinfo is found in <netdb.h> (as is the declaration for getaddrinfo(3) and the other functions described in this section). The structure has the following format: +Consult the 'man' page for getaddrinfo(3) for detailed information about the various fields; this HowTo only describes a subset of them, and only to the extent necessary for normal IPv6 programming. +The ai_family, ai_socktype, and ai_protocol fields have the exact same meaning as the parameters to the socket(2) system call. The ai_family field indicates the protocol family (not the address family) associated with the record, and will be PF_INET6 for IPv6 or PF_INET for IPv4. The ai_socktype parameter indicates the type of socket to which the record corresponds; SOCK_STREAM for a reliable connection-oriented byte-stream or SOCK_DGRAM for connectionless communication. The ai_protocol field specifies the underlying transport protocol for the record. +The ai_addr field points to a generic struct sockaddr object. Depending on the value in the ai_family field, it will point to either a struct sockaddr_in (PF_INET) or a struct sockaddr_in6 (PF_INET6). The ai_addrlen field contains the size of the object pointed at by the ai_addr field. +As mentioned, getaddrinfo(3) returns a list of address records. The ai_next field points to the next record in the list. +The hints parameter to getaddrinfo(3) is also of type struct addrinfo and acts as a filter for the address records returned in res. If hints is NULL, all matching records are returned; but if hints is non-NULL, the referenced structure gives "hints" to getaddrinfo(3) about which records to return. Only the ai_flags, ai_family, ai_socktype, and ai_protocol fields are significant in the hints structure, and all other fields should be set to zero. +Programs can use hints->ai_family to specify the protocol family. For example, if it is set to PF_INET6, then only IPv6 address records are returned. Likewise, setting hints->ai_family to PF_INET results in only IPv4 address records being returned. If an application wants both IPv4 and IPv6 records, the field should be set to PF_UNSPEC. +The hints->socktype field can be set to SOCK_STREAM to return only records that correspond to connection-oriented byte streams, SOCK_DGRAM to return only records corresponding to connectionless communication, or 0 to return both. +For the Internet protocols, there is only one protocol associated with connection-oriented sockets (TCP) and one protocol associated with connectionless sockets (UDP), so setting hints->ai_socktype to SOCK_STREAM or SOCK_DGRAM is the same as saying, "Give me only TCP records," or "Give me only UDP records," respectively. With that in mind, the hints->ai_protocol field isn't really that important with the Internet protocols, and pretty much mirrors the hints->ai_socktype field. Nevertheless, hints->ai_protocol can be set to IPPROTO_TCP to return only TCP records, IPPROTO_UDP to return only UDP records, or 0 for both. +The node or service parameter to gethostbyname(3) can be NULL, but not both. If node is NULL, then the ai_flags field of the hints parameter specifies how the network address in a returned record is set (i.e. the sin_addr or sin6_addr field of the object pointed at by the ai_addr component in a returned record). If the AI_PASSIVE flag is set in hints, then the returned network addresses are left unresolved (all zeros). This is how server applications would use getaddrinfo(3). If the flag is not set, then the address is set to the local loopback address (::1 for IPv6 or 127.0.0.1 for IPv4). This is one way a client application can specify that the target server is running on the same machine as the client. If the service parameter is NULL, the port number in the returned address records remains unresolved. +The getaddrinfo(3) function returns zero on success, or an error code. In the case of an error, the gai_strerror(3) function is used to obtain a character pointer to an error message corresponding to the error code, just like strerror(3) does in the standard 'C' library. +Once the address list is no longer needed, it must be freed by the application. This is done with the freeaddrinfo(3) function. +The last function that will be mentioned in this section is getnameinfo(3). This function is the inverse of getaddrinfo(3); it is used to create a string representation of the hostname and service from a generic struct sockaddr data object. It has the following prototype. +The sa parameter points to the address structure in question, and salen contains its size. The host parameter points to a buffer where the null-terminated hostname string is placed, and the hostlen parameter is the size of that buffer. If there is no hostname that corresponds to the address, then the network address (dotted decimal or colon-hex) is placed in host. Likewise, the serv parameter points to a buffer where the null-terminated service name string (or port number) is placed, and the servlen parameter is the size of that buffer. The flags parameter modifies the function's behavior; in particular, the NI_NUMERICHOST flag indicates that the converted hostname should always be formatted in numeric form (i.e. dotted decimal or colon-hex), and the NI_NUMERICSERV flag indicates that the converted service should always be in numeric form (i.e. the port number). +The symbols NI_MAXHOST and NI_MAXSERV are available to applications and represent the maximum size of any converted hostname or service name, respectively. Use these when declaring output buffers for getnameinfo(3). +Quirks Encountered +Before jumping into the programming examples, there are several quirks in IPv6 of which the reader should be aware. The more significant ones (in addition to the non-uniqueness of IPv6 network addresses already discussed) are described in the paragraphs below. +IPv4 Mapped Addresses +For security reasons that this author won't pretend to understand, "IPv4 mapped addresses" should not be allowed in IPv6-capable server applications. To put it in terms that everyone can understand, this simply means that a server should not accept IPv4 traffic on an IPv6 socket (an otherwise legal operation). An IPv4 mapped address is a mixed-format address of the form: +where the first portion is in IPv6 colon-hex format and the last portion is in IPv4 dotted decimal notation. The dotted decimal IPv4 address is the actual network address, but it is being mapped into an IPv6 compatible format. +To prevent IPv4 mapped addresses from being accepted on an IPv6 socket, server applications must explicitly set the IPV6_V6ONLY socket option on all IPv6 sockets created [the Hagino book implies that this is only a concern with server applications. However, it has been observed during testing that if a client application uses an IPv4 mapped address to specify the target server, and the target server has IPv4 mapped addresses disabled, the connection still completes regardless. On the server side, the connection endpoint is an IPv4 socket as desired; but on the client side, the connection endpoint is an IPv6 socket. Setting the IPV6_V6ONLY socket option on the client side as well as the server side prevents any connection from being established at all.]. There's only one problem. Apparently, IPV6_V6ONLY isn't defined on all systems [or at least it wasn't in 2005 when the Hagino book was written]. The server example at the end of this chapter provides a method for handling this problem. +If IPv4 traffic cannot be handled on IPv6 sockets, then that implies that server applications must open both an IPv4 and IPv6 socket for a particular network service if it wants to handle requests from either protocol. This goes back to the flexibility issue mentioned earlier. If getaddrinfo(3) returns multiple address records, then server applications should traverse the list and open a passive socket for each address provided. +Cannot Specify the Scope Identifier in /etc/hosts +It is possible to assign a hostname to an IPv6 network address in /etc/hosts. For example, the following is an excerpt from the /etc/hosts file on the author's development system. +The "localhost" and "pt141" hostnames can be translated to either an IPv4 or IPv6 network address. So, for example, if "pt141" is passed as the node parameter to getaddrinfo(3), the function returns both an IPv4 and IPv6 address record for the host (assuming the behavior hasn't been modified by the hints parameter). Unfortunately, a scoped address cannot be used in /etc/hosts. Doing so results in getaddrinfo(3) returning only the IPv4 record. +Client & Server Residing on the Same Machine +Suppose a machine has the IPv4 address 192.0.2.1. A client application running on that machine can connect to a server application on the same machine by using either the local loopback address (127.0.0.1) or the network address (192.0.2.1) as the target server. Much to this author's surprise (and dismay), it turns out that an IPv6 client application cannot connect to a server application on the same machine if it uses the network address of that machine as the target; it must use the local loopback address (::1). +Putting It All Together (A Client-Server Programming Example) +Now it's time to put everything discussed thus far together into a sample client-server application. The remainder of this section is devoted to a remote time-of-day application (the 'daytime' Internet service) [I noticed that Ms. Castro used a 'daytime' example in her Porting applications to IPv6 HowTo. For the record, the source code presented here is original, developed from scratch, and any similarity between it and any other publicly available 'daytime' example is purely coincidental.]. The source code presented in this section was developed and tested on a RedHat Linux release using the 2.6 kernel (2.6.9 to be specific). Readers may use the source code freely, so long as proper credit is attributed; but of course the standard disclaimer must be given first:
+Although the sample source code is believed to be free of errors, the author makes no guarantees as to its reliability, especially considering that some error paths were intentionally omitted for brevity. Use it at your own risk! +
When you get right down to it, there really aren't that many differences between IPv4 and IPv6 applications. The trick is to code IPv6 applications in a protocol-independent manner, such that they can handle both IPv4 and IPv6 simultaneously and transparently. This sample application does just that. The only protocol-dependent code in the example occurs when printing network addresses in verbose mode; but only after the ai_family field in the addrinfo structure has been checked, so the programs know exactly what type of address they're handling at the time. +'Daytime' Server Code +The server code is found in file tod6d.c (time-of-day IPv6 daemon). Once built, the server may be started using the following command syntax (assuming tod6d is the executable file): +ARGUMENTS: +serviceThe service (or well-known port) on which to listen. Default is "daytime".OPTIONS: +-vTurn on verbose mode.The server handles both TCP and UDP requests on the network. The server source code contained in tod6d.c follows: + /* errno declaration & error codes. */ +#include /* getaddrinfo(3) et al. */ +#include /* sockaddr_in & sockaddr_in6 definition. */ +#include /* printf(3) et al. */ +#include /* exit(2). */ +#include /* String manipulation & memory functions. */ +#include /* poll(2) and related definitions. */ +#include /* Socket functions (socket(2), bind(2), etc). */ +#include /* time(2) & ctime(3). */ +#include /* getopt(3), read(2), etc. */ +/* +** Constants. +*/ +#define DFLT_SERVICE "daytime" /* Default service name. */ +#define INVALID_DESC -1 /* Invalid file descriptor. */ +#define MAXCONNQLEN 3 /* Max nbr of connection requests to queue. */ +#define MAXTCPSCKTS 2 /* One TCP socket for IPv4 & one for IPv6. */ +#define MAXUDPSCKTS 2 /* One UDP socket for IPv4 & one for IPv6. */ +#define VALIDOPTS "v" /* Valid command options. */ +/* +** Simple boolean type definition. +*/ +typedef enum { false = 0, true } boolean; +/* +** Prototypes for internal helper functions. +*/ +static int openSckt( const char *service, + const char *protocol, + int desc[ ], + size_t *descSize ); +static void tod( int tSckt[ ], + size_t tScktSize, + int uSckt[ ], + size_t uScktSize ); +/* +** Global (within this file only) data objects. +*/ +static char hostBfr[ NI_MAXHOST ]; /* For use w/getnameinfo(3). */ +static const char *pgmName; /* Program name w/o dir prefix. */ +static char servBfr[ NI_MAXSERV ]; /* For use w/getnameinfo(3). */ +static boolean verbose = false; /* Verbose mode indication. */ +/* +** Usage macro for command syntax violations. +*/ +#define USAGE \ + { \ + fprintf( stderr, \ + "Usage: %s [-v] [service]\n", \ + pgmName ); \ + exit( 127 ); \ + } /* End USAGE macro. */ +/* +** Macro to terminate the program if a system call error occurs. The system +** call must be one of the usual type that returns -1 on error. This macro is +** a modified version of a macro authored by Dr. V. Vinge, SDSU Dept. of +** Computer Science (retired)... best professor I ever had. I hear he writes +** great science fiction in addition to robust code, too. +*/ +#define CHK(expr) \ + do \ + { \ + if ( (expr) == -1 ) \ + { \ + fprintf( stderr, \ + "%s (line %d): System call ERROR - %s.\n", \ + pgmName, \ + __LINE__, \ + strerror( errno ) ); \ + exit( 1 ); \ + } /* End IF system call failed. */ \ + } while ( false ) +/****************************************************************************** +* Function: main +* +* Description: +* Set up a time-of-day server and handle network requests. This server +* handles both TCP and UDP requests. +* +* Parameters: +* The usual argc and argv parameters to a main() function. +* +* Return Value: +* This is a daemon program and never returns. However, in the degenerate +* case where no sockets are created, the function returns zero. +******************************************************************************/ +int main( int argc, + char *argv[ ] ) +{ + int opt; + const char *service = DFLT_SERVICE; + int tSckt[ MAXTCPSCKTS ]; /* Array of TCP socket descriptors. */ + size_t tScktSize = MAXTCPSCKTS; /* Size of uSckt (# of elements). */ + int uSckt[ MAXUDPSCKTS ]; /* Array of UDP socket descriptors. */ + size_t uScktSize = MAXUDPSCKTS; /* Size of uSckt (# of elements). */ + /* + ** Set the program name (w/o directory prefix). + */ + pgmName = strrchr( argv[ 0 ], '/' ); + pgmName = pgmName == NULL ? argv[ 0 ] : pgmName + 1; + /* + ** Process command options. + */ + opterr = 0; /* Turns off "invalid option" error messages. */ + while ( ( opt = getopt( argc, argv, VALIDOPTS ) ) >= 0 ) + { + switch ( opt ) + { + case 'v': /* Verbose mode. */ + { + verbose = true; + break; + } + default: + { + USAGE; + } + } /* End SWITCH on command option. */ + } /* End WHILE processing options. */ + /* + ** Process command line arguments. + */ + switch ( argc - optind ) + { + case 0: break; + case 1: service = argv[ optind ]; break; + default: USAGE; + } /* End SWITCH on number of command line arguments. */ + /* + ** Open both a TCP and UDP socket, for both IPv4 & IPv6, on which to receive + ** service requests. + */ + if ( ( openSckt( service, "tcp", tSckt, &tScktSize ) < 0 ) || + ( openSckt( service, "udp", uSckt, &uScktSize ) < 0 ) ) + { + exit( 1 ); + } + /* + ** Run the time-of-day server. + */ + if ( ( tScktSize > 0 ) || ( uScktSize > 0 ) ) + { + tod( tSckt, /* tod() never returns. */ + tScktSize, + uSckt, + uScktSize ); + } + /* + ** Since tod() never returns, execution only gets here if no sockets were + ** created. + */ + if ( verbose ) + { + fprintf( stderr, + "%s: No sockets opened... terminating.\n", + pgmName ); + } + return 0; +} /* End main() */ +/****************************************************************************** +* Function: openSckt +* +* Description: +* Open passive (server) sockets for the indicated inet service & protocol. +* Notice in the last sentence that "sockets" is plural. During the interim +* transition period while everyone is switching over to IPv6, the server +* application has to open two sockets on which to listen for connections... +* one for IPv4 traffic and one for IPv6 traffic. +* +* Parameters: +* service - Pointer to a character string representing the well-known port +* on which to listen (can be a service name or a decimal number). +* protocol - Pointer to a character string representing the transport layer +* protocol (only "tcp" or "udp" are valid). +* desc - Pointer to an array into which the socket descriptors are +* placed when opened. +* descSize - This is a value-result parameter. On input, it contains the +* max number of descriptors that can be put into 'desc' (i.e. the +* number of elements in the array). Upon return, it will contain +* the number of descriptors actually opened. Any unused slots in +* 'desc' are set to INVALID_DESC. +* +* Return Value: +* 0 on success, -1 on error. +******************************************************************************/ +static int openSckt( const char *service, + const char *protocol, + int desc[ ], + size_t *descSize ) +{ + struct addrinfo *ai; + int aiErr; + struct addrinfo *aiHead; + struct addrinfo hints = { .ai_flags = AI_PASSIVE, /* Server mode. */ + .ai_family = PF_UNSPEC }; /* IPv4 or IPv6. */ + size_t maxDescs = *descSize; + /* + ** Initialize output parameters. When the loop completes, *descSize is 0. + */ + while ( *descSize > 0 ) + { + desc[ --( *descSize ) ] = INVALID_DESC; + } + /* + ** Check which protocol is selected (only TCP and UDP are valid). + */ + if ( strcmp( protocol, "tcp" ) == 0 ) /* TCP protocol. */ + { + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + } + else if ( strcmp( protocol, "udp" ) == 0 ) /* UDP protocol. */ + { + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + } + else /* Invalid protocol. */ + { + fprintf( stderr, + "%s (line %d): ERROR - Unknown transport " + "layer protocol \"%s\".\n", + pgmName, + __LINE__, + protocol ); + return -1; + } + /* + ** Look up the service's well-known port number. Notice that NULL is being + ** passed for the 'node' parameter, and that the AI_PASSIVE flag is set in + ** 'hints'. Thus, the program is requesting passive address information. + ** The network address is initialized to :: (all zeros) for IPv6 records, or + ** 0.0.0.0 for IPv4 records. + */ + if ( ( aiErr = getaddrinfo( NULL, + service, + &hints, + &aiHead ) ) != 0 ) + { + fprintf( stderr, + "%s (line %d): ERROR - %s.\n", + pgmName, + __LINE__, + gai_strerror( aiErr ) ); + return -1; + } + /* + ** For each of the address records returned, attempt to set up a passive + ** socket. + */ + for ( ai = aiHead; + ( ai != NULL ) && ( *descSize < maxDescs ); + ai = ai->ai_next ) + { + if ( verbose ) + { + /* + ** Display the current address info. Start with the protocol- + ** independent fields first. + */ + fprintf( stderr, + "Setting up a passive socket based on the " + "following address info:\n" + " ai_flags = 0x%02X\n" + " ai_family = %d (PF_INET = %d, PF_INET6 = %d)\n" + " ai_socktype = %d (SOCK_STREAM = %d, SOCK_DGRAM = %d)\n" + " ai_protocol = %d (IPPROTO_TCP = %d, IPPROTO_UDP = %d)\n" + " ai_addrlen = %d (sockaddr_in = %d, " + "sockaddr_in6 = %d)\n", + ai->ai_flags, + ai->ai_family, + PF_INET, + PF_INET6, + ai->ai_socktype, + SOCK_STREAM, + SOCK_DGRAM, + ai->ai_protocol, + IPPROTO_TCP, + IPPROTO_UDP, + ai->ai_addrlen, + sizeof( struct sockaddr_in ), + sizeof( struct sockaddr_in6 ) ); + /* + ** Now display the protocol-specific formatted socket address. Note + ** that the program is requesting that getnameinfo(3) convert the + ** host & service into numeric strings. + */ + getnameinfo( ai->ai_addr, + ai->ai_addrlen, + hostBfr, + sizeof( hostBfr ), + servBfr, + sizeof( servBfr ), + NI_NUMERICHOST | NI_NUMERICSERV ); + switch ( ai->ai_family ) + { + case PF_INET: /* IPv4 address record. */ + { + struct sockaddr_in *p = (struct sockaddr_in*) ai->ai_addr; + fprintf( stderr, + " ai_addr = sin_family: %d (AF_INET = %d, " + "AF_INET6 = %d)\n" + " sin_addr: %s\n" + " sin_port: %s\n", + p->sin_family, + AF_INET, + AF_INET6, + hostBfr, + servBfr ); + break; + } /* End CASE of IPv4. */ + case PF_INET6: /* IPv6 address record. */ + { + struct sockaddr_in6 *p = (struct sockaddr_in6*) ai->ai_addr; + fprintf( stderr, + " ai_addr = sin6_family: %d (AF_INET = %d, " + "AF_INET6 = %d)\n" + " sin6_addr: %s\n" + " sin6_port: %s\n" + " sin6_flowinfo: %d\n" + " sin6_scope_id: %d\n", + p->sin6_family, + AF_INET, + AF_INET6, + hostBfr, + servBfr, + p->sin6_flowinfo, + p->sin6_scope_id ); + break; + } /* End CASE of IPv6. */ + default: /* Can never get here, but just for completeness. */ + { + fprintf( stderr, + "%s (line %d): ERROR - Unknown protocol family (%d).\n", + pgmName, + __LINE__, + ai->ai_family ); + freeaddrinfo( aiHead ); + return -1; + } /* End DEFAULT case (unknown protocol family). */ + } /* End SWITCH on protocol family. */ + } /* End IF verbose mode. */ + /* + ** Create a socket using the info in the addrinfo structure. + */ + CHK( desc[ *descSize ] = socket( ai->ai_family, + ai->ai_socktype, + ai->ai_protocol ) ); + /* + ** Here is the code that prevents "IPv4 mapped addresses", as discussed + ** in Section 22.1.3.1. If an IPv6 socket was just created, then set the + ** IPV6_V6ONLY socket option. + */ + if ( ai->ai_family == PF_INET6 ) + { +#if defined( IPV6_V6ONLY ) + /* + ** Disable IPv4 mapped addresses. + */ + int v6Only = 1; + CHK( setsockopt( desc[ *descSize ], + IPPROTO_IPV6, + IPV6_V6ONLY, + &v6Only, + sizeof( v6Only ) ) ); +#else + /* + ** IPV6_V6ONLY is not defined, so the socket option can't be set and + ** thus IPv4 mapped addresses can't be disabled. Print a warning + ** message and close the socket. Design note: If the + ** #if...#else...#endif construct were removed, then this program + ** would not compile (because IPV6_V6ONLY isn't defined). That's an + ** acceptable approach; IPv4 mapped addresses are certainly disabled + ** if the program can't build! However, since this program is also + ** designed to work for IPv4 sockets as well as IPv6, I decided to + ** allow the program to compile when IPV6_V6ONLY is not defined, and + ** turn it into a run-time warning rather than a compile-time error. + ** IPv4 mapped addresses are still disabled because _all_ IPv6 traffic + ** is disabled (all IPv6 sockets are closed here), but at least this + ** way the server can still service IPv4 network traffic. + */ + fprintf( stderr, + "%s (line %d): WARNING - Cannot set IPV6_V6ONLY socket " + "option. Closing IPv6 %s socket.\n", + pgmName, + __LINE__, + ai->ai_protocol == IPPROTO_TCP ? "TCP" : "UDP" ); + CHK( close( desc[ *descSize ] ) ); + continue; /* Go to top of FOR loop w/o updating *descSize! */ +#endif /* IPV6_V6ONLY */ + } /* End IF this is an IPv6 socket. */ + /* + ** Bind the socket. Again, the info from the addrinfo structure is used. + */ + CHK( bind( desc[ *descSize ], + ai->ai_addr, + ai->ai_addrlen ) ); + /* + ** If this is a TCP socket, put the socket into passive listening mode + ** (listen is only valid on connection-oriented sockets). + */ + if ( ai->ai_socktype == SOCK_STREAM ) + { + CHK( listen( desc[ *descSize ], + MAXCONNQLEN ) ); + } + /* + ** Socket set up okay. Bump index to next descriptor array element. + */ + *descSize += 1; + } /* End FOR each address info structure returned. */ + /* + ** Dummy check for unused address records. + */ + if ( verbose && ( ai != NULL ) ) + { + fprintf( stderr, + "%s (line %d): WARNING - Some address records were " + "not processed due to insufficient array space.\n", + pgmName, + __LINE__ ); + } /* End IF verbose and some address records remain unprocessed. */ + /* + ** Clean up. + */ + freeaddrinfo( aiHead ); + return 0; +} /* End openSckt() */ +/****************************************************************************** +* Function: tod +* +* Description: +* Listen on a set of sockets and send the current time-of-day to any +* clients. This function never returns. +* +* Parameters: +* tSckt - Array of TCP socket descriptors on which to listen. +* tScktSize - Size of the tSckt array (nbr of elements). +* uSckt - Array of UDP socket descriptors on which to listen. +* uScktSize - Size of the uSckt array (nbr of elements). +* +* Return Value: None. +******************************************************************************/ +static void tod( int tSckt[ ], + size_t tScktSize, + int uSckt[ ], + size_t uScktSize ) +{ + char bfr[ 256 ]; + ssize_t count; + struct pollfd *desc; + size_t descSize = tScktSize + uScktSize; + int idx; + int newSckt; + struct sockaddr *sadr; + socklen_t sadrLen; + struct sockaddr_storage sockStor; + int status; + size_t timeLen; + char *timeStr; + time_t timeVal; + ssize_t wBytes; + /* + ** Allocate memory for the poll(2) array. + */ + desc = malloc( descSize * sizeof( struct pollfd ) ); + if ( desc == NULL ) + { + fprintf( stderr, + "%s (line %d): ERROR - %s.\n", + pgmName, + __LINE__, + strerror( ENOMEM ) ); + exit( 1 ); + } + /* + ** Initialize the poll(2) array. + */ + for ( idx = 0; idx < descSize; idx++ ) + { + desc[ idx ].fd = idx < tScktSize ? tSckt[ idx ] + : uSckt[ idx - tScktSize ]; + desc[ idx ].events = POLLIN; + desc[ idx ].revents = 0; + } + /* + ** Main time-of-day server loop. Handles both TCP & UDP requests. This is + ** an interative server, and all requests are handled directly within the + ** main loop. + */ + while ( true ) /* Do forever. */ + { + /* + ** Wait for activity on one of the sockets. The DO..WHILE construct is + ** used to restart the system call in the event the process is + ** interrupted by a signal. + */ + do + { + status = poll( desc, + descSize, + -1 /* Wait indefinitely for input. */ ); + } while ( ( status < 0 ) && ( errno == EINTR ) ); + CHK( status ); /* Check for a bona fide system call error. */ + /* + ** Get the current time. + */ + timeVal = time( NULL ); + timeStr = ctime( &timeVal ); + timeLen = strlen( timeStr ); + /* + ** Indicate that there is new network activity. + */ + if ( verbose ) + { + char *s = malloc( timeLen+1 ); + strcpy( s, timeStr ); + s[ timeLen-1 ] = '\0'; /* Overwrite '\n' in date string. */ + fprintf( stderr, + "%s: New network activity on %s.\n", + pgmName, + s ); + free( s ); + } /* End IF verbose. */ + /* + ** Process sockets with input available. + */ + for ( idx = 0; idx < descSize; idx++ ) + { + switch ( desc[ idx ].revents ) + { + case 0: /* No activity on this socket; try the next. */ + continue; + case POLLIN: /* Network activity. Go process it. */ + break; + default: /* Invalid poll events. */ + { + fprintf( stderr, + "%s (line %d): ERROR - Invalid poll event (0x%02X).\n", + pgmName, + __LINE__, + desc[ idx ].revents ); + exit( 1 ); + } + } /* End SWITCH on returned poll events. */ + /* + ** Determine if this is a TCP request or UDP request. + */ + if ( idx < tScktSize ) + { + /* + ** TCP connection requested. Accept it. Notice the use of + ** the sockaddr_storage data type. + */ + sadrLen = sizeof( sockStor ); + sadr = (struct sockaddr*) &sockStor; + CHK( newSckt = accept( desc[ idx ].fd, + sadr, + &sadrLen ) ); + CHK( shutdown( newSckt, /* Server never recv's anything. */ + SHUT_RD ) ); + if ( verbose ) + { + /* + ** Display the socket address of the remote client. Begin with + ** the address-independent fields. + */ + fprintf( stderr, + "Sockaddr info for new TCP client:\n" + " sa_family = %d (AF_INET = %d, AF_INET6 = %d)\n" + " addr len = %d (sockaddr_in = %d, " + "sockaddr_in6 = %d)\n", + sadr->sa_family, + AF_INET, + AF_INET6, + sadrLen, + sizeof( struct sockaddr_in ), + sizeof( struct sockaddr_in6 ) ); + /* + ** Display the address-specific fields. + */ + getnameinfo( sadr, + sadrLen, + hostBfr, + sizeof( hostBfr ), + servBfr, + sizeof( servBfr ), + NI_NUMERICHOST | NI_NUMERICSERV ); + /* + ** Notice that we're switching on an address family now, not a + ** protocol family. + */ + switch ( sadr->sa_family ) + { + case AF_INET: /* IPv4 address. */ + { + struct sockaddr_in *p = (struct sockaddr_in*) sadr; + fprintf( stderr, + " sin_addr = sin_family: %d\n" + " sin_addr: %s\n" + " sin_port: %s\n", + p->sin_family, + hostBfr, + servBfr ); + break; + } /* End CASE of IPv4. */ + case AF_INET6: /* IPv6 address. */ + { + struct sockaddr_in6 *p = (struct sockaddr_in6*) sadr; + fprintf( stderr, + " sin6_addr = sin6_family: %d\n" + " sin6_addr: %s\n" + " sin6_port: %s\n" + " sin6_flowinfo: %d\n" + " sin6_scope_id: %d\n", + p->sin6_family, + hostBfr, + servBfr, + p->sin6_flowinfo, + p->sin6_scope_id ); + break; + } /* End CASE of IPv6. */ + default: /* Can never get here, but for completeness. */ + { + fprintf( stderr, + "%s (line %d): ERROR - Unknown address " + "family (%d).\n", + pgmName, + __LINE__, + sadr->sa_family ); + break; + } /* End DEFAULT case (unknown address family). */ + } /* End SWITCH on address family. */ + } /* End IF verbose mode. */ + /* + ** Send the TOD to the client. + */ + wBytes = timeLen; + while ( wBytes > 0 ) + { + do + { + count = write( newSckt, + timeStr, + wBytes ); + } while ( ( count < 0 ) && ( errno == EINTR ) ); + CHK( count ); /* Check for a bona fide error. */ + wBytes -= count; + } /* End WHILE there is data to send. */ + CHK( close( newSckt ) ); + } /* End IF this was a TCP connection request. */ + else + { + /* + ** This is a UDP socket, and a datagram is available. The funny + ** thing about UDP requests is that this server doesn't require any + ** client input; but it can't send the TOD unless it knows a client + ** wants the data, and the only way that can occur with UDP is if + ** the server receives a datagram from the client. Thus, the + ** server must receive _something_, but the content of the datagram + ** is irrelevant. Read in the datagram. Again note the use of + ** sockaddr_storage to receive the address. + */ + sadrLen = sizeof( sockStor ); + sadr = (struct sockaddr*) &sockStor; + CHK( count = recvfrom( desc[ idx ].fd, + bfr, + sizeof( bfr ), + 0, + sadr, + &sadrLen ) ); + /* + ** Display whatever was received on stdout. + */ + if ( verbose ) + { + ssize_t rBytes = count; + fprintf( stderr, + "%s: UDP datagram received (%d bytes).\n", + pgmName, + count ); + while ( count > 0 ) + { + fputc( bfr[ rBytes - count-- ], + stdout ); + } + if ( bfr[ rBytes-1 ] != '\n' ) + fputc( '\n', stdout ); /* Newline also flushes stdout. */ + /* + ** Display the socket address of the remote client. Address- + ** independent fields first. + */ + fprintf( stderr, + "Remote client's sockaddr info:\n" + " sa_family = %d (AF_INET = %d, AF_INET6 = %d)\n" + " addr len = %d (sockaddr_in = %d, " + "sockaddr_in6 = %d)\n", + sadr->sa_family, + AF_INET, + AF_INET6, + sadrLen, + sizeof( struct sockaddr_in ), + sizeof( struct sockaddr_in6 ) ); + /* + ** Display the address-specific information. + */ + getnameinfo( sadr, + sadrLen, + hostBfr, + sizeof( hostBfr ), + servBfr, + sizeof( servBfr ), + NI_NUMERICHOST | NI_NUMERICSERV ); + switch ( sadr->sa_family ) + { + case AF_INET: /* IPv4 address. */ + { + struct sockaddr_in *p = (struct sockaddr_in*) sadr; + fprintf( stderr, + " sin_addr = sin_family: %d\n" + " sin_addr: %s\n" + " sin_port: %s\n", + p->sin_family, + hostBfr, + servBfr ); + break; + } /* End CASE of IPv4 address. */ + case AF_INET6: /* IPv6 address. */ + { + struct sockaddr_in6 *p = (struct sockaddr_in6*) sadr; + fprintf( stderr, + " sin6_addr = sin6_family: %d\n" + " sin6_addr: %s\n" + " sin6_port: %s\n" + " sin6_flowinfo: %d\n" + " sin6_scope_id: %d\n", + p->sin6_family, + hostBfr, + servBfr, + p->sin6_flowinfo, + p->sin6_scope_id ); + break; + } /* End CASE of IPv6 address. */ + default: /* Can never get here, but for completeness. */ + { + fprintf( stderr, + "%s (line %d): ERROR - Unknown address " + "family (%d).\n", + pgmName, + __LINE__, + sadr->sa_family ); + break; + } /* End DEFAULT case (unknown address family). */ + } /* End SWITCH on address family. */ + } /* End IF verbose mode. */ + /* + ** Send the time-of-day to the client. + */ + wBytes = timeLen; + while ( wBytes > 0 ) + { + do + { + count = sendto( desc[ idx ].fd, + timeStr, + wBytes, + 0, + sadr, /* Address & address length */ + sadrLen ); /* received in recvfrom(). */ + } while ( ( count < 0 ) && ( errno == EINTR ) ); + CHK( count ); /* Check for a bona fide error. */ + wBytes -= count; + } /* End WHILE there is data to send. */ + } /* End ELSE a UDP datagram is available. */ + desc[ idx ].revents = 0; /* Clear the returned poll events. */ + } /* End FOR each socket descriptor. */ + } /* End WHILE forever. */ +} /* End tod() */ +]]> +'Daytime' TCP Client Code +The TCP client code is found in file tod6tc.c (time-of-day IPv6 TCP client). Once built, the TCP client may be started using the following command syntax (assuming tod6tc is the executable file): +ARGUMENTS: +hostThe hostname or IP address (dotted decimal or colon-hex) of the remote host providing the service. Default is "localhost".serviceThe TCP service (or well-known port number) to which a connection attempt is made. Default is "daytime".OPTIONS: +-sThis option is only meaningful for IPv6 addresses, and is used to set the scope identifier (i.e. the network interface on which to establish the connection). Default is "eth0". If host is a scoped address, this option is ignored.-vTurn on verbose mode.The TCP client source code contained in tod6tc.c follows: + /* errno declaration and error codes. */ +#include /* if_nametoindex(3). */ +#include /* getaddrinfo(3) and associated definitions. */ +#include /* sockaddr_in and sockaddr_in6 definitions. */ +#include /* printf(3) et al. */ +#include /* exit(2). */ +#include /* String manipulation and memory functions. */ +#include /* Socket functions (socket(2), connect(2), etc). */ +#include /* getopt(3), read(2), etc. */ +/* +** Constants & macros. +*/ +#define DFLT_HOST "localhost" /* Default server name. */ +#define DFLT_SCOPE_ID "eth0" /* Default scope identifier. */ +#define DFLT_SERVICE "daytime" /* Default service name. */ +#define INVALID_DESC -1 /* Invalid file (socket) descriptor. */ +#define MAXBFRSIZE 256 /* Max bfr sz to read remote TOD. */ +#define VALIDOPTS "s:v" /* Valid command options. */ +/* +** Type definitions (for convenience). +*/ +typedef enum { false = 0, true } boolean; +typedef struct sockaddr_in sockaddr_in_t; +typedef struct sockaddr_in6 sockaddr_in6_t; +/* +** Prototypes for internal helper functions. +*/ +static int openSckt( const char *host, + const char *service, + unsigned int scopeId ); +static void tod( int sckt ); +/* +** Global (within this file only) data objects. +*/ +static const char *pgmName; /* Program name (w/o directory). */ +static boolean verbose = false; /* Verbose mode. */ +/* +** Usage macro. +*/ +#define USAGE \ + { \ + fprintf( stderr, \ + "Usage: %s [-v] [-s scope_id] [host [service]]\n", \ + pgmName ); \ + exit( 127 ); \ + } /* End USAGE macro. */ +/* +** This "macro" (even though it's really a function) is loosely based on the +** CHK() macro by Dr. V. Vinge (see server code). The status parameter is +** a boolean expression indicating the return code from one of the usual system +** calls that returns -1 on error. If a system call error occurred, an alert +** is written to stderr. It returns a boolean value indicating success/failure +** of the system call. +** +** Example: if ( !SYSCALL( "write", +** count = write( fd, bfr, size ) ) ) +** { +** // Error processing... but SYSCALL() will have already taken +** // care of dumping an error alert to stderr. +** } +*/ +static __inline boolean SYSCALL( const char *syscallName, + int lineNbr, + int status ) +{ + if ( ( status == -1 ) && verbose ) + { + fprintf( stderr, + "%s (line %d): System call failed ('%s') - %s.\n", + pgmName, + lineNbr, + syscallName, + strerror( errno ) ); + } + return status != -1; /* True if the system call was successful. */ +} /* End SYSCALL() */ +/****************************************************************************** +* Function: main +* +* Description: +* Connect to a remote time-of-day service and write the remote host's TOD to +* stdout. +* +* Parameters: +* The usual argc & argv parameters to a main() program. +* +* Return Value: +* This function always returns zero. +******************************************************************************/ +int main( int argc, + char *argv[ ] ) +{ + const char *host = DFLT_HOST; + int opt; + int sckt; + unsigned int scopeId = if_nametoindex( DFLT_SCOPE_ID ); + const char *service = DFLT_SERVICE; + /* + ** Determine the program name (w/o directory prefix). + */ + pgmName = (const char*) strrchr( argv[ 0 ], '/' ); + pgmName = pgmName == NULL ? argv[ 0 ] : pgmName+1; + /* + ** Process command line options. + */ + opterr = 0; /* Turns off "invalid option" error messages. */ + while ( ( opt = getopt( argc, argv, VALIDOPTS ) ) != -1 ) + { + switch ( opt ) + { + case 's': /* Scope identifier (IPv6 kluge). */ + { + scopeId = if_nametoindex( optarg ); + if ( scopeId == 0 ) + { + fprintf( stderr, + "%s: Unknown network interface (%s).\n", + pgmName, + optarg ); + USAGE; + } + break; + } + case 'v': /* Verbose mode. */ + { + verbose = true; + break; + } + default: + { + USAGE; + } + } /* End SWITCH on command option. */ + } /* End WHILE processing command options. */ + /* + ** Process command arguments. At the end of the above loop, optind is the + ** index of the first NON-option argv element. + */ + switch ( argc - optind ) + { + case 2: /* Both host & service are specified on the command line. */ + { + service = argv[ optind + 1 ]; + /***** Fall through *****/ + } + case 1: /* Host is specified on the command line. */ + { + host = argv[ optind ]; + /***** Fall through *****/ + } + case 0: /* Use default host & service. */ + { + break; + } + default: + { + USAGE; + } + } /* End SWITCH on number of command arguments. */ + /* + ** Open a connection to the indicated host/service. + ** + ** Note that if all three of the following conditions are met, then the + ** scope identifier remains unresolved at this point. + ** 1) The default network interface is unknown for some reason. + ** 2) The -s option was not used on the command line. + ** 3) An IPv6 "scoped address" was not specified for the hostname on the + ** command line. + ** If the above three conditions are met, then only an IPv4 socket can be + ** opened (connect(2) fails without the scope ID properly set for IPv6 + ** sockets). + */ + if ( ( sckt = openSckt( host, + service, + scopeId ) ) == INVALID_DESC ) + { + fprintf( stderr, + "%s: Sorry... a connection could not be established.\n", + pgmName ); + exit( 1 ); + } + /* + ** Get the remote time-of-day. + */ + tod( sckt ); + /* + ** Close the connection and terminate. + */ + (void) SYSCALL( "close", + __LINE__, + close( sckt ) ); + return 0; +} /* End main() */ +/****************************************************************************** +* Function: openSckt +* +* Description: +* Sets up a TCP connection to a remote server. Getaddrinfo(3) is used to +* perform lookup functions and can return multiple address records (i.e. a +* list of 'struct addrinfo' records). This function traverses the list and +* tries to establish a connection to the remote server. The function ends +* when either a connection has been established or all records in the list +* have been processed. +* +* Parameters: +* host - A pointer to a character string representing the hostname or IP +* address (IPv4 or IPv6) of the remote server. +* service - A pointer to a character string representing the service name or +* well-known port number. +* scopeId - For IPv6 sockets only. This is the index corresponding to the +* network interface on which to set up the connection. This +* parameter is ignored for IPv4 sockets or when an IPv6 "scoped +* address" is specified in 'host' (i.e. where the colon-hex +* network address is augmented with the scope ID). +* +* Return Value: +* Returns the socket descriptor for the connection, or INVALID_DESC if all +* address records have been processed and a connection could not be +* established. +******************************************************************************/ +static int openSckt( const char *host, + const char *service, + unsigned int scopeId ) +{ + struct addrinfo *ai; + int aiErr; + struct addrinfo *aiHead; + struct addrinfo hints; + sockaddr_in6_t *pSadrIn6; + int sckt; + /* + ** Initialize the 'hints' structure for getaddrinfo(3). + ** + ** Notice that the 'ai_family' field is set to PF_UNSPEC, indicating to + ** return both IPv4 and IPv6 address records for the host/service. Most of + ** the time, the user isn't going to care whether an IPv4 connection or an + ** IPv6 connection is established; the user simply wants to exchange data + ** with the remote host and doesn't care how it's done. Sometimes, however, + ** the user might want to explicitly specify the type of underlying socket. + ** It is left as an exercise for the motivated reader to add a command line + ** option allowing the user to specify the IP protocol, and then process the + ** list of addresses accordingly (it's not that difficult). + */ + memset( &hints, 0, sizeof( hints ) ); + hints.ai_family = PF_UNSPEC; /* IPv4 or IPv6 records (don't care). */ + hints.ai_socktype = SOCK_STREAM; /* Connection-oriented byte stream. */ + hints.ai_protocol = IPPROTO_TCP; /* TCP transport layer protocol only. */ + /* + ** Look up the host/service information. + */ + if ( ( aiErr = getaddrinfo( host, + service, + &hints, + &aiHead ) ) != 0 ) + { + fprintf( stderr, + "%s (line %d): ERROR - %s.\n", + pgmName, + __LINE__, + gai_strerror( aiErr ) ); + return INVALID_DESC; + } + /* + ** Go through the list and try to open a connection. Continue until either + ** a connection is established or the entire list is exhausted. + */ + for ( ai = aiHead, sckt = INVALID_DESC; + ( ai != NULL ) && ( sckt == INVALID_DESC ); + ai = ai->ai_next ) + { + /* + ** IPv6 kluge. Make sure the scope ID is set. + */ + if ( ai->ai_family == PF_INET6 ) + { + pSadrIn6 = (sockaddr_in6_t*) ai->ai_addr; + if ( pSadrIn6->sin6_scope_id == 0 ) + { + pSadrIn6->sin6_scope_id = scopeId; + } /* End IF the scope ID wasn't set. */ + } /* End IPv6 kluge. */ + /* + ** Display the address info for the remote host. + */ + if ( verbose ) + { + /* + ** Temporary character string buffers for host & service. + */ + char hostBfr[ NI_MAXHOST ]; + char servBfr[ NI_MAXSERV ]; + /* + ** Display the address information just fetched. Start with the + ** common (protocol-independent) stuff first. + */ + fprintf( stderr, + "Address info:\n" + " ai_flags = 0x%02X\n" + " ai_family = %d (PF_INET = %d, PF_INET6 = %d)\n" + " ai_socktype = %d (SOCK_STREAM = %d, SOCK_DGRAM = %d)\n" + " ai_protocol = %d (IPPROTO_TCP = %d, IPPROTO_UDP = %d)\n" + " ai_addrlen = %d (sockaddr_in = %d, " + "sockaddr_in6 = %d)\n", + ai->ai_flags, + ai->ai_family, + PF_INET, + PF_INET6, + ai->ai_socktype, + SOCK_STREAM, + SOCK_DGRAM, + ai->ai_protocol, + IPPROTO_TCP, + IPPROTO_UDP, + ai->ai_addrlen, + sizeof( struct sockaddr_in ), + sizeof( struct sockaddr_in6 ) ); + /* + ** Display the protocol-specific formatted address. + */ + getnameinfo( ai->ai_addr, + ai->ai_addrlen, + hostBfr, + sizeof( hostBfr ), + servBfr, + sizeof( servBfr ), + NI_NUMERICHOST | NI_NUMERICSERV ); + switch ( ai->ai_family ) + { + case PF_INET: /* IPv4 address record. */ + { + sockaddr_in_t *pSadrIn = (sockaddr_in_t*) ai->ai_addr; + fprintf( stderr, + " ai_addr = sin_family: %d (AF_INET = %d, " + "AF_INET6 = %d)\n" + " sin_addr: %s\n" + " sin_port: %s\n", + pSadrIn->sin_family, + AF_INET, + AF_INET6, + hostBfr, + servBfr ); + break; + } /* End CASE of IPv4 record. */ + case PF_INET6: /* IPv6 address record. */ + { + pSadrIn6 = (sockaddr_in6_t*) ai->ai_addr; + fprintf( stderr, + " ai_addr = sin6_family: %d (AF_INET = %d, " + "AF_INET6 = %d)\n" + " sin6_addr: %s\n" + " sin6_port: %s\n" + " sin6_flowinfo: %d\n" + " sin6_scope_id: %d\n", + pSadrIn6->sin6_family, + AF_INET, + AF_INET6, + hostBfr, + servBfr, + pSadrIn6->sin6_flowinfo, + pSadrIn6->sin6_scope_id ); + break; + } /* End CASE of IPv6 record. */ + default: /* Can never get here, but just for completeness. */ + { + fprintf( stderr, + "%s (line %d): ERROR - Unknown protocol family (%d).\n", + pgmName, + __LINE__, + ai->ai_family ); + break; + } /* End DEFAULT case (unknown protocol family). */ + } /* End SWITCH on protocol family. */ + } /* End IF verbose mode. */ + /* + ** Create a socket. + */ + if ( !SYSCALL( "socket", + __LINE__, + sckt = socket( ai->ai_family, + ai->ai_socktype, + ai->ai_protocol ) ) ) + { + sckt = INVALID_DESC; + continue; /* Try the next address record in the list. */ + } + /* + ** Connect to the remote host. + */ + if ( !SYSCALL( "connect", + __LINE__, + connect( sckt, + ai->ai_addr, + ai->ai_addrlen ) ) ) + { + (void) close( sckt ); /* Could use SYSCALL() again here, but why? */ + sckt = INVALID_DESC; + continue; /* Try the next address record in the list. */ + } + } /* End FOR each address record returned by getaddrinfo(3). */ + /* + ** Clean up & return. + */ + freeaddrinfo( aiHead ); + return sckt; +} /* End openSckt() */ +/****************************************************************************** +* Function: tod +* +* Description: +* Receive the time-of-day from the remote server and write it to stdout. +* +* Parameters: +* sckt - The socket descriptor for the connection. +* +* Return Value: None. +******************************************************************************/ +static void tod( int sckt ) +{ + char bfr[ MAXBFRSIZE+1 ]; + int inBytes; + /* + ** The client never sends anything, so shut down the write side of the + ** connection. + */ + if ( !SYSCALL( "shutdown", + __LINE__, + shutdown( sckt, SHUT_WR ) ) ) + { + return; + } + /* + ** Read the time-of-day from the remote host. + */ + do + { + if ( !SYSCALL( "read", + __LINE__, + inBytes = read( sckt, + bfr, + MAXBFRSIZE ) ) ) + { + return; + } + bfr[ inBytes ] = '\0'; /* Null-terminate the received string. */ + fputs( bfr, stdout ); /* Null string if EOF (inBytes == 0). */ + } while ( inBytes > 0 ); + fflush( stdout ); +} /* End tod() */ +]]> +'Daytime' UDP Client Code +The UDP client code is found in file tod6uc.c (time-of-day IPv6 UDP client). It is almost an exact duplicate of the TCP client (and in fact was derived from it), but is included in this HowTo for completeness. Once built, the UDP client may be started using the following command syntax (assuming tod6uc is the executable file): +ARGUMENTS: +hostThe hostname or IP address (dotted decimal or colon-hex) of the remote host providing the service. Default is "localhost".serviceThe UDP service (or well-known port number) to which datagrams are sent. Default is "daytime".OPTIONS: +-sThis option is only meaningful for IPv6 addresses, and is used to set the scope identifier (i.e. the network interface on which to exchange datagrams). Default is "eth0". If host is a scoped address, this option is ignored.-vTurn on verbose mode.The UDP client source code contained in tod6uc.c follows: + /* errno declaration and error codes. */ +#include /* if_nametoindex(3). */ +#include /* getaddrinfo(3) and associated definitions. */ +#include /* sockaddr_in and sockaddr_in6 definitions. */ +#include /* printf(3) et al. */ +#include /* exit(2). */ +#include /* String manipulation and memory functions. */ +#include /* Socket functions (socket(2), connect(2), etc). */ +#include /* getopt(3), recvfrom(2), sendto(2), etc. */ +/* +** Constants & macros. +*/ +#define DFLT_HOST "localhost" /* Default server name. */ +#define DFLT_SCOPE_ID "eth0" /* Default scope identifier. */ +#define DFLT_SERVICE "daytime" /* Default service name. */ +#define INVALID_DESC -1 /* Invalid file (socket) descriptor. */ +#define MAXBFRSIZE 256 /* Max bfr sz to read remote TOD. */ +#define VALIDOPTS "s:v" /* Valid command options. */ +/* +** Type definitions (for convenience). +*/ +typedef enum { false = 0, true } boolean; +typedef struct sockaddr_in sockaddr_in_t; +typedef struct sockaddr_in6 sockaddr_in6_t; +/* +** Prototypes for internal helper functions. +*/ +static int openSckt( const char *host, + const char *service, + unsigned int scopeId ); +static void tod( int sckt ); +/* +** Global (within this file only) data objects. +*/ +static const char *pgmName; /* Program name (w/o directory). */ +static boolean verbose = false; /* Verbose mode. */ +/* +** Usage macro. +*/ +#define USAGE \ + { \ + fprintf( stderr, \ + "Usage: %s [-v] [-s scope_id] [host [service]]\n", \ + pgmName ); \ + exit( 127 ); \ + } /* End USAGE macro. */ +/* +** This "macro" (even though it's really a function) is loosely based on the +** CHK() macro by Dr. V. Vinge (see server code). The status parameter is +** a boolean expression indicating the return code from one of the usual system +** calls that returns -1 on error. If a system call error occurred, an alert +** is written to stderr. It returns a boolean value indicating success/failure +** of the system call. +** +** Example: if ( !SYSCALL( "write", +** count = write( fd, bfr, size ) ) ) +** { +** // Error processing... but SYSCALL() will have already taken +** // care of dumping an error alert to stderr. +** } +*/ +static __inline boolean SYSCALL( const char *syscallName, + int lineNbr, + int status ) +{ + if ( ( status == -1 ) && verbose ) + { + fprintf( stderr, + "%s (line %d): System call failed ('%s') - %s.\n", + pgmName, + lineNbr, + syscallName, + strerror( errno ) ); + } + return status != -1; /* True if the system call was successful. */ +} /* End SYSCALL() */ +/****************************************************************************** +* Function: main +* +* Description: +* Connect to a remote time-of-day service and write the remote host's TOD to +* stdout. +* +* Parameters: +* The usual argc & argv parameters to a main() program. +* +* Return Value: +* This function always returns zero. +******************************************************************************/ +int main( int argc, + char *argv[ ] ) +{ + const char *host = DFLT_HOST; + int opt; + int sckt; + unsigned int scopeId = if_nametoindex( DFLT_SCOPE_ID ); + const char *service = DFLT_SERVICE; + /* + ** Determine the program name (w/o directory prefix). + */ + pgmName = (const char*) strrchr( argv[ 0 ], '/' ); + pgmName = pgmName == NULL ? argv[ 0 ] : pgmName+1; + /* + ** Process command line options. + */ + opterr = 0; /* Turns off "invalid option" error messages. */ + while ( ( opt = getopt( argc, argv, VALIDOPTS ) ) != -1 ) + { + switch ( opt ) + { + case 's': /* Scope identifier (IPv6 kluge). */ + { + scopeId = if_nametoindex( optarg ); + if ( scopeId == 0 ) + { + fprintf( stderr, + "%s: Unknown network interface (%s).\n", + pgmName, + optarg ); + USAGE; + } + break; + } + case 'v': /* Verbose mode. */ + { + verbose = true; + break; + } + default: + { + USAGE; + } + } /* End SWITCH on command option. */ + } /* End WHILE processing command options. */ + /* + ** Process command arguments. At the end of the above loop, optind is the + ** index of the first NON-option argv element. + */ + switch ( argc - optind ) + { + case 2: /* Both host & service are specified on the command line. */ + { + service = argv[ optind + 1 ]; + /***** Fall through *****/ + } + case 1: /* Host is specified on the command line. */ + { + host = argv[ optind ]; + /***** Fall through *****/ + } + case 0: /* Use default host & service. */ + { + break; + } + default: + { + USAGE; + } + } /* End SWITCH on number of command arguments. */ + /* + ** Open a connection to the indicated host/service. + ** + ** Note that if all three of the following conditions are met, then the + ** scope identifier remains unresolved at this point. + ** 1) The default network interface is unknown for some reason. + ** 2) The -s option was not used on the command line. + ** 3) An IPv6 "scoped address" was not specified for the hostname on the + ** command line. + ** If the above three conditions are met, then only an IPv4 socket can be + ** opened (connect(2) fails without the scope ID properly set for IPv6 + ** sockets). + */ + if ( ( sckt = openSckt( host, + service, + scopeId ) ) == INVALID_DESC ) + { + fprintf( stderr, + "%s: Sorry... a connectionless socket could " + "not be set up.\n", + pgmName ); + exit( 1 ); + } + /* + ** Get the remote time-of-day. + */ + tod( sckt ); + /* + ** Close the connection and terminate. + */ + (void) SYSCALL( "close", + __LINE__, + close( sckt ) ); + return 0; +} /* End main() */ +/****************************************************************************** +* Function: openSckt +* +* Description: +* Sets up a UDP socket to a remote server. Getaddrinfo(3) is used to +* perform lookup functions and can return multiple address records (i.e. a +* list of 'struct addrinfo' records). This function traverses the list and +* tries to establish a connection to the remote server. The function ends +* when either a connection has been established or all records in the list +* have been processed. +* +* Parameters: +* host - A pointer to a character string representing the hostname or IP +* address (IPv4 or IPv6) of the remote server. +* service - A pointer to a character string representing the service name or +* well-known port number. +* scopeId - For IPv6 sockets only. This is the index corresponding to the +* network interface on which to exchange datagrams. This +* parameter is ignored for IPv4 sockets or when an IPv6 "scoped +* address" is specified in 'host' (i.e. where the colon-hex +* network address is augmented with the scope ID). +* +* Return Value: +* Returns the socket descriptor for the connection, or INVALID_DESC if all +* address records have been processed and a socket could not be initialized. +******************************************************************************/ +static int openSckt( const char *host, + const char *service, + unsigned int scopeId ) +{ + struct addrinfo *ai; + int aiErr; + struct addrinfo *aiHead; + struct addrinfo hints; + sockaddr_in6_t *pSadrIn6; + int sckt; + /* + ** Initialize the 'hints' structure for getaddrinfo(3). + ** + ** Notice that the 'ai_family' field is set to PF_UNSPEC, indicating to + ** return both IPv4 and IPv6 address records for the host/service. Most of + ** the time, the user isn't going to care whether an IPv4 connection or an + ** IPv6 connection is established; the user simply wants to exchange data + ** with the remote host and doesn't care how it's done. Sometimes, however, + ** the user might want to explicitly specify the type of underlying socket. + ** It is left as an exercise for the motivated reader to add a command line + ** option allowing the user to specify the IP protocol, and then process the + ** list of addresses accordingly (it's not that difficult). + */ + memset( &hints, 0, sizeof( hints ) ); + hints.ai_family = PF_UNSPEC; /* IPv4 or IPv6 records (don't care). */ + hints.ai_socktype = SOCK_DGRAM; /* Connectionless communication. */ + hints.ai_protocol = IPPROTO_UDP; /* UDP transport layer protocol only. */ + /* + ** Look up the host/service information. + */ + if ( ( aiErr = getaddrinfo( host, + service, + &hints, + &aiHead ) ) != 0 ) + { + fprintf( stderr, + "%s (line %d): ERROR - %s.\n", + pgmName, + __LINE__, + gai_strerror( aiErr ) ); + return INVALID_DESC; + } + /* + ** Go through the list and try to open a connection. Continue until either + ** a connection is established or the entire list is exhausted. + */ + for ( ai = aiHead, sckt = INVALID_DESC; + ( ai != NULL ) && ( sckt == INVALID_DESC ); + ai = ai->ai_next ) + { + /* + ** IPv6 kluge. Make sure the scope ID is set. + */ + if ( ai->ai_family == PF_INET6 ) + { + pSadrIn6 = (sockaddr_in6_t*) ai->ai_addr; + if ( pSadrIn6->sin6_scope_id == 0 ) + { + pSadrIn6->sin6_scope_id = scopeId; + } /* End IF the scope ID wasn't set. */ + } /* End IPv6 kluge. */ + /* + ** Display the address info for the remote host. + */ + if ( verbose ) + { + /* + ** Temporary character string buffers for host & service. + */ + char hostBfr[ NI_MAXHOST ]; + char servBfr[ NI_MAXSERV ]; + /* + ** Display the address information just fetched. Start with the + ** common (protocol-independent) stuff first. + */ + fprintf( stderr, + "Address info:\n" + " ai_flags = 0x%02X\n" + " ai_family = %d (PF_INET = %d, PF_INET6 = %d)\n" + " ai_socktype = %d (SOCK_STREAM = %d, SOCK_DGRAM = %d)\n" + " ai_protocol = %d (IPPROTO_TCP = %d, IPPROTO_UDP = %d)\n" + " ai_addrlen = %d (sockaddr_in = %d, " + "sockaddr_in6 = %d)\n", + ai->ai_flags, + ai->ai_family, + PF_INET, + PF_INET6, + ai->ai_socktype, + SOCK_STREAM, + SOCK_DGRAM, + ai->ai_protocol, + IPPROTO_TCP, + IPPROTO_UDP, + ai->ai_addrlen, + sizeof( struct sockaddr_in ), + sizeof( struct sockaddr_in6 ) ); + /* + ** Display the protocol-specific formatted address. + */ + getnameinfo( ai->ai_addr, + ai->ai_addrlen, + hostBfr, + sizeof( hostBfr ), + servBfr, + sizeof( servBfr ), + NI_NUMERICHOST | NI_NUMERICSERV ); + switch ( ai->ai_family ) + { + case PF_INET: /* IPv4 address record. */ + { + sockaddr_in_t *pSadrIn = (sockaddr_in_t*) ai->ai_addr; + fprintf( stderr, + " ai_addr = sin_family: %d (AF_INET = %d, " + "AF_INET6 = %d)\n" + " sin_addr: %s\n" + " sin_port: %s\n", + pSadrIn->sin_family, + AF_INET, + AF_INET6, + hostBfr, + servBfr ); + break; + } /* End CASE of IPv4 record. */ + case PF_INET6: /* IPv6 address record. */ + { + pSadrIn6 = (sockaddr_in6_t*) ai->ai_addr; + fprintf( stderr, + " ai_addr = sin6_family: %d (AF_INET = %d, " + "AF_INET6 = %d)\n" + " sin6_addr: %s\n" + " sin6_port: %s\n" + " sin6_flowinfo: %d\n" + " sin6_scope_id: %d\n", + pSadrIn6->sin6_family, + AF_INET, + AF_INET6, + hostBfr, + servBfr, + pSadrIn6->sin6_flowinfo, + pSadrIn6->sin6_scope_id ); + break; + } /* End CASE of IPv6 record. */ + default: /* Can never get here, but just for completeness. */ + { + fprintf( stderr, + "%s (line %d): ERROR - Unknown protocol family (%d).\n", + pgmName, + __LINE__, + ai->ai_family ); + break; + } /* End DEFAULT case (unknown protocol family). */ + } /* End SWITCH on protocol family. */ + } /* End IF verbose mode. */ + /* + ** Create a socket. + */ + if ( !SYSCALL( "socket", + __LINE__, + sckt = socket( ai->ai_family, + ai->ai_socktype, + ai->ai_protocol ) ) ) + { + sckt = INVALID_DESC; + continue; /* Try the next address record in the list. */ + } + /* + ** Set the target destination for the remote host on this socket. That + ** is, this socket only communicates with the specified host. + */ + if ( !SYSCALL( "connect", + __LINE__, + connect( sckt, + ai->ai_addr, + ai->ai_addrlen ) ) ) + { + (void) close( sckt ); /* Could use SYSCALL() again here, but why? */ + sckt = INVALID_DESC; + continue; /* Try the next address record in the list. */ + } + } /* End FOR each address record returned by getaddrinfo(3). */ + /* + ** Clean up & return. + */ + freeaddrinfo( aiHead ); + return sckt; +} /* End openSckt() */ +/****************************************************************************** +* Function: tod +* +* Description: +* Receive the time-of-day from the remote server and write it to stdout. +* +* Parameters: +* sckt - The socket descriptor for the connection. +* +* Return Value: None. +******************************************************************************/ +static void tod( int sckt ) +{ + char bfr[ MAXBFRSIZE+1 ]; + int inBytes; + /* + ** Send a datagram to the server to wake it up. The content isn't + ** important, but something must be sent to let it know we want the TOD. + */ + if ( !SYSCALL( "write", + __LINE__, + write( sckt, "Are you there?", 14 ) ) ) + { + return; + } + /* + ** Read the time-of-day from the remote host. + */ + if ( !SYSCALL( "read", + __LINE__, + inBytes = read( sckt, + bfr, + MAXBFRSIZE ) ) ) + { + return; + } + bfr[ inBytes ] = '\0'; /* Null-terminate the received string. */ + fputs( bfr, stdout ); /* Null string if EOF (inBytes == 0). */ + fflush( stdout ); +} /* End tod() */ +]]>
+Other programming languages -C -(please contribute) JAVA -Sun Java version 1.4 and 1.5 (5.0) are IPv6 enabled, see Inet6Address (1.5/5.0) class. Hints are available in the Networking IPv6 User Guide for JDK/JRE 1.4 and 1.5 (5.0).
+Sun Java versions since 1.4 are IPv6 enabled, see e.g. Inet6Address (1.5/5.0) class. Hints are available in the Networking IPv6 User Guide for JDK/JRE 1.4 and 1.5 (5.0). +Perl +As of May 2007 it's not known that the Perl core itself already supports IPv6. It can be added by using following modules: +Socket6Anyway, some other modules exist for/with IPv6 support (e.g. Net::IP), search for “IPv6” on http://search.cpan.org/. <!-- anchor id="chapter-interoperability" -->Interoperability The TAHI Project checks the interoperability of different operating systems regarding the implementation of IPv6 features. Linux kernel already got the IPv6 Ready Logo Phase 1. <!-- anchor id="chapter-information" -->Further information and URLs @@ -2577,7 +4462,7 @@ SourceForge: Project Info - DeepSpace6 / (Not only) Linux IPv6 Portal - Italy (Mirror)IPv6-HowTo for Linux by Peter Bieringer - Germany, and his Bieringer / IPv6 - software archiveLinux+IPv6 status by Peter Bieringer - Germany (going obsolete)DeepSpace6 / IPv6 Status Page - Italy (Mirror) (will superseed upper one)USAGI project - Japan, and their USAGI project - software archiveLinux Optimized Link State Routing Protocol (OLSR) IPv6 HOWTO Linux related per distribution -PLDPLD Linux Distribution ("market leader" in containing IPv6 enabled packages)Red HatRed Hat Enterprise Linux, Pekka Savola's IPv6 packagesFedora CoreFedora Core LinuxDebianDebian Linux, Craig Small's IPv6 information and statusNovell/SuSENovell/SuSE LinuxMandrivaMandrivaFor more see the IPv6+Linux Status Distributions page. +PLDPLD Linux Distribution (“market leader” in containing IPv6 enabled packages)Red HatRed Hat Enterprise Linux, Pekka Savola's IPv6 packagesFedora CoreFedora Core LinuxDebianDebian Linux, Craig Small's IPv6 information and statusNovell/SuSENovell/SuSE LinuxMandrivaMandrivaFor more see the IPv6+Linux Status Distributions page. General IPv6.org6boneUK IPv6 Resource Centre - UKWIDE project - JapanSWITCH IPv6 Pilot - SwitzerlandIPv6 Corner of Hubert Feyrer - GermanyIPv6 Forum - a world-wide consortium of leading Internet vendors, Research & Education Networks...Playground.sun.com / IPv6 Info Page - maintained by Robert Hinden, Nokia. Get any information about IPv6, from overviews, through RFCs & drafts, to implementations (including availability of stacks on various platforms & source code for IPv6 stacks).6INIT - IPv6 Internet Initiative - an EU Fifth Framework Project under the IST Programme.IPv6 Task Force (European Union)6init - IPv6 INternet IniTiative IP Next Generation OverviewIPv6: The New Version of the Internet Protocol, by Steve Deering.IPv6: The Next Generation Internet Protocol, by Gary C. Kessler. IPv6: Next Generation Internet Protocol - 3Cominternet || site and internet2 Working Group - Presentation (HTML + PPT) from IPv6 Workshops: (Stateless Autoconfiguration, IPv6 Addressing, USAGI, Provider Independent IPv6 Addressing and other topics).NetworkWorldFusion: Search / Doc Finder: searched for IPv6 (102 documents found 22.12.2002)The Register (Search for IPv6 will result in 30 documents, 22.12.2002)ZDNet Search for IPv6TechTarget Search for IPv6IPv6 & TCP Resources ListKlingon IPv6 tools, Klingon IPv6 tools (native IPv6 only access): IPv6 firewall examples, bandwith testing and portscannerSomething missing? Suggestions are welcome! @@ -2602,14 +4487,14 @@ SourceForge: Project Info - Carl's Australian IPv6 Pages (old content) Belgium - -Brasil +Suggestions are welcome! +Brazil IPv6 Summit in BrazilIPv6 do Brasil China - +Suggestions are welcome! Czech - +Suggestions are welcome! Germany OpenBC / IPv6 @@ -2793,7 +4678,6 @@ Press Release: Hurricane E <!-- anchor id="information-maillists" -->Maillists Lists of maillists are available at: DeepSpace6 / Mailling ListsMajor Mailinglists are listed in following table: -. @@ -2915,13 +4799,13 @@ Publisher: MarketResearch.com; ISBN B00006334Y; (November 1, 2001) Versions x.y.z are work-in-progress and published as LyX and SGML file on CVS. Because Deep Space 6 mirrors these SGML files and generate independend from TLDP public versions, this versions will show up there and also on its mirrors. Releases 0.x -0.512006-11-08/PB: remove broken URLs, add a new book (credits to Bryan Vukich)0.50.22006-10-25/PB: fix typo in dhcp6 section (credits to Michele Ferritto)0.50.12006-09-23/PB: add some URLs0.502006-08-24/PB: check RFC URLs, fix URL to Chinese translation, finalize for publishing0.49.52006-08-23/PB: fix/remove broken URLs0.49.42006-08-21/PB: some review, update and enhancement of the content, replace old 6bone example addresses with the current defined ones.0.49.32006-08-20/PB: fix bug in maillist entries, 'mobility' is now a separate chapter0.49.22006-08-20/PB: update and cleanup of maillist entries0.49.12006-06-13/PB: major update of mobility section (contributed by Benjamin Thery)0.492005-10-03/PB: add configuration hints for DHCPv6, major broken URL cleanup (credits to Necdet Yucel)0.48.12005-01-15/PB: minor fixes0.482005-01-11/PB: grammar check and minor review of IPv6 IPsec section0.47.12005-01-01/PB: add information and examples about IPv6 IPsec, add some URLs0.472004-08-30/PB: add some notes about proftpd, vsftpd and other daemons, add some URLs, minor fixes, update status of Spanish translation0.46.42004-07-19/PB: minor fixes0.46.32004-06-23/PB: add note about started Greek translation, replace Taiwanese with Chinese for related translation0.46.22004-05-22/PB: minor fixes0.46.12004-04-18/PB: minor fixes0.462004-03-04/PB: announce Italian translation, add information about DHCPv6, minor updates0.45.12004-01-12/PB: add note about the official example address space0.452004-01-11/PB: minor fixes, add/fix some URLs, some extensions0.44.22003-10-30/PB: fix some copy&paste text bugs0.44.12003-10-19/PB: add note about start of Italian translation0.442003-08-15/PB: fix URLs, add hint on tcp_wrappers (about broken notation in some versions) and Apache20.43.42003-07-26/PB: fix URL, add archive URL for maillist users at ipv6.org, add some ds6 URLs0.43.32003-06-19/PB: fix typos0.43.22003-06-11/PB: fix URL0.43.12003-06-07/PB: fix some URLs, fix credits, add some notes at IPsec0.432003-06-05/PB: add some notes about configuration in SuSE Linux, add URL of French translation0.422003-05-09/PB: minor fixes, announce French translation0.41.42003-05-02/PB: Remove a broken URL, update some others.0.41.32003-04-23/PB: Minor fixes, remove a broken URL, fix URL to Taiwanese translation0.41.22003-04-13/PB: Fix some typos, add a note about a French translation is in progress0.41.12003-03-31/PB: Remove a broken URL, fix another0.412003-03-22/PB: Add URL of German translation0.40.22003-02-27/PB: Fix a misaddressed URL0.40.12003-02-12/PB: Add Debian-Linux-Configuration, add a minor note on translations0.402003-02-10/PB: Announcing available German version0.39.22003-02-10/GK: Minor syntax and spelling fixes0.39.12003-01-09/PB: fix an URL (draft adopted to an RFC)0.392003-01-13/PB: fix a bug (forgotten 'link" on "ip link set" (credits to Yaniv Kaul)0.38.12003-01-09/PB: a minor fix0.382003-01-06/PB: minor fixes0.37.12003-01-05/PB: minor updates0.372002-12-31/GK: 270 new links added (searched in 1232 SearchEngines) in existing and 53 new (sub)sections0.36.12002-12-20/PB: Minor fixes0.362002-12-16/PB: Check of and fix broken links (credits to Georg Käfer), some spelling fixes0.352002-12-11/PB: Some fixes and extensions0.34.12002-11-25/PB: Some fixes (e.g. broken linuxdoc URLs)0.342002-11-19/PB: Add information about German translation (work in progress), some fixes, create a small shortcut explanation list, extend "used terms" and add two German books0.332002-11-18/PB: Fix broken RFC-URLs, add parameter ttl on 6to4 tunnel setup example0.322002-11-03/PB: Add information about Taiwanese translation0.31.12002-10-06/PB: Add another maillist0.312002-09-29/PB: Extend information in proc-filesystem entries0.302002-09-27/PB: Add some maillists0.292002-09-18/PB: Update statement about nmap (triggered by Fyodor)0.28.12002-09-16/PB: Add note about ping6 to multicast addresses, add some labels0.282002-08-17/PB: Fix broken LDP/CVS links, add info about Polish translation, add URL of the IPv6 Address Oracle0.272002-08-10/PB: Some minor updates0.26.22002-07-15/PB: Add information neighbor discovery, split of firewalling (got some updates) and security into extra chapters0.26.12002-07-13/PB: Update nmap/IPv6 information0.262002-07-13/PB: Fill /proc-filesystem chapter, update DNS information about depricated A6/DNAME, change P-t-P tunnel setup to use of "ip" only0.25.22002-07-11/PB: Minor spelling fixes0.25.12002-06-23/PB: Minor spelling and other fixes0.252002-05-16/PB: Cosmetic fix for 2\^{ }128, thanks to José Abílio Oliveira Matos for help with LyX0.242002-05-02/PB: Add entries in URL list, minor spelling fixes0.232002-03-27/PB: Add entries in URL list and at maillists, add a label and minor information about IPv6 on RHL0.222002-03-04/PB: Add info about 6to4 support in kernel series 2.2.x and add an entry in URL list and at maillists0.212002-02-26/PB: Migrate next grammar checks submitted by John Ronan 0.20.42002-02-21/PB: Migrate more grammar checks submitted by John Ronan, add some additional hints at DNS section 0.20.32002-02-12/PB: Migrate a minor grammar check patch submitted by John Ronan0.20.22002-02-05/PB: Add mipl to maillist table0.20.12002-01-31/PB: Add a hint how to generate 6to4 addresses0.202002-01-30/PB: Add a hint about default route problem, some minor updates0.19.22002-01-29/PB: Add many new URLs0.19.12002-01-27/PB: Add some forgotten URLs0.192002-01-25/PB: Add two German books, fix quote entinities in exported SGML code0.18.22002-01-23/PB: Add a FAQ on the program chapter0.18.12002-01-23/PB: Move "the end" to the end, add USAGI to maillists0.182002-01-22/PB: Fix bugs in explanation of multicast address types0.17.22002-01-22/PB: Cosmetic fix double existing text in history (at 0.16), move all credits to the end of the document0.17.12002-01-20/PB: Add a reference, fix URL text in online-test-tools0.172002-01-19/PB: Add some forgotten information and URLs about global IPv6 addresses0.162002-01-19/PB: Minor fixes, remove "bold" and "emphasize" formats on code lines, fix "too long unwrapped code lines" using selfmade utility, extend list of URLs.0.152002-01-15/PB: Fix bug in addresstype/anycast, move content related credits to end of document0.142002-01-14/PB: Minor review at all, new chapter "debugging", review "addresses", spell checking, grammar checking (from beginning to 3.4.1) by Martin Krafft, add tcpdump examples, copy firewalling/netfilter6 from IPv6+Linux-HowTo, minor enhancements0.132002-01-05/PB: Add example BIND9/host, move revision history to end of document, minor extensions0.122002-01-03/PB: Merge review of David Ranch0.112002-01-02/PB: Spell checking and merge review of Pekka Savola0.102002-01-02/PB: First public release of chapter 1 +0.602007-05-29/PB: import major contribution to Programming using C-API written by John Wenker, minor fixes0.522007-05-23/PB: update firewalling chapter, improve document for proper SGML validation, minor bugfixes0.512006-11-08/PB: remove broken URLs, add a new book (credits to Bryan Vukich)0.50.22006-10-25/PB: fix typo in dhcp6 section (credits to Michele Ferritto)0.50.12006-09-23/PB: add some URLs0.502006-08-24/PB: check RFC URLs, fix URL to Chinese translation, finalize for publishing0.49.52006-08-23/PB: fix/remove broken URLs0.49.42006-08-21/PB: some review, update and enhancement of the content, replace old 6bone example addresses with the current defined ones.0.49.32006-08-20/PB: fix bug in maillist entries, 'mobility' is now a separate chapter0.49.22006-08-20/PB: update and cleanup of maillist entries0.49.12006-06-13/PB: major update of mobility section (contributed by Benjamin Thery)0.492005-10-03/PB: add configuration hints for DHCPv6, major broken URL cleanup (credits to Necdet Yucel)0.48.12005-01-15/PB: minor fixes0.482005-01-11/PB: grammar check and minor review of IPv6 IPsec section0.47.12005-01-01/PB: add information and examples about IPv6 IPsec, add some URLs0.472004-08-30/PB: add some notes about proftpd, vsftpd and other daemons, add some URLs, minor fixes, update status of Spanish translation0.46.42004-07-19/PB: minor fixes0.46.32004-06-23/PB: add note about started Greek translation, replace Taiwanese with Chinese for related translation0.46.22004-05-22/PB: minor fixes0.46.12004-04-18/PB: minor fixes0.462004-03-04/PB: announce Italian translation, add information about DHCPv6, minor updates0.45.12004-01-12/PB: add note about the official example address space0.452004-01-11/PB: minor fixes, add/fix some URLs, some extensions0.44.22003-10-30/PB: fix some copy&paste text bugs0.44.12003-10-19/PB: add note about start of Italian translation0.442003-08-15/PB: fix URLs, add hint on tcp_wrappers (about broken notation in some versions) and Apache20.43.42003-07-26/PB: fix URL, add archive URL for maillist users at ipv6.org, add some ds6 URLs0.43.32003-06-19/PB: fix typos0.43.22003-06-11/PB: fix URL0.43.12003-06-07/PB: fix some URLs, fix credits, add some notes at IPsec0.432003-06-05/PB: add some notes about configuration in SuSE Linux, add URL of French translation0.422003-05-09/PB: minor fixes, announce French translation0.41.42003-05-02/PB: Remove a broken URL, update some others.0.41.32003-04-23/PB: Minor fixes, remove a broken URL, fix URL to Taiwanese translation0.41.22003-04-13/PB: Fix some typos, add a note about a French translation is in progress0.41.12003-03-31/PB: Remove a broken URL, fix another0.412003-03-22/PB: Add URL of German translation0.40.22003-02-27/PB: Fix a misaddressed URL0.40.12003-02-12/PB: Add Debian-Linux-Configuration, add a minor note on translations0.402003-02-10/PB: Announcing available German version0.39.22003-02-10/GK: Minor syntax and spelling fixes0.39.12003-01-09/PB: fix an URL (draft adopted to an RFC)0.392003-01-13/PB: fix a bug (forgotten 'link” on “ip link set” (credits to Yaniv Kaul)0.38.12003-01-09/PB: a minor fix0.382003-01-06/PB: minor fixes0.37.12003-01-05/PB: minor updates0.372002-12-31/GK: 270 new links added (searched in 1232 SearchEngines) in existing and 53 new (sub)sections0.36.12002-12-20/PB: Minor fixes0.362002-12-16/PB: Check of and fix broken links (credits to Georg Käfer), some spelling fixes0.352002-12-11/PB: Some fixes and extensions0.34.12002-11-25/PB: Some fixes (e.g. broken linuxdoc URLs)0.342002-11-19/PB: Add information about German translation (work in progress), some fixes, create a small shortcut explanation list, extend “used terms” and add two German books0.332002-11-18/PB: Fix broken RFC-URLs, add parameter ttl on 6to4 tunnel setup example0.322002-11-03/PB: Add information about Taiwanese translation0.31.12002-10-06/PB: Add another maillist0.312002-09-29/PB: Extend information in proc-filesystem entries0.302002-09-27/PB: Add some maillists0.292002-09-18/PB: Update statement about nmap (triggered by Fyodor)0.28.12002-09-16/PB: Add note about ping6 to multicast addresses, add some labels0.282002-08-17/PB: Fix broken LDP/CVS links, add info about Polish translation, add URL of the IPv6 Address Oracle0.272002-08-10/PB: Some minor updates0.26.22002-07-15/PB: Add information neighbor discovery, split of firewalling (got some updates) and security into extra chapters0.26.12002-07-13/PB: Update nmap/IPv6 information0.262002-07-13/PB: Fill /proc-filesystem chapter, update DNS information about depricated A6/DNAME, change P-t-P tunnel setup to use of “ip” only0.25.22002-07-11/PB: Minor spelling fixes0.25.12002-06-23/PB: Minor spelling and other fixes0.252002-05-16/PB: Cosmetic fix for 2\^{ }128, thanks to José Abílio Oliveira Matos for help with LyX0.242002-05-02/PB: Add entries in URL list, minor spelling fixes0.232002-03-27/PB: Add entries in URL list and at maillists, add a label and minor information about IPv6 on RHL0.222002-03-04/PB: Add info about 6to4 support in kernel series 2.2.x and add an entry in URL list and at maillists0.212002-02-26/PB: Migrate next grammar checks submitted by John Ronan 0.20.42002-02-21/PB: Migrate more grammar checks submitted by John Ronan, add some additional hints at DNS section 0.20.32002-02-12/PB: Migrate a minor grammar check patch submitted by John Ronan0.20.22002-02-05/PB: Add mipl to maillist table0.20.12002-01-31/PB: Add a hint how to generate 6to4 addresses0.202002-01-30/PB: Add a hint about default route problem, some minor updates0.19.22002-01-29/PB: Add many new URLs0.19.12002-01-27/PB: Add some forgotten URLs0.192002-01-25/PB: Add two German books, fix quote entinities in exported SGML code0.18.22002-01-23/PB: Add a FAQ on the program chapter0.18.12002-01-23/PB: Move “the end” to the end, add USAGI to maillists0.182002-01-22/PB: Fix bugs in explanation of multicast address types0.17.22002-01-22/PB: Cosmetic fix double existing text in history (at 0.16), move all credits to the end of the document0.17.12002-01-20/PB: Add a reference, fix URL text in online-test-tools0.172002-01-19/PB: Add some forgotten information and URLs about global IPv6 addresses0.162002-01-19/PB: Minor fixes, remove “bold” and “emphasize” formats on code lines, fix “too long unwrapped code lines” using selfmade utility, extend list of URLs.0.152002-01-15/PB: Fix bug in addresstype/anycast, move content related credits to end of document0.142002-01-14/PB: Minor review at all, new chapter “debugging”, review “addresses”, spell checking, grammar checking (from beginning to 3.4.1) by Martin Krafft, add tcpdump examples, copy firewalling/netfilter6 from IPv6+Linux-HowTo, minor enhancements0.132002-01-05/PB: Add example BIND9/host, move revision history to end of document, minor extensions0.122002-01-03/PB: Merge review of David Ranch0.112002-01-02/PB: Spell checking and merge review of Pekka Savola0.102002-01-02/PB: First public release of chapter 1 <!-- anchor id="credits" -->Credits The quickest way to be added to this nice list is to send bug fixes, corrections, and/or updates to me ;-). If you want to do a major review, you can use the native LyX file (see original source) and send diffs against it, because diffs against SGML don't help too much. <!-- anchor id="major-credits" -->Major credits -David Ranch <dranch at trinnet dot net>: For encouraging me to write this HOWTO, his editorial comments on the first few revisions, and his contributions to various IPv6 testing results on my IPv6 web site. Also for his major reviews and suggestions.Pekka Savola <pekkas at netcore dot fi>: For major reviews, input and suggestions.Martin F. Krafft <madduck at madduck dot net>: For grammar checks and general reviewing of the document.John Ronan <j0n at tssg dot wit dot ie>: For grammar checks.Georg Käfer <gkaefer at gmx dot at>: For detection of no proper PDF creation (fixed now by LDP maintainer Greg Ferguson), input for German books, big list of URLs, checking all URLs, many more suggestions, corrections and contributions, and the German translationMichel Boucey <mboucey at free dot fr>: Finding typos and some broken URLs, contribute some suggestions and URLs, and the French translationMichele Ferritto <m dot ferritto at virgilio dot it>: Finding bugs and the Italian translationDaniel Roesen <dr at cluenet dot de>: For grammar checksBenjamin Thery <benjamin dot thery at bull dot net>: For contribution of updated mobility section +David Ranch <dranch at trinnet dot net>: For encouraging me to write this HOWTO, his editorial comments on the first few revisions, and his contributions to various IPv6 testing results on my IPv6 web site. Also for his major reviews and suggestions.Pekka Savola <pekkas at netcore dot fi>: For major reviews, input and suggestions.Martin F. Krafft <madduck at madduck dot net>: For grammar checks and general reviewing of the document.John Ronan <j0n at tssg dot wit dot ie>: For grammar checks.Georg Käfer <gkaefer at gmx dot at>: For detection of no proper PDF creation (fixed now by LDP maintainer Greg Ferguson), input for German books, big list of URLs, checking all URLs, many more suggestions, corrections and contributions, and the German translationMichel Boucey <mboucey at free dot fr>: Finding typos and some broken URLs, contribute some suggestions and URLs, and the French translationMichele Ferritto <m dot ferritto at virgilio dot it>: Finding bugs and the Italian translationDaniel Roesen <dr at cluenet dot de>: For grammar checksBenjamin Thery <benjamin dot thery at bull dot net>: For contribution of updated mobility sectionJohn Wenker <jjw at pt dot com>: major contribution to Programming using C-API Other credits Document technique related @@ -2929,7 +4813,7 @@ Publisher: MarketResearch.com; ISBN B00006334Y; (November 1, 2001) Authors of the LDP Author GuideB. Guillon: For his DocBook with LyX HOWTO <!-- anchor id="content-related-credits" -->Content related credits Credits for fixes and hints are listed here, will grow sure in the future -S .P. Meenakshi <meena at cs dot iitm dot ernet dot in>: For a hint using a "send mail" shell program on tcp_wrapper/hosts.denyFrank Dinies <FrankDinies at web dot de>: For a bugfix on IPv6 address explanationJohn Freed <jfreed at linux-mag dot com>: For finding a bug in IPv6 multicast address explanationCraig Rodrigues <crodrigu at bbn dot com>: For suggestion about RHL IPv6 setupFyodor <fyodor at insecure dot org>: Note me about outdated nmap informationMauro Tortonesi <mauro at deepspace6 dot net>: For some suggestionsTom Goodale <goodale at aei-potsdam dot mpg dot de>: For some suggestionsMartin Luemkemann <mluemkem at techfak dot uni-bielefeld dot de>: For a suggestionJean-Marc V. Liotier <jim at jipo dot com>: Finding a bugYaniv Kaul <ykaul at checkpoint dot com>: Finding a bugArnout Engelen <arnouten at bzzt dot net>: For sending note about a draft was adopted to RFC nowStephane Bortzmeyer <bortzmeyer at nic dot fr>: Contributing persistent configuration on Debianlithis von saturnsys <lithis at saturnsys dot com>: Reporting a misaddressed URLGuy Hulbert <gwhulbert at rogers dot com>: Send a note that RFC1924 is probably an April fool's jokeTero Pelander <tpeland at tkukoulu dot fi>: Reporting a broken URLWalter Jontofsohn <wjontof at gmx dot de>: Hints for SuSE Linux 8.0/8.1Benjamin Hofstetter <benjamin dot hofstetter at netlabs dot org>: Reporting a mispointing URLJ.P. Larocque <piranha at ely dot ath dot cx>: Reporting archive URL for maillist users at ipv6 dot orgJorrit Kronjee <jorrit at wafel dot org>: Reporting broken URLsColm MacCarthaigh <colm dot maccarthaigh at heanet dot ie>: Hint for sendfile issue on Apache2Tiago Camilo <tandre at ipg dot pt>: Contribute some URLs about Mobile IPv6Harald Geiger: Reporting a bug in how described the bit counting of the universal/global bitBjoern Jacke <bjoern at j3e dot de>: Triggered me to fix some outdated information on xinetdChristoph Egger <cegger at chrrr dot com>: Sending note about "ip" has problems with IPv4-compatible addresses on SuSE Linux 9.0 and trigger to add a hint on 6to4-radvd exampleDavid Lee Haw Ling <hawling at singnet dot com dot sg>: Sending information about a tunnel brokerMichael H. Warfield <mhw at iss dot net>: Sending note about suffix for 6to4 routersTomasz Mrugalski <thomson at klub dot com dot pl>: Sending updates for DHCPv6 sectionJan Minar <jjminar at fastmail dot fm>: Reporting minor bugsKalin KOZHUHAROV <kalin at tar dot bz>: Fixing a not so well explanationRoel van Dijk <rdvdijk at planet dot nl>: Reporting broken URLsCatalin Muresan <catalin dot muresan at astral dot ro>: Reporting minor bugsDennis van Dok <dvandok at quicknet dot nl>: Reporting minor bugsNecdet Yucel <nyucel at comu dot edu dot tr>: Reporting broken URLsBryan Vukich: Reporting a broken URL +S .P. Meenakshi <meena at cs dot iitm dot ernet dot in>: For a hint using a “send mail” shell program on tcp_wrapper/hosts.denyFrank Dinies <FrankDinies at web dot de>: For a bugfix on IPv6 address explanationJohn Freed <jfreed at linux-mag dot com>: For finding a bug in IPv6 multicast address explanationCraig Rodrigues <crodrigu at bbn dot com>: For suggestion about RHL IPv6 setupFyodor <fyodor at insecure dot org>: Note me about outdated nmap informationMauro Tortonesi <mauro at deepspace6 dot net>: For some suggestionsTom Goodale <goodale at aei-potsdam dot mpg dot de>: For some suggestionsMartin Luemkemann <mluemkem at techfak dot uni-bielefeld dot de>: For a suggestionJean-Marc V. Liotier <jim at jipo dot com>: Finding a bugYaniv Kaul <ykaul at checkpoint dot com>: Finding a bugArnout Engelen <arnouten at bzzt dot net>: For sending note about a draft was adopted to RFC nowStephane Bortzmeyer <bortzmeyer at nic dot fr>: Contributing persistent configuration on Debianlithis von saturnsys <lithis at saturnsys dot com>: Reporting a misaddressed URLGuy Hulbert <gwhulbert at rogers dot com>: Send a note that RFC1924 is probably an April fool's jokeTero Pelander <tpeland at tkukoulu dot fi>: Reporting a broken URLWalter Jontofsohn <wjontof at gmx dot de>: Hints for SuSE Linux 8.0/8.1Benjamin Hofstetter <benjamin dot hofstetter at netlabs dot org>: Reporting a mispointing URLJ.P. Larocque <piranha at ely dot ath dot cx>: Reporting archive URL for maillist users at ipv6 dot orgJorrit Kronjee <jorrit at wafel dot org>: Reporting broken URLsColm MacCarthaigh <colm dot maccarthaigh at heanet dot ie>: Hint for sendfile issue on Apache2Tiago Camilo <tandre at ipg dot pt>: Contribute some URLs about Mobile IPv6Harald Geiger: Reporting a bug in how described the bit counting of the universal/global bitBjoern Jacke <bjoern at j3e dot de>: Triggered me to fix some outdated information on xinetdChristoph Egger <cegger at chrrr dot com>: Sending note about “ip” has problems with IPv4-compatible addresses on SuSE Linux 9.0 and trigger to add a hint on 6to4-radvd exampleDavid Lee Haw Ling <hawling at singnet dot com dot sg>: Sending information about a tunnel brokerMichael H. Warfield <mhw at iss dot net>: Sending note about suffix for 6to4 routersTomasz Mrugalski <thomson at klub dot com dot pl>: Sending updates for DHCPv6 sectionJan Minar <jjminar at fastmail dot fm>: Reporting minor bugsKalin KOZHUHAROV <kalin at tar dot bz>: Fixing a not so well explanationRoel van Dijk <rdvdijk at planet dot nl>: Reporting broken URLsCatalin Muresan <catalin dot muresan at astral dot ro>: Reporting minor bugsDennis van Dok <dvandok at quicknet dot nl>: Reporting minor bugsNecdet Yucel <nyucel at comu dot edu dot tr>: Reporting broken URLsBryan Vukich: Reporting a broken URLDaniele Masini: reporting a broken iptables exampleYao Zhao: reporting a bug in IPv6 route remove descriptionAaron Kunde: reporting a broken URL and a content related bug The End Thanks for reading. Hope it helps! -If you have any questions, subscribe to proper maillist and describe your problem providing as much as information as possible.
+If you have any questions, subscribe to proper maillist and describe your problem providing as much as information as possible. \ No newline at end of file