The meaning of the various bits in IPv6 addresses is considerably more complex than v4 - so much so that there is an entire RFC (RFC-2373) dedicated to it. The two major issues presented by IPv6 addresses are that they are large (usually too large to fit in a single CPU register) and there are many of them. The approach of assigning a single "unsigned long
"-sized IP address field to each interface usually works quite well with IPv4, however IPv6 requires something more complex.
This section of the manual is merely an overview of these concepts and some insight on how they are implemented in the InterNiche code.
When an InterNiche stack is built with the IP_V6
compile-time #ifdef
set in ipport.h
, several fields are added to each net
structure. Among these is an array of pointers to structures that contain IPv6 addressing information for that interface.
struct ip6_inaddr { struct ip6_inaddr * next; /* for application use */ ip6_addr addr; /* address value (maybe unassigned) */ int prefix; /* number of bits in prefix */ struct net * ifp; /* iface associated with it (maybe NULL) */ int flags; /* mask of the IA_ bits below */ u_long tmo1; /* expiration ctick if IA_LEASE flag set */ u_long tmo2; /* expiration ctick of address */ u_long lasttm; /* last action for dup checking, etc. */ int dups; /* Dup. check packets sent */ };
Programmers must use care when accessing these addresses, since the pointers in the net
structure may be NULL
. Further, the structure may exist but the pointer to the actual address data (addr
) may be NULL
.
The "dot notation" used to express IPv4 address in human-readable format would be rather tedious for IPv6, since IPv6 addresses have considerable length. A different notation has been set down by RFC-2373 to express IPv6 addresses. IPv6 notation conforms to the following rules:
Here is a typical example of a printed IPv6 address in this format:
FE80::0248:54FF:FE86:D329
Ten bytes of non-zero data are displayed in five 16-bit values. Bytes three through eight of the address are assumed to be all zeros, as indicated by the double-colon after "FE80
".
RFC-2553 "Basic Socket Interface Extensions for IPv6" specifies a C language API to convert these strings between 128-bit binary strings and ASCII. The InterNiche code provides both routines (inet_pton()
and inet_ntop()
) in the source file ip6menu.c
.
In IPv4 implementations, IP addresses are almost always passed by value. Since most CPUs could store 32 bits on a CPU stack or in registers, there was not reason not to do it this way. The larger IPv6 addresses use more time and space when passed by value, so instead the InterNiche code makes every effort to pass IPv6 addresses by reference (pointers).
This helps performance, but makes the code somewhat harder to maintain, since the programmer must always be aware of the scope of the address passed by pointer. If the address is transient, for example a pointer into a PACKET
buffer, then the pointer obviously should not be stored in a more permanent structure, such as a routing table entry. In cases like this, the address should be copied into the longer-lived structure via IP6CPY()
. This means the structures also must be designed with some awareness of how they are to be used in the code - long lived structures which get info from short-lived storage should always provide for a local copy of the IP address.
The IP routing table entry mentioned above is a good example of a structure that gets a long-lived IP address from a potentially short-lived source. The "nexthop
" field in PACKET
structure illustrates the inverse scenario. This IPv6 address is set by routing code when an IPv6 packet is about to be sent. It is set on every packet transmission, so setting it should be fast. The source is either the routing table or the ND cache (see the discussion of Neighbor Discovery in Section 9.7), both of which are very long lived when compared to the longevity of a PACKET
. Additionally, the routing table and ND cache are protected from having entries deleted during the transmit process. Overall, it makes sense to have nexthop
be a pointer to the IP address in the routing table or ND cache, rather than have an additional copy of the IP address.
IPv6 supports a number of different types of IP address, with a wide variety of scopes and purposes. Excluding multicast and internal addresses, the three main types are:
Link local | Address valid only on the local network segment |
Site local | Address valid only on the local site |
Global Unicast | Globally unique, globally usable address |
These addresses are assigned and maintained on a per-interface basis. Additionally, the required predefined multicast addresses (see the list in Section 11.7) are added to the interface's multicast list at startup time.
The two types of IPv6 address that seem to have the most importance are the Link local and the global types. The global type is analogous to a public IPv4 address. Its assignment is controlled by the ICANN (via regional providers in each country), and once assigned these addresses can be used to reach any other global IPv6 host attached to the Internet. Until the ratification of a DNCPv6 specification, these addresses must be assigned manually by a system administrator.
The link local addresses are somewhat analogous to private IPv4 addresses (e.g. 10.0.0.1
). They can only be used to communicate between hosts on the same network segment. A router should never forward link local addresses.
The primary advantage of link-local addresses is that they can be configured from the MAC addresses of Ethernet devices (and similar network adapters). The first 8 bytes are defined as:
FE80:0000:0000:0000
The remaining 8 bytes contain an encoding of the Ethernet MAC address. The ninth through eleventh bytes of the IPv6 address contain the first three bytes of the Ethernet MAC (the vendor ID). The next two bytes of the IPv6 address is predefined as "0xFFFE
" and the last three bytes of the IPv6 address contain the last three bytes of the MAC address. An added complexity is that the second last bit of the first byte is inverted. RFC-2373 explains the reasoning for MAC encoding.
Thus the MAC address: 00 48 54 86 D3 29
produces the link-local IPv6 address: FE80::0248:54FF:FE86:D329
.
The InterNiche IPv6 code configures each Ethernet interface with a link local address.
Two classes of address are defined by RFC-2373 which do not yet have a well-defined purpose. These are site local address and the node local address.
The site local address is generally described as an IP address that may only be used at a certain site. Packets containing these addresses may be routed between segments in a single campus or corporate network, but may not be sent across the greater Internet. The IETF working group has not yet agreed upon an exact definition of a site, and so the site-local address is not used as of the beginning of 2003. InterNiche IPv6 interfaces have a placeholder for a site local address in the "net
" structure, however the field is currently unused.
A node local address is defined as an address that is only valid in a single node; somewhat as a site-local address is only valid at a single site. It was originally intended for testing purposes, however the universally defined IPv6 loopback is more suitable for tests. InterNiche IPv6 interfaces do not have a field for a node local address in the "net
" structure. [Note: If support for this is added, it may be implemented by adding IP layer code that looks for the node-local prefix (0xFE40
), and direct all outgoing node-local packets to the loopback interface.]
There is also a plethora of predefined IPv6 IP addresses which all systems are required to support. These addresses are summarized in this section, along with descriptions of how they are handled by InterNiche implementation.
The IPv6 loopback address is all zeros except for a "1
" in the final bit, a.k.a."::1
". The InterNiche IPv6 code implements loopback by created a dedicated loopback network interface. No attempt is made to allow for "looping back" packets without a loopback interface.
Link local and site local loopback addresses are created by adding the appropriate prefixes to the address - FE80::1
and FE40::1
, respectively. All three address types - link local, site local, and global - are filled into the loopback driver by #ifdeff
ed code in the loopback driver, macloop.c
.
The all-nodes multicast is a predefined multicast group that has as its members all IPv6 hosts. The address is FF02::1
. This is essentially the same as the IPv4 broadcast address, 255.255.255.255
. The InterNiche code supports this by adding the address to the multicast list via calls to in_addmulti()
. One call is made for this address for every interface in the IPv6 system.
The all-routers multicast is a predefined multicast group that has as its members all IPv6 routers. The address is FF02::2
. On builds compiled with the IP_ROUTING
flag set in ipport.h
, the InterNiche code supports this by adding the address to the multicast list via calls to in_addmulti()
. One call is made for this address for each interface in the IPv6 system.
The MAC specific multicast is an IPv6 address made for each interface by encoding part of the interface's MAC address into a predefined multicast prefix. The prefix value is FF02::01:FF00:0
. The last three bytes of the address are copied from the last three bytes of the MAC address. The MAC specific multicast address for MAC address 00 48 54 86 D3 29
is FF02::01:FF86:D329
.
The InterNiche code supports MAC specific address via two somewhat overlapping mechanisms. First, it builds the address for each MAC interface, and places the address in the interface's ip6_inaddr[ ] slot reserved for this purpose. Second, it adds the address to the multicast list via calls to in_addmulti()
. One call is made for this address for every interface in the IPv6 system.
Lastly, section 2.5.2 of RFC-2373 specifies an "unspecified address" which is designed to be an indicator that an IPv6 address filed has not been set, or (in the case of some socket calls) is to be considered a wildcard address. The value of the unspecified address is all zeros.
IPv6 on Ethernet LANs utilizes the "Neighbor Discovery" protocol to discover the MAC address of other nodes on the LAN and is the replacement for IPv4's ARP protocol. InterNiche Neighbor Discovery maintains a cache (the "ND cache") of link local addresses of known neighbors as described in RFC 2461. This cache is not intended to be statically configured, or to be manually modified.
When an IPv6 packet is transmitted, both the ND cache and the routing table are used to determine how the packet is sent. The InterNiche code uses the routine ip6_destif()
to make this invisible to the sending code. Nominally ip6_destif()
figures out which interface and what "next" address to use for routing an IPv6 datagram. It tries routing first, and falls back to a lookup in the ND cache if there is no route and the address is local. In either case, it returns the net to be used, and fills in several fields in the packet passed.
Link-local addresses require a pre-existing ND entry or other method of routing. BSD systems require that name of the interface to be used be appended to link local addresses to facilitate initial sending. The InterNiche code is somewhat more friendly, being able to reach link local hosts as long as they already have an entry in the ND cache.
For additional information, see the utility functions ip6_addrcango() and ip6_matchmyaddr().