RFC 2292
Advanced Sockets API for IPv6
Status of this Memo ๐
Okay bestie, this one's an Informational doc ๐ง It gives info to the Internet community, but it doesn't set any kind of Internet standard. Anyone can pass it around freely.
Copyright Notice ๐
Copyright (C) The Internet Society (1998). All Rights Reserved. ๐
Abstract ๐ฆ
Work is in progress on changes to the sockets API so it can support IP version 6 [RFC-2133]. These changes are for TCP and UDP-based apps and will cover most everyday end-user stuff like Telnet and FTP clients and servers, HTTP clients and servers, and the like. ๐
But there's another group of apps that will also run under IPv6. We call these "advanced" applications, and right now that includes things like Ping, Traceroute, routing daemons, multicast routing daemons, router discovery daemons, and the like ๐ What makes them "advanced" is the API feature they usually use: a raw socket to access ICMPv4, IGMPv4, or IPv4, plus some knowledge of the packet header formats those protocols use. So to keep raw-socket apps portable under IPv6, we need some standardization for the advanced API features ๐ฅ
There are other IPv6 features some apps will need to reach. That's interface identification (picking the outgoing interface and figuring out the incoming one) and IPv6 extension headers that [RFC-2133] doesn't cover: Hop-by-Hop options, Destination options, and the Routing header (source routing). This document gives API access to those features too โจ
Table of Contents ๐๏ธ
1. Introduction ................................................3
2. Common Structures and Definitions ...........................5
2.1. The ip6_hdr Structure ..................................5
2.1.1. IPv6 Next Header Values .........................6
2.1.2. IPv6 Extension Headers ..........................6
2.2. The icmp6_hdr Structure ................................8
2.2.1. ICMPv6 Type and Code Values .....................8
2.2.2. ICMPv6 Neighbor Discovery Type and Code Values ..9
2.3. Address Testing Macros .................................12
2.4. Protocols File .........................................12
3. IPv6 Raw Sockets ............................................13
3.1. Checksums ..............................................14
3.2. ICMPv6 Type Filtering ..................................14
4. Ancillary Data ..............................................17
4.1. The msghdr Structure ...................................18
4.2. The cmsghdr Structure ..................................18
4.3. Ancillary Data Object Macros ...........................19
4.3.1. CMSG_FIRSTHDR ...................................20
4.3.2. CMSG_NXTHDR .....................................22
4.3.3. CMSG_DATA .......................................22
4.3.4. CMSG_SPACE ......................................22
4.3.5. CMSG_LEN ........................................22
4.4. Summary of Options Described Using Ancillary Data ......23
4.5. IPV6_PKTOPTIONS Socket Option ..........................24
4.5.1. TCP Sticky Options ..............................25
4.5.2. UDP and Raw Socket Sticky Options ...............26
5. Packet Information ..........................................26
5.1. Specifying/Receiving the Interface .....................27
5.2. Specifying/Receiving Source/Destination Address ........27
5.3. Specifying/Receiving the Hop Limit .....................28
5.4. Specifying the Next Hop Address ........................29
5.5. Additional Errors with sendmsg() .......................29
6. Hop-By-Hop Options ..........................................30
6.1. Receiving Hop-by-Hop Options ...........................31
6.2. Sending Hop-by-Hop Options .............................31
6.3. Hop-by-Hop and Destination Options Processing ..........32
6.3.1. inet6_option_space ..............................32
6.3.2. inet6_option_init ...............................32
6.3.3. inet6_option_append .............................33
6.3.4. inet6_option_alloc ..............................33
6.3.5. inet6_option_next ...............................34
6.3.6. inet6_option_find ...............................35
6.3.7. Options Examples ................................35
7. Destination Options .........................................42
7.1. Receiving Destination Options ..........................42
7.2. Sending Destination Options ............................43
8. Routing Header Option .......................................43
8.1. inet6_rthdr_space ......................................44
8.2. inet6_rthdr_init .......................................45
8.3. inet6_rthdr_add ........................................45
8.4. inet6_rthdr_lasthop ....................................46
8.5. inet6_rthdr_reverse ....................................46
8.6. inet6_rthdr_segments ...................................46
8.7. inet6_rthdr_getaddr ....................................46
8.8. inet6_rthdr_getflags ...................................47
8.9. Routing Header Example .................................47
9. Ordering of Ancillary Data and IPv6 Extension Headers .......53
10. IPv6-Specific Options with IPv4-Mapped IPv6 Addresses .......54
11. rresvport_af ................................................55
12. Future Items ................................................55
12.1. Flow Labels ...........................................55
12.2. Path MTU Discovery and UDP ............................56
12.3. Neighbor Reachability and UDP .........................56
13. Summary of New Definitions ..................................56
14. Security Considerations .....................................59
15. Change History ..............................................59
16. References ..................................................65
17. Acknowledgments .............................................65
18. Authors' Addresses ..........................................66
19. Full Copyright Statement ....................................67
1. Introduction โจ
Specifications are in progress for changes to the sockets API so it can support IP version 6 [RFC-2133]. These changes are for TCP and UDP-based apps. This doc defines some of the "advanced" features of the sockets API that applications need to take advantage of extra IPv6 features ๐ฆ
Right now, IPv4 raw sockets are pretty portable, but that's mostly because most IPv4 implementations started from a common base (the Berkeley source code), or at least from the Berkeley headers. That's how programs like Ping and Traceroute can compile with minimal effort on lots of hosts that support the sockets API ๐ With IPv6, though, there's no common source code base for implementors to start from, so the chance of different implementations diverging at this level is high ๐ญ To avoid a total lack of portability for apps that use raw IPv6 sockets, some standardization is necessary.
There are also features from the basic IPv6 specification that [RFC-2133] doesn't address: sending and receiving Hop-by-Hop options, Destination options, and Routing headers, specifying the outgoing interface, and being told about the receiving interface ๐ธ
This document can be split into the following main sections ๐
- Definitions of the basic constants and structures that applications need to use raw IPv6 sockets. This includes structure definitions for the IPv6 and ICMPv6 headers and all their associated constants (for example, values for the Next Header field).
- Some basic semantic definitions for IPv6 raw sockets. For example, a raw ICMPv4 socket requires the application to calculate and store the ICMPv4 header checksum. With IPv6, though, that would force the application to choose the source IPv6 address, because the source address is part of the pseudo header that ICMPv6 now uses for its checksum computation. So it should be defined that with a raw ICMPv6 socket, the kernel always calculates and stores the ICMPv6 header checksum ๐ฅ
- Packet information: how applications can get the received interface, destination address, and received hop limit, plus how to specify these values on a per-packet basis. There's a class of applications that need this capability, and the technique should be portable ๐
- Access to the optional Hop-by-Hop, Destination, and Routing headers.
- Additional features needed for IPv6 application portability โจ
The packet information and access to the extension headers (Hop-by-Hop options, Destination options, and Routing header) are specified using the "ancillary data" fields that were added to the 4.3BSD Reno sockets API in 1990. The reason is that these ancillary data fields are part of the Posix.1g standard (which should be approved in 1997), so most vendors should adopt them ๐ง
This document does not address application access to either the authentication header or the encapsulating security payload header ๐
All examples in this document leave out error checking for the sake of brevity and clarity ๐
We note that many of the functions and socket options defined in this document may have error returns that this document doesn't define. Many of these possible error returns will only be recognized as implementations progress ๐
Datatypes in this document follow the Posix.1g format: intN_t means a signed integer of exactly N bits (e.g., int16_t), and uintN_t means an unsigned integer of exactly N bits (e.g., uint32_t) ๐
Note that we use the (unofficial) terminology ICMPv4, IGMPv4, and ARPv4 so there's no confusion with the newer ICMPv6 protocol ๐
2. Common Structures and Definitions ๐ง
Lots of advanced applications look at fields in the IPv6 header and set and examine fields in the various ICMPv6 headers. Common structure definitions for these headers are required, along with common constant definitions for the structure members ๐ซถ
Two new headers are defined: <netinet/ip6.h> and <netinet/icmp6.h> ๐
When an include file is specified, that include file is allowed to include other files that do the actual declaration or definition โจ
2.1. The ip6_hdr Structure ๐
Okay bestie, including <netinet/ip6.h> gives you this struct. Heads up, it's a brand-new header โจ Here's the struct plus its shortcut macros:
struct ip6_hdr {
union {
struct ip6_hdrctl {
uint32_t ip6_un1_flow; /* 24 bits of flow-ID */
uint16_t ip6_un1_plen; /* payload length */
uint8_t ip6_un1_nxt; /* next header */
uint8_t ip6_un1_hlim; /* hop limit */
} ip6_un1;
uint8_t ip6_un2_vfc; /* 4 bits version, 4 bits priority */
} ip6_ctlun;
struct in6_addr ip6_src; /* source address */
struct in6_addr ip6_dst; /* destination address */
};
#define ip6_vfc ip6_ctlun.ip6_un2_vfc
#define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow
#define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen
#define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt
#define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim
#define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim
2.1.1. IPv6 Next Header Values ๐ฆ
IPv6 adds a whole bunch of new values for the Next Header field, so slay. Including <netinet/in.h> gives you these constants:
#define IPPROTO_HOPOPTS 0 /* IPv6 Hop-by-Hop options */
#define IPPROTO_IPV6 41 /* IPv6 header */
#define IPPROTO_ROUTING 43 /* IPv6 Routing header */
#define IPPROTO_FRAGMENT 44 /* IPv6 fragmentation header */
#define IPPROTO_ESP 50 /* encapsulating security payload */
#define IPPROTO_AH 51 /* authentication header */
#define IPPROTO_ICMPV6 58 /* ICMPv6 */
#define IPPROTO_NONE 59 /* IPv6 no next header */
#define IPPROTO_DSTOPTS 60 /* IPv6 Destination options */
Quick side note ๐ Berkeley-derived IPv4 implementations also define IPPROTO_IP as 0. That's not a problem, because IPPROTO_IP is only used with IPv4 sockets and IPPROTO_HOPOPTS is only used with IPv6 sockets. No drama ๐
2.1.2. IPv6 Extension Headers ๐
Six extension headers are defined for IPv6. We define structs for all of them except the Authentication header and the Encapsulating Security Payload header, both of which are out of scope for this doc. The following structs come from including <netinet/ip6.h>:
/* Hop-by-Hop options header */
/* XXX should we pad it to force alignment on an 8-byte boundary? */
struct ip6_hbh {
uint8_t ip6h_nxt; /* next header */
uint8_t ip6h_len; /* length in units of 8 octets */
/* followed by options */
};
/* Destination options header */
/* XXX should we pad it to force alignment on an 8-byte boundary? */
struct ip6_dest {
uint8_t ip6d_nxt; /* next header */
uint8_t ip6d_len; /* length in units of 8 octets */
/* followed by options */
};
/* Routing header */
struct ip6_rthdr {
uint8_t ip6r_nxt; /* next header */
uint8_t ip6r_len; /* length in units of 8 octets */
uint8_t ip6r_type; /* routing type */
uint8_t ip6r_segleft; /* segments left */
/* followed by routing type specific data */
};
/* Type 0 Routing header */
struct ip6_rthdr0 {
uint8_t ip6r0_nxt; /* next header */
uint8_t ip6r0_len; /* length in units of 8 octets */
uint8_t ip6r0_type; /* always zero */
uint8_t ip6r0_segleft; /* segments left */
uint8_t ip6r0_reserved; /* reserved field */
uint8_t ip6r0_slmap[3]; /* strict/loose bit map */
struct in6_addr ip6r0_addr[1]; /* up to 23 addresses */
};
/* Fragment header */
struct ip6_frag {
uint8_t ip6f_nxt; /* next header */
uint8_t ip6f_reserved; /* reserved field */
uint16_t ip6f_offlg; /* offset, reserved, and flag */
uint32_t ip6f_ident; /* identification */
};
#if BYTE_ORDER == BIG_ENDIAN
#define IP6F_OFF_MASK 0xfff8 /* mask out offset from _offlg */
#define IP6F_RESERVED_MASK 0x0006 /* reserved bits in ip6f_offlg */
#define IP6F_MORE_FRAG 0x0001 /* more-fragments flag */
#else /* BYTE_ORDER == LITTLE_ENDIAN */
#define IP6F_OFF_MASK 0xf8ff /* mask out offset from _offlg */
#define IP6F_RESERVED_MASK 0x0600 /* reserved bits in ip6f_offlg */
#define IP6F_MORE_FRAG 0x0100 /* more-fragments flag */
#endif
Okay, byte order is the important bit ๐ Defined constants for fields bigger than 1 byte depend on the byte ordering being used. This API assumes the fields in the protocol headers are left in network byte order, which is big-endian for the Internet protocols. If they aren't, then either these constants or the fields being tested must be converted at run-time, using something like htons() or htonl().
(Note: we show an implementation that supports both big-endian and little-endian byte ordering, assuming a hypothetical compile-time#iftest to determine the byte ordering. TheBYTE_ORDERconstant, with values ofBIG_ENDIANandLITTLE_ENDIAN, is only there for example purposes. If an implementation runs on only one type of hardware, it only needs to define the set of constants for that hardware's byte ordering.) ๐ง
2.2. The icmp6_hdr Structure ๐ธ
The ICMPv6 header is needed by a bunch of IPv6 apps, including Ping, Traceroute, router discovery daemons, and neighbor discovery daemons. The following struct comes from including <netinet/icmp6.h>. Heads up, this is also a new header ๐
struct icmp6_hdr {
uint8_t icmp6_type; /* type field */
uint8_t icmp6_code; /* code field */
uint16_t icmp6_cksum; /* checksum field */
union {
uint32_t icmp6_un_data32[1]; /* type-specific field */
uint16_t icmp6_un_data16[2]; /* type-specific field */
uint8_t icmp6_un_data8[4]; /* type-specific field */
} icmp6_dataun;
};
#define icmp6_data32 icmp6_dataun.icmp6_un_data32
#define icmp6_data16 icmp6_dataun.icmp6_un_data16
#define icmp6_data8 icmp6_dataun.icmp6_un_data8
#define icmp6_pptr icmp6_data32[0] /* parameter prob */
#define icmp6_mtu icmp6_data32[0] /* packet too big */
#define icmp6_id icmp6_data16[0] /* echo request/reply */
#define icmp6_seq icmp6_data16[1] /* echo request/reply */
#define icmp6_maxdelay icmp6_data16[0] /* mcast group membership */
2.2.1. ICMPv6 Type and Code Values โจ
Besides the common struct for the ICMPv6 header, we also need common definitions for the ICMPv6 type and code fields. The following constants come from including <netinet/icmp6.h> too:
#define ICMP6_DST_UNREACH 1
#define ICMP6_PACKET_TOO_BIG 2
#define ICMP6_TIME_EXCEEDED 3
#define ICMP6_PARAM_PROB 4
#define ICMP6_INFOMSG_MASK 0x80 /* all informational messages */
#define ICMP6_ECHO_REQUEST 128
#define ICMP6_ECHO_REPLY 129
#define ICMP6_MEMBERSHIP_QUERY 130
#define ICMP6_MEMBERSHIP_REPORT 131
#define ICMP6_MEMBERSHIP_REDUCTION 132
#define ICMP6_DST_UNREACH_NOROUTE 0 /* no route to destination */
#define ICMP6_DST_UNREACH_ADMIN 1 /* communication with */
/* destination */
/* administratively */
/* prohibited */
#define ICMP6_DST_UNREACH_NOTNEIGHBOR 2 /* not a neighbor */
#define ICMP6_DST_UNREACH_ADDR 3 /* address unreachable */
#define ICMP6_DST_UNREACH_NOPORT 4 /* bad port */
#define ICMP6_TIME_EXCEED_TRANSIT 0 /* Hop Limit == 0 in transit */
#define ICMP6_TIME_EXCEED_REASSEMBLY 1 /* Reassembly time out */
#define ICMP6_PARAMPROB_HEADER 0 /* erroneous header field */
#define ICMP6_PARAMPROB_NEXTHEADER 1 /* unrecognized Next Header */
#define ICMP6_PARAMPROB_OPTION 2 /* unrecognized IPv6 option */
The five ICMP message types defined by IPv6 neighbor discovery (133-137) are covered in the next section ๐ฆ
2.2.2. ICMPv6 Neighbor Discovery Type and Code Values โจ
okay so this bit is where <netinet/icmp6.h> gives you all the neighbor discovery goodies ๐ง literally the whole ND crew, bestie ๐
#define ND_ROUTER_SOLICIT 133
#define ND_ROUTER_ADVERT 134
#define ND_NEIGHBOR_SOLICIT 135
#define ND_NEIGHBOR_ADVERT 136
#define ND_REDIRECT 137
struct nd_router_solicit { /* router solicitation */
struct icmp6_hdr nd_rs_hdr;
/* could be followed by options */
};
#define nd_rs_type nd_rs_hdr.icmp6_type
#define nd_rs_code nd_rs_hdr.icmp6_code
#define nd_rs_cksum nd_rs_hdr.icmp6_cksum
#define nd_rs_reserved nd_rs_hdr.icmp6_data32[0]
struct nd_router_advert { /* router advertisement */
struct icmp6_hdr nd_ra_hdr;
uint32_t nd_ra_reachable; /* reachable time */
uint32_t nd_ra_retransmit; /* retransmit timer */
/* could be followed by options */
};
#define nd_ra_type nd_ra_hdr.icmp6_type
#define nd_ra_code nd_ra_hdr.icmp6_code
#define nd_ra_cksum nd_ra_hdr.icmp6_cksum
#define nd_ra_curhoplimit nd_ra_hdr.icmp6_data8[0]
#define nd_ra_flags_reserved nd_ra_hdr.icmp6_data8[1]
#define ND_RA_FLAG_MANAGED 0x80
#define ND_RA_FLAG_OTHER 0x40
#define nd_ra_router_lifetime nd_ra_hdr.icmp6_data16[1]
struct nd_neighbor_solicit { /* neighbor solicitation */
struct icmp6_hdr nd_ns_hdr;
struct in6_addr nd_ns_target; /* target address */
/* could be followed by options */
};
#define nd_ns_type nd_ns_hdr.icmp6_type
#define nd_ns_code nd_ns_hdr.icmp6_code
#define nd_ns_cksum nd_ns_hdr.icmp6_cksum
#define nd_ns_reserved nd_ns_hdr.icmp6_data32[0]
struct nd_neighbor_advert { /* neighbor advertisement */
struct icmp6_hdr nd_na_hdr;
struct in6_addr nd_na_target; /* target address */
/* could be followed by options */
};
#define nd_na_type nd_na_hdr.icmp6_type
#define nd_na_code nd_na_hdr.icmp6_code
#define nd_na_cksum nd_na_hdr.icmp6_cksum
#define nd_na_flags_reserved nd_na_hdr.icmp6_data32[0]
#if BYTE_ORDER == BIG_ENDIAN
#define ND_NA_FLAG_ROUTER 0x80000000
#define ND_NA_FLAG_SOLICITED 0x40000000
#define ND_NA_FLAG_OVERRIDE 0x20000000
#else /* BYTE_ORDER == LITTLE_ENDIAN */
#define ND_NA_FLAG_ROUTER 0x00000080
#define ND_NA_FLAG_SOLICITED 0x00000040
#define ND_NA_FLAG_OVERRIDE 0x00000020
#endif
struct nd_redirect { /* redirect */
struct icmp6_hdr nd_rd_hdr;
struct in6_addr nd_rd_target; /* target address */
struct in6_addr nd_rd_dst; /* destination address */
/* could be followed by options */
};
#define nd_rd_type nd_rd_hdr.icmp6_type
#define nd_rd_code nd_rd_hdr.icmp6_code
#define nd_rd_cksum nd_rd_hdr.icmp6_cksum
#define nd_rd_reserved nd_rd_hdr.icmp6_data32[0]
struct nd_opt_hdr { /* Neighbor discovery option header */
uint8_t nd_opt_type;
uint8_t nd_opt_len; /* in units of 8 octets */
/* followed by option specific data */
};
#define ND_OPT_SOURCE_LINKADDR 1
#define ND_OPT_TARGET_LINKADDR 2
#define ND_OPT_PREFIX_INFORMATION 3
#define ND_OPT_REDIRECTED_HEADER 4
#define ND_OPT_MTU 5
struct nd_opt_prefix_info { /* prefix information */
uint8_t nd_opt_pi_type;
uint8_t nd_opt_pi_len;
uint8_t nd_opt_pi_prefix_len;
uint8_t nd_opt_pi_flags_reserved;
uint32_t nd_opt_pi_valid_time;
uint32_t nd_opt_pi_preferred_time;
uint32_t nd_opt_pi_reserved2;
struct in6_addr nd_opt_pi_prefix;
};
#define ND_OPT_PI_FLAG_ONLINK 0x80
#define ND_OPT_PI_FLAG_AUTO 0x40
struct nd_opt_rd_hdr { /* redirected header */
uint8_t nd_opt_rh_type;
uint8_t nd_opt_rh_len;
uint16_t nd_opt_rh_reserved1;
uint32_t nd_opt_rh_reserved2;
/* followed by IP header and data */
};
struct nd_opt_mtu { /* MTU option */
uint8_t nd_opt_mtu_type;
uint8_t nd_opt_mtu_len;
uint16_t nd_opt_mtu_reserved;
uint32_t nd_opt_mtu_mtu;
};
ngl the nd_na_flags_reserved flags have the same byte ordering drama we already talked about with ip6f_offlg ๐ญ so keep that in mind, it's giving dรฉjร vu ๐
2.3. Address Testing Macros ๐ธ
so the basic API ([RFC-2133]) already has some macros for checking whether an IPv6 address has certain properties. this API adds more address testing macros of its own, and they come from including <netinet/in.h> ๐
int IN6_ARE_ADDR_EQUAL(const struct in6_addr *,
const struct in6_addr *);
2.4. Protocols File ๐
a bunch of hosts have a file called /etc/protocols that lists the names of the IP protocols along with their protocol numbers (like the value of the protocol field in the IPv4 header, so 1 for ICMP, for example). some programs call getprotobyname() to grab the protocol value, which then gets passed as the third argument to socket(). the Ping program does it like this ๐ง
struct protoent *proto;
proto = getprotobyname("icmp");
s = socket(AF_INET, SOCK_RAW, proto->p_proto);
the new IPv6 protocols need common names in this file too, so that apps calling the getprotoXXX() functions stay portable ๐ซถ
we define the following protocol names with the values shown. these are taken from ftp://ftp.isi.edu/in-notes/iana/assignments/protocol-numbers.
hopopt 0 # hop-by-hop options for ipv6
ipv6 41 # ipv6
ipv6-route 43 # routing header for ipv6
ipv6-frag 44 # fragment header for ipv6
esp 50 # encapsulating security payload for ipv6
ah 51 # authentication header for ipv6
ipv6-icmp 58 # icmp for ipv6
ipv6-nonxt 59 # no next header for ipv6
ipv6-opts 60 # destination options for ipv6
3. IPv6 Raw Sockets ๐ฆ
okay so raw sockets skip the transport layer (TCP or UDP) entirely ๐ with IPv4, they were used for ICMPv4, IGMPv4, and for reading and writing IPv4 datagrams with a protocol field the kernel doesn't process. OSPF's routing daemon is the classic example, since it uses IPv4 protocol field 89. with IPv6, raw sockets get used for ICMPv6, plus reading and writing IPv6 datagrams with a Next Header field the kernel doesn't process. examples are an OSPF-for-IPv6 routing daemon and RSVP (protocol field 46) ๐
all data sent via raw sockets MUST be in network byte order, and all data received via raw sockets will be in network byte order. this is different from IPv4 raw sockets, which didn't specify a byte order and usually just used the host's byte order ๐
another difference from IPv4 raw sockets: you can't read or write complete packets (IPv6 packets with extension headers) using the IPv6 raw sockets API ๐ญ instead, ancillary data objects carry the extension headers, as described later in this document. if an app needs the complete IPv6 packet, it has to use some other technique, like the datalink interfaces BPF or DLPI โจ
every field in the IPv6 header that an app might want to change (basically everything except the version number) can be modified for output using ancillary data and/or socket options. on input, every field in a received IPv6 header (except the version number and Next Header fields) and all extension headers are also handed to the app as ancillary data. so there's no need for anything like the IPv4 IP_HDRINCL socket option, periodt ๐
when you write to a raw socket, the kernel automatically fragments the packet if it's bigger than the path MTU, and inserts the required fragmentation headers. on input, the kernel reassembles received fragments, so the raw socket reader never sees any fragment headers ๐ง
when we say "an ICMPv6 raw socket," we mean a socket created by calling the socket function with the three arguments PF_INET6, SOCK_RAW, and IPPROTO_ICMPV6 ๐ธ
most IPv4 implementations treat a raw socket made with a third socket() argument of IPPROTO_RAW (normally 255) specially. this value has no special meaning for an IPv6 raw socket, and IANA currently reserves the value 255 when it's used as a next-header field ๐ (Note: This feature was added to IPv4 in 1988 by Van Jacobson to support traceroute, allowing a complete IP header to be passed by the application, before the IP_HDRINCL socket option was added.) ๐
3.1. Checksums ๐
the kernel will calculate and insert the ICMPv6 checksum for ICMPv6 raw sockets, since this checksum is mandatory โจ
for other raw IPv6 sockets (ones created with a third argument other than IPPROTO_ICMPV6), the app must set the new IPV6_CHECKSUM socket option so the kernel can (1) compute and store a checksum for output, and (2) verify the received checksum on input, discarding the packet if the checksum is wrong ๐ฅ this option means apps don't have to do source address selection on the packets they send ๐ซถ the checksum includes the IPv6 pseudo-header, defined in Section 8.1 of [RFC-1883]. this new socket option also specifies an integer offset into the user data where the checksum lives ๐
int offset = 2;
setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &offset, sizeof(offset));
by default this socket option is disabled ๐ setting the offset to -1 also disables it. by disabled we mean (1) the kernel won't calculate and store a checksum for outgoing packets, and (2) the kernel won't verify a checksum for received packets ๐
(Note: Since the checksum is always calculated by the kernel for an ICMPv6 socket, applications are not able to generate ICMPv6 packets with incorrect checksums (presumably for testing purposes) using this API.) ๐ง
3.2. ICMPv6 Type Filtering ๐ฆ
okay so ICMPv4 raw sockets get most of the ICMPv4 messages the kernel receives, ngl. (we say "most" and not "all" bc Berkeley-derived kernels never pass echo requests, timestamp requests, or address mask requests to a raw socket. instead the kernel handles those three entirely by itself ๐) but ICMPv6 is a superset of ICMPv4, and it also covers the functionality of IGMPv4 and ARPv4. so an ICMPv6 raw socket can potentially receive WAY more messages than an ICMPv4 one: ICMP messages similar to ICMPv4, plus neighbor solicitations, neighbor advertisements, and the three group membership messages. main character energy ๐
most apps using an ICMPv6 raw socket only care about a small subset of the message types. moving extraneous ICMPv6 messages from the kernel up to user space can cost a significant amount of overhead ๐ญ so this API includes a way to filter ICMPv6 messages by the ICMPv6 type field. ๐ง
Each ICMPv6 raw socket has an associated filter, and its datatype is defined as
struct icmp6_filter;
this structure, along with the macros and constants defined later in this section, comes from including the <netinet/icmp6.h> header. ๐
the current filter gets fetched and stored using getsockopt() and setsockopt() with a level of IPPROTO_ICMPV6 and an option name of ICMP6_FILTER. โจ
there are six macros that operate on an icmp6_filter structure:
void ICMP6_FILTER_SETPASSALL (struct icmp6_filter *);
void ICMP6_FILTER_SETBLOCKALL(struct icmp6_filter *);
void ICMP6_FILTER_SETPASS ( int, struct icmp6_filter *);
void ICMP6_FILTER_SETBLOCK( int, struct icmp6_filter *);
int ICMP6_FILTER_WILLPASS (int, const struct icmp6_filter *);
int ICMP6_FILTER_WILLBLOCK(int, const struct icmp6_filter *);
the first argument to the last four macros (an integer) is an ICMPv6 message type, between 0 and 255. the pointer argument to all six macros is a pointer to a filter. the first four macros modify that filter, and the last two examine it. ๐ธ
the first two macros, SETPASSALL and SETBLOCKALL, let you say that all ICMPv6 messages get passed to the application, or that all of them get blocked from being passed to it. ๐
the next two, SETPASS and SETBLOCK, let you say that messages of a given ICMPv6 type should be passed to the application, or not passed (blocked). ๐
the final two, WILLPASS and WILLBLOCK, return true or false depending on whether the specified message type is passed or blocked by the filter pointed to by the second argument. ๐ฅ
when an ICMPv6 raw socket gets created, it passes all ICMPv6 message types to the application by default. ๐
as an example, a program that only wants to receive router advertisements could do the following:
struct icmp6_filter myfilt;
fd = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
ICMP6_FILTER_SETBLOCKALL(&myfilt);
ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &myfilt);
setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &myfilt, sizeof(myfilt));
the filter structure gets declared and then initialized to block all message types. then it gets changed to allow router advertisement messages through to the application, and the filter gets installed with setsockopt(). this is the cutest little setup ever ๐งโจ
the icmp6_filter structure is similar to the fd_set datatype used with the select() function in the sockets API. it's an opaque datatype, so the application shouldn't care how it's implemented. all the application does with it is allocate a variable of this type, pass a pointer to that variable to getsockopt() and setsockopt(), and operate on it using the six macros we just defined. ๐
that said, it's worth showing a simple implementation of this datatype and the six macros. ๐ซถ
struct icmp6_filter {
uint32_t icmp6_filt[8]; /* 8*32 = 256 bits */
};
#define ICMP6_FILTER_WILLPASS(type, filterp) \
((((filterp)->icmp6_filt[(type) >> 5]) & (1 << ((type) & 31))) != 0)
#define ICMP6_FILTER_WILLBLOCK(type, filterp) \
((((filterp)->icmp6_filt[(type) >> 5]) & (1 << ((type) & 31))) == 0)
#define ICMP6_FILTER_SETPASS(type, filterp) \
((((filterp)->icmp6_filt[(type) >> 5]) |= (1 << ((type) & 31))))
#define ICMP6_FILTER_SETBLOCK(type, filterp) \
((((filterp)->icmp6_filt[(type) >> 5]) &= ~(1 << ((type) & 31))))
#define ICMP6_FILTER_SETPASSALL(filterp) \
memset((filterp), 0xFF, sizeof(struct icmp6_filter))
#define ICMP6_FILTER_SETBLOCKALL(filterp) \
memset((filterp), 0, sizeof(struct icmp6_filter))
(Note: These sample definitions have two limitations that an implementation may want to change. The first four macros evaluate their first argument two times. The second two macros require the inclusion of the<string.h>header for thememset()function.)
4. Ancillary Data โจ
4.2BSD let file descriptors get 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. then when the OSI protocols got added to 4.3BSD Reno in 1990, the names of those two fields changed to msg_control and msg_controllen, because the OSI protocols used them for "control information". the comments in the source code call this "ancillary data" though. ๐ง
other than the OSI protocols, ancillary data has been pretty rare. in 4.4BSD, for example, the only use of ancillary data with IPv4 is returning 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. ๐ธ
still, the ancillary data fields of the msghdr structure are a clean way to pass information alongside the data being read or written. including the msg_control and msg_controllen members of the msghdr structure, along with the cmsghdr structure that msg_control points to, is required by the Posix.1g sockets API standard (which was supposed to be completed during 1997). ๐
in this document, ancillary data is used to exchange the following optional information between the application and the kernel:
1. the send/receive interface and source/destination address,
2. the hop limit,
3. next hop address,
4. Hop-by-Hop options,
5. Destination options, and
6. Routing header.
before we get into these uses in detail, we'll review the definition of the msghdr structure itself, the cmsghdr structure that defines an ancillary data object, and some functions that operate on those objects. ๐ฆ
4.1. The msghdr Structure ๐
the msghdr structure is used by the recvmsg() and sendmsg() functions. its Posix.1g 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 */
size_t 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 by including <sys/socket.h>. ๐ง
(Note: Before Posix.1g, the twovoid *pointers were typicallychar *, and the twosocklen_tmembers and thesize_tmember were typically integers. Earlier drafts of Posix.1g had the twosocklen_tmembers assize_t, but Draft 6.6 of Posix.1g, apparently the final draft, changed these tosocklen_tto simplify binary portability for 64-bit implementations and to align Posix.1g with X/Open's Networking Services, Issue 5. The change inmsg_controlto avoid *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 Type 0 Routing header is 376 bytes.) ๐ญ๐
4.2. The cmsghdr Structure ๐
the cmsghdr structure describes ancillary data objects transferred by recvmsg() and sendmsg(). its Posix.1g 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 by including <sys/socket.h>. ๐
as shown in this definition, there normally isn't a member named cmsg_data[]. instead, the data portion is accessed using the CMSG_xxx() macros, which we'll cover shortly. still, it's common to refer to the cmsg_data[] member. ๐ซถ
(Note: Before Posix.1g, thecmsg_lenmember was an integer, not asocklen_t. See the Note in the previous section for whysocklen_tis used here.)
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 -------------------------->|
| |
|<----- ancillary data object ----->|<----- ancillary data object ----->|
|<---------- CMSG_SPACE() --------->|<---------- CMSG_SPACE() --------->|
| | |
|<---------- cmsg_len ---------->| |<--------- cmsg_len ----------->| |
|<--------- CMSG_LEN() --------->| |<-------- CMSG_LEN() ---------->| |
| | | | |
+-----+-----+-----+--+-----------+--+-----+-----+-----+--+-----------+--+
|cmsg_|cmsg_|cmsg_|XX| |XX|cmsg_|cmsg_|cmsg_|XX| |XX|
|len |level|type |XX|cmsg_data[]|XX|len |level|type |XX|cmsg_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 the implementation requires it. ๐
4.3. Ancillary Data Object Macros โจ
okay so, to make ancillary data less painful to handle, three macros from 4.4BSD got adopted by Posix.1g: CMSG_DATA(), CMSG_NXTHDR(), and CMSG_FIRSTHDR(). ๐ Before we get into them, here's 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_level == ... && cmsgptr->cmsg_type == ... ) {
u_char *ptr;
ptr = CMSG_DATA(cmsgptr);
/* process data pointed to by ptr */
}
}
We'll go through the three Posix.1g macros first, then the two new ones that this API adds: CMSG_SPACE() and CMSG_LEN() ๐ All of these macros are defined as a result of including <sys/socket.h> โจ
4.3.1. CMSG_FIRSTHDR
struct cmsghdr *CMSG_FIRSTHDR(const struct msghdr *mhdr);
CMSG_FIRSTHDR() gives you a pointer to the first cmsghdr structure inside the msghdr structure that mhdr points to ๐ฆ It returns NULL if there's no ancillary data in that msghdr structure. That happens when either msg_control is NULL, or when msg_controllen is smaller 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 don't check msg_controllen and just return the value of msg_control ๐ The value of msg_controllen must be tested, though! If the application asks recvmsg() to return ancillary data by pointing msg_control at its own buffer and setting msg_controllen to that buffer's length, the kernel says "no ancillary data here" by setting msg_controllen to 0 on return. It's also easier to put this check inside the macro than to make every application do it. ๐ง)
4.3.2. CMSG_NXTHDR
struct cmsghdr *CMSG_NXTHDR(const struct msghdr *mhdr,
const struct cmsghdr *cmsg);
CMSG_NXTHDR() returns a pointer to the cmsghdr structure that describes the next ancillary data object ๐ธ mhdr is a pointer to a msghdr structure, and cmsg is a pointer to a cmsghdr structure. If there isn't another ancillary data object, the return value is NULL.
This next bit is the new behavior for this API, and I'm obsessed with it ๐ญ If the cmsg pointer is NULL, you get back a pointer to the cmsghdr structure describing the first ancillary data object. So CMSG_NXTHDR(mhdr, NULL) is equivalent to CMSG_FIRSTHDR(mhdr). If there are no ancillary data objects at all, the return value is NULL. This gives you a different way to write the processing loop from earlier:
struct msghdr msg;
struct cmsghdr *cmsgptr = NULL;
/* fill in msg */
/* call recvmsg() */
while ((cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) != NULL) {
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((cmsg)->cmsg_len) \
+ ALIGN(sizeof(struct cmsghdr)) > \
(u_char *)((mhdr)->msg_control) + (mhdr)->msg_controllen) ? \
(struct cmsghdr *)NULL : \
(struct cmsghdr *)((u_char *)(cmsg) + ALIGN((cmsg)->cmsg_len))) )
The ALIGN() macro is implementation dependent ๐ It rounds its argument up to the next even multiple of whatever alignment is required (probably a multiple of 4 or 8 bytes).
4.3.3. CMSG_DATA
unsigned char *CMSG_DATA(const struct cmsghdr *cmsg);
CMSG_DATA() returns a pointer to the data that sits right after a cmsghdr structure. This is what gets called the cmsg_data[] member, even though no such member is actually defined in the structure ๐
One possible implementation could be:
#define CMSG_DATA(cmsg) ( (u_char *)(cmsg) + \
ALIGN(sizeof(struct cmsghdr)) )
4.3.4. CMSG_SPACE
unsigned int CMSG_SPACE(unsigned int length);
This one is new with this API ๐ Given the length of an ancillary data object, CMSG_SPACE() returns the space the object needs, including its cmsghdr structure and any padding needed to meet alignment requirements. You can use it, for example, to allocate space dynamically for the ancillary data ๐ฆ This macro should not be used to initialize the cmsg_len member of a cmsghdr structure. Use the CMSG_LEN() macro for that instead โญ
One possible implementation could be:
#define CMSG_SPACE(length) ( ALIGN(sizeof(struct cmsghdr)) + \
ALIGN(length) )
4.3.5. CMSG_LEN
unsigned int CMSG_LEN(unsigned int length);
Also new with this API โจ Given the length of an ancillary data object, CMSG_LEN() returns the value to store in the cmsg_len member of the cmsghdr structure, counting any padding needed for alignment ๐
One possible implementation could be:
#define CMSG_LEN(length) ( ALIGN(sizeof(struct cmsghdr)) + length
)
Now, the difference between CMSG_SPACE() and CMSG_LEN() is super important, and it's also shown in the figure in Section 4.2 ๐ซถ CMSG_SPACE() accounts for any padding required at the end of the ancillary data object. CMSG_LEN() gives the actual length to store in the cmsg_len member of the ancillary data object. Different jobs, different macros, no cap ๐
4.4. Summary of Options Described Using Ancillary Data
There are six types of optional information in this document that get passed between the application and the kernel using ancillary data ๐ธ
1. the send/receive interface and source/destination address,
2. the hop limit,
3. next hop address,
4. Hop-by-Hop options,
5. Destination options, and
6. Routing header.
First up, to receive any of this optional information (except the next hop address, which can only be set), the application has to call setsockopt() to turn on the matching flag ๐ฅ
int on = 1;
setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on));
setsockopt(fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, sizeof(on));
setsockopt(fd, IPPROTO_IPV6, IPV6_HOPOPTS, &on, sizeof(on));
setsockopt(fd, IPPROTO_IPV6, IPV6_DSTOPTS, &on, sizeof(on));
setsockopt(fd, IPPROTO_IPV6, IPV6_RTHDR, &on, sizeof(on));
When any of these options are enabled, recvmsg() hands the matching data back as control information, as one or more ancillary data objects ๐ง
Sending is way easier ๐ There's nothing special to do for any of this optional information. The application just calls sendmsg() and includes one or more ancillary data objects as control information.
Here's a summary of the three cmsghdr fields that describe the ancillary data objects ๐
cmsg_level cmsg_type cmsg_data[] #times
------------ ------------ ------------------------ ------
IPPROTO_IPV6 IPV6_PKTINFO in6_pktinfo structure once
IPPROTO_IPV6 IPV6_HOPLIMIT int once
IPPROTO_IPV6 IPV6_NEXTHOP socket address structure once
IPPROTO_IPV6 IPV6_HOPOPTS implementation dependent mult.
IPPROTO_IPV6 IPV6_DSTOPTS implementation dependent mult.
IPPROTO_IPV6 IPV6_RTHDR implementation dependent once
The last column tells you how many times an ancillary data object of that type can show up as control information. Hop-by-Hop and Destination options can appear multiple times, and all the others can appear only one time ๐
All of these options are described in detail in the sections that follow. Every constant starting with IPV6_ is defined as a result of including the <netinet/in.h> header ๐ฆ
(Note: we deliberately use the same constant for the cmsg_level member that's used as the second argument to getsockopt() and setsockopt(), which is called the "level." We also use the same constant for the cmsg_type member that's used as the third argument to those calls, which is called the "option name." This matches how ancillary data already works in 4.4BSD, where the destination address of an IPv4 datagram gets returned. ๐)
(Note: what the implementation passes as ancillary data for the Hop-by-Hop option, the Destination option, and the Routing header option is up to the implementation. That's because the API for these features goes through a set of inet6_option_XXX() and inet6_rthdr_XXX() functions, which we define later. Those functions do two jobs: they make the interface to these features simpler (so the application doesn't need to know the intimate details of the extension header formats), and they hide the actual implementation from the application. Even so, we show some examples of these features that store the actual extension header as the ancillary data. Implementations don't need to use this technique. ๐งโจ)
4.5. IPV6_PKTOPTIONS Socket Option ๐
okay so the summary in the last section assumed you're using a UDP socket, and with UDP it's easy bestie ๐ฆ. You call sendmsg() and recvmsg() instead of sendto() and recvfrom(), and that's that.
But TCP apps want this stuff too, ngl ๐ฅบ. A TCP client might want to specify a Routing header, and that has to happen before connect() is called. A TCP server might want to know which interface it received on after accept() returns, plus any Destination options. ๐
So there's a new socket option that gives you access to all that optional info from the previous section, but without touching recvmsg() or sendmsg() โจ. You set it like this:
setsockopt(fd, IPPROTO_IPV6, IPV6_PKTOPTIONS, &buf, len);
The fourth argument points to a buffer holding one or more ancillary data objects, and the fifth argument is the total length of all of them together. You fill that buffer exactly as if you were handing it to sendmsg() as control information, so no new brain cells required ๐ง ๐.
The options you set with IPV6_PKTOPTIONS are called "sticky" options, because once they're set they apply to all the packets sent on that socket ๐งท. You can call setsockopt() again to change all the sticky options, or call it with a length of 0 to remove every sticky option from the socket ๐งน.
The receiving side is getsockopt():
getsockopt(fd, IPPROTO_IPV6, IPV6_PKTOPTIONS, &buf, &len);
This gives you back a buffer with one or more ancillary data objects, covering all the optional receive info the app has previously said it wants ๐ฆ. The fourth argument points to the buffer that gets filled in. The fifth argument is a pointer to a value-result integer, which is a fancy way of saying: when you call it, the integer tells the kernel how big your buffer is, and when it returns, the integer tells you how many bytes actually came back ๐ธ. You process this buffer exactly as if it came back from recvmsg() as control information.
Okay, quick housekeeping ๐. In the rest of this doc, whenever we say "can be specified as ancillary data to sendmsg()," we really mean "can be specified as ancillary data to sendmsg(), or specified as a sticky option using setsockopt() and the IPV6_PKTOPTIONS socket option." And when we say "can be returned as ancillary data by recvmsg()," we mean "can be returned as ancillary data by recvmsg(), or returned by getsockopt() with the IPV6_PKTOPTIONS socket option." Same vibe, less typing ๐.
4.5.1. TCP Sticky Options
With getsockopt() and the IPV6_PKTOPTIONS option on a TCP socket, only the options from the most recently received segment get kept and returned to the caller, and only once the socket option has been set ๐. So TCP doesn't have to start saving copies of the options until the app says so. Lowkey efficient ๐ฅ.
The app is not allowed to specify ancillary data in a call to sendmsg() on a TCP socket, and none of the ancillary data described in this document ever gets returned as control information by recvmsg() on a TCP socket ๐ โโ๏ธ. TCP said "not me" to the whole ancillary data thing, and that's valid ๐.
4.5.2. UDP and Raw Socket Sticky Options
The IPV6_PKTOPTIONS socket option can also be used with a UDP socket or a raw IPv6 socket ๐. The usual move is to set some options once, instead of passing them with every single sendmsg() call.
Unlike the TCP case, though, sticky options can be overridden on a per-packet basis. If you specify any ancillary data in a call to sendmsg() on a UDP or raw IPv6 socket, then none of the sticky options get sent with that datagram ๐ฆ. The one-off ancillary data wins for that packet, and the sticky options sit it out.
5. Packet Information โจ
There are four pieces of info an app can specify for an outgoing packet using ancillary data ๐ :
- the source IPv6 address,
- the outgoing interface index,
- the outgoing hop limit, and
- the next hop address.
Three similar pieces of info can come back for a received packet as ancillary data ๐ง:
- the destination IPv6 address,
- the arriving interface index, and
- the arriving hop limit.
The first two pieces of info live in an in6_pktinfo structure, which gets sent as ancillary data with sendmsg() and received as ancillary data with recvmsg(). This structure is defined when you include the <netinet/in.h> header ๐. Here's the lineup:
struct in6_pktinfo {
struct in6_addr ipi6_addr; /* src/dst IPv6 address */
unsigned int ipi6_ifindex; /* send/recv interface index */
};
In the cmsghdr structure holding this ancillary data, the cmsg_level member is IPPROTO_IPV6, the cmsg_type member is IPV6_PKTINFO, and the first byte of cmsg_data[] is the first byte of the in6_pktinfo structure ๐.
This info only gets returned as ancillary data by recvmsg() if the app has enabled the IPV6_PKTINFO socket option, like so:
int on = 1;
setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on));
Sending this info is simple, no drama. Just specify the control information as ancillary data for sendmsg() ๐.
(Note: The hop limit is not contained in thein6_pktinfostructure, and here's why ๐. Some UDP servers want to answer client requests by sending their reply out the same interface the request arrived on, with the reply's source IPv6 address set to the request's destination IPv6 address. To do this, the app can enable just the IPV6_PKTINFO socket option, then use the control information received from recvmsg() as the outgoing control information for sendmsg(). The app doesn't need to examine or modify thein6_pktinfostructure at all. But if the hop limit lived in that structure, the app would have to parse the received control information and change the hop limit member, since the received hop limit isn't the value you want for an outgoing packet.)
5.1. Specifying/Receiving the Interface
Interfaces on an IPv6 node are identified by a small positive integer, as described in Section 4 of [RFC-2133] ๐. That document also describes a function that maps an interface name to its interface index, a function that maps an interface index to its name, and a function that returns all the interface names and indexes. Notice that, from this document, no interface is ever assigned an index of 0 ๐ โโ๏ธ.
When you're specifying the outgoing interface, if the ipi6_ifindex value is 0, the kernel picks the outgoing interface for you ๐คทโโ๏ธ. If the app specifies an outgoing interface for a multicast packet, the interface given in the ancillary data overrides any interface set by the IPV6_MULTICAST_IF socket option (described in [RFC-2133]), but only for that one call to sendmsg() ๐ฆ.
When the IPV6_PKTINFO socket option is enabled, the received interface index is always returned as the ipi6_ifindex member of the in6_pktinfo structure โจ.
5.2. Specifying/Receiving Source/Destination Address
The source IPv6 address can be specified by calling bind() before each output operation ๐ . But supplying the source address together with the data needs less overhead (fewer system calls) and less state to store and protect in a multithreaded app, so it's the more iconic move ๐.
When you specify the source IPv6 address as ancillary data, here's how it plays out. If the ipi6_addr member of the in6_pktinfo structure is the unspecified address (IN6ADDR_ANY_INIT), then (a) if an address is currently bound to the socket, that address is used as the source address, or (b) if no address is bound to the socket, the kernel picks the source address ๐ง . If ipi6_addr is not the unspecified address, but the socket has already bound a source address, then the ipi6_addr value overrides the bound source address for that output operation only ๐ฅ.
The kernel must verify that the requested source address is indeed a unicast address assigned to the node ๐ก๏ธ.
When the in6_pktinfo structure comes back as ancillary data from recvmsg(), the ipi6_addr member contains the destination IPv6 address from the received packet ๐.
5.3. Specifying/Receiving the Hop Limit
The outgoing hop limit is normally set with either the IPV6_UNICAST_HOPS socket option or the IPV6_MULTICAST_HOPS socket option, and both are described in [RFC-2133] ๐. Specifying the hop limit as ancillary data lets the app override either the kernel's default or a previously set value, for a unicast or multicast destination, for one single output operation ๐ . Getting the received hop limit back is useful for programs like Traceroute, and for IPv6 apps that need to check that the received hop limit is 255 (for example, to confirm that the packet has not been forwarded) ๐ง.
The received hop limit is returned as ancillary data by recvmsg() only if the app has enabled the IPV6_HOPLIMIT socket option:
int on = 1;
setsockopt(fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, sizeof(on));
In the cmsghdr structure holding this ancillary data, the cmsg_level member is IPPROTO_IPV6, the cmsg_type member is IPV6_HOPLIMIT, and the first byte of cmsg_data[] is the first byte of the integer hop limit ๐ธ.
Specifying the outgoing hop limit is also easy: just specify the control information as ancillary data for sendmsg() ๐. As specified in [RFC-2133], the integer hop limit value is interpreted like this:
x < -1: return an error of EINVAL
x == -1: use kernel default
0 <= x <= 255: use x
x >= 256: return an error of EINVAL
5.4. Specifying the Next Hop Address
The IPV6_NEXTHOP ancillary data object specifies the next hop for the datagram, as a socket address structure ๐ฃ๏ธ. In the cmsghdr structure holding this ancillary data, the cmsg_level member is IPPROTO_IPV6, the cmsg_type member is IPV6_NEXTHOP, and the first byte of cmsg_data[] is the first byte of the socket address structure โจ.
This is a privileged option ๐. (Note: It is implementation defined, and beyond the scope of this document, to define what "privileged" means. Unix systems use this term to mean the process must have an effective user ID of 0.)
If the socket address structure contains an IPv6 address (for example, when the sin6_family member is AF_INET6), then the node identified by that address must be a neighbor of the sending host ๐ก. If that address equals the destination IPv6 address of the datagram, then this is equivalent to the existing SO_DONTROUTE socket option ๐ซถ.
5.5. Additional Errors with sendmsg()
With the IPV6_PKTINFO socket option, there are no additional errors possible with the call to recvmsg() ๐. But when you specify the outgoing interface or the source address, extra errors become possible from sendmsg() ๐ญ. The following are examples. Some of these may not be provided by some implementations, and some implementations may define additional errors too ๐.
ENXIO The interface specified by ipi6_ifindex does not exist.
ENETDOWN The interface specified by ipi6_ifindex is not enabled
for IPv6 use.
EADDRNOTAVAIL ipi6_ifindex specifies an interface but the address
ipi6_addr is not available for use on that interface.
EHOSTUNREACH No route to the destination exists over the interface
specified by ifi6_ifindex.
6. Hop-By-Hop Options โจ
okay bestie, one Hop-by-Hop options header can hold a variable number of options ๐ and every single one is TLV-encoded, which means type, length, and value all lined up like a little squad ๐
Right now only three Hop-by-Hop options are defined for IPv6 [RFC-1883]: Jumbo Payload, Pad1, and PadN ๐ธ. There's also a proposal floating around for a router-alert Hop-by-Hop option, so it's giving "coming soon" ๐. The Jumbo Payload option should not be passed back to an application, and an application should get an error if it tries to set it ๐ โโ๏ธ. The kernel handles this one entirely on its own. Datagram-based apps kinda specify it indirectly, as the size of the datagram they send, and they get it back indirectly as the length of the datagram they receive ๐ฆ. The two pad options are just for alignment. A sending kernel adds them automatically when needed, and the receiving kernel ignores them ๐ง. So this part of the API is really set aside for future Hop-by-Hop options that an application might need to set and receive ๐
Individual Hop-by-Hop options (and Destination options, which we'll get to shortly and which are pretty similar to Hop-by-Hop ones) can have specific alignment requirements ๐. For example, the 4-byte Jumbo Payload length should sit on a 4-byte boundary, and IPv6 addresses are normally aligned on an 8-byte boundary ๐ซถ. The requirements and terminology are covered in Section 4.2 and Appendix A of [RFC-1883]. Each option's alignment comes from two values, called x and y, written as "xn + y". This means the option must appear at an integer multiple of x bytes from the beginning of the options header (x can be 1, 2, 4, or 8), plus y bytes (y is between 0 and 7, inclusive) โจ. Pad1 and PadN get inserted as needed to keep everything aligned. Whatever code builds either a Hop-by-Hop options header or a Destination options header needs to know the x and y values for each option ๐ง
Multiple Hop-by-Hop options can be specified by the application. Normally one ancillary data object describes all of them (since each option is already TLV-encoded), but the application can also specify multiple ancillary data objects for the Hop-by-Hop options, with each object holding one or more options ๐. Be careful designing the API for these, because:
- it may be possible for some future Hop-by-Hop options to be generated by the application and processed entirely by the application (e.g., the kernel may not know the alignment restrictions for the option),
- it must be possible for the kernel to insert its own Hop-by-Hop options in an outgoing packet (e.g., the Jumbo Payload option),
- the application can place one or more Hop-by-Hop options into a single ancillary data object,
- if the application specifies multiple ancillary data objects, each containing one or more Hop-by-Hop options, the kernel must combine these into a single Hop-by-Hop options header, and
- it must be possible for the kernel to remove some Hop-by-Hop options from a received packet before returning the remaining Hop-by-Hop options to the application. (This removal might consist of the kernel converting the option into a pad option of the same length.)
Last thing ๐: access to some Hop-by-Hop options, or to some Destination options, might need special privilege. Normal apps (without special privilege) might be forbidden from setting certain options in outgoing packets, and might never see certain options in received packets ๐
6.1. Receiving Hop-by-Hop Options
To receive Hop-by-Hop options, the application has to enable the IPV6_HOPOPTS socket option ๐ง:
int on = 1;
setsockopt(fd, IPPROTO_IPV6, IPV6_HOPOPTS, &on, sizeof(on));
All the Hop-by-Hop options come back as one ancillary data object described by a cmsghdr structure. The cmsg_level member will be IPPROTO_IPV6 and the cmsg_type member will be IPV6_HOPOPTS. After that, these options get processed by calling the inet6_option_next() and inet6_option_find() functions, which we'll cover shortly ๐
6.2. Sending Hop-by-Hop Options
To send one or more Hop-by-Hop options, the application just specifies them as ancillary data in a call to sendmsg(). No socket option needs to be set ๐
Normally all the Hop-by-Hop options are specified by a single ancillary data object. You can also specify multiple ancillary data objects, each containing one or more Hop-by-Hop options, and then the kernel will combine all of them into a single Hop-by-Hop extension header. But it should be more efficient to use one ancillary data object for all the Hop-by-Hop options ๐โโ๏ธ. The cmsg_level member is set to IPPROTO_IPV6 and the cmsg_type member is set to IPV6_HOPOPTS. The option is normally constructed using the inet6_option_init(), inet6_option_append(), and inet6_option_alloc() functions, which we'll get to shortly ๐ง
Additional errors may be possible from sendmsg() if the specified option is in error.
6.3. Hop-by-Hop and Destination Options Processing
Building and parsing Hop-by-Hop and Destination options is complicated for the reasons we covered earlier, so we define a set of functions to help the application ๐ ๏ธ. The function prototypes for these are all in the <netinet/in.h> header.
6.3.1. inet6_option_space
int inet6_option_space(int nbytes);
This function returns the number of bytes needed to hold an option when it's stored as ancillary data. That count includes the cmsghdr structure at the beginning, plus any padding at the end (to make the size a multiple of 8 bytes) ๐. The argument is the size of the structure defining the option, which must include any pad bytes at the beginning (the value y in the alignment term "xn + y"), the type byte, the length byte, and the option data.
(Note: If multiple options are stored in a single ancillary data object, which is the recommended technique, this function overestimates the amount of space needed by the size of N-1 cmsghdr structures, where N is the number of options to be stored in the object. This is of little consequence, since it is assumed that most Hop-by-Hop option headers and Destination option headers carry only one option (p. 33 of [RFC-1883]).)
6.3.2. inet6_option_init
int inet6_option_init(void *bp, struct cmsghdr **cmsgp, int
type);
This function gets called once per ancillary data object that will contain either Hop-by-Hop or Destination options. It returns 0 on success or -1 on an error ๐.
bp is a pointer to previously allocated space that will hold the ancillary data object. It must be large enough to contain all the individual options that get added by later calls to inet6_option_append() and inet6_option_alloc().
cmsgp is a pointer to a pointer to a cmsghdr structure. *cmsgp is initialized by this function to point to the cmsghdr structure it constructs in the buffer pointed to by bp.
type is either IPV6_HOPOPTS or IPV6_DSTOPTS. This type gets stored in the cmsg_type member of the cmsghdr structure pointed to by *cmsgp.
6.3.3. inet6_option_append
int inet6_option_append(struct cmsghdr *cmsg, const uint8_t *typep,
int multx, int plusy);
This function appends a Hop-by-Hop option or a Destination option into an ancillary data object that has been initialized by inet6_option_init(). It returns 0 if it succeeds or -1 on an error ๐
cmsg is a pointer to the cmsghdr structure that must have been initialized by inet6_option_init().
typep is a pointer to the 8-bit option type. It's assumed that this field is immediately followed by the 8-bit option data length field, which is then followed immediately by the option data. The caller initializes these three fields (the type-length-value, or TLV) before calling this function.
The option type must have a value from 2 to 255, inclusive. (0 and 1 are reserved for the Pad1 and PadN options, respectively.)
The option data length must have a value between 0 and 255, inclusive, and is the length of the option data that follows.
multx is the value x in the alignment term "xn + y" described earlier. It must have a value of 1, 2, 4, or 8.
plusy is the value y in the alignment term "xn + y" described earlier. It must have a value between 0 and 7, inclusive.
6.3.4. inet6_option_alloc
uint8_t *inet6_option_alloc(struct cmsghdr *cmsg, int datalen,
int multx, int plusy);
This function appends a Hop-by-Hop option or a Destination option into an ancillary data object that has been initialized by inet6_option_init(). On success it returns a pointer to the 8-bit option type field that starts the option, or NULL on an error ๐ฏ.
The difference between this function and inet6_option_append() is that inet6_option_append() copies the contents of a previously built option into the ancillary data object, while this function returns a pointer to the space in the data object where the option's TLV must then be built by the caller.
cmsg is a pointer to the cmsghdr structure that must have been initialized by inet6_option_init().
datalen is the value of the option data length byte for this option. This value is required as an argument so the function can determine whether padding must be appended at the end of the option. (The inet6_option_append() function doesn't need a data length argument, since the caller must already have stored the option data length.)
multx is the value x in the alignment term "xn + y" described earlier. It must have a value of 1, 2, 4, or 8.
plusy is the value y in the alignment term "xn + y" described earlier. It must have a value between 0 and 7, inclusive.
6.3.5. inet6_option_next
int inet6_option_next(const struct cmsghdr *cmsg, uint8_t
**tptrp);
This function processes the next Hop-by-Hop option or Destination option in an ancillary data object ๐. If another option is left to process, the return value is 0 and *tptrp points to the 8-bit option type field (which is followed by the 8-bit option data length, then the option data). If no more options are left, the return value is -1 and *tptrp is NULL. If an error occurs, the return value is -1 and *tptrp is not NULL.
cmsg is a pointer to a cmsghdr structure where cmsg_level equals IPPROTO_IPV6 and cmsg_type equals either IPV6_HOPOPTS or IPV6_DSTOPTS.
tptrp is a pointer to a pointer to an 8-bit byte. The function uses *tptrp to remember its place in the ancillary data object each time it's called. The first time this function is called for a given ancillary data object, *tptrp must be set to NULL.
Each time the function returns success, *tptrp points to the 8-bit option type field for the next option to be processed ๐ซง
6.3.6. inet6_option_find ๐ฏ
int inet6_option_find(const struct cmsghdr *cmsg, uint8_t *tptrp,
int type);
okay so this one is basically inet6_option_next() but make it iconic ๐
instead of grabbing whatever option comes next, YOU get to pick the exact type you're hunting for. it's giving main character energy ๐
cmsg is a pointer to a cmsghdr structure where cmsg_level equals IPPROTO_IPV6 and cmsg_type equals either IPV6_HOPOPTS or IPV6_DSTOPTS ๐
tptrp is a pointer to a pointer to an 8-bit byte. the function uses *tptrp to remember its place in the ancillary data object every time you call it ๐ง the first time you call this for a given ancillary data object, *tptrp must be set to NULL. bestie, don't skip this step ๐
the search starts right after whatever *tptrp is pointing at. if it finds an option of the type you asked for, it returns 0 and *tptrp points to the 8-bit option type field of that option โจ if it doesn't find one, the return value is -1 and *tptrp is NULL ๐ and if something goes wrong, the return value is -1 but *tptrp is not NULL, so you can tell the difference between "not found" and "oops, error" ๐ซถ
6.3.7. Options Examples ๐ง
okay the fun part ๐ธ we're going to build two Hop-by-Hop options in one go. first we define two options, called X and Y, taken from the example in Appendix A of [RFC-1883]. we assume that all options will have structure definitions similar to what is shown below ๐
/* option X and option Y are defined in [RFC-1883], pp. 33-34 */
#define IP6_X_OPT_TYPE X /* replace X with assigned value */
#define IP6_X_OPT_LEN 12
#define IP6_X_OPT_MULTX 8 /* 8n + 2 alignment */
#define IP6_X_OPT_OFFSETY 2
struct ip6_X_opt {
uint8_t ip6_X_opt_pad[IP6_X_OPT_OFFSETY];
uint8_t ip6_X_opt_type;
uint8_t ip6_X_opt_len;
uint32_t ip6_X_opt_val1;
uint64_t ip6_X_opt_val2;
};
#define IP6_Y_OPT_TYPE Y /* replace Y with assigned value */
#define IP6_Y_OPT_LEN 7
#define IP6_Y_OPT_MULTX 4 /* 4n + 3 alignment */
#define IP6_Y_OPT_OFFSETY 3
struct ip6_Y_opt {
uint8_t ip6_Y_opt_pad[IP6_Y_OPT_OFFSETY];
uint8_t ip6_Y_opt_type;
uint8_t ip6_Y_opt_len;
uint8_t ip6_Y_opt_val1;
uint16_t ip6_Y_opt_val2;
uint32_t ip6_Y_opt_val3;
};
now the code fragment that builds ONE ancillary data object holding both options ๐ it's giving efficiency ๐ฅ
struct msghdr msg;
struct cmsghdr *cmsgptr;
struct ip6_X_opt optX;
struct ip6_Y_opt optY;
msg.msg_control = malloc(inet6_option_space(sizeof(optX) +
sizeof(optY)));
inet6_option_init(msg.msg_control, &cmsgptr, IPV6_HOPOPTS);
optX.ip6_X_opt_type = IP6_X_OPT_TYPE;
optX.ip6_X_opt_len = IP6_X_OPT_LEN;
optX.ip6_X_opt_val1 = <32-bit value>;
optX.ip6_X_opt_val2 = <64-bit value>;
inet6_option_append(cmsgptr, &optX.ip6_X_opt_type,
IP6_X_OPT_MULTX, IP6_X_OPT_OFFSETY);
optY.ip6_Y_opt_type = IP6_Y_OPT_TYPE;
optY.ip6_Y_opt_len = IP6_Y_OPT_LEN;
optY.ip6_Y_opt_val1 = <8-bit value>;
optY.ip6_Y_opt_val2 = <16-bit value>;
optY.ip6_Y_opt_val3 = <32-bit value>;
inet6_option_append(cmsgptr, &optY.ip6_Y_opt_type,
IP6_Y_OPT_MULTX, IP6_Y_OPT_OFFSETY);
msg.msg_controllen = cmsgptr->cmsg_len;
okay bestie, the inet6_option_init() call is the one that actually builds the cmsghdr inside the control buffer ๐
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cmsg_len = CMSG_LEN(0) = 12 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cmsg_level = IPPROTO_IPV6 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cmsg_type = IPV6_HOPOPTS |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
quick vibe check on the setup ๐
: we're pretending we're on a 32-bit architecture where sizeof(struct cmsghdr) is 12, and everything has to line up on 4-byte boundaries. That's the ALIGN() macro from the sample CMSG_xxx() implementations, and it rounds up to a multiple of 4. no cap, the alignment math is the whole mood ๐ง
The first inet6_option_append() call adds option X ๐ฆ. Since it's the very first option in the ancillary data object, we grab 2 bytes for the Next Header byte and the Hdr Ext Len byte. The kernel fills in Next Header later, based on whatever header comes after this one, and Hdr Ext Len gets set to 1. Those 2 bytes are the padding (IP6_X_OPT_OFFSETY) REQUIRED at the front of this option, so it's giving very organized ๐
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cmsg_len = 28 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cmsg_level = IPPROTO_IPV6 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cmsg_type = IPV6_HOPOPTS |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Next Header | Hdr Ext Len=1 | Option Type=X |Opt Data Len=12|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 4-octet field |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ 8-octet field +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
the cmsg_len member of the cmsghdr structure gets bumped up by 16, which is the size of the option ๐
The next inet6_option_append() call tacks on option Y ๐ธ
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cmsg_len = 44 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cmsg_level = IPPROTO_IPV6 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cmsg_type = IPV6_HOPOPTS |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Next Header | Hdr Ext Len=3 | Option Type=X |Opt Data Len=12|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 4-octet field |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ 8-octet field +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| PadN Option=1 |Opt Data Len=1 | 0 | Option Type=Y |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Opt Data Len=7 | 1-octet field | 2-octet field |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 4-octet field |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| PadN Option=1 |Opt Data Len=2 | 0 | 0 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
16 bytes get appended by this function, so cmsg_len becomes 44. The inet6_option_append() function notices the appended data needs 4 bytes of padding at the end, to make the size a multiple of 8, and it slaps the PadN option on before returning ๐ The Hdr Ext Len byte goes up by 2, so it's 3 now.
Alternately, the app could build two ancillary data objects, one per option ๐คท. This will probably be less efficient than combining the two options into a single ancillary data object (like we just did). The kernel MUST combine these into a single Hop-by-Hop extension header in the final IPv6 packet ๐
Each call to inet6_option_init() builds a new cmsghdr structure, and the final result looks like this:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cmsg_len = 28 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cmsg_level = IPPROTO_IPV6 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cmsg_type = IPV6_HOPOPTS |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Next Header | Hdr Ext Len=1 | Option Type=X |Opt Data Len=12|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 4-octet field |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ 8-octet field +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cmsg_len = 28 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cmsg_level = IPPROTO_IPV6 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cmsg_type = IPV6_HOPOPTS |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Next Header | Hdr Ext Len=1 | Pad1 Option=0 | Option Type=Y |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Opt Data Len=7 | 1-octet field | 2-octet field |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 4-octet field |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| PadN Option=1 |Opt Data Len=2 | 0 | 0 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
when the kernel combines these two options into a single Hop-by-Hop extension header, the first 3 bytes of the second ancillary data object (the Next Header byte, the Hdr Ext Len byte, and the Pad1 option) get merged into a PadN option that takes up 3 bytes ๐งฉ
Next up is the redo of the first example (two options in one ancillary data object), but this time with inet6_option_alloc() ๐
uint8_t *typep;
struct msghdr msg;
struct cmsghdr *cmsgptr;
struct ip6_X_opt *optXp; /* now a pointer, not a struct */
struct ip6_Y_opt *optYp; /* now a pointer, not a struct */
msg.msg_control = malloc(inet6_option_space(sizeof(*optXp) +
sizeof(*optYp)));
inet6_option_init(msg.msg_control, &cmsgptr, IPV6_HOPOPTS);
typep = inet6_option_alloc(cmsgptr, IP6_X_OPT_LEN,
IP6_X_OPT_MULTX, IP6_X_OPT_OFFSETY);
optXp = (struct ip6_X_opt *) (typep - IP6_X_OPT_OFFSETY);
optXp->ip6_X_opt_type = IP6_X_OPT_TYPE;
optXp->ip6_X_opt_len = IP6_X_OPT_LEN;
optXp->ip6_X_opt_val1 = <32-bit value>;
optXp->ip6_X_opt_val2 = <64-bit value>;
typep = inet6_option_alloc(cmsgptr, IP6_Y_OPT_LEN,
IP6_Y_OPT_MULTX, IP6_Y_OPT_OFFSETY);
optYp = (struct ip6_Y_opt *) (typep - IP6_Y_OPT_OFFSETY);
optYp->ip6_Y_opt_type = IP6_Y_OPT_TYPE;
optYp->ip6_Y_opt_len = IP6_Y_OPT_LEN;
optYp->ip6_Y_opt_val1 = <8-bit value>;
optYp->ip6_Y_opt_val2 = <16-bit value>;
optYp->ip6_Y_opt_val3 = <32-bit value>;
msg.msg_controllen = cmsgptr->cmsg_len;
heads up ๐: inet6_option_alloc() hands back a pointer to the 8-bit option type field. If your program wants a pointer to the option structure that includes the front padding (like our ip6_X_opt and ip6_Y_opt definitions), the y-offset at the beginning of the structure MUST be subtracted from the returned pointer.
โ ๏ธ bestie, don't skip that subtraction. The returned pointer points at the option type field, not the start of the padding, so the struct math is off without it ๐
Last bit: here's how to process Hop-by-Hop options with inet6_option_next() ๐ฅ
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_level == IPPROTO_IPV6 &&
cmsgptr->cmsg_type == IPV6_HOPOPTS) {
uint8_t *tptr = NULL;
while (inet6_option_next(cmsgptr, &tptr) == 0) {
if (*tptr == IP6_X_OPT_TYPE) {
struct ip6_X_opt *optXp;
optXp = (struct ip6_X_opt *) (tptr - IP6_X_OPT_OFFSETY);
<do whatever with> optXp->ip6_X_opt_val1;
<do whatever with> optXp->ip6_X_opt_val2;
} else if (*tptr == IP6_Y_OPT_TYPE) {
struct ip6_Y_opt *optYp;
optYp = (struct ip6_Y_opt *) (tptr - IP6_Y_OPT_OFFSETY);
<do whatever with> optYp->ip6_Y_opt_val1;
<do whatever with> optYp->ip6_Y_opt_val2;
<do whatever with> optYp->ip6_Y_opt_val3;
}
}
if (tptr != NULL)
<error encountered by inet6_option_next()>;
}
}
7. Destination Options โจ
okay so a variable number of Destination options can show up in one or more Destination option headers ๐. As defined in [RFC-1883], a Destination options header that appears before a Routing header gets processed by the first destination plus any subsequent destinations listed in the Routing header ๐ฆ. One that appears after a Routing header is only processed by the final destination. Just like Hop-by-Hop options, each option in a Destination options header is TLV-encoded with a type, length, and value ๐.
Today no Destination options are defined for IPv6 [RFC-1883], although proposals exist to use Destination options with mobility and anycasting ๐ธ.
7.1. Receiving Destination Options
To receive Destination options, the app has to turn on the IPV6_DSTOPTS socket option ๐ :
int on = 1;
setsockopt(fd, IPPROTO_IPV6, IPV6_DSTOPTS, &on, sizeof(on));
All the Destination options before a Routing header come back as one ancillary data object described by a cmsghdr structure, and all the ones after a Routing header come back as another ancillary data object described by a cmsghdr structure โจ. For these objects, cmsg_level will be IPPROTO_IPV6 and cmsg_type will be IPV6_HOPOPTS (yes, that's what it says ๐). Then you process them by calling inet6_option_next() and inet6_option_find() ๐ง.
7.2. Sending Destination Options
To send one or more Destination options, the app just specifies them as ancillary data in a call to sendmsg() ๐. No socket option needs to be set.
As described earlier, one set of Destination options can go before a Routing header, and one set can go after one. Each set can have one or more options.
Normally all the Destination options in a set go in a single ancillary data object, since each option is already TLV-encoded. Multiple ancillary data objects, each holding one or more Destination options, can also be specified. In that case the kernel combines all the Destination options in the set into a single Destination extension header. But it should be more efficient to use one ancillary data object for all the Destination options in a set ๐ฅ. cmsg_level is set to IPPROTO_IPV6 and cmsg_type is set to IPV6_DSTOPTS. The option is normally built using inet6_option_init(), inet6_option_append(), and inet6_option_alloc() โจ.
Additional errors may be possible from sendmsg() if the specified option is in error.
8. Routing Header Option ๐
Source routing in IPv6 is done by specifying a Routing header as an extension header. There can be different types of Routing headers, but IPv6 currently defines only the Type 0 Routing header [RFC-1883]. This type supports up to 23 intermediate nodes. With that maximum number of intermediate nodes, plus a source and a destination, there are 24 hops, each of which is defined as a strict or loose hop.
Source routing with the IPv4 sockets API (the IP_OPTIONS socket option) requires the app to build the source route in the format that appears as the IPv4 header option. That means you need intimate knowledge of the IPv4 options format ๐ญ. This IPv6 API, however, defines eight functions that the app calls to build and examine a Routing header, which is so much nicer ๐. Four functions build a Routing header:
inet6_rthdr_space() - return #bytes required for ancillary data
inet6_rthdr_init() - initialize ancillary data for Routing header
inet6_rthdr_add() - add IPv6 address & flags to Routing header
inet6_rthdr_lasthop() - specify the flags for the final hop
Four functions deal with a returned Routing header:
inet6_rthdr_reverse() - reverse a Routing header
inet6_rthdr_segments() - return #segments in a Routing header
inet6_rthdr_getaddr() - fetch one address from a Routing header
inet6_rthdr_getflags() - fetch one flag from a Routing header
The function prototypes for these functions are all in the <netinet/in.h> header.
To receive a Routing header, the app must enable the IPV6_RTHDR socket option:
int on = 1;
setsockopt(fd, IPPROTO_IPV6, IPV6_RTHDR, &on, sizeof(on));
To send a Routing header, the app just specifies it as ancillary data in a call to sendmsg().
A Routing header is passed between the app and the kernel as an ancillary data object. cmsg_level has a value of IPPROTO_IPV6, and cmsg_type has a value of IPV6_RTHDR. The contents of the cmsg_data[] member are implementation dependent and should not be accessed directly by the app. Instead, access them with the eight functions we're about to describe ๐ .
The following constants are defined in the <netinet/in.h> header:
#define IPV6_RTHDR_LOOSE 0 /* this hop need not be a neighbor */
#define IPV6_RTHDR_STRICT 1 /* this hop must be a neighbor */
#define IPV6_RTHDR_TYPE_0 0 /* IPv6 Routing header type 0 */
When a Routing header is specified, the destination address given to connect(), sendto(), or sendmsg() is the final destination address of the datagram. The Routing header then contains the addresses of all the intermediate nodes ๐ฆ.
8.1. inet6_rthdr_space
size_t inet6_rthdr_space(int type, int segments);
This function returns the number of bytes required to hold a Routing header of the specified type containing the specified number of segments (addresses). For an IPv6 Type 0 Routing header, the number of segments must be between 1 and 23, inclusive. The return value includes the size of the cmsghdr structure that precedes the Routing header, and any required padding.
If the return value is 0, then either the type of the Routing header is not supported by this implementation, or the number of segments is invalid for this type of Routing header.
(Note: This function returns the size but does not allocate the space required for the ancillary data. This lets an application allocate a larger buffer if other ancillary data objects are desired, since all the ancillary data objects must be specified to sendmsg() as a single msg_control buffer. ๐งบ)
8.2. inet6_rthdr_init
struct cmsghdr *inet6_rthdr_init(void *bp, int type);
This function initializes the buffer pointed to by bp to contain a cmsghdr structure followed by a Routing header of the specified type. The cmsg_len member of the cmsghdr structure is initialized to the size of the structure plus the amount of space required by the Routing header. The cmsg_level and cmsg_type members are also initialized as required.
The caller must allocate the buffer, and its size can be determined by calling inet6_rthdr_space().
Upon success, the return value is the pointer to the cmsghdr structure, and this is then used as the first argument to the next two functions. Upon an error, the return value is NULL.
8.3. inet6_rthdr_add
int inet6_rthdr_add(struct cmsghdr *cmsg,
const struct in6_addr *addr, unsigned int flags);
This function adds the address pointed to by addr to the end of the Routing header being constructed, and sets the type of this hop to the value of flags. For an IPv6 Type 0 Routing header, flags must be either IPV6_RTHDR_LOOSE or IPV6_RTHDR_STRICT.
If successful, the cmsg_len member of the cmsghdr structure is updated to account for the new address in the Routing header, and the return value of the function is 0. Upon an error, the return value of the function is -1.
8.4. inet6_rthdr_lasthop
int inet6_rthdr_lasthop(struct cmsghdr *cmsg,
unsigned int flags);
This function specifies the Strict/Loose flag for the final hop of a Routing header. For an IPv6 Type 0 Routing header, flags must be either IPV6_RTHDR_LOOSE or IPV6_RTHDR_STRICT.
The return value of the function is 0 upon success, or -1 upon an error.
Notice that a Routing header specifying N intermediate nodes requires N+1 Strict/Loose flags. This requires N calls to inet6_rthdr_add() followed by one call to inet6_rthdr_lasthop() ๐งฎ.
8.5. inet6_rthdr_reverse
int inet6_rthdr_reverse(const struct cmsghdr *in, struct cmsghdr *out);
This function takes a Routing header that was received as ancillary data (pointed to by the first argument) and writes a new Routing header that sends datagrams along the reverse of that route. Both arguments are allowed to point to the same buffer, which means the reversal can happen in place ๐.
The return value of the function is 0 on success, or -1 upon an error.
8.6. inet6_rthdr_segments
int inet6_rthdr_segments(const struct cmsghdr *cmsg);
This function returns the number of segments (addresses) contained in the Routing header described by cmsg. On success, the return value is between 1 and 23, inclusive. The return value of the function is -1 upon an error.
8.7. inet6_rthdr_getaddr
struct in6_addr *inet6_rthdr_getaddr(struct cmsghdr *cmsg, int
index);
This function returns a pointer to the IPv6 address specified by index (which must have a value between 1 and the value returned by inet6_rthdr_segments()) in the Routing header described by cmsg. An application should first call inet6_rthdr_segments() to obtain the number of segments in the Routing header ๐.
Upon an error, the return value of the function is NULL.
8.8. inet6_rthdr_getflags
int inet6_rthdr_getflags(const struct cmsghdr *cmsg, int index);
This function returns the flags value specified by index (which must have a value between 0 and the value returned by inet6_rthdr_segments()) in the Routing header described by cmsg. For an IPv6 Type 0 Routing header, the return value will be either IPV6_RTHDR_LOOSE or IPV6_RTHDR_STRICT.
Upon an error, the return value of the function is -1.
(Note: Addresses are indexed starting at 1, and flags starting at 0, to maintain consistency with the terminology and figures in [RFC-1883]. ๐ง )
8.9. Routing Header Example โจ
okay bestie, let's walk through the Routing header functions with the example on p. 18 of [RFC-1883] ๐ฆ we've got a source S, a destination D, and three middle stops I1, I2, and I3. each hop has a Strict/Loose flag, f0, f1, f2, and f3 ๐
f0 f1 f2 f3
S -----> I1 -----> I2 -----> I3 -----> D
Here's the full picture of what the packet looks like at each stage ๐ธ
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
check: f0 f1 f2 f3
so src and dst are the source and destination IPv6 addresses in the IPv6 header, and A[1], A[2], and A[3] are the three addresses sitting in the Routing header. #seg is the Segments Left field in that Routing header, and check tells you which bit of the Strict/Loose Bit Map (0 through 3, aka f0 through f3) each node looks at ๐ง
The six values under node S are what the application puts into the Routing header with sendmsg(). the sender's function calls look like this ๐
void *ptr;
struct msghdr msg;
struct cmsghdr *cmsgptr;
struct sockaddr_in6 I1, I2, I3, D;
unsigned int f0, f1, f2, f3;
ptr = malloc(inet6_rthdr_space(IPV6_RTHDR_TYPE_0, 3));
cmsgptr = inet6_rthdr_init(ptr, IPV6_RTHDR_TYPE_0);
inet6_rthdr_add(cmsgptr, &I1.sin6_addr, f0);
inet6_rthdr_add(cmsgptr, &I2.sin6_addr, f1);
inet6_rthdr_add(cmsgptr, &I3.sin6_addr, f2);
inet6_rthdr_lasthop(cmsgptr, f3);
msg.msg_control = ptr;
msg.msg_controllen = cmsgptr->cmsg_len;
/* finish filling in msg{}, msg_name = D */
/* call sendmsg() */
we're also assuming the socket doesn't have a source address set, which is that little asterisk in the figure ๐
now the four columns of six values in the middle are the packet fields while it's in transit between each pair of nodes. the vibe check: before S actually sends the packet, the source address gets picked (replacing the asterisk), I1 becomes the datagram's destination, A[2] and A[3] get "shifted up", and D gets moved into A[3] ๐ฅ also, if f0 is IPV6_RTHDR_STRICT, then I1 must be a neighbor of S.
the columns under the destination node are what recvmsg() hands back, assuming the app has turned on both the IPV6_PKTINFO and IPV6_RTHDR socket options ๐ the source is S (sitting in the sockaddr_in6 structure that msg_name points to), and the destination is D (returned as an ancillary data object in an in6_pktinfo structure). the ancillary data object for the Routing header holds three addresses (I1, I2, and I3) and four flags (f0, f1, f2, and f3). the number of segments is read from the Hdr Ext Len field in the Routing header, where a value of 6 means 3 addresses ๐
so the receiver's calls go like this: inet6_rthdr_segments() returns 3, inet6_rthdr_getaddr(1) gives back I1, inet6_rthdr_getaddr(2) gives back I2, and inet6_rthdr_getaddr(3) gives back I3. inet6_rthdr_flags(0) returns f0, inet6_rthdr_flags(1) returns f1, inet6_rthdr_flags(2) returns f2, and inet6_rthdr_flags(3) returns f3 โจ
if the receiving app then calls inet6_rthdr_reverse(), the three addresses flip to I3, I2, and I1, and the four Strict/Loose flags flip to f3, f2, f1, and f0 ๐ฆ
okay now the fun part ๐ง this is what an implementation might actually store in the ancillary data object while the sending process is building the Routing header. say we're on a 32-bit architecture where sizeof(struct cmsghdr) is 12, with 4-byte boundary alignment. then calling inet6_rthdr_space(3) returns 68: 12 bytes for the cmsghdr structure, plus 56 bytes for the Routing header (8 + 3*16) ๐
the call to inet6_rthdr_init() sets up the ancillary data object so it holds a Type 0 Routing header:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cmsg_len = 20 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cmsg_level = IPPROTO_IPV6 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cmsg_type = IPV6_RTHDR |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Next Header | Hdr Ext Len=0 | Routing Type=0| Seg Left=0 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Reserved | Strict/Loose Bit Map |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
the first inet6_rthdr_add() call adds I1 to the list ๐
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cmsg_len = 36 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cmsg_level = IPPROTO_IPV6 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cmsg_type = IPV6_RTHDR |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Next Header | Hdr Ext Len=2 | Routing Type=0| Seg Left=1 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Reserved |X| Strict/Loose Bit Map |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Address[1] = I1 +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
bit 0 of the Strict/Loose Bit Map holds f0, which we're just marking as X ๐ซถ cmsg_len goes up by 16, the Hdr Ext Len field goes up by 2, and the Segments Left field goes up by 1.
the next inet6_rthdr_add() call adds I2 to the list ๐
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cmsg_len = 52 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cmsg_level = IPPROTO_IPV6 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cmsg_type = IPV6_RTHDR |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Next Header | Hdr Ext Len=4 | Routing Type=0| Seg Left=2 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Reserved |X|X| Strict/Loose Bit Map |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Address[1] = I1 +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Address[2] = I2 +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
the next bit of the Strict/Loose Bit Map holds f1. cmsg_len goes up by 16, the Hdr Ext Len field goes up by 2, and the Segments Left field goes up by 1 ๐
the last inet6_rthdr_add() call 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 |X|X|X| Strict/Loose Bit Map |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Address[1] = I1 +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Address[2] = I2 +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Address[3] = I3 +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
the next bit of the Strict/Loose Bit Map holds f2. cmsg_len goes up by 16, the Hdr Ext Len field goes up by 2, and the Segments Left field goes up by 1 ๐
finally, inet6_rthdr_lasthop() sets the next bit of the Strict/Loose Bit Map to whatever f3 is. all the lengths stay the same, and that's the whole example, bestie ๐โจ
9. Ordering of Ancillary Data and IPv6 Extension Headers โจ
okay so bestie, three IPv6 extension headers can be set by the app and handed back to the app through ancillary data with sendmsg() and recvmsg() ๐: Hop-by-Hop options, Destination options, and the Routing header. when you pass in several ancillary data objects and they're any of these three, where they sit in the control buffer is directly tied to where they end up in the IPv6 datagram ๐ so the API has some ordering rules you need to know when you use multiple objects with sendmsg() ๐
same-type Hop-by-Hop options go into the Hop-by-Hop options header in the same order they show up in the control buffer ๐ฆ but if you give multiple Hop-by-Hop options with different option types, the kernel is allowed to shuffle them around to cut down on padding in the Hop-by-Hop options header โจ Hop-by-Hop options can show up anywhere in the control buffer, and the kernel always gathers them up into one single Hop-by-Hop options header that sits right after the IPv6 header ๐
Destination options follow the same vibe: (1) same-type ones keep their order, and (2) different-type ones can get reordered ๐ธ the kernel builds up to two Destination options headers, one before the Routing header and one after it ๐ซถ if you specify a Routing header, every Destination option that comes before it in the control buffer lands in a Destination options header before the Routing header, and those options might get reordered too, following the two rules we just covered ๐ same deal for the Destination options that come after the Routing header: they land in a Destination options header after the Routing header and can be shuffled, same two rules ๐
let's do an example, it'll make it click ๐ง say an app passes six ancillary data objects to sendmsg(): the first has two Hop-by-Hop options, the second has one Destination option, the third has two Destination options, the fourth has a Routing header, the fifth has a Hop-by-Hop option, and the sixth has two Destination options ๐ we'll assume all the Hop-by-Hop options are different types, and so are all the Destination options. we number the options 1-9 by their order in the control buffer, and they're on the left below ๐
the middle shows the final arrangement of the options in the extension headers the kernel builds, and the right shows the four ancillary data objects the receiving app gets back ๐ญ
Sender's Receiver's
Ancillary Data --> IPv6 Extension --> Ancillary Data
Objects Headers Objects
------------------ --------------- --------------
HOPOPT-1,2 (first) HOPHDR(J,7,1,2) HOPOPT-7,1,2
DSTOPT-3 DSTHDR(4,5,3) DSTOPT-4,5,3
DSTOPT-4,5 RTHDR(6) RTHDR-6
RTHDR-6 DSTHDR(8,9) DSTOPT-8,9
HOPOPT-7
DSTOPT-8,9 (last)
the sender's two Hop-by-Hop objects got reordered, and so did the first two Destination objects โจ the kernel also slips in a Jumbo Payload option (that's the J) before the sender's three Hop-by-Hop options ๐ฅ the first three Destination options have to be in a Destination header before the Routing header, and the last two have to be in a Destination header after it ๐
last thing: if Destination options come after a Routing header in the control buffer, or if there's no Routing header at all, the kernel puts those Destination options after an authentication header and/or an encapsulating security payload header, if one's there ๐ฆ
10. IPv6-Specific Options with IPv4-Mapped IPv6 Addresses ๐
the socket options and ancillary data stuff in this doc only applies to true IPv6 sockets ๐ you can make an IPv6 socket that actually sends and receives IPv4 packets using IPv4-mapped IPv6 addresses, but how the options in this doc map onto an IPv4 datagram is totally out of scope here ๐
basically, if you try to set an IPv6-only option, like Hop-by-Hop options, Destination options, or a Routing header, on an IPv6 socket using IPv4-mapped IPv6 addresses, it'll probably throw an error ๐ญ some implementations might still let you get at the packet info (source/destination address, send/receive interface, and hop limit) on that kind of socket though ๐ง
11. rresvport_af ๐
rresvport() gets used by rcmd(), and rcmd() gets called by a bunch of the "r" commands like rlogin ๐ new apps aren't being written with rcmd() anymore, but legacy apps like rlogin are still gonna use it, and they'll get ported to IPv6 ๐ซถ
rresvport() makes an IPv4/TCP socket and binds a "reserved port" to it ๐ธ instead of making an IPv6 version of that function, we define a new function that takes an address family as an argument โจ
#include <unistd.h>
int rresvport_af(int *port, int family);
it acts just like the existing rresvport() function, except instead of only making an IPv4/TCP socket, it can also make an IPv6/TCP socket ๐ the family argument is AF_INET or AF_INET6, and there's a new error return, EAFNOSUPPORT, for when the address family isn't supported ๐
(Note: there's not a lot of agreement on which header defines the rresvport() and rcmd() function prototypes. 4.4BSD defines them in <unistd.h>, others use <netdb.h>, and some don't define the prototypes at all. ๐)
(Note: we define this function only, and we don't define anything like rcmd_af() or rcmd6(). the reason is that rcmd() calls gethostbyname(), which returns the type of address (AF_INET or AF_INET6). so it should be possible to tweak rcmd() to support IPv4 or IPv6 based on the address family gethostbyname() returns ๐ฆ)
12. Future Items ๐ฎ
some extra stuff might need standardizing, but nobody has come up with a concrete proposal for the API to handle these tasks yet ๐ท they might get covered in a later document ๐ง
12.1. Flow Labels ๐
earlier revisions of this doc specified a set of inet6_flow_XXX() functions to assign, share, and free IPv6 flow labels ๐ but consensus said it was too early to specify this part of the API ๐ญ
12.2. Path MTU Discovery and UDP ๐ธ
it might be nice to have a standard method for a UDP app to figure out the "maximum send transport-message size" (Section 5.1 of [RFC-1981]) to a given destination โจ that would let the UDP app send smaller datagrams to the destination and avoid fragmentation ๐
12.3. Neighbor Reachability and UDP ๐ซถ
it might also be good to have a standard method for a UDP app to tell the kernel that it's making forward progress with a given peer (Section 7.3.1 of [RFC-1970]) ๐ฆ that could save unneeded neighbor solicitations and neighbor advertisements ๐
13. Summary of New Definitions ๐
this list sums up the constants and structure definitions from 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_NOPORT
<netinet/icmp6.h> ICMP6_DST_UNREACH_NOROUTE
<netinet/icmp6.h> ICMP6_DST_UNREACH_NOTNEIGHBOR
<netinet/icmp6.h> ICMP6_ECHO_REPLY
<netinet/icmp6.h> ICMP6_ECHO_REQUEST
<netinet/icmp6.h> ICMP6_INFOMSG_MASK
<netinet/icmp6.h> ICMP6_MEMBERSHIP_QUERY
<netinet/icmp6.h> ICMP6_MEMBERSHIP_REDUCTION
<netinet/icmp6.h> ICMP6_MEMBERSHIP_REPORT
<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_TIME_EXCEEDED
<netinet/icmp6.h> ICMP6_TIME_EXCEED_REASSEMBLY
<netinet/icmp6.h> ICMP6_TIME_EXCEED_TRANSIT
<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 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/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_DSTOPTS
<netinet/in.h> IPV6_HOPLIMIT
<netinet/in.h> IPV6_HOPOPTS
<netinet/in.h> IPV6_NEXTHOP
<netinet/in.h> IPV6_PKTINFO
<netinet/in.h> IPV6_PKTOPTIONS
<netinet/in.h> IPV6_RTHDR
<netinet/in.h> IPV6_RTHDR_LOOSE
<netinet/in.h> IPV6_RTHDR_STRICT
<netinet/in.h> IPV6_RTHDR_TYPE_0
<netinet/in.h> struct in6_pktinfo{};
<netinet/ip6.h> IP6F_OFF_MASK
<netinet/ip6.h> IP6F_RESERVED_MASK
<netinet/ip6.h> IP6F_MORE_FRAG
<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_rthdr{};
<netinet/ip6.h> struct ip6_rthdr0{};
<sys/socket.h> struct cmsghdr{};
<sys/socket.h> struct msghdr{};
and this list sums up the function and macro prototypes from this memo, also 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> uint8_t *inet6_option_alloc(struct cmsghdr *,
int, int, int);
<netinet/in.h> int inet6_option_append(struct cmsghdr *,
const uint8_t *, int, int);
<netinet/in.h> int inet6_option_find(const struct cmsghdr *,
uint8_t *, int);
<netinet/in.h> int inet6_option_init(void *, struct cmsghdr **, int);
<netinet/in.h> int inet6_option_next(const struct cmsghdr *,
uint8_t **);
<netinet/in.h> int inet6_option_space(int);
<netinet/in.h> int inet6_rthdr_add(struct cmsghdr *,
const struct in6_addr *,
unsigned int);
<netinet/in.h> struct in6_addr inet6_rthdr_getaddr(struct cmsghdr *,
int);
<netinet/in.h> int inet6_rthdr_getflags(const struct cmsghdr *, int);
<netinet/in.h> struct cmsghdr *inet6_rthdr_init(void *, int);
<netinet/in.h> int inet6_rthdr_lasthop(struct cmsghdr *,
unsigned int);
<netinet/in.h> int inet6_rthdr_reverse(const struct cmsghdr *,
struct cmsghdr *);
<netinet/in.h> int inet6_rthdr_segments(const struct cmsghdr *);
<netinet/in.h> size_t inet6_rthdr_space(int, int);
<sys/socket.h> unsigned char *CMSG_DATA(const struct cmsghdr *);
<sys/socket.h> struct cmsghdr *CMSG_FIRSTHDR(const struct msghdr *);
<sys/socket.h> unsigned int CMSG_LEN(unsigned int);
<sys/socket.h> struct cmsghdr *CMSG_NXTHDR(const struct msghdr *mhdr,
const struct cmsghdr *);
<sys/socket.h> unsigned int CMSG_SPACE(unsigned int);
<unistd.h> int rresvport_af(int *, int);
14. Security Considerations ๐โจ
okay bestie, security is where we get serious ๐ค the setting of certain Hop-by-Hop options and Destination options may be restricted to privileged processes ๐ก๏ธ and similarly, some Hop-by-Hop options and Destination options may not be returned to nonprivileged applications ๐ so not every app gets to peek at everything, periodt ๐
15. Change History ๐๐ธ
okay so this is the tea on every edition and draft, the full timeline ๐ซถ
Changes from the June 1997 Edition (-03 draft) ๐
- Added a note that defined constants for multibyte fields are in network byte order. This affects the
ip6f_offlgmember of the Fragment header (Section 2.1.2) and thend_na_flags_reservedmember of thend_neighbor_advertstructure (Section 2.2.2). - Section 5: the
ipi6_ifindexmember of thein6_pktinfostructure should be"unsigned int"instead of"int", for consistency with the interface indexes in [RFC-2133]. - Section 6.3.7: the three calls to
inet6_option_space()in the examples needed to be arguments tomalloc(). The final one of these was missing the"6"in the name"inet6_option_space". - Section 8.6: the function prototype for
inet6_rthdr_segments()was missing the ending semicolon.
Changes from the March 1997 Edition (-02 draft) ๐ฆ
- In May 1997 Draft 6.6 of Posix 1003.1g (called Posix.1g herein) passed ballot and will be forwarded to the IEEE Standards Board later in 1997 for final approval. Some changes made for this final Posix draft are incorporated into this Internet Draft, specifically the datatypes mentioned in Section 1 (and used throughout the text), and the
socklen_tdatatype used in Section 4.1 and 4.2. - Section 1: Added the
intN_tsigned datatypes, changed the datatypeu_intN_ttouintN_t(no underscore after the "u"), and removed the datatypeu_intNm_t, as per Draft 6.6 of Posix.1g. - Name space issues for structure and constant names in Section 2: Many of the structure member names and constant names were changed so that the prefixes are the same. The following prefixes are used for structure members:
"ip6_","icmp6_", and"nd_". All constants have the prefixes"ICMP6_"and"ND_". - New definitions: Section 2.1.2: contains definitions for the IPv6 extension headers, other than AH and ESP. Section 2.2.2: contains additional structures and constants for the neighbor discovery option header and redirected header.
- Section 2.2.2: the enum for the neighbor discovery option field was changed to be a set of
#defineconstants. - Changed the word "function" to "macro" for references to all the uppercase names in Sections 2.3 (
IN6_ARE_ADDR_EQUAL), 3.2 (ICMPV6_FILTER_xxx), and 4.3 (CMSG_xxx). - Added more protocols to the
/etc/protocolsfile (Section 2.4) and changed the name of"icmpv6"to"ipv6-icmp". - Section 3: Made it more explicit that an application cannot read or write entire IPv6 packets, that all extension headers are passed as ancillary data. Added a sentence that the kernel fragments packets written to an IPv6 raw socket when necessary. Added a note that
IPPROTO_RAWraw IPv6 sockets are not special. - Section 3.1: Explicitly stated that the checksum option applies to both outgoing packets and received packets.
- Section 3.2: Changed the array name within the
icmp6_filterstructure from"data"to"icmp6_filt". Changed the prefix for the filter macros from"ICMPV6_"to"ICMP6_", for consistency with the names in Section 2.2. Changed the example from a ping program to a program that wants to receive only router advertisements. - Section 4.1: Changed
msg_namelenandmsg_controllenfromsize_tto the Posix.1gsocklen_tdatatype. Updated the Note that follows. - Section 4.2: Changed
cmsg_lenfromsize_tto the Posix.1gsocklen_tdatatype. Updated the Note that follows. - Section 4.4: Added a Note that the second and third arguments to
getsockopt()andsetsockopt()are intentionally the same as thecmsg_levelandcmsg_typemembers. - Section 4.5: Reorganized the section into a description of the option, followed by the TCP semantics, and the UDP and raw socket semantics. Added a sentence on how to clear all the sticky options. Added a note that TCP need not save the options from the most recently received segment until the application says to do so. Added the statement that ancillary data is never passed with
sendmsg()orrecvmsg()on a TCP socket. Simplified the interaction of the sticky options with ancillary data for UDP or raw IP: none of the sticky options are sent if ancillary data is specified. - Final paragraph of Section 5.1:
ipi6_indexshould beipi6_ifindex. - Section 5.4: Added a note on the term "privileged".
- Section 5.5: Noted that the errors listed are examples, and the actual errors depend on the implementation.
- Removed Section 6 (
"Flow Labels") as the consensus is that it is premature to try and specify an API for this feature. Access to the flow label field in the IPv6 header is still provided through thesin6_flowinfomember of the IPv6 socket address structure in [RFC-2133]. Added a subsection to Section 13 that this is a future item.
All remaining changes are identified by their section number in the previous draft. With the removal of Section 6, the section numbers are decremented by one.
- Section 7.3.7: the calls to
malloc()in all three examples should be calls toinet6_option_space()instead. The two calls toinet6_option_append()in the third example should be calls toinet6_option_alloc(). The two calls toCMSG_SPACE()in the first and third examples should be calls toCMSG_LEN(). The second call toCMSG_SPACE()in the second example should be a call toCMSG_LEN(). - Section 7.3.7: All the
opt_X_andopt_Y_structure member names were changed to beip6_X_opt_andip6_Y_opt_. The two structure namesipv6_opt_Xandipv6_opt_Ywere changed toip6_X_optandip6_Y_opt. The constants beginning withIPV6_OPT_X_andIPV6_OPT_Y_were changed to begin withIP6_X_OPT_andIP6_Y_OPT_. - Use the term "Routing header" throughout the draft, instead of "source routing". Changed the names of the eight
inet6_srcrt_XXX()functions in Section 9 toinet6_rthdr_XXX(). Changed the name of the socket option fromIPV6_SRCRTtoIPV6_RTHDR, and the names of the threeIPV6_SRCRT_xxxconstants in Section 9 toIPV6_RTHDR_xxx. - Added a paragraph to Section 9 on how to receive and send a Routing header.
- Changed
inet6_rthdr_add()andinet6_rthdr_reverse()so that they return -1 upon an error, instead of anExxxerrno value. - In the description of
inet6_rthdr_space()in Section 9.1, added the qualifier "For an IPv6 Type 0 Routing header" to the restriction of between 1 and 23 segments. - Refer to final function argument in Sections 9.7 and 9.8 as
index, notoffset. - Updated Section 14 with new names from Section 2.
- Changed the References from
"[n]"to"[RFC-abcd]".
Changes from the February 1997 Edition (-01 draft) ๐
- Changed the name of the
ip6hdrstructure toip6_hdr(Section 2.1) for consistency with theicmp6hdrstructure. Also changed the name of theip6hdrctlstructure contained within theip6_hdrstructure toip6_hdrctl(Section 2.1). Finally, changed the name of theicmp6hdrstructure toicmp6_hdr(Section 2.2). All other occurrences of this structure name, within the Neighbor Discovery structures in Section 2.2.1, already contained the underscore. - The
"struct nd_router_solicit"and"struct nd_router_advert"should both begin with"nd6_". (Section 2.2.2). - Changed the name of
in6_are_addr_equaltoIN6_ARE_ADDR_EQUAL(Section 2.3) for consistency with basic API address testing functions. The header defining this macro is<netinet/in.h>. getprotobyname("ipv6")now returns 41, not 0 (Section 2.4).- The first occurrence of
"struct icmpv6_filter"in Section 3.2 should be"struct icmp6_filter". - Changed the name of the
CMSG_LENGTH()macro toCMSG_LEN()(Section 4.3.5), sinceLENis used throughout the<netinet/*.h>headers. - Corrected the argument name for the sample implementations of the
CMSG_SPACE()andCMSG_LEN()macros to be"length"(Sections 4.3.4 and 4.3.5). - Corrected the socket option mentioned in Section 5.1 to specify the interface for multicasting from
IPV6_ADD_MEMBERSHIPtoIPV6_MULTICAST_IF. - There were numerous errors in the previous draft that specified
<netinet/ip6.h>that should have been<netinet/in.h>. These have all been corrected and the locations of all definitions is now summarized in the new Section 14 ("Summary of New Definitions").
Changes from the October 1996 Edition (-00 draft) ๐
- Numerous rationale added using the format (Note: ...).
- Added note that not all errors may be defined.
- Added note about ICMPv4, IGMPv4, and ARPv4 terminology.
- Changed the name of
<netinet/ip6_icmp.h>to<netinet/icmp6.h>. - Changed some names in Section 2.2.1:
ICMPV6_PKT_TOOBIGtoICMPV6_PACKET_TOOBIG,ICMPV6_TIME_EXCEEDtoICMPV6_TIME_EXCEEDED,ICMPV6_ECHORQSTtoICMPV6_ECHOREQUEST,ICMPV6_ECHORPLYtoICMPV6_ECHOREPLY,ICMPV6_PARAMPROB_HDRtoICMPV6_PARAMPROB_HEADER,ICMPV6_PARAMPROB_NXT_HDRtoICMPV6_PARAMPROB_NEXTHEADER, andICMPV6_PARAMPROB_OPTStoICMPV6_PARAMPROB_OPTION. ๐ - Prepend the prefix "icmp6_" to the three members of the
icmp6_dataununion of theicmp6hdrstructure (Section 2.2). โจ - Moved the neighbor discovery definitions into the
<netinet/icmp6.h>header, instead of being in their own header (Section 2.2.1). ๐ - Changed Section 2.3 ("Address Testing"). The basic macros are now in the basic API. ๐ธ
- Added the new Section 2.4 on "Protocols File". ๐ฆ
- Added note to raw sockets description that something like BPF or DLPI must be used to read or write entire IPv6 packets. ๐ฅ
- Corrected example of
IPV6_CHECKSUMsocket option (Section 3.1). Also defined value of-1to disable. ๐ - Noted that
<netinet/icmp6.h>defines all the ICMPv6 filtering constants, macros, and structures (Section 3.2). ๐ง - Added note on magic number
10240for amount of ancillary data (Section 4.1). ๐ - Added possible padding to picture of ancillary data (Section 4.2). ๐
- Defined
<sys/socket.h>header forCMSG_xxx()functions (Section 4.2). ๐ - Note that the data returned by
getsockopt(IPV6_PKTOPTIONS)for a TCP socket is just from the optional headers, if present, of the most recently received segment. Also note that control information is never returned byrecvmsg()for a TCP socket. ๐ญ - Changed header for
struct in6_pktinfofrom<netinet.in.h>to<netinet/ip6.h>(Section 5). ๐ - Removed the old Sections 5.1 and 5.2, because the interface identification functions went into the basic API. ๐ซถ
- Redid Section 5 to support the hop limit field. โญ
- New Section 5.4 ("Next Hop Address"). ๐
- New Section 6 ("Flow Labels"). ๐ฆ
- Changed all of Sections 7 and 8 dealing with Hop-by-Hop and Destination options. We now define a set of
inet6_option_XXX()functions. ๐ - Changed header for
IPV6_SRCRT_xxxconstants from<netinet.in.h>to<netinet/ip6.h>(Section 9). ๐ธ - Add
inet6_rthdr_lasthop()function, and fix errors in description of Routing header (Section 9). ๐ฅ - Reworded some of the Routing header descriptions to conform to the terminology in [RFC-1883]. โจ
- Added the example from [RFC-1883] for the Routing header (Section 9.9). ๐
- Expanded the example in Section 10 to show multiple options per ancillary data object, and to show the receiver's ancillary data objects. ๐ง
- New Section 11 ("IPv6-Specific Options with IPv4-Mapped IPv6 Addresses"). ๐
- New Section 12 ("rresvport_af"). ๐
- Redid old Section 10 ("Additional Items") into new Section 13 ("Future Items"). ๐
16. References ๐
- [RFC-1883] Deering, S., and R. Hinden, "Internet Protocol, Version 6 (IPv6), Specification", RFC 1883, December 1995.
- [RFC-2133] Gilligan, R., Thomson, S., Bound, J., and W. Stevens, "Basic Socket Interface Extensions for IPv6", RFC 2133, April 1997.
- [RFC-1981] McCann, J., Deering, S., and J. Mogul, "Path MTU Discovery for IP version 6", RFC 1981, August 1996.
- [RFC-1970] Narten, T., Nordmark, E., and W. Simpson, "Neighbor Discovery for IP Version 6 (IPv6)", RFC 1970, August 1996.
17. Acknowledgments ๐ซถ
Matt Thomas and Jim Bound have been working on the technical details in this draft 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.
The following provided comments on earlier drafts: Pascal Anelli, Hamid Asayesh, Ran Atkinson, Karl Auerbach, Hamid Asayesh, Matt Crawford, Sam T. Denton, Richard Draves, Francis Dupont, Bob Gilligan, Tim Hartrick, Masaki Hirabaru, Yoshinobu Inoue, Mukesh Kacker, A. N. Kuznetsov, Pedro Marques, Jack McCann, der Mouse, John Moy, Thomas Narten, Erik Nordmark, Steve Parker, Charles Perkins, Tom Pusateri, Pedro Roque, Sameer Shah, Peter Sjodin, Stephen P. Spackman, Jinmei Tatuya, Karen Tracey, Quaizar Vohra, Carl Williams, Steve Wise, and Kazu Yamamoto.
18. Authors' Addresses ๐ฆ
W. Richard Stevens
1202 E. Paseo del Zorro
Tucson, AZ 85718
EMail: rstevens@kohala.com
Matt Thomas
AltaVista Internet Software
LJO2-1/J8
30 Porter Rd
Littleton, MA 01460
EMail: matt.thomas@altavista-software.com
19. Full Copyright Statement ๐
Copyright (C) The Internet Society (1998). 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.
๐ฆ where this page comes from
- ietf: rfc2292 1998-02
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms