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
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.
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
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.
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
<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{};
<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
<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 **);
<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.
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.
- 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.
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.
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
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.
|<--------------------------- 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() */
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.)
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.
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.
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).
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 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
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.
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.
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.
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);
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 */
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));
/* 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:
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); }
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
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.