tech-invite   World Map     

3GPP     Specs     Glossaries     Architecture     IMS     UICC       IETF     RFCs     Groups     SIP     ABNFs       Search

RFC 3542

 
 
 

Advanced Sockets Application Program Interface (API) for IPv6

Part 3 of 3, p. 48 to 77
Prev RFC Part

 


prevText      Top      Up      ToC       Page 48 
12.  Ordering of Ancillary Data and IPv6 Extension Headers

   Three IPv6 extension headers can be specified by the application and
   returned to the application using ancillary data with sendmsg() and
   recvmsg(): the Routing header, Hop-by-Hop options header, and
   Destination options header.  When multiple ancillary data objects are
   transferred via recvmsg() and these objects represent any of these
   three extension headers, their placement in the control buffer is
   directly tied to their location in the corresponding IPv6 datagram.
   For example, when the application has enabled the IPV6_RECVRTHDR and
   IPV6_RECVDSTOPTS options and later receives an IPv6 packet with
   extension headers in the following order:

      The IPv6 header
      A Hop-by-Hop options header
      A Destination options header (1)
      A Routing header
      An Authentication header
      A Destination options header (2)
      A UDP header and UDP data

Top      Up      ToC       Page 49 
   then the application will receive three ancillary data objects in the
   following order:

      an object with cmsg_type set to IPV6_DSTOPTS, which represents
      the destination options header (1)
      an object with cmsg_type set to IPV6_RTHDR, which represents the
      Routing header
      an object with cmsg_type set to IPV6_DSTOPTS, which represents the
      destination options header (2)

   This example follows the header ordering described in [RFC-2460], but
   the receiving side of this specification does not assume the
   ordering.  Applications may receive any numbers of objects in any
   order according to the ordering of the received IPv6 datagram.

   For the sending side, however, this API imposes some ordering
   constraints according to [RFC-2460].  Applications using this API
   cannot make a packet with extension headers that do not follow the
   ordering.  Note, however, that this does not mean applications must
   always follow the restriction.  This is just a limitation in this API
   in order to give application programmers a guideline to construct
   headers in a practical manner.  Should an application need to make an
   outgoing packet in an arbitrary order about the extension headers,
   some other technique, such as the datalink interfaces BPF or DLPI,
   must be used.

   The followings are more details about the constraints:

   -  Each IPV6_xxx ancillary data object for a particular type of
      extension header can be specified at most once in a single control
      buffer.

   -  IPV6_xxx ancillary data objects can appear in any order in a
      control buffer, because there is no ambiguity of the ordering.

   -  Each set of IPV6_xxx ancillary data objects and sticky options
      will be put in the outgoing packet along with the header ordering
      described in [RFC-2460].

   -  An ancillary data object or a sticky option of IPV6_RTHDRDSTOPTS
      will affect the outgoing packet only when a Routing header is
      specified as an ancillary data object or a sticky option.
      Otherwise, the specified value for IPV6_RTHDRDSTOPTS will be
      ignored.

Top      Up      ToC       Page 50 
   For example, when an application sends a UDP datagram with a control
   data buffer containing ancillary data objects in the following order:

      an object with cmsg_type set to IPV6_DSTOPTS
      an object with cmsg_type set to IPV6_RTHDRDSTOPTS
      an object with cmsg_type set to IPV6_HOPOPTS

   and the sending socket does not have any sticky options, then the
   outgoing packet would be constructed as follows:

      The IPv6 header
      A Hop-by-Hop options header
      A Destination options header
      A UDP header and UDP data

   where the destination options header corresponds to the ancillary
   data object with the type IPV6_DSTOPTS.

   Note that the constraints above do not necessarily mean that the
   outgoing packet sent on the wire always follows the header ordering
   specified in this API document.  The kernel may insert additional
   headers that break the ordering as a result.  For example, if the
   kernel supports Mobile IPv6, an additional destination options header
   may be inserted before an authentication header, even without a
   routing header.

   This API does not provide access to any other extension headers than
   the supported three types of headers.  In particular, no information
   is provided about the IP security headers on an incoming packet, nor
   can be specified for an outgoing packet.  This API is for
   applications that do not care about the existence of IP security
   headers.

13.  IPv6-Specific Options with IPv4-Mapped IPv6 Addresses

   The various socket options and ancillary data specifications defined
   in this document apply only to true IPv6 sockets.  It is possible to
   create an IPv6 socket that actually sends and receives IPv4 packets,
   using IPv4-mapped IPv6 addresses, but the mapping of the options
   defined in this document to an IPv4 datagram is beyond the scope of
   this document.

   In general, attempting to specify an IPv6-only option, such as the
   Hop-by-Hop options, Destination options, or Routing header on an IPv6
   socket that is using IPv4-mapped IPv6 addresses, will probably result
   in an error.  Some implementations, however, may provide access to

Top      Up      ToC       Page 51 
   the packet information (source/destination address, send/receive
   interface, and hop limit) on an IPv6 socket that is using IPv4-mapped
   IPv6 addresses.

14.  Extended interfaces for rresvport, rcmd and rexec

   Library functions that support the "r" commands hide the creation of
   a socket and the name resolution procedure from an application.  When
   the libraries return an AF_INET6 socket to an application that do not
   support the address family, the application may encounter an
   unexpected result when, e.g., calling getpeername() for the socket.
   In order to support AF_INET6 sockets for the "r" commands while
   keeping backward compatibility, this section defines some extensions
   to the libraries.

14.1.  rresvport_af

   The rresvport() function is used by the rcmd() function, and this
   function is in turn called by many of the "r" commands such as
   rlogin.  While new applications are not being written to use the
   rcmd() function, legacy applications such as rlogin will continue to
   use it and these will be ported to IPv6.

   rresvport() creates an IPv4/TCP socket and binds a "reserved port" to
   the socket.  Instead of defining an IPv6 version of this function we
   define a new function that takes an address family as its argument.

      #include <unistd.h>

      int  rresvport_af(int *port, int family);

   This function behaves the same as the existing rresvport() function,
   but instead of creating an AF_INET TCP socket, it can also create an
   AF_INET6 TCP socket.  The family argument is either AF_INET or
   AF_INET6, and a new error return is EAFNOSUPPORT if the address
   family is not supported.

   (Note: There is little consensus on which header defines the
   rresvport() and rcmd() function prototypes.  4.4BSD defines it in
   <unistd.h>, others in <netdb.h>, and others don't define the function
   prototypes at all.)

14.2.  rcmd_af

   The existing rcmd() function can not transparently use AF_INET6
   sockets since an application would not be prepared to handle AF_INET6
   addresses returned by e.g., getpeername() on the file descriptor
   created by rcmd().  Thus a new function is needed.

Top      Up      ToC       Page 52 
      int rcmd_af(char **ahost, unsigned short rport,
                  const char *locuser, const char *remuser,
                  const char *cmd, int *fd2p, int af)

   This function behaves the same as the existing rcmd() function, but
   instead of creating an AF_INET TCP socket, it can also create an
   AF_INET6 TCP socket.  The family argument is AF_INET, AF_INET6, or
   AF_UNSPEC.  When either AF_INET or AF_INET6 is specified, this
   function will create a socket of the specified address family.  When
   AF_UNSPEC is specified, it will try all possible address families
   until a connection can be established, and will return the associated
   socket of the connection.  A new error EAFNOSUPPORT will be returned
   if the address family is not supported.

14.3.  rexec_af

   The existing rexec() function can not transparently use AF_INET6
   sockets since an application would not be prepared to handle AF_INET6
   addresses returned by e.g., getpeername() on the file descriptor
   created by rexec().  Thus a new function is needed.

      int rexec_af(char **ahost, unsigned short rport, const char *name,
                   const char *pass, const char *cmd, int *fd2p, int af)

   This function behaves the same as the existing rexec() function, but
   instead of creating an AF_INET TCP socket, it can also create an
   AF_INET6 TCP socket.  The family argument is AF_INET, AF_INET6, or
   AF_UNSPEC.  When either AF_INET or AF_INET6 is specified, this
   function will create a socket of the specified address family.  When
   AF_UNSPEC is specified, it will try all possible address families
   until a connection can be established, and will return the associated
   socket of the connection.  A new error EAFNOSUPPORT will be returned
   if the address family is not supported.

15.  Summary of New Definitions

   The following list summarizes the constants and structure,
   definitions discussed in this memo, sorted by header.

      <netinet/icmp6.h> ICMP6_DST_UNREACH
      <netinet/icmp6.h> ICMP6_DST_UNREACH_ADDR
      <netinet/icmp6.h> ICMP6_DST_UNREACH_ADMIN
      <netinet/icmp6.h> ICMP6_DST_UNREACH_BEYONDSCOPE
      <netinet/icmp6.h> ICMP6_DST_UNREACH_NOPORT
      <netinet/icmp6.h> ICMP6_DST_UNREACH_NOROUTE
      <netinet/icmp6.h> ICMP6_ECHO_REPLY
      <netinet/icmp6.h> ICMP6_ECHO_REQUEST
      <netinet/icmp6.h> ICMP6_INFOMSG_MASK

Top      Up      ToC       Page 53 
      <netinet/icmp6.h> ICMP6_PACKET_TOO_BIG
      <netinet/icmp6.h> ICMP6_PARAMPROB_HEADER
      <netinet/icmp6.h> ICMP6_PARAMPROB_NEXTHEADER
      <netinet/icmp6.h> ICMP6_PARAMPROB_OPTION
      <netinet/icmp6.h> ICMP6_PARAM_PROB
      <netinet/icmp6.h> ICMP6_ROUTER_RENUMBERING
      <netinet/icmp6.h> ICMP6_RR_FLAGS_FORCEAPPLY
      <netinet/icmp6.h> ICMP6_RR_FLAGS_PREVDONE
      <netinet/icmp6.h> ICMP6_RR_FLAGS_REQRESULT
      <netinet/icmp6.h> ICMP6_RR_FLAGS_SPECSITE
      <netinet/icmp6.h> ICMP6_RR_FLAGS_TEST
      <netinet/icmp6.h> ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME
      <netinet/icmp6.h> ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME
      <netinet/icmp6.h> ICMP6_RR_PCOUSE_RAFLAGS_AUTO
      <netinet/icmp6.h> ICMP6_RR_PCOUSE_RAFLAGS_ONLINK
      <netinet/icmp6.h> ICMP6_RR_RESULT_FLAGS_FORBIDDEN
      <netinet/icmp6.h> ICMP6_RR_RESULT_FLAGS_OOB
      <netinet/icmp6.h> ICMP6_TIME_EXCEEDED
      <netinet/icmp6.h> ICMP6_TIME_EXCEED_REASSEMBLY
      <netinet/icmp6.h> ICMP6_TIME_EXCEED_TRANSIT
      <netinet/icmp6.h> MLD_LISTENER_QUERY
      <netinet/icmp6.h> MLD_LISTENER_REDUCTION
      <netinet/icmp6.h> MLD_LISTENER_REPORT
      <netinet/icmp6.h> ND_NA_FLAG_OVERRIDE
      <netinet/icmp6.h> ND_NA_FLAG_ROUTER
      <netinet/icmp6.h> ND_NA_FLAG_SOLICITED
      <netinet/icmp6.h> ND_NEIGHBOR_ADVERT
      <netinet/icmp6.h> ND_NEIGHBOR_SOLICIT
      <netinet/icmp6.h> ND_OPT_MTU
      <netinet/icmp6.h> ND_OPT_PI_FLAG_AUTO
      <netinet/icmp6.h> ND_OPT_PI_FLAG_ONLINK
      <netinet/icmp6.h> ND_OPT_PREFIX_INFORMATION
      <netinet/icmp6.h> ND_OPT_REDIRECTED_HEADER
      <netinet/icmp6.h> ND_OPT_SOURCE_LINKADDR
      <netinet/icmp6.h> ND_OPT_TARGET_LINKADDR
      <netinet/icmp6.h> ND_RA_FLAG_MANAGED
      <netinet/icmp6.h> ND_RA_FLAG_OTHER
      <netinet/icmp6.h> ND_REDIRECT
      <netinet/icmp6.h> ND_ROUTER_ADVERT
      <netinet/icmp6.h> ND_ROUTER_SOLICIT

      <netinet/icmp6.h> struct icmp6_filter{};
      <netinet/icmp6.h> struct icmp6_hdr{};
      <netinet/icmp6.h> struct icmp6_router_renum{};
      <netinet/icmp6.h> struct mld_hdr{};
      <netinet/icmp6.h> struct nd_neighbor_advert{};
      <netinet/icmp6.h> struct nd_neighbor_solicit{};
      <netinet/icmp6.h> struct nd_opt_hdr{};

Top      Up      ToC       Page 54 
      <netinet/icmp6.h> struct nd_opt_mtu{};
      <netinet/icmp6.h> struct nd_opt_prefix_info{};
      <netinet/icmp6.h> struct nd_opt_rd_hdr{};
      <netinet/icmp6.h> struct nd_redirect{};
      <netinet/icmp6.h> struct nd_router_advert{};
      <netinet/icmp6.h> struct nd_router_solicit{};
      <netinet/icmp6.h> struct rr_pco_match{};
      <netinet/icmp6.h> struct rr_pco_use{};
      <netinet/icmp6.h> struct rr_result{};

      <netinet/in.h>    IPPROTO_AH
      <netinet/in.h>    IPPROTO_DSTOPTS
      <netinet/in.h>    IPPROTO_ESP
      <netinet/in.h>    IPPROTO_FRAGMENT
      <netinet/in.h>    IPPROTO_HOPOPTS
      <netinet/in.h>    IPPROTO_ICMPV6
      <netinet/in.h>    IPPROTO_IPV6
      <netinet/in.h>    IPPROTO_NONE
      <netinet/in.h>    IPPROTO_ROUTING
      <netinet/in.h>    IPV6_CHECKSUM
      <netinet/in.h>    IPV6_DONTFRAG
      <netinet/in.h>    IPV6_DSTOPTS
      <netinet/in.h>    IPV6_HOPLIMIT
      <netinet/in.h>    IPV6_HOPOPTS

      <netinet/in.h>    IPV6_NEXTHOP
      <netinet/in.h>    IPV6_PATHMTU
      <netinet/in.h>    IPV6_PKTINFO
      <netinet/in.h>    IPV6_RECVDSTOPTS
      <netinet/in.h>    IPV6_RECVHOPLIMIT
      <netinet/in.h>    IPV6_RECVHOPOPTS
      <netinet/in.h>    IPV6_RECVPKTINFO
      <netinet/in.h>    IPV6_RECVRTHDR
      <netinet/in.h>    IPV6_RECVTCLASS
      <netinet/in.h>    IPV6_RTHDR
      <netinet/in.h>    IPV6_RTHDRDSTOPTS
      <netinet/in.h>    IPV6_RTHDR_TYPE_0
      <netinet/in.h>    IPV6_RECVPATHMTU
      <netinet/in.h>    IPV6_TCLASS
      <netinet/in.h>    IPV6_USE_MIN_MTU
      <netinet/in.h>    struct in6_pktinfo{};
      <netinet/in.h>    struct ip6_mtuinfo{};

      <netinet/ip6.h>   IP6F_MORE_FRAG
      <netinet/ip6.h>   IP6F_OFF_MASK
      <netinet/ip6.h>   IP6F_RESERVED_MASK
      <netinet/ip6.h>   IP6OPT_JUMBO
      <netinet/ip6.h>   IP6OPT_JUMBO_LEN

Top      Up      ToC       Page 55 
      <netinet/ip6.h>   IP6OPT_MUTABLE
      <netinet/ip6.h>   IP6OPT_NSAP_ADDR
      <netinet/ip6.h>   IP6OPT_PAD1
      <netinet/ip6.h>   IP6OPT_PADN
      <netinet/ip6.h>   IP6OPT_ROUTER_ALERT
      <netinet/ip6.h>   IP6OPT_TUNNEL_LIMIT
      <netinet/ip6.h>   IP6OPT_TYPE_DISCARD
      <netinet/ip6.h>   IP6OPT_TYPE_FORCEICMP
      <netinet/ip6.h>   IP6OPT_TYPE_ICMP
      <netinet/ip6.h>   IP6OPT_TYPE_SKIP
      <netinet/ip6.h>   IP6_ALERT_AN
      <netinet/ip6.h>   IP6_ALERT_MLD
      <netinet/ip6.h>   IP6_ALERT_RSVP
      <netinet/ip6.h>   struct ip6_dest{};
      <netinet/ip6.h>   struct ip6_frag{};
      <netinet/ip6.h>   struct ip6_hbh{};
      <netinet/ip6.h>   struct ip6_hdr{};
      <netinet/ip6.h>   struct ip6_opt{};
      <netinet/ip6.h>   struct ip6_opt_jumbo{};
      <netinet/ip6.h>   struct ip6_opt_nsap{};
      <netinet/ip6.h>   struct ip6_opt_router{};
      <netinet/ip6.h>   struct ip6_opt_tunnel{};
      <netinet/ip6.h>   struct ip6_rthdr{};
      <netinet/ip6.h>   struct ip6_rthdr0{};

   The following list summarizes the function and macro prototypes
   discussed in this memo, sorted by header.

      <netinet/icmp6.h> void ICMP6_FILTER_SETBLOCK(int, struct
                                               icmp6_filter *);
      <netinet/icmp6.h> void
                        ICMP6_FILTER_SETBLOCKALL(struct icmp6_filter *);
      <netinet/icmp6.h> void
                        ICMP6_FILTER_SETPASS(int,
                                             struct icmp6_filter *);
      <netinet/icmp6.h> void
                        ICMP6_FILTER_SETPASSALL(struct icmp6_filter *);
      <netinet/icmp6.h> int  ICMP6_FILTER_WILLBLOCK(int,
                                           const struct icmp6_filter *);
      <netinet/icmp6.h> int  ICMP6_FILTER_WILLPASS(int,
                                           const struct icmp6_filter *);

      <netinet/in.h>    int IN6_ARE_ADDR_EQUAL(const struct in6_addr *,
                                           const struct in6_addr *);

      <netinet/in.h>    int inet6_opt_append(void *, socklen_t, int,
                                             uint8_t, socklen_t, uint_t,
                                             void **);

Top      Up      ToC       Page 56 
      <netinet/in.h>    int inet6_opt_get_val(void *, int, void *,
                                              socklen_t);
      <netinet/in.h>    int inet6_opt_find(void *, socklen_t,
                                           int, uint8_t ,
                                           socklen_t *, void **);
      <netinet/in.h>    int inet6_opt_finish(void *, socklen_t, int);
      <netinet/in.h>    int inet6_opt_init(void *, socklen_t);
      <netinet/in.h>    int inet6_opt_next(void *, socklen_t,
                                           int, uint8_t *,
                                           socklen_t *, void **);
      <netinet/in.h>    int inet6_opt_set_val(void *, int,
                                              void *, socklen_t);

      <netinet/in.h>    int inet6_rth_add(void *,
                                          const struct in6_addr *);
      <netinet/in.h>    struct in6_addr inet6_rth_getaddr(const void *,
                                                          int);
      <netinet/in.h>    void *inet6_rth_init(void *, socklen_t,
                                             int, int);
      <netinet/in.h>    int inet6_rth_reverse(const void *, void *);
      <netinet/in.h>    int inet6_rth_segments(const void *);
      <netinet/in.h>    soccklen_t inet6_rth_space(int, int);

      <netinet/ip6.h>   int  IP6OPT_TYPE(uint8_t);

      <sys/socket.h>    socklen_t CMSG_LEN(socklen_t);
      <sys/socket.h>    socklen_t CMSG_SPACE(socklen_t);

      <unistd.h>        int rresvport_af(int *, int);
      <unistd.h>        int rcmd_af(char **, unsigned short,
                                    const char *, const char *,
                                    const char *, int *, int);
      <unistd.h>        int rexec_af(char **, unsigned short,
                                     const char *, const char *,
                                     const char *, int *, int);

16.  Security Considerations

   The setting of certain Hop-by-Hop options and Destination options may
   be restricted to privileged processes.  Similarly some Hop-by-Hop
   options and Destination options may not be returned to non-privileged
   applications.

   The ability to specify an arbitrary source address using IPV6_PKTINFO
   must be prevented; at least for non-privileged processes.

Top      Up      ToC       Page 57 
17.  Changes from RFC 2292

   Significant changes that affect the compatibility to RFC 2292:

   -  Removed the IPV6_PKTOPTIONS socket option by allowing sticky
      options to be set with individual setsockopt() calls.

   -  Removed the ability to be able to specify Hop-by-Hop and
      Destination options using multiple ancillary data items.  The
      application, using the inet6_opt_xxx() routines (see below), is
      responsible for formatting the whole extension header.

   -  Removed the support for the loose/strict Routing header since that
      has been removed from the IPv6 specification.

   -  Loosened the constraints for jumbo payload option that this option
      was always hidden from applications.

   -  Disabled the use of the IPV6_HOPLIMIT sticky option.

   -  Removed ip6r0_addr field from the ip6_rthdr structure.

   -  Intentionally unspecified how to get received packet's information
      on TCP sockets.

   New features:

   -  Added IPV6_RTHDRDSTOPTS to specify a Destination Options header
      before the Routing header.

   -  Added separate IPV6_RECVxxx options to enable the receipt of the
      corresponding ancillary data items.

   -  Added inet6_rth_xxx() and inet6_opt_xxx() functions to deal with
      routing or IPv6 options headers.

   -  Added extensions of libraries for the "r" commands.

   -  Introduced additional IPv6 option definitions such as IP6OPT_PAD1.

   -  Added MLD and router renumbering definitions.

   -  Added MTU-related socket options and ancillary data items.

   -  Added options and ancillary data items to manipulate the traffic
      class field.

Top      Up      ToC       Page 58 
   -  Changed the name of ICMPv6 unreachable code 2 to be "beyond scope
      of source address."  ICMP6_DST_UNREACH_NOTNEIGHBOR was removed
      with this change.

   Clarifications:

   -  Added clarifications on extension headers ordering; for the
      sending side, assume the recommended ordering described in RFC
      2460.  For the receiving side, do not assume any ordering and pass
      all headers to the application in the received order.

   -  Added a summary about the interface selection rule.

   -  Clarified the ordering between IPV6_MULTICAST_IF and the
      IPV6_PKTINFO sticky option for multicast packets.

   -  Clarified how sticky options and the ICMPv6 filter are turned off
      and that getsockopt() of a sticky option returns what was set with
      setsockopt().

   -  Clarified that IPV6_NEXTHOP should be ignored for a multicast
      destination, that it should not contradict with the specified
      outgoing interface, and that the next hop should be a sockaddr_in6
      structure.

   -  Clarified corner cases of IPV6_CHECKSUM.

   -  Aligned with the POSIX standard.

   Editorial changes:

   -  Replaced MUST with must (since this is an informational document).

   -  Revised abstract to be more clear and concise, particularly
      concentrating on differences from RFC 2292.

   -  Made the URL of assigned numbers less specific so that it would be
      more robust for future changes.

   -  Updated the reference to the basic API.

   -  Added a reference to the latest POSIX standard.

   -  Moved general specifications of ancillary data and CMSG macros to
      the appendix.

Top      Up      ToC       Page 59 
18.  References

   [RFC-1981]    McCann, J., Deering, S. and J. Mogul, "Path MTU
                 Discovery for IP version 6", RFC 1981, August 1996.

   [RFC-2460]    Deering, S. and R. Hinden, "Internet Protocol, Version
                 6 (IPv6) Specification", RFC 2460, December 1998.

   [RFC-3493]    Gilligan, R., Thomson, S., Bound, J., McCann, J.  and
                 W. Stevens, "Basic Socket Interface Extensions for
                 IPv6", RFC 3493, March 2003.

   [POSIX]       IEEE Std. 1003.1-2001 Standard for Information
                 Technology -- Portable Operating System Interface
                 (POSIX). Open group Technical Standard: Base
                 Specifications, Issue 6, December 2001. ISO/IEC
                 9945:2002. http://www.opengroup.org/austin

   [TCPIPILLUST] Wright, G., Stevens, W., "TCP/IP Illustrated, Volume 2:
                 The Implementation", Addison Wesley, 1994.

19.  Acknowledgments

   Matt Thomas and Jim Bound have been working on the technical details
   in this document for over a year.  Keith Sklower is the original
   implementor of ancillary data in the BSD networking code.  Craig Metz
   provided lots of feedback, suggestions, and comments based on his
   implementing many of these features as the document was being
   written.  Mark Andrews first proposed the idea of the
   IPV6_USE_MIN_MTU option.  Jun-ichiro Hagino contributed text for the
   traffic class API from a document of his own.

   The following provided comments on earlier drafts: Pascal Anelli,
   Hamid Asayesh, Ran Atkinson, Karl Auerbach, Hamid Asayesh, Don
   Coolidge, Matt Crawford, Sam T. Denton, Richard Draves, Francis
   Dupont, Toerless Eckert, Lilian Fernandes, Bob Gilligan, Gerri
   Harter, Tim Hartrick, Bob Halley, Masaki Hirabaru, Michael Hunter,
   Yoshinobu Inoue, Mukesh Kacker, A. N. Kuznetsov, Sam Manthorpe, Pedro
   Marques, Jack McCann, der Mouse, John Moy, Lori Napoli, Thomas
   Narten, Atsushi Onoe, Steve Parker, Charles Perkins, Ken Powell, Tom
   Pusateri, Pedro Roque, Sameer Shah, Peter Sjodin, Stephen P.
   Spackman, Jinmei Tatuya, Karen Tracey, Sowmini Varadhan, Quaizar
   Vohra, Carl Williams, Steve Wise, Eric Wong, Farrell Woods, Kazu
   Yamamoto, Vladislav Yasevich, and Yoshifuji Hideaki.

Top      Up      ToC       Page 60 
20.  Appendix A: Ancillary Data Overview

   4.2BSD allowed file descriptors to be transferred between separate
   processes across a UNIX domain socket using the sendmsg() and
   recvmsg() functions.  Two members of the msghdr structure,
   msg_accrights and msg_accrightslen, were used to send and receive the
   descriptors.  When the OSI protocols were added to 4.3BSD Reno in
   1990 the names of these two fields in the msghdr structure were
   changed to msg_control and msg_controllen, because they were used by
   the OSI protocols for "control information", although the comments in
   the source code call this "ancillary data".

   Other than the OSI protocols, the use of ancillary data has been
   rare.  In 4.4BSD, for example, the only use of ancillary data with
   IPv4 is to return the destination address of a received UDP datagram
   if the IP_RECVDSTADDR socket option is set.  With Unix domain sockets
   ancillary data is still used to send and receive descriptors.

   Nevertheless the ancillary data fields of the msghdr structure
   provide a clean way to pass information in addition to the data that
   is being read or written.  The inclusion of the msg_control and
   msg_controllen members of the msghdr structure along with the cmsghdr
   structure that is pointed to by the msg_control member is required by
   the Posix sockets API standard.

20.1.  The msghdr Structure

   The msghdr structure is used by the recvmsg() and sendmsg()
   functions.  Its Posix definition is:

      struct msghdr {
        void      *msg_name;        /* ptr to socket address
                                       structure */
        socklen_t  msg_namelen;     /* size of socket address
                                       structure */
        struct iovec  *msg_iov;     /* scatter/gather array */
        int        msg_iovlen;      /* # elements in msg_iov */
        void      *msg_control;     /* ancillary data */
        socklen_t  msg_controllen;  /* ancillary data buffer length */
        int        msg_flags;       /* flags on received message */
      };

   The structure is declared as a result of including <sys/socket.h>.

   (Note: Before Posix the two "void *" pointers were typically "char
   *", and the two socklen_t members were typically integers.  Earlier
   drafts of Posix had the two socklen_t members as size_t, but it then
   changed these to socklen_t to simplify binary portability for 64-bit

Top      Up      ToC       Page 61 
   implementations and to align Posix with X/Open's Networking Services,
   Issue 5.  The change in msg_control to a "void *" pointer affects any
   code that increments this pointer.)

   Most Berkeley-derived implementations limit the amount of ancillary
   data in a call to sendmsg() to no more than 108 bytes (an mbuf).
   This API requires a minimum of 10240 bytes of ancillary data, but it
   is recommended that the amount be limited only by the buffer space
   reserved by the socket (which can be modified by the SO_SNDBUF socket
   option).  (Note: This magic number 10240 was picked as a value that
   should always be large enough.  108 bytes is clearly too small as the
   maximum size of a Routing header is 2048 bytes.)

20.2.  The cmsghdr Structure

   The cmsghdr structure describes ancillary data objects transferred by
   recvmsg() and sendmsg().  Its Posix definition is:

      struct cmsghdr {
        socklen_t  cmsg_len;   /* #bytes, including this header */
        int        cmsg_level; /* originating protocol */
        int        cmsg_type;  /* protocol-specific type */
                   /* followed by unsigned char cmsg_data[]; */
      };

   This structure is declared as a result of including <sys/socket.h>.

   (Note: Before Posix the cmsg_len member was an integer, and not a
   socklen_t.  See the Note in the previous section for why socklen_t is
   used here.)

   As shown in this definition, normally there is no member with the
   name cmsg_data[].  Instead, the data portion is accessed using the
   CMSG_xxx() macros, as described in Section 20.3.  Nevertheless, it is
   common to refer to the cmsg_data[] member.

   When ancillary data is sent or received, any number of ancillary data
   objects can be specified by the msg_control and msg_controllen
   members of the msghdr structure, because each object is preceded by a
   cmsghdr structure defining the object's length (the cmsg_len member).
   Historically Berkeley-derived implementations have passed only one
   object at a time, but this API allows multiple objects to be passed
   in a single call to sendmsg() or recvmsg().  The following example
   shows two ancillary data objects in a control buffer.

Top      Up      ToC       Page 62 
|<--------------------------- msg_controllen ------------------------->|
|                                 OR                                   |
|<--------------------------- msg_controllen ---------------------->|
|                                                                      |
|<----- ancillary data object ----->|<---- ancillary data object ----->|
|<------ min CMSG_SPACE() --------->|<----- min CMSG_SPACE() --------->|
|                                   |                                  |
|<---------- cmsg_len ---------->|  |<-------- cmsg_len ----------->|  |
|<--------- CMSG_LEN() --------->|  |<------- CMSG_LEN() ---------->|  |
|                                |  |                               |  |
+-----+-----+-----+--+-----------+--+-----+-----+-----+--+----------+--+
|cmsg_|cmsg_|cmsg_|XX|   cmsg_   |XX|cmsg_|cmsg_|cmsg_|XX|  cmsg_   |XX|
|len  |level|type |XX|   data[]  |XX|len  |level|type |XX|  data[]  |XX|
+-----+-----+-----+--+-----------+--+-----+-----+-----+--+----------+--+
^
|
msg_control
points here

   The fields shown as "XX" are possible padding, between the cmsghdr
   structure and the data, and between the data and the next cmsghdr
   structure, if required by the implementation.  While sending an
   application may or may not include padding at the end of last
   ancillary data in msg_controllen and implementations must accept both
   as valid.  On receiving a portable application must provide space for
   padding at the end of the last ancillary data as implementations may
   copy out the padding at the end of the control message buffer and
   include it in the received msg_controllen.  When recvmsg() is called
   if msg_controllen is too small for all the ancillary data items
   including any trailing padding after the last item an implementation
   may set MSG_CTRUNC.

20.3.  Ancillary Data Object Macros

   To aid in the manipulation of ancillary data objects, three macros
   from 4.4BSD are defined by Posix: CMSG_DATA(), CMSG_NXTHDR(), and
   CMSG_FIRSTHDR().  Before describing these macros, we show the
   following example of how they might be used with a call to recvmsg().

      struct msghdr   msg;
      struct cmsghdr  *cmsgptr;

      /* fill in msg */

      /* call recvmsg() */

Top      Up      ToC       Page 63 
      for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL;
           cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
          if (cmsgptr->cmsg_len == 0) {
              /* Error handling */
           break;
          }
          if (cmsgptr->cmsg_level == ... &&
              cmsgptr->cmsg_type == ... ) {
              u_char  *ptr;

              ptr = CMSG_DATA(cmsgptr);
              /* process data pointed to by ptr */
          }
      }

   We now describe the three Posix macros, followed by two more that are
   new with this API: CMSG_SPACE() and CMSG_LEN().  All these macros are
   defined as a result of including <sys/socket.h>.

20.3.1.  CMSG_FIRSTHDR

      struct cmsghdr *CMSG_FIRSTHDR(const struct msghdr *mhdr);

   CMSG_FIRSTHDR() returns a pointer to the first cmsghdr structure in
   the msghdr structure pointed to by mhdr.  The macro returns NULL if
   there is no ancillary data pointed to by the msghdr structure (that
   is, if either msg_control is NULL or if msg_controllen is less than
   the size of a cmsghdr structure).

   One possible implementation could be

      #define CMSG_FIRSTHDR(mhdr) \
          ( (mhdr)->msg_controllen >= sizeof(struct cmsghdr) ? \
            (struct cmsghdr *)(mhdr)->msg_control : \
            (struct cmsghdr *)NULL )

   (Note: Most existing implementations do not test the value of
   msg_controllen, and just return the value of msg_control.  The value
   of msg_controllen must be tested, because if the application asks
   recvmsg() to return ancillary data, by setting msg_control to point
   to the application's buffer and setting msg_controllen to the length
   of this buffer, the kernel indicates that no ancillary data is
   available by setting msg_controllen to 0 on return.  It is also
   easier to put this test into this macro, than making the application
   perform the test.)

Top      Up      ToC       Page 64 
20.3.2.  CMSG_NXTHDR

   As described in Section 5.1, CMSG_NXTHDR has been extended to handle
   a NULL 2nd argument to mean "get the first header".  This provides an
   alternative way of coding the processing loop shown earlier:

      struct msghdr  msg;
      struct cmsghdr  *cmsgptr = NULL;

      /* fill in msg */

      /* call recvmsg() */

      while ((cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) != NULL) {
          if (cmsgptr->cmsg_len == 0) {
              /* Error handling */
           break;
          }
          if (cmsgptr->cmsg_level == ... &&
              cmsgptr->cmsg_type == ... ) {
              u_char  *ptr;

              ptr = CMSG_DATA(cmsgptr);
              /* process data pointed to by ptr */
          }
      }

   One possible implementation could be:

      #define CMSG_NXTHDR(mhdr, cmsg) \
        (((cmsg) == NULL) ? CMSG_FIRSTHDR(mhdr) : \
         (((u_char *)(cmsg) + ALIGN_H((cmsg)->cmsg_len) \
                            + ALIGN_D(sizeof(struct cmsghdr)) > \
           (u_char *)((mhdr)->msg_control) + (mhdr)->msg_controllen) ? \
          (struct cmsghdr *)NULL : \
          (struct cmsghdr *)((u_char *)(cmsg) + \
                                        ALIGN_H((cmsg)->cmsg_len))))

   The macros ALIGN_H() and ALIGN_D(), which are implementation
   dependent, round their arguments up to the next even multiple of
   whatever alignment is required for the start of the cmsghdr structure
   and the data, respectively.  (This is probably a multiple of 4 or 8
   bytes.)  They are often the same macro in implementations platforms
   where alignment requirement for header and data is chosen to be
   identical.

Top      Up      ToC       Page 65 
20.3.3.  CMSG_DATA

      unsigned char *CMSG_DATA(const struct cmsghdr *cmsg);

   CMSG_DATA() returns a pointer to the data (what is called the
   cmsg_data[] member, even though such a member is not defined in the
   structure) following a cmsghdr structure.

   One possible implementation could be:

      #define CMSG_DATA(cmsg) ( (u_char *)(cmsg) + \
                                ALIGN_D(sizeof(struct cmsghdr)) )

20.3.4.  CMSG_SPACE

   CMSG_SPACE is new with this API (see Section 5.2).  It is used to
   determine how much space needs to be allocated for an ancillary data
   item.

   One possible implementation could be:

      #define CMSG_SPACE(length) ( ALIGN_D(sizeof(struct cmsghdr)) + \
                                   ALIGN_H(length) )

20.3.5.  CMSG_LEN

   CMSG_LEN is new with this API (see Section 5.3).  It  returns the
   value to store in the cmsg_len member of the cmsghdr structure,
   taking into account any padding needed to satisfy alignment
   requirements.

   One possible implementation could be:

      #define CMSG_LEN(length) ( ALIGN_D(sizeof(struct cmsghdr)) + \
                                 length )

21.  Appendix B: Examples Using the inet6_rth_XXX() Functions

   Here we show an example for both sending Routing headers and
   processing and reversing a received Routing header.

21.1.  Sending a Routing Header

   As an example of these Routing header functions defined in this
   document, we go through the function calls for the example on p. 17
   of [RFC-2460].  The source is S, the destination is D, and the three
   intermediate nodes are I1, I2, and I3.

Top      Up      ToC       Page 66 
              S -----> I1 -----> I2 -----> I3 -----> D

      src:    *    S         S         S         S   S
      dst:    D   I1        I2        I3         D   D
      A[1]:  I1   I2        I1        I1        I1  I1
      A[2]:  I2   I3        I3        I2        I2  I2
      A[3]:  I3    D         D         D        I3  I3
      #seg:   3    3         2         1         0   3

   src and dst are the source and destination IPv6 addresses in the IPv6
   header.  A[1], A[2], and A[3] are the three addresses in the Routing
   header.  #seg is the Segments Left field in the Routing header.

   The six values in the column beneath node S are the values in the
   Routing header specified by the sending application using sendmsg()
   of setsockopt().  The function calls by the sender would look like:

      void  *extptr;
      socklen_t   extlen;
      struct msghdr  msg;
      struct cmsghdr  *cmsgptr;
      int   cmsglen;
      struct sockaddr_in6  I1, I2, I3, D;

      extlen = inet6_rth_space(IPV6_RTHDR_TYPE_0, 3);
      cmsglen = CMSG_SPACE(extlen);
      cmsgptr = malloc(cmsglen);
      cmsgptr->cmsg_len = CMSG_LEN(extlen);
      cmsgptr->cmsg_level = IPPROTO_IPV6;
      cmsgptr->cmsg_type = IPV6_RTHDR;

      extptr = CMSG_DATA(cmsgptr);
      extptr = inet6_rth_init(extptr, extlen, IPV6_RTHDR_TYPE_0, 3);

      inet6_rth_add(extptr, &I1.sin6_addr);
      inet6_rth_add(extptr, &I2.sin6_addr);
      inet6_rth_add(extptr, &I3.sin6_addr);

      msg.msg_control = cmsgptr;
      msg.msg_controllen = cmsglen;

      /* finish filling in msg{}, msg_name = D */
      /* call sendmsg() */

   We also assume that the source address for the socket is not
   specified (i.e., the asterisk in the figure).

Top      Up      ToC       Page 67 
   The four columns of six values that are then shown between the five
   nodes are the values of the fields in the packet while the packet is
   in transit between the two nodes.  Notice that before the packet is
   sent by the source node S, the source address is chosen (replacing
   the asterisk), I1 becomes the destination address of the datagram,
   the two addresses A[2] and A[3] are "shifted up", and D is moved to
   A[3].

   The columns of values that are shown beneath the destination node are
   the values returned by recvmsg(), assuming the application has
   enabled both the IPV6_RECVPKTINFO and IPV6_RECVRTHDR socket options.
   The source address is S (contained in the sockaddr_in6 structure
   pointed to by the msg_name member), the destination address is D
   (returned as an ancillary data object in an in6_pktinfo structure),
   and the ancillary data object specifying the Routing header will
   contain three addresses (I1, I2, and I3).  The number of segments in
   the Routing header is known from the Hdr Ext Len field in the Routing
   header (a value of 6, indicating 3 addresses).

   The return value from inet6_rth_segments() will be 3 and
   inet6_rth_getaddr(0) will return I1, inet6_rth_getaddr(1) will return
   I2, and inet6_rth_getaddr(2) will return I3,

   If the receiving application then calls inet6_rth_reverse(), the
   order of the three addresses will become I3, I2, and I1.

   We can also show what an implementation might store in the ancillary
   data object as the Routing header is being built by the sending
   process.  If we assume a 32-bit architecture where sizeof(struct
   cmsghdr) equals 12, with a desired alignment of 4-byte boundaries,
   then the call to inet6_rth_space(3) returns 68: 12 bytes for the
   cmsghdr structure and 56 bytes for the Routing header (8 + 3*16).

   The call to inet6_rth_init() initializes the ancillary data object to
   contain a Type 0 Routing header:

      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |       cmsg_len = 20                                           |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |       cmsg_level = IPPROTO_IPV6                               |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |       cmsg_type = IPV6_RTHDR                                  |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |  Next Header  | Hdr Ext Len=6 | Routing Type=0|  Seg Left=0   |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                           Reserved                            |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Top      Up      ToC       Page 68 
   The first call to inet6_rth_add() adds I1 to the list.

      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |       cmsg_len = 36                                           |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |       cmsg_level = IPPROTO_IPV6                               |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |       cmsg_type = IPV6_RTHDR                                  |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |  Next Header  | Hdr Ext Len=6 | Routing Type=0|  Seg Left=1   |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                           Reserved                            |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                                                               |
      +                                                               +
      |                                                               |
      +                           Address[1] = I1                     +
      |                                                               |
      +                                                               +
      |                                                               |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   cmsg_len is incremented by 16, and the Segments Left field is
   incremented by 1.

Top      Up      ToC       Page 69 
   The next call to inet6_rth_add() adds I2 to the list.

      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |       cmsg_len = 52                                           |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |       cmsg_level = IPPROTO_IPV6                               |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |       cmsg_type = IPV6_RTHDR                                  |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |  Next Header  | Hdr Ext Len=6 | Routing Type=0|  Seg Left=2   |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                           Reserved                            |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                                                               |
      +                                                               +
      |                                                               |
      +                           Address[1] = I1                     +
      |                                                               |
      +                                                               +
      |                                                               |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                                                               |
      +                                                               +
      |                                                               |
      +                           Address[2] = I2                     +
      |                                                               |
      +                                                               +
      |                                                               |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   cmsg_len is incremented by 16, and the Segments Left field is
   incremented by 1.

Top      Up      ToC       Page 70 
   The last call to inet6_rth_add() adds I3 to the list.

      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |       cmsg_len = 68                                           |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |       cmsg_level = IPPROTO_IPV6                               |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |       cmsg_type = IPV6_RTHDR                                  |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |  Next Header  | Hdr Ext Len=6 | Routing Type=0|  Seg Left=3   |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                           Reserved                            |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                                                               |
      +                                                               +
      |                                                               |
      +                           Address[1] = I1                     +
      |                                                               |
      +                                                               +
      |                                                               |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                                                               |
      +                                                               +
      |                                                               |
      +                           Address[2] = I2                     +
      |                                                               |
      +                                                               +
      |                                                               |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                                                               |
      +                                                               +
      |                                                               |
      +                           Address[3] = I3                     +
      |                                                               |
      +                                                               +
      |                                                               |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   cmsg_len is incremented by 16, and the Segments Left field is
   incremented by 1.

21.2.  Receiving Routing Headers

   This example assumes that the application has enabled IPV6_RECVRTHDR
   socket option.  The application prints and reverses a source route
   and uses that to echo the received data.

Top      Up      ToC       Page 71 
      struct sockaddr_in6     addr;
      struct msghdr           msg;
      struct iovec            iov;
      struct cmsghdr          *cmsgptr;
      socklen_t               cmsgspace;
      void                    *extptr;
      int                     extlen;

      int                     segments;
      int                     i;
      char                    databuf[8192];

      segments = 100;        /* Enough */
      extlen = inet6_rth_space(IPV6_RTHDR_TYPE_0, segments);
      cmsgspace = CMSG_SPACE(extlen);
      cmsgptr = malloc(cmsgspace);
      if (cmsgptr == NULL) {
              perror("malloc");
              exit(1);
      }
      extptr = CMSG_DATA(cmsgptr);

      msg.msg_control = cmsgptr;
      msg.msg_controllen = cmsgspace;
      msg.msg_name = (struct sockaddr *)&addr;
      msg.msg_namelen = sizeof (addr);
      msg.msg_iov = &iov;
      msg.msg_iovlen = 1;
      iov.iov_base = databuf;
      iov.iov_len = sizeof (databuf);
      msg.msg_flags = 0;
      if (recvmsg(s, &msg, 0) == -1) {
              perror("recvmsg");
              return;
      }
      if (msg.msg_controllen != 0 &&
          cmsgptr->cmsg_level == IPPROTO_IPV6 &&
          cmsgptr->cmsg_type == IPV6_RTHDR) {
              struct in6_addr *in6;
              char asciiname[INET6_ADDRSTRLEN];
              struct ip6_rthdr *rthdr;

              rthdr = (struct ip6_rthdr *)extptr;
              segments = inet6_rth_segments(extptr);
              printf("route (%d segments, %d left): ",
                  segments, rthdr->ip6r_segleft);
              for (i = 0; i < segments; i++) {
                      in6 = inet6_rth_getaddr(extptr, i);

Top      Up      ToC       Page 72 
                      if (in6 == NULL)
                              printf("<NULL> ");
                      else
                              printf("%s ", inet_ntop(AF_INET6,
                                  (void *)in6->s6_addr,
                                  asciiname, INET6_ADDRSTRLEN));
              }
              if (inet6_rth_reverse(extptr, extptr) == -1) {
                      printf("reverse failed");
                      return;
              }
      }
      iov.iov_base = databuf;
      iov.iov_len = strlen(databuf);
      if (sendmsg(s, &msg, 0) == -1)
              perror("sendmsg");
      if (cmsgptr != NULL)
              free(cmsgptr);

   Note: The above example is a simple illustration.  It skips some
   error checks, including those involving the MSG_TRUNC and MSG_CTRUNC
   flags.  It also leaves some type mismatches in favor of brevity.

22.  Appendix C: Examples Using the inet6_opt_XXX() Functions

   This shows how Hop-by-Hop and Destination options can be both built
   as well as parsed using the inet6_opt_XXX() functions.  These
   examples assume that there are defined values for OPT_X and OPT_Y.

   Note: The example is a simple illustration.  It skips some error
   checks and leaves some type mismatches in favor of brevity.

22.1.  Building Options

   We now provide an example that builds two Hop-by-Hop options using
   the example in Appendix B of [RFC-2460].

      void *extbuf;
      socklen_t extlen;
      int currentlen;
      void *databuf;
      int offset;
      uint8_t value1;
      uint16_t value2;
      uint32_t value4;
      uint64_t value8;

      /* Estimate the length */

Top      Up      ToC       Page 73 
      currentlen = inet6_opt_init(NULL, 0);
      if (currentlen == -1)
              return (-1);
      currentlen = inet6_opt_append(NULL, 0, currentlen, OPT_X,
                                    12, 8, NULL);
      if (currentlen == -1)
              return (-1);
      currentlen = inet6_opt_append(NULL, 0, currentlen, OPT_Y,
                                    7, 4, NULL);
      if (currentlen == -1)
              return (-1);
      currentlen = inet6_opt_finish(NULL, 0, currentlen);
      if (currentlen == -1)
              return (-1);
      extlen = currentlen;

      extbuf = malloc(extlen);
      if (extbuf == NULL) {
              perror("malloc");
              return (-1);
      }
      currentlen = inet6_opt_init(extbuf, extlen);
      if (currentlen == -1)
              return (-1);

      currentlen = inet6_opt_append(extbuf, extlen, currentlen,
          OPT_X, 12, 8, &databuf);
      if (currentlen == -1)
              return (-1);
      /* Insert value 0x12345678 for 4-octet field */
      offset = 0;
      value4 = 0x12345678;
      offset = inet6_opt_set_val(databuf, offset,
                                 &value4, sizeof (value4));
      /* Insert value 0x0102030405060708 for 8-octet field */
      value8 = 0x0102030405060708;
      offset = inet6_opt_set_val(databuf, offset,
                                 &value8, sizeof (value8));

      currentlen = inet6_opt_append(extbuf, extlen, currentlen,
          OPT_Y, 7, 4, &databuf);
      if (currentlen == -1)
              return (-1);
      /* Insert value 0x01 for 1-octet field */
      offset = 0;
      value1 = 0x01;
      offset = inet6_opt_set_val(databuf, offset,
                                 &value1, sizeof (value1));

Top      Up      ToC       Page 74 
      /* Insert value 0x1331 for 2-octet field */
      value2 = 0x1331;
      offset = inet6_opt_set_val(databuf, offset,
                                 &value2, sizeof (value2));
      /* Insert value 0x01020304 for 4-octet field */
      value4 = 0x01020304;
      offset = inet6_opt_set_val(databuf, offset,
                                 &value4, sizeof (value4));

      currentlen = inet6_opt_finish(extbuf, extlen, currentlen);
      if (currentlen == -1)
              return (-1);
      /* extbuf and extlen are now completely formatted */

22.2.  Parsing Received Options

   This example parses and prints the content of the two options in the
   previous example.

      int
      print_opt(void *extbuf, socklen_t extlen)
      {
              struct ip6_dest *ext;
              int currentlen;
              uint8_t type;
              socklen_t len;
              void *databuf;
              int offset;
              uint8_t value1;
              uint16_t value2;
              uint32_t value4;
              uint64_t value8;

              ext = (struct ip6_dest *)extbuf;
              printf("nxt %u, len %u (bytes %d)\n", ext->ip6d_nxt,
                  ext->ip6d_len, (ext->ip6d_len + 1) * 8);

              currentlen = 0;
              while (1) {
                      currentlen = inet6_opt_next(extbuf, extlen,
                                                  currentlen, &type,
                                                  &len, &databuf);
                      if (currentlen == -1)
                              break;
                      printf("Received opt %u len %u\n",
                          type, len);
                      switch (type) {
                      case OPT_X:

Top      Up      ToC       Page 75 
                              offset = 0;
                              offset =
                                  inet6_opt_get_val(databuf, offset,
                                                    &value4,
                                                    sizeof (value4));
                              printf("X 4-byte field %x\n", value4);
                              offset =
                                  inet6_opt_get_val(databuf, offset,
                                                    &value8,
                                                    sizeof (value8));
                              printf("X 8-byte field %llx\n", value8);
                              break;
                      case OPT_Y:
                              offset = 0;
                              offset =
                                  inet6_opt_get_val(databuf, offset,
                                                    &value1,
                                                    sizeof (value1));
                              printf("Y 1-byte field %x\n", value1);
                              offset =
                                  inet6_opt_get_val(databuf, offset,
                                                    &value2,
                                                    sizeof (value2));
                              printf("Y 2-byte field %x\n", value2);
                              offset =
                                  inet6_opt_get_val(databuf, offset,
                                                    &value4,
                                                    sizeof (value4));
                              printf("Y 4-byte field %x\n", value4);
                              break;
                      default:
                              printf("Unknown option %u\n", type);
                              break;
                      }
              }
              return (0);
      }

Top      Up      ToC       Page 76 
23. Authors' Addresses

   W. Richard Stevens (deceased)


   Matt Thomas
   3am Software Foundry
   8053 Park Villa Circle
   Cupertino, CA 95014

   EMail: matt@3am-software.com


   Erik Nordmark
   Sun Microsystems Laboratories, Europe
   180, avenue de l'Europe
   38334 SAINT ISMIER Cedex, France

   Phone: +33 (0)4 74 18 88 03
   Fax:   +33 (0)4 76 18 88 88
   EMail: Erik.Nordmark@sun.com


   Tatuya JINMEI
   Corporate Research & Development Center, Toshiba Corporation
   1 Komukai Toshiba-cho, Kawasaki-shi
   Kanagawa 212-8582, Japan

   EMail: jinmei@isl.rdc.toshiba.co.jp

Top      Up      ToC       Page 77 
24. Full Copyright Statement

   Copyright (C) The Internet Society (2003).  All Rights Reserved.

   This document and translations of it may be copied and furnished to
   others, and derivative works that comment on or otherwise explain it
   or assist in its implementation may be prepared, copied, published
   and distributed, in whole or in part, without restriction of any
   kind, provided that the above copyright notice and this paragraph are
   included on all such copies and derivative works.  However, this
   document itself may not be modified in any way, such as by removing
   the copyright notice or references to the Internet Society or other
   Internet organizations, except as needed for the purpose of
   developing Internet standards in which case the procedures for
   copyrights defined in the Internet Standards process must be
   followed, or as required to translate it into languages other than
   English.

   The limited permissions granted above are perpetual and will not be
   revoked by the Internet Society or its successors or assigns.

   This document and the information contained herein is provided on an
   "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
   TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
   BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
   HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.

Acknowledgement

   Funding for the RFC Editor function is currently provided by the
   Internet Society.