RFC 3493

Basic Socket Interface Extensions for IPv6

๐Ÿ“… published March 2003, from ietf

RFC 3493: Basic Socket Interface Extensions for IPv6 โœจ

okay bestie, let's get into it ๐ŸŽ€ this is RFC 3493, titled "Basic Socket Interface Extensions for IPv6." It came from the Network Working Group in February 2003, and it's an Informational document, so it's not an Internet standard ๐Ÿค“. The authors are R. Gilligan (Intransa, Inc.), S. Thomson (Cisco), J. Bound, J. McCann (Hewlett-Packard), and W. Stevens ๐Ÿซถ. It obsoletes RFC 2553 ๐Ÿ’…. It doesn't update anything else.


Status of This Memo

okay so this memo is just giving information for the Internet community ๐Ÿ“š. It is not specifying an Internet standard of any kind, so chill ๐Ÿ˜Œ Distribution of this memo is unlimited ๐ŸŒธ.

Copyright (C) The Internet Society (2003). All Rights Reserved. ยฉ๏ธ it's giving legal stuff, but it matters ๐Ÿ’ผ

Abstract

okay, the de facto standard Application Program Interface (API) for TCP/IP applications is the "sockets" interface ๐Ÿ”Œ It was developed for Unix in the early 1980s, but it has also been implemented on a wide variety of non-Unix systems ๐ŸŒˆ TCP/IP apps built on the sockets API have historically had a huge degree of portability, and we want that same portability for IPv6 apps ๐Ÿฅน But the sockets API needs changes to support IPv6, and this memo describes those changes โœจ

The changes include a new socket address structure to carry IPv6 addresses, new address conversion functions, and some new socket options ๐Ÿ’– They're designed to give access to the basic IPv6 features that TCP and UDP apps need, including multicasting, while introducing a minimum of change into the system and providing complete compatibility for existing IPv4 apps ๐Ÿงš Additional extensions for advanced IPv6 features (raw sockets and access to the IPv6 extension headers) are defined in another document ๐Ÿ“„

Table of Contents

1.  Introduction................................................3
2.  Design Considerations.......................................4
    2.1  What Needs to be Changed...............................4
    2.2  Data Types.............................................6
    2.3  Headers................................................6
    2.4  Structures.............................................6
3.  Socket Interface............................................6
    3.1  IPv6 Address Family and Protocol Family................6
    3.2  IPv6 Address Structure.................................7
    3.3  Socket Address Structure for 4.3BSD-Based Systems......7
    3.4  Socket Address Structure for 4.4BSD-Based Systems......9
    3.5  The Socket Functions...................................9
    3.6  Compatibility with IPv4 Applications..................10
    3.7  Compatibility with IPv4 Nodes.........................11
    3.8  IPv6 Wildcard Address.................................11
    3.9  IPv6 Loopback Address.................................13
    3.10 Portability Additions.................................14
4.  Interface Identification...................................16
    4.1  Name-to-Index.........................................17
    4.2  Index-to-Name.........................................17
    4.3  Return All Interface Names and Indexes................18
    4.4  Free Memory...........................................18
5.  Socket Options.............................................18
    5.1  Unicast Hop Limit.....................................19
    5.2  Sending and Receiving Multicast Packets...............19
    5.3  IPV6_V6ONLY option for AF_INET6 Sockets...............22
6.  Library Functions..........................................22
    6.1  Protocol-Independent Nodename and
         Service Name Translation..............................23
    6.2  Socket Address Structure to Node Name
         and Service Name......................................28
    6.3  Address Conversion Functions..........................31
    6.4  Address Testing Macros................................33
7.  Summary of New Definitions.................................33
8.  Security Considerations....................................35
9.  Changes from RFC 2553......................................35
10. Acknowledgments............................................36
11. References.................................................37
12. Authors' Addresses.........................................38
13. Full Copyright Statement...................................39

1. Introduction

okay so IPv4 addresses are 32 bits long, but IPv6 addresses are 128 bits long ๐Ÿ˜ณ The socket interface makes the size of an IP address super visible to an app, and basically every TCP/IP app for BSD-based systems knows how big an IP address is ๐Ÿ‘€ So the parts of the API that expose addresses have to change to fit the bigger IPv6 size ๐Ÿ’… IPv6 also brings new features, and some of them need to be visible to apps through the API ๐Ÿฆ‹

This memo defines a set of extensions to the socket interface that support the larger address size and the new IPv6 features ๐ŸŽ€ It defines "basic" extensions that are useful to a broad range of apps. A companion document, the "advanced" API [4], covers extensions for more specialized apps, like routing daemons and the "ping" and "traceroute" utilities ๐Ÿ›ฐ๏ธ

The development of this API started in 1994 in the IETF IPng working group โณ It evolved over the years, first published in RFC 2133, then again in RFC 2553, and it reached its final form in this document ๐ŸŒธ

As the API matured and stabilized, it got folded into the Open Group's Networking Services (XNS) specification, issue 5.2, and that was later incorporated into a joint Open Group/IEEE/ISO standard [3] ๐Ÿ†

They've tried really hard to make sure this document and [3] have the same info about the API definitions ๐Ÿค But heads up: this document is for informational purposes only, and the official standard spec for the sockets API is [3] ๐Ÿซถ

Any future standardization work on this API is expected to be done by the Open Group Base Working Group [6] ๐Ÿ“‹

also, this document only covers the parts of the API needed for IPv4 and IPv6 communications ๐Ÿ™ˆ Other potential uses, like using getaddrinfo() and getnameinfo() with the AF_UNIX address family, are outside the scope of this document ๐Ÿšซ

2. Design Considerations

there are a bunch of important things to think about when changing this well-worn API ๐Ÿง

  • The API changes should provide both source and binary compatibility for programs written to the original API ๐Ÿ’– That means existing program binaries should keep working when run on a system that supports the new API โœจ Also, existing apps that get recompiled and run on a system supporting the new API should keep working too ๐Ÿ™Œ Simply put, the IPv6 API changes should not break existing programs ๐Ÿ˜ค One extra way for implementations to check this is to confirm that the new symbols are protected by Feature Test Macros, as described in [3] (Such Feature Test Macros are not defined by this RFC.) ๐Ÿ”
  • The changes to the API should be as small as possible, so converting existing IPv4 apps to IPv6 is easier ๐Ÿงธ
  • Where possible, apps should be able to use this API to interoperate with both IPv6 and IPv4 hosts ๐ŸŒ Apps shouldn't need to know which type of host they're talking to ๐Ÿซ 
  • IPv6 addresses carried in data structures should be 64-bit aligned ๐Ÿ“ This is necessary to get optimum performance on 64-bit machine architectures ๐Ÿš€

Because IPv4 compatibility matters so much in this API, these extensions are explicitly designed to run on machines that fully support both IPv4 and IPv6 ๐Ÿ’œ A subset of this API could probably be designed for systems that support only IPv6, but this memo doesn't address that ๐Ÿคทโ€โ™€๏ธ

2.1 What Needs to be Changed

the socket interface API is made of a few distinct components ๐Ÿงฉ

  • Core socket functions.
  • Address data structures.
  • Name-to-address translation functions.
  • Address conversion functions.

The core socket functions are the ones that handle things like setting up and tearing down TCP connections, and sending and receiving UDP packets ๐Ÿ“ก They were designed to be transport independent ๐Ÿง  When protocol addresses are passed as function arguments, they're carried via opaque pointers ๐Ÿซฅ A protocol-specific address data structure is defined for each protocol that the socket functions support ๐Ÿ—‚๏ธ Apps have to cast pointers to these protocol-specific address structures into pointers to the generic "sockaddr" address structure when using the socket functions ๐Ÿ™ƒ These functions don't need to change for IPv6, but a new IPv6-specific address data structure is needed ๐Ÿ’…

The "sockaddr_in" structure is the protocol-specific data structure for IPv4 ๐ŸŒท It includes 8 octets of unused space, and it's tempting to use that space to adapt sockaddr_in for IPv6 ๐Ÿ˜ Unfortunately, sockaddr_in isn't big enough to hold the 16-octet IPv6 address and the other info it needs, like the address family and port number ๐Ÿ’€ So a brand new address data structure must be defined for IPv6 ๐ŸŒˆ

IPv6 addresses are scoped [2], so they could be link-local, site, organization, global, or other scopes that aren't defined yet ๐ŸŒ To support apps that want to identify a set of interfaces for a specific scope, the IPv6 sockaddr_in structure must support a field that an implementation can use to identify a set of interfaces that define the scope for an IPv6 address ๐ŸŽฏ

The IPv4 name-to-address translation functions in the socket interface are gethostbyname() and gethostbyaddr() ๐Ÿ”ค These are left as is, and new functions are defined that support both IPv4 and IPv6 โœจ

The IPv4 address conversion functions, inet_ntoa() and inet_addr(), convert IPv4 addresses between binary and printable form ๐Ÿ” They're pretty specific to 32-bit IPv4 addresses ๐Ÿ˜ฌ So the designers came up with two analogous functions that convert both IPv4 and IPv6 addresses, and they carry an address type parameter so they can be extended to other protocol families too ๐Ÿฆ‹

Finally, a few miscellaneous features are needed to support IPv6 ๐Ÿง A new interface is needed to support the IPv6 hop limit header field ๐Ÿƒโ€โ™€๏ธ New socket options are needed to control sending and receiving of IPv6 multicast packets ๐Ÿ“ฃ

The socket interface will get enhanced in the future to provide access to other IPv6 features ๐Ÿ”ฎ Some of those extensions are described in [4] ๐Ÿ“–

2.2 Data Types

the data types of the structure elements in this memo are meant to track the relevant standards ๐Ÿ“ uintN_t means an unsigned integer of exactly N bits (e.g., uint16_t) ๐Ÿ”ข The sa_family_t and in_port_t types are defined in [3] ๐Ÿ’œ

2.3 Headers

when function prototypes and structures are shown, we also show the headers that must be #included to make that item defined ๐ŸŽ€

2.4. Structures

okay so when the RFC describes a structure, the members it shows are the ones every implementation MUST have ๐Ÿ’… (lowercase "must" here, but yes, it's still a requirement, bestie). You're allowed to add your own nonstandard members too โœจ As an extra safety net, those extras could be checked with Feature Test Macros as described in [3]. (Those macros aren't defined by this RFC, so don't go looking for them here ๐Ÿ™ˆ)

The members are listed in a recommended order that takes alignment of multibyte members into account ๐Ÿงš An implementation is still allowed to order them differently, though.

3. Socket Interface โœจ

this section is where the socket interface gets its IPv6 glow-up ๐Ÿฆ‹

3.1. IPv6 Address Family and Protocol Family

a new address family name, AF_INET6, shows up in <sys/socket.h> ๐ŸŒธ It's what lets the system tell the original sockaddr_in apart from the new sockaddr_in6 ๐Ÿ‘‘

there's also a new protocol family name, PF_INET6, in <sys/socket.h> ๐Ÿ’œ Like most protocol family names, it'll usually have the same value as its matching address family name, so it's basically just:

   #define PF_INET6        AF_INET6

and AF_INET6 goes in the first argument to socket() when you want an IPv6 socket ๐ŸŽ€

3.2. IPv6 Address Structure

the in6_addr structure holds one IPv6 address, and you get it by including <netinet/in.h> โœจ

   struct in6_addr {
       uint8_t  s6_addr[16];      /* IPv6 address */
   };

it's an array of sixteen 8-bit elements, which together make up one 128-bit IPv6 address ๐Ÿ’– The address is stored in network byte order ๐ŸŒˆ

ngl, real implementations usually wrap this in an embedded union with extra fields so it lines up the way BSD's struct in_addr does ๐Ÿซถ The RFC skips those details to keep things simple, so the example looks like this:

   struct in6_addr {
        union {
            uint8_t  _S6_u8[16];
            uint32_t _S6_u32[4];
            uint64_t _S6_u64[2];
        } _S6_un;
   };
   #define s6_addr _S6_un._S6_u8

3.3. Socket Address Structure for 4.3BSD-Based Systems

every protocol suite gets its own protocol-specific address structure, and each one can be cast into the protocol-independent sockaddr structure ๐Ÿฆ‹ Every one of them has a "family" field that lines up with sa_family in sockaddr, and that field says what kind of structure you're looking at ๐Ÿ’…

sockaddr_in is the IPv4 one ๐ŸŒธ For IPv6, the RFC defines sockaddr_in6 (you get it by including <netinet/in.h>):

struct sockaddr_in6 {
    sa_family_t     sin6_family;    /* AF_INET6 */
    in_port_t       sin6_port;      /* transport layer port # */
    uint32_t        sin6_flowinfo;  /* IPv6 flow information */
    struct in6_addr sin6_addr;      /* IPv6 address */
    uint32_t        sin6_scope_id;  /* set of interfaces for a scope */
};

this structure is built to stay compatible with the 4.3BSD sockaddr ๐ŸŽ€

  • sin6_family says this is a sockaddr_in6. It sits right on top of sa_family when you cast the buffer to sockaddr, and its value MUST be AF_INET6. (Lowercase in the original, so I'm keeping it that way, but the vibe is "do not skip this" ๐Ÿ’€)
  • sin6_port holds the 16-bit UDP or TCP port number ๐Ÿ“ It's used the same way as sin_port in sockaddr_in, and it's stored in network byte order.
  • sin6_flowinfo is a 32-bit field meant for flow-related info ๐Ÿงš How it maps to or from a packet isn't specified yet. Until someone defines its use, applications should set it to zero when they build a sockaddr_in6, and ignore it when the system hands one back.
  • sin6_addr is one in6_addr (from the last section) holding one 128-bit IPv6 address, also in network byte order ๐Ÿ’œ
  • sin6_scope_id is a 32-bit integer that identifies a set of interfaces that fit the scope [2] of the address in sin6_addr โœจ How it maps to actual interfaces is left up to the implementation and to future specs about scoped addresses.

the ordering of the elements is intentional ๐ŸŒˆ If sin6_addr is aligned on a 64-bit boundary, the start of the whole structure ends up aligned on one too. That's a performance win on 64-bit architectures ๐Ÿš€

heads up, bestie: sockaddr_in6 is usually bigger than the generic sockaddr. On lots of existing implementations, sizeof(struct sockaddr_in) and sizeof(struct sockaddr) are both 16 bytes. Any existing code that assumes they're always the same size needs a careful look when you port it to IPv6 ๐Ÿ™ˆ

3.4. Socket Address Structure for 4.4BSD-Based Systems

4.4BSD made a small change that breaks compatibility ๐Ÿ˜ญ The 16-bit sa_family in sockaddr became an 8-bit value, and the freed-up space got used for a length field called sa_len. Because of that, the sockaddr_in6 from the previous section can't be cast into the newer sockaddr correctly ๐Ÿ’€ So for 4.4BSD-based systems, the RFC gives an alternative IPv6 address structure (also from including <netinet/in.h>):

struct sockaddr_in6 {
    uint8_t         sin6_len;       /* length of this struct */
    sa_family_t     sin6_family;    /* AF_INET6 */
    in_port_t       sin6_port;      /* transport layer port # */
    uint32_t        sin6_flowinfo;  /* IPv6 flow information */
    struct in6_addr sin6_addr;      /* IPv6 address */
    uint32_t        sin6_scope_id;  /* set of interfaces for a scope */
};

the only differences from the 4.3BSD version are the new length field and the family field switching to an 8-bit type ๐ŸŽ€ Everything else is identical to the structure in the previous section ๐Ÿซถ

systems that provide this version MUST also declare SIN6_LEN when you include <netinet/in.h>. (Again, lowercase "must" in the original, but it's a real requirement ๐Ÿ’…) That macro lets apps figure out whether they're being built on a system with the 4.3BSD variant or the 4.4BSD variant ๐Ÿฆ‹

3.5. The Socket Functions

apps call socket() to create a socket descriptor, which is basically a communication endpoint ๐ŸŒธ The arguments tell the system which protocol to use and what address structure format the later functions will use ๐Ÿ’– For example, an IPv4/TCP socket looks like this:

   s = socket(AF_INET, SOCK_STREAM, 0);

and an IPv4/UDP socket looks like this:

   s = socket(AF_INET, SOCK_DGRAM, 0);

apps can also make IPv6/TCP and IPv6/UDP sockets (which can handle IPv4 traffic too, see section 3.7 โœจ) by swapping in AF_INET6 for AF_INET in the first argument ๐Ÿงš An IPv6/TCP socket:

   s = socket(AF_INET6, SOCK_STREAM, 0);

and an IPv6/UDP socket:

   s = socket(AF_INET6, SOCK_DGRAM, 0);

once an app has an AF_INET6 socket, it must use sockaddr_in6 when it passes addresses into the system ๐Ÿ‘‘ (Lowercase again, but this is the rule you follow.) The functions apps use to pass addresses in are:

      bind()
      connect()
      sendmsg()
      sendto()

and when the system returns addresses to apps on AF_INET6 sockets, it uses sockaddr_in6 too. The functions that hand an address back to the app are:

      accept()
      recvfrom()
      recvmsg()
      getpeername()
      getsockname()

the best part? none of the socket function syntax has to change for IPv6 ๐Ÿฆ‹ All the "address carrying" functions take an opaque address pointer and pass the address length as an argument, so they're already flexible enough โœจ

3.6. Compatibility with IPv4 Applications

okay this is the big promise ๐Ÿซถ To support the huge pile of apps built on the original API, systems MUST give complete source and binary compatibility with that API ๐Ÿ’… That means they have to keep supporting AF_INET sockets and the sockaddr_in address structure. Apps must be able to make IPv4/TCP and IPv4/UDP sockets with AF_INET in socket(), same as in the last section ๐ŸŒธ Apps SHOULD also be able to run a mix of IPv4/TCP, IPv4/UDP, IPv6/TCP and IPv6/UDP sockets all at once in the same process ๐ŸŒˆ

apps using the original API should keep working exactly like they did on IPv4-only systems ๐Ÿ’œ In other words, they should keep talking to IPv4 nodes just fine โœจ

3.7. Compatibility with IPv4 Nodes

this is the other kind of compatibility: IPv6 apps being able to talk to IPv4 apps ๐Ÿฆ‹ It works through the IPv4-mapped IPv6 address format from the IPv6 addressing architecture spec [2]. That format lets an IPv4 node's address be written as an IPv6 address ๐ŸŽ€ The IPv4 address goes in the low-order 32 bits, and the high-order 96 bits are the fixed prefix 0:0:0:0:0:FFFF. These addresses look like this:

      ::FFFF:<IPv4-address>

getaddrinfo() can generate these addresses automatically, as described in Section 6.1 ๐Ÿงš

apps can use AF_INET6 sockets to open TCP connections to IPv4 nodes or send UDP packets to them ๐Ÿซถ You just encode the destination's IPv4 address as an IPv4-mapped IPv6 address and pass it inside a sockaddr_in6 to connect() or sendto() ๐Ÿ’– When apps on AF_INET6 sockets accept TCP connections from IPv4 nodes, or receive UDP packets from them, the system hands back the peer's address in a sockaddr_in6 encoded that same way, through accept(), recvfrom(), or getpeername() ๐ŸŒˆ

honestly, few apps will need to know whether they're talking to an IPv4 or IPv6 node ๐Ÿ™ˆ But if one does need to know, the IN6_IS_ADDR_V4MAPPED() macro (defined in Section 6.4) is there for exactly that ๐Ÿ’…โœจ

3.8 IPv6 Wildcard Address

okay so bind() lets apps pick the source IP address for UDP packets and TCP connections ๐Ÿ’– but honestly? Apps often just want the system to pick one for them ๐Ÿฆ‹ With IPv4, you pass the symbolic constant INADDR_ANY (aka the "wildcard" address) to bind(), or you just skip bind() entirely ๐Ÿ™ˆ

here's the tea: the IPv6 address type is a structure (struct in6_addr) ๐Ÿ’… so a symbolic constant can initialize an IPv6 address variable but it can't be used in an assignment. ngl that's a plot twist. So systems give you the IPv6 wildcard address in two forms โœจ

form one is a global variable called "in6addr_any" that's an in6_addr structure. Its extern declaration lives in <netinet/in.h>:

extern const struct in6_addr in6addr_any;

apps use in6addr_any the same way they use INADDR_ANY in IPv4 ๐ŸŽ€ For example, to bind a socket to port number 23 but let the system pick the source address, you could do this:

struct sockaddr_in6 sin6;
 . . .
sin6.sin6_family = AF_INET6;
sin6.sin6_flowinfo = 0;
sin6.sin6_port = htons(23);
sin6.sin6_addr = in6addr_any;  /* structure assignment */
 . . .
if (bind(s, (struct sockaddr *) &sin6, sizeof(sin6)) == -1)
        . . .

form two is a symbolic constant named IN6ADDR_ANY_INIT, defined in <netinet/in.h> ๐Ÿงš It can initialize an in6_addr structure:

struct in6_addr anyaddr = IN6ADDR_ANY_INIT;

and this is the important bit ๐Ÿšจ this constant can ONLY be used at declaration time. It can NOT be used to assign a previously declared in6_addr structure. So this code will not work:

/* This is the WRONG way to assign an unspecified address */
struct sockaddr_in6 sin6;
 . . .
sin6.sin6_addr = IN6ADDR_ANY_INIT; /* will NOT compile */
๐Ÿ’€ bestie, be aware that the IPv4 INADDR_xxx constants are all defined in host byte order, but the IPv6 IN6ADDR_xxx constants and the IPv6 in6addr_xxx externals are defined in network byte order. Mixing these up is so not it.

3.9 IPv6 Loopback Address

apps sometimes need to send UDP packets to, or start TCP connections with, services living on the local node ๐Ÿ  In IPv4 they do this with the constant IPv4 address INADDR_LOOPBACK in their connect(), sendto(), or sendmsg() call.

IPv6 has its own loopback address for reaching local TCP and UDP services ๐ŸŒˆ Just like the unspecified address, the IPv6 loopback address comes in two forms: a global variable and a symbolic constant โœจ

the global variable is an in6_addr structure named "in6addr_loopback." Its extern declaration is in <netinet/in.h>:

extern const struct in6_addr in6addr_loopback;

apps use in6addr_loopback the way they'd use INADDR_LOOPBACK in IPv4 apps (but beware of the byte ordering difference I mentioned at the end of the last section ๐Ÿ‘€). For example, to open a TCP connection to the local telnet server:

struct sockaddr_in6 sin6;
 . . .
sin6.sin6_family = AF_INET6;
sin6.sin6_flowinfo = 0;
sin6.sin6_port = htons(23);
sin6.sin6_addr = in6addr_loopback;  /* structure assignment */
 . . .
if (connect(s, (struct sockaddr *) &sin6, sizeof(sin6)) == -1)
        . . .

the symbolic constant is named IN6ADDR_LOOPBACK_INIT and is defined in <netinet/in.h> ๐Ÿฆ‹ It can be used at declaration time ONLY, for example:

struct in6_addr loopbackaddr = IN6ADDR_LOOPBACK_INIT;

same deal as IN6ADDR_ANY_INIT: this constant can't be used in an assignment to a previously declared IPv6 address variable ๐Ÿ™…โ€โ™€๏ธ

3.10 Portability Additions

one simple addition to the sockets API that really helps app writers is "struct sockaddr_storage" ๐ŸŽ€ This data structure makes it way easier to write code that's portable across multiple address families and platforms. It was designed with these goals in mind:

  • Large enough to hold all supported protocol-specific address structures.
  • Aligned at an appropriate boundary, so pointers to it can be cast as pointers to protocol-specific address structures and used to access their fields without alignment problems.

the sockaddr_storage structure has a field called ss_family, which is of type sa_family_t ๐Ÿ’œ When you cast a sockaddr_storage to a sockaddr, its ss_family field maps onto the sa_family field of the sockaddr structure. When you cast a sockaddr_storage as a protocol-specific address structure, ss_family maps onto whichever field of that structure is of type sa_family_t and identifies the protocol's address family. it's giving matching energy ๐Ÿซถ

here's an example implementation design of this data structure ๐Ÿ‘‘

/*
 * Desired design of maximum size and alignment
 */
#define _SS_MAXSIZE    128  /* Implementation specific max size */
#define _SS_ALIGNSIZE  (sizeof (int64_t))
                         /* Implementation specific desired alignment */
/*
 * Definitions used for sockaddr_storage structure paddings design.
 */
#define _SS_PAD1SIZE   (_SS_ALIGNSIZE - sizeof (sa_family_t))
#define _SS_PAD2SIZE   (_SS_MAXSIZE - (sizeof (sa_family_t) +
                              _SS_PAD1SIZE + _SS_ALIGNSIZE))
struct sockaddr_storage {
    sa_family_t  ss_family;     /* address family */
    /* Following fields are implementation specific */
    char      __ss_pad1[_SS_PAD1SIZE];
              /* 6 byte pad, this is to make implementation
              /* specific pad up to alignment field that */
              /* follows explicit in the data structure */
    int64_t   __ss_align;     /* field to force desired structure */
               /* storage alignment */
    char      __ss_pad2[_SS_PAD2SIZE];
              /* 112 byte pad to achieve desired size, */
              /* _SS_MAXSIZE value minus size of ss_family */
              /* __ss_pad1, __ss_align fields is 112 */
};

so this example lines up on a 64-bit boundary ๐Ÿ”ฅ the implementation-specific "__ss_align" field, along with "__ss_pad1", forces that 64-bit alignment, which is good enough for what sockaddr_in6 (IPv6) and sockaddr_in (IPv4) need. The size of __ss_pad1 depends on the chosen alignment boundary, and the size of __ss_pad2 depends on the overall size picked for the structure. In the example these are shown with implementation-specific (not required) constants: _SS_MAXSIZE (chosen value 128) and _SS_ALIGNSIZE (chosen value 8). The derived constants _SS_PAD1SIZE (derived value 6) and _SS_PAD2SIZE (derived value 112) are also just for illustration and not required. Those derived values assume sa_family_t is 2 bytes. The implementation-specific names all start with an underscore, which marks them as implementation private namespace ๐Ÿ”’ Portable code isn't expected to touch those fields or constants, so hands off, bestie ๐Ÿ™Œ

on implementations where the sockaddr data structure has a "sa_len" field, this data structure looks like this:

/*
 * Definitions used for sockaddr_storage structure paddings design.
 */
#define _SS_PAD1SIZE (_SS_ALIGNSIZE -
                            (sizeof (uint8_t) + sizeof (sa_family_t))
#define _SS_PAD2SIZE (_SS_MAXSIZE -
                            (sizeof (uint8_t) + sizeof (sa_family_t) +
                             _SS_PAD1SIZE + _SS_ALIGNSIZE))
struct sockaddr_storage {
    uint8_t      ss_len;        /* address length */
    sa_family_t  ss_family;     /* address family */
    /* Following fields are implementation specific */
    char         __ss_pad1[_SS_PAD1SIZE];
                  /* 6 byte pad, this is to make implementation
                  /* specific pad up to alignment field that */
                  /* follows explicit in the data structure */
    int64_t      __ss_align;  /* field to force desired structure */
                  /* storage alignment */
    char         __ss_pad2[_SS_PAD2SIZE];
                  /* 112 byte pad to achieve desired size, */
                  /* _SS_MAXSIZE value minus size of ss_len, */
                  /* __ss_family, __ss_pad1, __ss_align fields is 112 */
};

4. Interface Identification โœจ

this API uses an interface index (a small positive integer) to identify the local interface a multicast group is joined on (Section 5.2) ๐Ÿฆ‹ The advanced API [4] also uses these same interface indexes to identify the interface a datagram is received on, or to specify the interface a datagram is sent on.

interfaces are usually known by names like "le0", "sl1", "ppp2", and so on ๐ŸŒธ On Berkeley-derived implementations, when an interface is made known to the system, the kernel assigns it a unique positive integer value (called the interface index). These are small positive integers that start at 1. (Note that 0 is never used for an interface index ๐Ÿ™…โ€โ™€๏ธ) There may be gaps, so there might be no current interface for a particular positive interface index.

this API defines two functions that map between an interface name and index ๐Ÿ—บ๏ธ plus a third function that returns all the interface names and indexes, and a fourth function that frees the dynamic memory the third one allocated. How these functions are implemented is left up to the implementation ๐Ÿคทโ€โ™€๏ธ 4.4BSD implementations can use the existing sysctl() function with the NET_RT_IFLIST command. Other implementations may want to use ioctl() for this instead.

4.1 Name-to-Index

the first function maps an interface name to its matching index ๐Ÿ’–

#include <net/if.h>

unsigned int  if_nametoindex(const char *ifname);

if ifname is the name of an interface, the if_nametoindex() function shall return the interface index corresponding to name ifname; otherwise, it shall return zero. No errors are defined ๐ŸŒˆ

4.2 Index-to-Name

the second function goes the other way, mapping an interface index to its name ๐Ÿ”

#include <net/if.h>

char  *if_indextoname(unsigned int ifindex, char *ifname);

when this function is called, the ifname argument shall point to a buffer of at least IF_NAMESIZE bytes. The function shall place the name of the interface with index ifindex in this buffer. (IF_NAMESIZE is also defined in <net/if.h>, and its value includes a terminating null byte at the end of the interface name ๐Ÿ“) If ifindex is an interface index, the function shall return the value supplied in ifname, which now points to a buffer containing the interface name. Otherwise, the function shall return a NULL pointer and set errno to indicate the error. If there's no interface matching the specified index, errno is set to ENXIO. If there was a system error (like running out of memory), errno would be set to the proper value (for example, ENOMEM) ๐Ÿ˜ญ

4.3 Return All Interface Names and Indexes โœจ

okay so the if_nameindex structure is what holds the info about a single interface ๐ŸŽ€. You get it by including <net/if.h> ๐Ÿ’…

struct if_nameindex {
  unsigned int   if_index;  /* 1, 2, ... */
  char          *if_name;   /* null terminated name: "le0", ... */
};

The last function in this group returns an array of if_nameindex structures, one structure per interface ๐Ÿฆ‹

#include <net/if.h>

struct if_nameindex  *if_nameindex(void);

The end of the array is marked by a structure with an if_index of 0 and an if_name of NULL ๐Ÿ If something goes wrong, the function returns a NULL pointer and would set errno to the appropriate value ๐Ÿ˜ญ

The memory for this array, plus the interface names that the if_name members point to, is allocated dynamically ๐Ÿง  This memory gets freed by the next function, so stay tuned ๐Ÿ“

4.4 Free Memory ๐Ÿงน

The following function frees the dynamic memory that if_nameindex() allocated for you.

#include <net/if.h>

void  if_freenameindex(struct if_nameindex *ptr);

The ptr argument shall be a pointer that was returned by if_nameindex(). Once if_freenameindex() has been called, the application shall not use the array that ptr points to. Don't be that person who keeps poking at freed memory ๐Ÿ’€

5. Socket Options ๐ŸŽ€

A bunch of new socket options are defined for IPv6 ๐Ÿ’– All of these new options live at the IPPROTO_IPV6 level. That means the "level" parameter in the getsockopt() and setsockopt() calls is IPPROTO_IPV6 when you're using these. Every new option name starts with the prefix IPV6_, which makes it super clear that they apply to IPv6 ๐ŸŒˆ

The declaration for IPPROTO_IPV6, the new IPv6 socket options, and the related constants in this section come from including <netinet/in.h> ๐Ÿงš

5.1 Unicast Hop Limit ๐ŸŒธ

A new setsockopt() option controls the hop limit on outgoing unicast IPv6 packets. It's called IPV6_UNICAST_HOPS and it's used at the IPPROTO_IPV6 layer. Here's how it looks in action:

int  hoplimit = 10;

if (setsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
               (char *) &hoplimit, sizeof(hoplimit)) == -1)
    perror("setsockopt IPV6_UNICAST_HOPS");

Once IPV6_UNICAST_HOPS is set with setsockopt(), that value is used as the hop limit for every later unicast packet sent through that socket ๐Ÿฆ‹ If you don't set it, the system picks a default value for you. The integer hop limit (called x) gets read 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

You can also use IPV6_UNICAST_HOPS with getsockopt() to check which hop limit the system will use for later unicast packets sent through that socket ๐Ÿ‘€ For example:

int  hoplimit;
socklen_t  len = sizeof(hoplimit);

if (getsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
               (char *) &hoplimit, &len) == -1)
    perror("getsockopt IPV6_UNICAST_HOPS");
else
    printf("Using %d for hop limit.\n", hoplimit);

5.2 Sending and Receiving Multicast Packets ๐Ÿซถ

IPv6 apps can send multicast packets by just putting an IPv6 multicast address in the destination address, like the destination argument of sendto() ๐Ÿ“

There are three socket options at the IPPROTO_IPV6 layer that control some of the parameters for sending multicast packets. Setting them is not required, since apps can send multicast packets without touching any of them ๐Ÿ™Œ Here's the lowdown on the setsockopt() options for sending multicast packets. All three can also be used with getsockopt().

  • IPV6_MULTICAST_IF ๐Ÿ”Œ
    • Sets the interface used for outgoing multicast packets. The argument is the index of the interface to use. If the interface index is zero, the system picks the interface itself (for example, by looking up the address in a routing table and using the resulting interface).
    • Argument type: unsigned int
  • IPV6_MULTICAST_HOPS ๐Ÿพ
    • Sets the hop limit for outgoing multicast packets. (Note that there's a separate option, IPV6_UNICAST_HOPS, for outgoing unicast packets.)
    • The argument is interpreted the same way as for IPV6_UNICAST_HOPS:
x < -1:        return an error of EINVAL
x == -1:       use kernel default
0 <= x <= 255: use x
x >= 256:      return an error of EINVAL
  • If IPV6_MULTICAST_HOPS is not set, the default is 1 (same as IPv4 today).
  • Argument type: int
  • IPV6_MULTICAST_LOOP ๐Ÿ”
    • If a multicast datagram is sent to a group the sending host itself belongs to (on the outgoing interface), a copy of the datagram gets looped back by the IP layer for local delivery when this option is set to 1. If it's set to 0, no copy is looped back. Any other value returns an error of EINVAL.
    • If IPV6_MULTICAST_LOOP is not set, the default is 1 (loopback; same as IPv4 today).
    • Argument type: unsigned int

Receiving multicast packets is controlled by the two setsockopt() options below ๐Ÿ“ป If you try to use these two with getsockopt(), you get an error of EOPNOTSUPP.

  • IPV6_JOIN_GROUP ๐ŸŽ‰
    • Joins a multicast group on a specified local interface. If the interface index is 0, the kernel picks the local interface. For example, some kernels look up the multicast group in the normal IPv6 routing table and use the resulting interface.
    • Argument type: struct ipv6_mreq
  • IPV6_LEAVE_GROUP ๐Ÿ‘‹
    • Leaves a multicast group on a specified interface. If the interface index is 0, the system may choose a multicast group membership to drop by matching the multicast address only.
    • Argument type: struct ipv6_mreq

Both of these options take an argument of type ipv6_mreq, which gets defined when you include <netinet/in.h> ๐Ÿ’œ

struct ipv6_mreq {
    struct in6_addr ipv6mr_multiaddr; /* IPv6 multicast addr */
    unsigned int    ipv6mr_interface; /* interface index */
};
Heads up, bestie: to receive multicast datagrams, a process must join the multicast group that datagrams will be sent to. UDP apps must also bind the UDP port those datagrams will be sent to. Some processes also bind the multicast group address to the socket, in addition to the port, so that other datagrams aimed at that same port don't get delivered to the socket ๐Ÿšจ

5.3 IPV6_V6ONLY option for AF_INET6 Sockets ๐Ÿ‘‘

This socket option restricts AF_INET6 sockets to IPv6 communications only. As mentioned in section <3.7 Compatibility with IPv4 Nodes>, AF_INET6 sockets can be used for both IPv4 and IPv6 communications. Some apps might want to limit their AF_INET6 socket to IPv6 only, and that's what IPV6_V6ONLY is for ๐Ÿฆ‹ When this option is turned on, the socket can only send and receive IPv6 packets. It's an IPPROTO_IPV6 level option that takes an int value. It's a boolean option, and it's off by default ๐Ÿ’…

Here's an example of setting it:

int on = 1;

if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
               (char *)&on, sizeof(on)) == -1)
    perror("setsockopt IPV6_V6ONLY");
else
    printf("IPV6_V6ONLY set\n");
Note - This option has no effect on the use of IPv4 Mapped addresses, which enter a node as valid IPv6 addresses for IPv6 communications as defined by Stateless IP/ICMP Translation Algorithm (SIIT) [5].

An example use of this option is letting two versions of the same server process run on the same port, with one providing the service over IPv6 and the other providing that same service over IPv4 ๐ŸŒˆโœจ

6. Library Functions ๐Ÿ’œ

New library functions are needed for all kinds of operations on IPv6 addresses ๐Ÿง We need functions to look up IPv6 addresses in the Domain Name System (DNS). Both forward lookup (nodename-to-address translation) and reverse lookup (address-to-nodename translation) need support. We also need functions that convert IPv6 addresses between their binary and textual forms ๐Ÿ”ฅ

We note that the two existing functions, gethostbyname() and gethostbyaddr(), are left as-is. The new functions are defined to handle both IPv4 and IPv6 addresses ๐ŸŽ€

The commonly used gethostbyname() just isn't enough for a lot of apps, and there are two big reasons for that. First, it gives the caller no way to say what kinds of addresses they want (IPv4 only, IPv6 only, IPv4-mapped IPv6 are OK, etc.). Second, a lot of implementations of this function aren't thread safe ๐Ÿ˜ญ RFC 2133 defined a function named gethostbyname2(), but it was also not enough. It needed a global option (RES_USE_INET6) to be set whenever IPv6 addresses were required, and it needed a flag argument so the caller could control the types of addresses they get. The gethostbyname2() function was deprecated in RFC 2553 and is no longer part of the basic API ๐Ÿ’€

6.1 Protocol-Independent Nodename and Service Name Translation ๐ŸŒ

Nodename-to-address translation is done in a protocol-independent way, using the getaddrinfo() function โœจ

#include <sys/socket.h>
#include <netdb.h>


int getaddrinfo(const char *nodename, const char *servname,
                const struct addrinfo *hints, struct addrinfo **res);

void freeaddrinfo(struct addrinfo *ai);

struct addrinfo {
  int     ai_flags;     /* AI_PASSIVE, AI_CANONNAME,
                           AI_NUMERICHOST, .. */
  int     ai_family;    /* AF_xxx */
  int     ai_socktype;  /* SOCK_xxx */
  int     ai_protocol;  /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
  socklen_t  ai_addrlen;   /* length of ai_addr */
  char   *ai_canonname; /* canonical name for nodename */
  struct sockaddr  *ai_addr; /* binary address */
  struct addrinfo  *ai_next; /* next structure in linked list */
};

The getaddrinfo() function takes the name of a service location (like a host name) and/or a service name, and returns a set of socket addresses plus associated info ๐Ÿง  You use that info to create a socket to talk to the specified service ๐Ÿฆ‹

The nodename and servname arguments are either null pointers or pointers to null-terminated strings. One or both of them must be a non-null pointer ๐Ÿ’–

The format of a valid name depends on the address family or families. If no specific family is given and the name could be read as valid in more than one supported family, the implementation will try to resolve the name in all supported families. As long as there are no errors, one or more results shall be returned ๐ŸŒธ

If nodename isn't null, it can be a descriptive name or an address string. If the specified address family is AF_INET, AF_INET6, or AF_UNSPEC, valid descriptive names include host names. If the specified address family is AF_INET or AF_UNSPEC, address strings written in Internet standard dot notation, as specified in inet_addr(), are valid. If the specified address family is AF_INET6 or AF_UNSPEC, standard IPv6 text forms described in inet_pton() are valid ๐Ÿซถ

Okay so, if nodename isn't null, the service location you want is named by nodename ๐Ÿ“. If it IS null, the location is just local to the caller, aka you ๐Ÿ .

If servname is null, the call shall return network-level addresses for the nodename you gave it ๐ŸŒ. If servname isn't null, it's a null-terminated character string that identifies the service you want ๐ŸŽ€. It can be a descriptive name or a numeric form that works with the address family or families. If the family is AF_INET, AF_INET6 or AF_UNSPEC, the service can be written as a string holding a decimal port number ๐Ÿ”ข.

If the hints argument isn't null, it points to a structure full of input values that steer the whole operation ๐Ÿงญ. You can use it for options, and you can limit what comes back to a certain socket type, address family and/or protocol. In this hints structure, every member other than ai_flags, ai_family, ai_socktype and ai_protocol shall be set to zero or a null pointer ๐Ÿซถ. An ai_family of AF_UNSPEC means the caller shall accept any address family ๐Ÿ’…. An ai_socktype of zero means the caller shall accept any socket type โœจ. An ai_protocol of zero means the caller shall accept any protocol ๐Ÿฆ‹. If hints is a null pointer, it behaves as if it pointed at a structure with zero in the ai_flags, ai_socktype and ai_protocol fields, and AF_UNSPEC in the ai_family field ๐Ÿ’œ.

Note:

  1. If the caller only handles TCP and not UDP, for example, then the ai_protocol member of the hints structure should be set to IPPROTO_TCP when getaddrinfo() is called ๐Ÿ“.
  2. If the caller only handles IPv4 and not IPv6, then the ai_family member of the hints structure should be set to AF_INET when getaddrinfo() is called ๐ŸŒธ.

The ai_flags field that the hints parameter points to shall be set to zero, or to the bitwise-inclusive OR of one or more of these values: AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST, AI_NUMERICSERV, AI_V4MAPPED, AI_ALL, and AI_ADDRCONFIG ๐Ÿ”ฅ.

If AI_PASSIVE is specified, the address info you get back shall be suitable for binding a socket that accepts incoming connections for that service (so, a call to bind()) ๐Ÿšช. In this case, if nodename is null, the IP address part of the socket address structure shall be set to INADDR_ANY for IPv4, or to IN6ADDR_ANY_INIT for IPv6 ๐Ÿ‘‘. If AI_PASSIVE is not specified, the address info shall be suitable for a call to connect() (for a connection-mode protocol), or for a call to connect(), sendto() or sendmsg() (for a connectionless protocol) ๐Ÿ“ž. In this case, if nodename is null, the IP address part of the socket address structure shall be set to the loopback address ๐Ÿ”. This flag is ignored if nodename isn't null ๐Ÿ™ˆ.

If AI_CANONNAME is specified and nodename isn't null, the function shall try to figure out the canonical name for nodename. For example, that's handy if nodename is an alias or a shorthand for a full name ๐ŸŽ€.

If AI_NUMERICHOST is specified, then a non-null nodename string you pass in shall be a numeric host address string. If it isn't, you get an [EAI_NONAME] error ๐Ÿ’€. This flag shall stop any kind of name resolution service (like DNS) from being invoked ๐Ÿšซ.

If AI_NUMERICSERV is specified, then a non-null servname string you pass in shall be a numeric port string. Otherwise, an [EAI_NONAME] error shall be returned ๐Ÿ˜ญ. This flag shall stop any kind of name resolution service (like NIS+) from being invoked ๐Ÿ›‘.

If AI_V4MAPPED is specified along with an ai_family of AF_INET6, then getaddrinfo() shall return IPv4-mapped IPv6 addresses when it finds no matching IPv6 addresses (ai_addrlen shall be 16) ๐Ÿฆ‹.

For example, when using DNS, if no AAAA records are found, a query is made for A records, and any it finds are returned as IPv4-mapped IPv6 addresses ๐ŸŒˆ.

The AI_V4MAPPED flag shall be ignored unless ai_family equals AF_INET6 ๐Ÿ™…โ€โ™€๏ธ.

If AI_ALL is used together with AI_V4MAPPED, then getaddrinfo() shall return all matching IPv6 and IPv4 addresses ๐Ÿ‘‘.

For example, when using DNS, queries go out for both AAAA records and A records, and getaddrinfo() returns the combined results of both. Any IPv4 addresses found come back as IPv4-mapped IPv6 addresses ๐Ÿ’….

The AI_ALL flag without AI_V4MAPPED is ignored ๐Ÿคทโ€โ™€๏ธ.

Note:

When ai_family is not specified (AF_UNSPEC), the AI_V4MAPPED and AI_ALL flags will only be used if AF_INET6 is supported ๐Ÿงš.

If AI_ADDRCONFIG is specified, IPv4 addresses shall only be returned if an IPv4 address is configured on the local system, and IPv6 addresses shall only be returned if an IPv6 address is configured on the local system. The loopback address doesn't count as a configured address for this check ๐Ÿ”.

For example, when using DNS, a query for AAAA records should only happen if the node has at least one IPv6 address configured (other than the IPv6 loopback), and a query for A records should only happen if the node has at least one IPv4 address configured (other than the IPv4 loopback) ๐ŸŒธ.

The ai_socktype field that hints points to specifies the socket type for the service, as defined for socket(). If no specific socket type is given (like a value of zero), and the service name could be valid with multiple supported socket types, the implementation shall try to resolve the service name for all supported socket types. In the absence of errors, all possible results shall be returned ๐Ÿฐ. A non-zero socket type value shall limit the returned info to values with that socket type ๐ŸŽฏ.

If the ai_family field that hints points to has the value AF_UNSPEC, addresses shall be returned for use with any address family that works with the specified nodename and/or servname โœจ. Otherwise, addresses shall only be returned for use with the specified address family ๐Ÿฆ„. If ai_family is not AF_UNSPEC and ai_protocol is not zero, then addresses are only returned for use with that address family and protocol. The value of ai_protocol shall be interpreted the same way it would be in a call to the socket() function, with the matching ai_family and ai_protocol values ๐Ÿ”ง.

The freeaddrinfo() function frees one or more addrinfo structures returned by getaddrinfo(), along with any extra storage tied to them (like the storage pointed to by the ai_canonname and ai_addr fields). An application must not reference that storage after its addrinfo structure has been freed ๐Ÿ—‘๏ธ. If the ai_next field of the structure is not null, the entire list gets freed. The freeaddrinfo() function must support freeing arbitrary sublists of an addrinfo list that getaddrinfo() originally returned ๐Ÿ’–.

Functions getaddrinfo() and freeaddrinfo() must be thread-safe ๐Ÿงต.

A zero return value from getaddrinfo() means successful completion, and a non-zero return value means failure. The possible failure values are listed below under Error Return Values ๐Ÿ“‹.

When getaddrinfo() returns successfully, the location that res points to shall refer to a linked list of addrinfo structures. Each one shall specify a socket address, plus information for creating a socket to use with that address ๐Ÿ”—. The list shall include at least one addrinfo structure. The ai_next field of each structure points to the next structure on the list, or is a null pointer if it's the last one ๐ŸŽ€. Each structure on the list shall include values for use with a call to the socket() function, and a socket address for use with connect() or, if AI_PASSIVE was specified, for use with bind(). The fields ai_family, ai_socktype, and ai_protocol shall be usable as the arguments to socket(), creating a socket suitable for the returned address ๐Ÿงฆ. The fields ai_addr and ai_addrlen are usable as the arguments to connect() or bind() with such a socket, depending on the AI_PASSIVE flag ๐Ÿซถ.

If nodename is not null, and AI_CANONNAME was requested, the ai_canonname field of the first returned addrinfo structure shall point to a null-terminated string containing the canonical name for the input nodename. If that canonical name isn't available, then ai_canonname shall refer to the nodename argument, or to a string with the same contents ๐ŸŒท. The contents of the ai_flags field in the returned structures are undefined ๐Ÿค.

All fields in socket address structures returned by getaddrinfo() that aren't filled in by an explicit argument (like sin6_flowinfo) shall be set to zero ๐Ÿงผ.

Note: This makes it way easier to compare socket address structures ๐Ÿ’….

Error Return Values:

The getaddrinfo() function shall fail and return the matching value if any of these happen ๐Ÿ˜ตโ€๐Ÿ’ซ:

[EAI_AGAIN]     The name could not be resolved at this time.  Future
                attempts may succeed.

[EAI_BADFLAGS]  The flags parameter had an invalid value.

[EAI_FAIL]      A non-recoverable error occurred when attempting to
                resolve the name.

[EAI_FAMILY]    The address family was not recognized.

[EAI_MEMORY]    There was a memory allocation failure when trying to
                allocate storage for the return value.

[EAI_NONAME]    The name does not resolve for the supplied
                parameters.  Neither nodename nor servname were
                supplied.  At least one of these must be supplied.

[EAI_SERVICE]   The service passed was not recognized for the
                specified socket type.

[EAI_SOCKTYPE]  The intended socket type was not recognized.

[EAI_SYSTEM]    A system error occurred; the error code can be found
                in errno.

The gai_strerror() function gives you a descriptive text string for an EAI_xxx error value ๐Ÿ“ฃ.

#include <netdb.h>

const char *gai_strerror(int ecode);

The argument is one of the EAI_xxx values defined for the getaddrinfo() and getnameinfo() functions. The return value points to a string describing the error. If the argument isn't one of the EAI_xxx values, the function still returns a pointer to a string whose contents indicate an unknown error ๐ŸŒˆ.

6.2. Socket Address Structure to Node Name and Service Name โœจ

okay so this is the reverse trip ๐Ÿฆ‹ getnameinfo() takes a socket address structure and turns it back into a node name and/or a service name. main character energy ๐Ÿ’…

#include <sys/socket.h>
#include <netdb.h>

int getnameinfo(const struct sockaddr *sa, socklen_t salen,
                    char *node, socklen_t nodelen,
                    char *service, socklen_t servicelen,
                      int flags);

getnameinfo() SHALL translate a socket address to a node name and service location, and everything in it is defined the same way as in getaddrinfo() ๐Ÿงš

The sa argument points to the socket address structure you want translated ๐Ÿ’–

The salen argument holds the size of the socket address structure that sa points to ๐Ÿ“

If the socket address structure contains an IPv4-mapped IPv6 address or an IPv4-compatible IPv6 address, the implementation SHALL extract the embedded IPv4 address and look up the node name for that IPv4 address ๐ŸŒˆ

Note: The IPv6 unspecified address ("::") and the IPv6 loopback address ("::1") are not IPv4-compatible addresses. If the address is the IPv6 unspecified address ("::"), a lookup is not performed, and the [EAI_NONAME] error is returned. ๐Ÿ™ˆ

If the node argument is non-NULL and the nodelen argument is nonzero, then node points to a buffer that can hold up to nodelen characters. That buffer receives the node name as a null-terminated string ๐Ÿ“ If node is NULL or nodelen is zero, the node name SHALL NOT be returned. And if the node's name can't be located, you get the numeric form of the node's address instead ๐Ÿ”ข

If the service argument is non-NULL and the servicelen argument is non-zero, then service points to a buffer that can hold up to servicelen bytes. That buffer receives the service name as a null-terminated string ๐ŸŽ€ If service is NULL or servicelen is zero, the service name SHALL NOT be returned. If the service's name can't be located, the numeric form of the service address (for example, its port number) SHALL be returned instead of its name ๐Ÿ”ข

The arguments node and service cannot both be NULL ๐Ÿšซ

The flags argument is a flag that changes the default actions of the function. By default, the fully-qualified domain name (FQDN) for the host SHALL be returned, but:

  • If the flag bit NI_NOFQDN is set, only the node name portion of the FQDN SHALL be returned for local hosts. โœ‚๏ธ
  • If the flag bit NI_NUMERICHOST is set, the numeric form of the host's address SHALL be returned instead of its name, under all circumstances. ๐Ÿ”ข
  • If the flag bit NI_NAMEREQD is set, an error SHALL be returned if the host's name can't be located. ๐Ÿšจ
  • If the flag bit NI_NUMERICSERV is set, the numeric form of the service address SHALL be returned (for example, its port number) instead of its name, under all circumstances. ๐Ÿ”ข
  • If the flag bit NI_DGRAM is set, that means the service is a datagram service (SOCK_DGRAM). The default behavior SHALL assume that the service is a stream service (SOCK_STREAM). ๐Ÿ“ฆ

Note:

  1. The NI_NUMERICxxx flags are required to support the "-n" flags that many commands provide. ๐Ÿ’ป
  2. The NI_DGRAM flag is required for the few AF_INET and AF_INET6 port numbers (for example, [512,514]) that represent different services for UDP and TCP. ๐Ÿฆ‹

getnameinfo() SHALL be thread safe ๐Ÿงต

A zero return value from getnameinfo() means successful completion. A non-zero return value means failure ๐Ÿ‘

Upon successful completion, getnameinfo() SHALL return the node and service names, if requested, in the buffers provided. The returned names are always null-terminated strings ๐Ÿ’–

Error Return Values:

The getnameinfo() function SHALL fail and return the corresponding value if:

[EAI_AGAIN]    The name could not be resolved at this time.
               Future attempts may succeed.

[EAI_BADFLAGS] The flags had an invalid value.

[EAI_FAIL]     A non-recoverable error occurred.

[EAI_FAMILY]   The address family was not recognized or the address
               length was invalid for the specified family.

[EAI_MEMORY]   There was a memory allocation failure.

[EAI_NONAME]   The name does not resolve for the supplied parameters.
               NI_NAMEREQD is set and the host's name cannot be
               located, or both nodename and servname were null.

[EAI_OVERFLOW] An argument buffer overflowed.

[EAI_SYSTEM]   A system error occurred.  The error code can be found
               in errno.

6.3. Address Conversion Functions ๐Ÿ”„

okay so IPv4 has inet_addr() and inet_ntoa() for converting an address between binary and text form. IPv6 apps need similar tools, so these two functions handle both IPv6 and IPv4 addresses ๐Ÿซถ

#include <arpa/inet.h>

int inet_pton(int af, const char *src, void *dst);

const char *inet_ntop(int af, const void *src,
                         char *dst, socklen_t size);

inet_pton() SHALL convert an address in its standard text presentation form into its numeric binary form ๐Ÿ”ข The af argument SHALL specify the family of the address. AF_INET and AF_INET6 SHALL be supported. The src argument points to the string being passed in. The dst argument points to a buffer where the function stores the numeric address. That buffer SHALL be large enough to hold the numeric address (32 bits for AF_INET, 128 bits for AF_INET6). inet_pton() SHALL return 1 if the conversion succeeds, with the address pointed to by dst in network byte order. It SHALL return 0 if the input is not a valid IPv4 dotted-decimal string or a valid IPv6 address string, or -1 with errno set to EAFNOSUPPORT if the af argument is unknown ๐Ÿ™…โ€โ™€๏ธ

If the af argument of inet_pton() is AF_INET, the src string SHALL be in the standard IPv4 dotted-decimal form:

ddd.ddd.ddd.ddd

where "ddd" is a one to three digit decimal number between 0 and 255. inet_pton() does not accept other formats (such as the octal numbers, hexadecimal numbers, and fewer than four numbers that inet_addr() accepts). so strict ๐Ÿ˜ค

If the af argument of inet_pton() is AF_INET6, the src string SHALL be in one of the standard IPv6 text forms defined in Section 2.2 of the addressing architecture specification [2] ๐ŸŒธ

inet_ntop() SHALL convert a numeric address into a text string suitable for presentation ๐ŸŽ€ The af argument SHALL specify the family of the address. This can be AF_INET or AF_INET6. The src argument points to a buffer holding an IPv4 address if af is AF_INET, or an IPv6 address if af is AF_INET6. The address MUST be in network byte order. Wait, correction: the address SHALL be in network byte order. The dst argument points to a buffer where the function stores the resulting text string, and it SHALL NOT be NULL. The size argument specifies the size of this buffer, which SHALL be large enough to hold the text string (INET_ADDRSTRLEN characters for IPv4, INET6_ADDRSTRLEN characters for IPv6) โœจ

So that apps can easily declare buffers of the right size for IPv4 and IPv6 addresses in string form, these two constants are defined in <netinet/in.h>:

#define INET_ADDRSTRLEN    16
#define INET6_ADDRSTRLEN   46

inet_ntop() SHALL return a pointer to the buffer containing the text string if the conversion succeeds, and NULL otherwise. On failure, errno is set to EAFNOSUPPORT if the af argument is invalid, or to ENOSPC if the size of the result buffer is inadequate ๐Ÿ’€


6.4. Address Testing Macros ๐Ÿงช

these macros let you test for special IPv6 addresses ๐Ÿฆ‹

#include <netinet/in.h>

int  IN6_IS_ADDR_UNSPECIFIED (const struct in6_addr *);
int  IN6_IS_ADDR_LOOPBACK    (const struct in6_addr *);
int  IN6_IS_ADDR_MULTICAST   (const struct in6_addr *);
int  IN6_IS_ADDR_LINKLOCAL   (const struct in6_addr *);
int  IN6_IS_ADDR_SITELOCAL   (const struct in6_addr *);
int  IN6_IS_ADDR_V4MAPPED    (const struct in6_addr *);
int  IN6_IS_ADDR_V4COMPAT    (const struct in6_addr *);

int  IN6_IS_ADDR_MC_NODELOCAL(const struct in6_addr *);
int  IN6_IS_ADDR_MC_LINKLOCAL(const struct in6_addr *);
int  IN6_IS_ADDR_MC_SITELOCAL(const struct in6_addr *);
int  IN6_IS_ADDR_MC_ORGLOCAL (const struct in6_addr *);
int  IN6_IS_ADDR_MC_GLOBAL   (const struct in6_addr *);

The first seven macros return true if the address is of the specified type, or false otherwise. The last five test the scope of a multicast address. They return true if the address is a multicast address of the specified scope, and false if the address is either not multicast or not of that scope ๐Ÿ”

Note that IN6_IS_ADDR_LINKLOCAL and IN6_IS_ADDR_SITELOCAL return true only for the two types of local-use IPv6 unicast addresses (Link-Local and Site-Local) defined in [2]. By that definition, the IN6_IS_ADDR_LINKLOCAL macro returns false for the IPv6 loopback address (::1). These two macros do NOT return true for IPv6 multicast addresses of either link-local or site-local scope ๐Ÿ™…โ€โ™€๏ธ


7. Summary of New Definitions ๐Ÿ“‹

okay here's the cheat sheet! It lists the constants, structures, and extern definitions from this memo, sorted by header ๐Ÿ—‚๏ธ

<net/if.h>      IF_NAMESIZE
<net/if.h>      struct if_nameindex{};

<netdb.h>       AI_ADDRCONFIG
<netdb.h>       AI_ALL
<netdb.h>       AI_CANONNAME
<netdb.h>       AI_NUMERICHOST
<netdb.h>       AI_NUMERICSERV
<netdb.h>       AI_PASSIVE
<netdb.h>       AI_V4MAPPED
<netdb.h>       EAI_AGAIN
<netdb.h>       EAI_BADFLAGS
<netdb.h>       EAI_FAIL
<netdb.h>       EAI_FAMILY
<netdb.h>       EAI_MEMORY
<netdb.h>       EAI_NONAME
<netdb.h>       EAI_OVERFLOW
<netdb.h>       EAI_SERVICE
<netdb.h>       EAI_SOCKTYPE
<netdb.h>       EAI_SYSTEM
<netdb.h>       NI_DGRAM
<netdb.h>       NI_NAMEREQD
<netdb.h>       NI_NOFQDN
<netdb.h>       NI_NUMERICHOST
<netdb.h>       NI_NUMERICSERV
<netdb.h>       struct addrinfo{};

<netinet/in.h>  IN6ADDR_ANY_INIT
<netinet/in.h>  IN6ADDR_LOOPBACK_INIT
<netinet/in.h>  INET6_ADDRSTRLEN
<netinet/in.h>  INET_ADDRSTRLEN
<netinet/in.h>  IPPROTO_IPV6
<netinet/in.h>  IPV6_JOIN_GROUP
<netinet/in.h>  IPV6_LEAVE_GROUP
<netinet/in.h>  IPV6_MULTICAST_HOPS
<netinet/in.h>  IPV6_MULTICAST_IF
<netinet/in.h>  IPV6_MULTICAST_LOOP
<netinet/in.h>  IPV6_UNICAST_HOPS
<netinet/in.h>  IPV6_V6ONLY
<netinet/in.h>  SIN6_LEN
<netinet/in.h>  extern const struct in6_addr in6addr_any;
<netinet/in.h>  extern const struct in6_addr in6addr_loopback;
<netinet/in.h>  struct in6_addr{};
<netinet/in.h>  struct ipv6_mreq{};
<netinet/in.h>  struct sockaddr_in6{};

<sys/socket.h>  AF_INET6
<sys/socket.h>  PF_INET6

<sys/socket.h> struct sockaddr_storage;


okay and the last struct on the list is `sockaddr_storage` ๐Ÿ“ฆ it's the big-enough-for-anything container, so you never have to guess which address family you're holding ๐Ÿ’…

here's the whole prototype cheat sheet, sorted by header, bestie ๐Ÿ“‹โœจ

<arpa/inet.h> int inet_pton(int, const char *, void *); <arpa/inet.h> const char *inet_ntop(int, const void *, char *, socklen_t);

<net/if.h> char *if_indextoname(unsigned int, char *); <net/if.h> unsigned int if_nametoindex(const char *); <net/if.h> void if_freenameindex(struct if_nameindex *); <net/if.h> struct if_nameindex *if_nameindex(void);

<netdb.h> int getaddrinfo(const char *, const char *, const struct addrinfo *, struct addrinfo **); <netdb.h> int getnameinfo(const struct sockaddr *, socklen_t, char *, socklen_t, char *, socklen_t, int); <netdb.h> void freeaddrinfo(struct addrinfo *); <netdb.h> const char *gai_strerror(int);

<netinet/in.h> int IN6_IS_ADDR_LINKLOCAL(const struct in6_addr *); <netinet/in.h> int IN6_IS_ADDR_LOOPBACK(const struct in6_addr *); <netinet/in.h> int IN6_IS_ADDR_MC_GLOBAL(const struct in6_addr *); <netinet/in.h> int IN6_IS_ADDR_MC_LINKLOCAL(const struct in6_addr *); <netinet/in.h> int IN6_IS_ADDR_MC_NODELOCAL(const struct in6_addr *); <netinet/in.h> int IN6_IS_ADDR_MC_ORGLOCAL(const struct in6_addr *); <netinet/in.h> int IN6_IS_ADDR_MC_SITELOCAL(const struct in6_addr *); <netinet/in.h> int IN6_IS_ADDR_MULTICAST(const struct in6_addr *); <netinet/in.h> int IN6_IS_ADDR_SITELOCAL(const struct in6_addr *); <netinet/in.h> int IN6_IS_ADDR_UNSPECIFIED(const struct in6_addr *); <netinet/in.h> int IN6_IS_ADDR_V4COMPAT(const struct in6_addr *); <netinet/in.h> int IN6_IS_ADDR_V4MAPPED(const struct in6_addr *);


# 8. Security Considerations ๐Ÿ”’

<red>IPv6 brings a whole bunch of new security mechanisms</red>, and a lot of them need to be reachable from applications, not just buried down in the network layer ๐Ÿ›ก๏ธ Companion memos spelling out how the socket interfaces get extended to support IPv6 security are being written right now, so keep an eye out ๐Ÿ‘€

# 9. Changes from RFC 2553 ๐Ÿ“

okay, the glow-up list since RFC 2553 ๐Ÿ’… here's what changed:

1. Add brief description of the history of this API and its relation to the Open Group/IEEE/ISO standards.

2. Alignments with [3].

3. Removed all references to `getipnodebyname()` and `getipnodebyaddr()`, which are deprecated in favor of `getaddrinfo()` and `getnameinfo()`. bye bye, old faves ๐Ÿ‘‹

4. Added `IPV6_V6ONLY` IP level socket option to permit nodes to not process IPv4 packets as IPv4 Mapped addresses in implementations. <mint>this one is a big deal, periodt</mint> ๐ŸŒธ

5. Added SIIT to references and added new contributors. ๐Ÿซถ

6. In previous versions of this specification, the `sin6_flowinfo` field was associated with the IPv6 traffic class and flow label, but its usage was not completely specified. The complete definition of the `sin6_flowinfo` field, including its association with the traffic class or flow label, is now deferred to a future specification. <yellow>so yeah, that one got pushed to a later date, no drama</yellow> ๐Ÿงš

# 10. Acknowledgments ๐Ÿซถ

<lavender>This specification's evolution and completeness were significantly influenced by the efforts of Richard Stevens, who has passed on. Richard's wisdom and talent made the specification what it is today. The co-authors will long think of Richard with great respect.</lavender> ๐Ÿ’œ

Thanks to the many people who made suggestions and provided feedback to this document, including: Werner Almesberger, Ran Atkinson, Fred Baker, Dave Borman, Andrew Cherenson, Alex Conta, Alan Cox, Steve Deering, Richard Draves, Francis Dupont, Robert Elz, Brian Haberman, Jun-ichiro itojun Hagino, Marc Hasson, Tom Herbert, Bob Hinden, Wan-Yen Hsu, Christian Huitema, Koji Imada, Markus Jork, Ron Lee, Alan Lloyd, Charles Lynn, Dan McDonald, Dave Mitton, Finnbarr Murphy, Thomas Narten, Josh Osborne, Craig Partridge, Jean-Luc Richier, Bill Sommerfield, Erik Scoredos, Keith Sklower, JINMEI Tatuya, Dave Thaler, Matt Thomas, Harvey Thompson, Dean D. Throop, Karen Tracey, Glenn Trewitt, Paul Vixie, David Waitzman, Carl Williams, Kazu Yamamoto, Vlad Yasevich, Stig Venaas, and Brian Zill. ๐Ÿ™Œ

The `getaddrinfo()` and `getnameinfo()` functions are taken from an earlier document by Keith Sklower. As noted in that document, William Durst, Steven Wise, Michael Karels, and Eric Allman provided many useful discussions on the subject of protocol-independent name-to-address translation, and reviewed early versions of Keith Sklower's original proposal. Eric Allman implemented the first prototype of `getaddrinfo()`. The observation that specifying the pair of name and service would suffice for connecting to a service independent of protocol details was made by Marshall Rose in a proposal to X/Open for a "Uniform Network Interface". ๐Ÿง 

Craig Metz, Jack McCann, Erik Nordmark, Tim Hartrick, and Mukesh Kacker made many contributions to this document. Ramesh Govindan made a number of contributions and co-authored an earlier version of this memo. ๐Ÿ’–

# 11. References ๐Ÿ“š

- [1] Deering, S. and R. Hinden, "Internet Protocol, Version 6 (IPv6) Specification", RFC 2460, December 1998.
- [2] Hinden, R. and S. Deering, "IP Version 6 Addressing Architecture", RFC 2373, July 1998.
- [3] IEEE Std. 1003.1-2001 Standard for Information Technology -- Portable Operating System Interface (POSIX). Open Group Technical Standard: Base Specifications, Issue 6, December 2001. ISO/IEC 9945:2002. `http://www.opengroup.org/austin`
- [4] Stevens, W. and M. Thomas, "Advanced Sockets API for IPv6", RFC 2292, February 1998.
- [5] Nordmark, E., "Stateless IP/ICMP Translation Algorithm (SIIT)", RFC 2765, February 2000.
- [6] The Open Group Base Working Group `http://www.opengroup.org/platform/base.html`

# 12. Authors' Addresses ๐Ÿ 

Bob Gilligan ๐ŸŒˆ

Bob Gilligan Intransa, Inc. 2870 Zanker Rd. San Jose, CA 95134

Phone: 408-678-8647 EMail: gilligan@intransa.com


Susan Thomson ๐ŸŒˆ

Susan Thomson Cisco Systems 499 Thornall Street, 8th floor Edison, NJ 08837

Phone: 732-635-3086 EMail: sethomso@cisco.com


Jim Bound ๐ŸŒˆ

Jim Bound Hewlett-Packard Company 110 Spitbrook Road ZKO3-3/W20 Nashua, NH 03062

Phone: 603-884-0062 EMail: Jim.Bound@hp.com


Jack McCann ๐ŸŒˆ

Jack McCann Hewlett-Packard Company 110 Spitbrook Road ZKO3-3/W20 Nashua, NH 03062

Phone: 603-884-2608 EMail: Jack.McCann@hp.com


# 13. Full Copyright Statement ยฉ๏ธ

Copyright (C) The Internet Society (2003). All Rights Reserved. ๐Ÿ”

Okay, the legal bit, but make it cute ๐ŸŽ€ 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, as long as the above copyright notice and this paragraph are included on all such copies and derivative works. However, this document itself may not be modified in any way, such as by removing the copyright notice or references to the Internet Society or other Internet organizations, except as needed for the purpose of developing Internet standards, in which case the procedures for copyrights defined in the Internet Standards process must be followed, or as required to translate it into languages other than English.

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

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

# Acknowledgement ๐Ÿ’–

<pink>Funding for the RFC Editor function is currently provided by the Internet Society.</pink> โœจ
๐Ÿ“ฆ where this page comes from