RFC3542 - Advanced Sockets Application Program Interface (AP(3)

时间:2005-02-17 来源: 作者: 点击:
there is no way to pass the destination via getsockopt(). When getsockopt() for this option is issued on a non-connected socket, the call will fail. Despite this limitation, this option is still usef
  
there is no way to pass the destination via getsockopt(). When
getsockopt() for this option is issued on a non-connected socket, the
call will fail. Despite this limitation, this option is still useful
from a practical point of view, because applications that care about
the path MTU tend to send a lot of packets to a single destination
and to connect the socket to the destination for performance reasons.
If the application needs to get the MTU value in a more generic way,
it should use a more generic interface, such as routing sockets
[TCPIPILLUST].

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 RFC2292

Significant changes that affect the compatibility to RFC2292:

- 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 RFC2292.

- 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", RFC1981, August 1996.

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

[RFC-3493] Gilligan, R., Thomson, S., Bound, J., McCann, J. and
W. Stevens, "Basic Socket Interface Extensions for
IPv6", RFC3493, 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:
------分隔线----------------------------
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
最新评论 查看所有评论
发表评论 查看所有评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 密码: 验证码:
推荐内容