xref: /onnv-gate/usr/src/cmd/cmd-inet/sbin/dhcpagent/README.v6 (revision 5381:6bff17151099)
13431ScarlsonjCDDL HEADER START
23431Scarlsonj
33431ScarlsonjThe contents of this file are subject to the terms of the
43431ScarlsonjCommon Development and Distribution License (the "License").
53431ScarlsonjYou may not use this file except in compliance with the License.
63431Scarlsonj
73431ScarlsonjYou can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
83431Scarlsonjor http://www.opensolaris.org/os/licensing.
93431ScarlsonjSee the License for the specific language governing permissions
103431Scarlsonjand limitations under the License.
113431Scarlsonj
123431ScarlsonjWhen distributing Covered Code, include this CDDL HEADER in each
133431Scarlsonjfile and include the License file at usr/src/OPENSOLARIS.LICENSE.
143431ScarlsonjIf applicable, add the following below this CDDL HEADER, with the
153431Scarlsonjfields enclosed by brackets "[]" replaced with your own identifying
163431Scarlsonjinformation: Portions Copyright [yyyy] [name of copyright owner]
173431Scarlsonj
183431ScarlsonjCDDL HEADER END
193431Scarlsonj
203431ScarlsonjCopyright 2007 Sun Microsystems, Inc.  All rights reserved.
213431ScarlsonjUse is subject to license terms.
223431Scarlsonj
233431Scarlsonjident	"%Z%%M%	%I%	%E% SMI"
243431Scarlsonj
25*5381Smeem
26*5381Smeem**  PLEASE NOTE:
27*5381Smeem**
28*5381Smeem**  This document discusses aspects of the DHCPv4 client design that have
29*5381Smeem**  since changed (e.g., DLPI is no longer used).  However, since those
30*5381Smeem**  aspects affected the DHCPv6 design, the discussion has been left for
31*5381Smeem**  historical record.
32*5381Smeem
33*5381Smeem
343431ScarlsonjDHCPv6 Client Low-Level Design
353431Scarlsonj
363431ScarlsonjIntroduction
373431Scarlsonj
383431Scarlsonj  This project adds DHCPv6 client-side (not server) support to
393431Scarlsonj  Solaris.  Future projects may add server-side support as well as
403431Scarlsonj  enhance the basic capabilities added here.  These future projects
413431Scarlsonj  are not discussed in detail in this document.
423431Scarlsonj
433431Scarlsonj  This document assumes that the reader is familiar with the following
443431Scarlsonj  other documents:
453431Scarlsonj
463431Scarlsonj  - RFC 3315: the primary description of DHCPv6
473431Scarlsonj  - RFCs 2131 and 2132: IPv4 DHCP
483431Scarlsonj  - RFCs 2461 and 2462: IPv6 NDP and stateless autoconfiguration
493431Scarlsonj  - RFC 3484: IPv6 default address selection
503431Scarlsonj  - ifconfig(1M): Solaris IP interface configuration
513431Scarlsonj  - in.ndpd(1M): Solaris IPv6 Neighbor and Router Discovery daemon
523431Scarlsonj  - dhcpagent(1M): Solaris DHCP client
533431Scarlsonj  - dhcpinfo(1): Solaris DHCP parameter utility
543431Scarlsonj  - ndpd.conf(4): in.ndpd configuration file
553431Scarlsonj  - netstat(1M): Solaris network status utility
563431Scarlsonj  - snoop(1M): Solaris network packet capture and inspection
573431Scarlsonj  - "DHCPv6 Client High-Level Design"
583431Scarlsonj
593431Scarlsonj  Several terms from those documents (such as the DHCPv6 IA_NA and
603431Scarlsonj  IAADDR options) are used without further explanation in this
613431Scarlsonj  document; see the reference documents above for details.
623431Scarlsonj
633431Scarlsonj  The overall plan is to enhance the existing Solaris dhcpagent so
643431Scarlsonj  that it is able to process DHCPv6.  It would also have been possible
653431Scarlsonj  to create a new, separate daemon process for this, or to integrate
663431Scarlsonj  the feature into in.ndpd.  These alternatives, and the reason for
673431Scarlsonj  the chosen design, are discussed in Appendix A.
683431Scarlsonj
693431Scarlsonj  This document discusses the internal design issues involved in the
703431Scarlsonj  protocol implementation, and with the associated components (such as
713431Scarlsonj  in.ndpd, snoop, and the kernel's source address selection
723431Scarlsonj  algorithm).  It does not discuss the details of the protocol itself,
733431Scarlsonj  which are more than adequately described in the RFC, nor the
743431Scarlsonj  individual lines of code, which will be in the code review.
753431Scarlsonj
763431Scarlsonj  As a cross-reference, Appendix B has a summary of the components
773431Scarlsonj  involved and the changes to each.
783431Scarlsonj
793431Scarlsonj
803431ScarlsonjBackground
813431Scarlsonj
823431Scarlsonj  In order to discuss the design changes for DHCPv6, it's necessary
833431Scarlsonj  first to talk about the current IPv4-only design, and the
843431Scarlsonj  assumptions built into that design.
853431Scarlsonj
863431Scarlsonj  The main data structure used in dhcpagent is the 'struct ifslist'.
873431Scarlsonj  Each instance of this structure represents a Solaris logical IP
883431Scarlsonj  interface under DHCP's control.  It also represents the shared state
893431Scarlsonj  with the DHCP server that granted the address, the address itself,
903431Scarlsonj  and copies of the negotiated options.
913431Scarlsonj
923431Scarlsonj  There is one list in dhcpagent containing all of the IP interfaces
933431Scarlsonj  that are under DHCP control.  IP interfaces not under DHCP control
943431Scarlsonj  (for example, those that are statically addressed) are not included
953431Scarlsonj  in this list, even when plumbed on the system.  These ifslist
963431Scarlsonj  entries are chained like this:
973431Scarlsonj
983431Scarlsonj  ifsheadp -> ifslist -> ifslist -> ifslist -> NULL
993431Scarlsonj	        net0	  net0:1     net1
1003431Scarlsonj
1013431Scarlsonj  Each ifslist entry contains the address, mask, lease information,
1023431Scarlsonj  interface name, hardware information, packets, protocol state, and
1033431Scarlsonj  timers.  The name of the logical IP interface under DHCP's control
1043431Scarlsonj  is also the name used in the administrative interfaces (dhcpinfo,
1053431Scarlsonj  ifconfig) and when logging events.
1063431Scarlsonj
1073431Scarlsonj  Each entry holds open a DLPI stream and two sockets.  The DLPI
1083431Scarlsonj  stream is nulled-out with a filter when not in use, but still
1093431Scarlsonj  consumes system resources.  (Most significantly, it causes data
1103431Scarlsonj  copies in the driver layer that end up sapping performance.)
1113431Scarlsonj
1123431Scarlsonj  The entry storage is managed by a insert/hold/release/remove model
1133431Scarlsonj  and reference counts.  In this model, insert_ifs() allocates a new
1143431Scarlsonj  ifslist entry and inserts it into the global list, with the global
1153431Scarlsonj  list holding a reference.  remove_ifs() removes it from the global
1163431Scarlsonj  list and drops that reference.  hold_ifs() and release_ifs() are
1173431Scarlsonj  used by data structures that refer to ifslist entries, such as timer
1183431Scarlsonj  entries, to make sure that the ifslist entry isn't freed until the
1193431Scarlsonj  timer has been dispatched or deleted.
1203431Scarlsonj
1213431Scarlsonj  The design is single-threaded, so code that walks the global list
1223431Scarlsonj  needn't bother taking holds on the ifslist structure.  Only
1233431Scarlsonj  references that may be used at a different time (i.e., pointers
1243431Scarlsonj  stored in other data structures) need to be recorded.
1253431Scarlsonj
1263431Scarlsonj  Packets are handled using PKT (struct dhcp; <netinet/dhcp.h>),
1273431Scarlsonj  PKT_LIST (struct dhcp_list; <dhcp_impl.h>), and dhcp_pkt_t (struct
1283431Scarlsonj  dhcp_pkt; "packet.h").  PKT is just the RFC 2131 DHCP packet
1293431Scarlsonj  structure, and has no additional information, such as packet length.
1303431Scarlsonj  PKT_LIST contains a PKT pointer, length, decoded option arrays, and
1313431Scarlsonj  linkage for putting the packet in a list.  Finally, dhcp_pkt_t has a
1323431Scarlsonj  PKT pointer and length values suitable for modifying the packet.
1333431Scarlsonj
1343431Scarlsonj  Essentially, PKT_LIST is a wrapper for received packets, and
1353431Scarlsonj  dhcp_pkt_t is a wrapper for packets to be sent.
1363431Scarlsonj
1373431Scarlsonj  The basic PKT structure is used in dhcpagent, inetboot, in.dhcpd,
1383431Scarlsonj  libdhcpagent, libwanboot, libdhcputil, and others.  PKT_LIST is used
1393431Scarlsonj  in a similar set of places, including the kernel NFS modules.
1403431Scarlsonj  dhcp_pkt_t is (as the header file implies) limited to dhcpagent.
1413431Scarlsonj
1423431Scarlsonj  In addition to these structures, dhcpagent maintains a set of
1433431Scarlsonj  internal supporting abstractions.  Two key ones involved in this
1443431Scarlsonj  project are the "async operation" and the "IPC action."  An async
1453431Scarlsonj  operation encapsulates the actions needed for a given operation, so
1463431Scarlsonj  that if cancellation is needed, there's a single point where the
1473431Scarlsonj  associated resources can be freed.  An IPC action represents the
1483431Scarlsonj  user state related to the private interface used by ifconfig.
1493431Scarlsonj
1503431Scarlsonj
1513431ScarlsonjDHCPv6 Inherent Differences
1523431Scarlsonj
1533431Scarlsonj  DHCPv6 naturally has some commonality with IPv4 DHCP, but also has
1543431Scarlsonj  some significant differences.
1553431Scarlsonj
1563431Scarlsonj  Unlike IPv4 DHCP, DHCPv6 relies on link-local IP addresses to do its
1573431Scarlsonj  work.  This means that, on Solaris, the client doesn't need DLPI to
1583431Scarlsonj  perform any of the I/O; regular IP sockets will do the job.  It also
1593431Scarlsonj  means that, unlike IPv4 DHCP, DHCPv6 does not need to obtain a lease
1603431Scarlsonj  for the address used in its messages to the server.  The system
1613431Scarlsonj  provides the address automatically.
1623431Scarlsonj
1633431Scarlsonj  IPv4 DHCP expects some messages from the server to be broadcast.
1643431Scarlsonj  DHCPv6 has no such mechanism; all messages from the server to the
1653431Scarlsonj  client are unicast.  In the case where the client and server aren't
1663431Scarlsonj  on the same subnet, a relay agent is used to get the unicast replies
1673431Scarlsonj  back to the client's link-local address.
1683431Scarlsonj
1693431Scarlsonj  With IPv4 DHCP, a single address plus configuration options is
1703431Scarlsonj  leased with a given client ID and a single state machine instance,
1713431Scarlsonj  and the implementation binds that to a single IP logical interface
1723431Scarlsonj  specified by the user.  The lease has a "Lease Time," a required
1733431Scarlsonj  option, as well as two timers, called T1 (renew) and T2 (rebind),
1743431Scarlsonj  which are controlled by regular options.
1753431Scarlsonj
1763431Scarlsonj  DHCPv6 uses a single client/server session to control the
1773431Scarlsonj  acquisition of configuration options and "identity associations"
1783431Scarlsonj  (IAs).  The identity associations, in turn, contain lists of
1793431Scarlsonj  addresses for the client to use and the T1/T2 timer values.  Each
1803431Scarlsonj  individual address has its own preferred and valid lifetime, with
1813431Scarlsonj  the address being marked "deprecated" at the end of the preferred
1823431Scarlsonj  interval, and removed at the end of the valid interval.
1833431Scarlsonj
1843431Scarlsonj  IPv4 DHCP leaves many of the retransmit decisions up to the client,
1853431Scarlsonj  and some things (such as RELEASE and DECLINE) are sent just once.
1863431Scarlsonj  Others (such as the REQUEST message used for renew and rebind) are
1873431Scarlsonj  dealt with by heuristics.  DHCPv6 treats each message to the server
1883431Scarlsonj  as a separate transaction, and resends each message using a common
1893431Scarlsonj  retransmission mechanism.  DHCPv6 also has separate messages for
1903431Scarlsonj  Renew, Rebind, and Confirm rather than reusing the Request
1913431Scarlsonj  mechanism.
1923431Scarlsonj
1933431Scarlsonj  The set of options (which are used to convey configuration
1943431Scarlsonj  information) for each protocol are distinct.  Notably, two of the
1953431Scarlsonj  mistakes from IPv4 DHCP have been fixed: DHCPv6 doesn't carry a
1963431Scarlsonj  client name, and doesn't attempt to impersonate a routing protocol
1973431Scarlsonj  by setting a "default route."
1983431Scarlsonj
1993431Scarlsonj  Another welcome change is the lack of a netmask/prefix length with
2003431Scarlsonj  DHCPv6.  Instead, the client uses the Router Advertisement prefixes
2013431Scarlsonj  to set the correct interface netmask.  This reduces the number of
2023431Scarlsonj  databases that need to be kept in sync.  (The equivalent mechanism
2033431Scarlsonj  in IPv4 would have been the use of ICMP Address Mask Request /
2043431Scarlsonj  Reply, but the BOOTP designers chose to embed it in the address
2053431Scarlsonj  assignment protocol itself.)
2063431Scarlsonj
2073431Scarlsonj  Otherwise, DHCPv6 is similar to IPv4 DHCP.  The same overall
2083431Scarlsonj  renew/rebind and lease expiry strategy is used, although the state
2093431Scarlsonj  machine events must now take into account multiple IAs and the fact
2103431Scarlsonj  that each can cause RENEWING or REBINDING state independently.
2113431Scarlsonj
2123431Scarlsonj
2133431ScarlsonjDHCPv6 And Solaris
2143431Scarlsonj
2153431Scarlsonj  The protocol distinctions above have several important implications.
2163431Scarlsonj  For the logical interfaces:
2173431Scarlsonj
2183431Scarlsonj    - Because Solaris uses IP logical interfaces to configure
2193431Scarlsonj      addresses, we must have multiple IP logical interfaces per IA
2203431Scarlsonj      with IPv6.
2213431Scarlsonj
2223431Scarlsonj    - Because we need to support multiple addresses (and thus multiple
2233431Scarlsonj      IP logical interfaces) per IA and multiple IAs per client/server
2243431Scarlsonj      session, the IP logical interface name isn't a unique name for
2253431Scarlsonj      the lease.
2263431Scarlsonj
2273431Scarlsonj  As a result, IP logical interfaces will come and go with DHCPv6,
2283431Scarlsonj  just as happens with the existing stateless address
2293431Scarlsonj  autoconfiguration support in in.ndpd.  The logical interface names
2303431Scarlsonj  (visible in ifconfig) have no administrative significance.
2313431Scarlsonj
2323431Scarlsonj  Fortunately, DHCPv6 does end up with one fixed name that can be used
2333431Scarlsonj  to identify a session.  Because DHCPv6 uses link local addresses for
2343431Scarlsonj  communication with the server, the name of the IP logical interface
2353431Scarlsonj  that has this link local address (normally the same as the IP
2363431Scarlsonj  physical interface) can be used as an identifier for dhcpinfo and
2373431Scarlsonj  logging purposes.
2383431Scarlsonj
2393431Scarlsonj
2403431ScarlsonjDhcpagent Redesign Overview
2413431Scarlsonj
2423431Scarlsonj  The redesign starts by refactoring the IP interface representation.
2433431Scarlsonj  Because we need to have multiple IP logical interfaces (LIFs) for a
2443431Scarlsonj  single identity association (IA), we should not store all of the
2453431Scarlsonj  DHCP state information along with the LIF information.
2463431Scarlsonj
2473431Scarlsonj  For DHCPv6, we will need to keep LIFs on a single IP physical
2483431Scarlsonj  interface (PIF) together, so this is probably also a good time to
2493431Scarlsonj  reconsider the way dhcpagent represents physical interfaces.  The
2503431Scarlsonj  current design simply replicates the state (notably the DLPI stream,
2513431Scarlsonj  but also the hardware address and other bits) among all of the
2523431Scarlsonj  ifslist entries on the same physical interface.
2533431Scarlsonj
2543431Scarlsonj  The new design creates two lists of dhcp_pif_t entries, one list for
2553431Scarlsonj  IPv4 and the other for IPv6.  Each dhcp_pif_t represents a PIF, with
2563431Scarlsonj  a list of dhcp_lif_t entries attached, each of which represents a
2573431Scarlsonj  LIF used by dhcpagent.  This structure mirrors the kernel's ill_t
2583431Scarlsonj  and ipif_t interface representations.
2593431Scarlsonj
2603431Scarlsonj  Next, the lease-tracking needs to be refactored.  DHCPv6 is the
2613431Scarlsonj  functional superset in this case, as it has two lifetimes per
2623431Scarlsonj  address (LIF) and IA groupings with shared T1/T2 timers.  To
2633431Scarlsonj  represent these groupings, we will use a new dhcp_lease_t structure.
2643431Scarlsonj  IPv4 DHCP will have one such structure per state machine, while
2653431Scarlsonj  DHCPv6 will have a list.  (Note: the initial implementation will
2663431Scarlsonj  have only one lease per DHCPv6 state machine, because each state
2673431Scarlsonj  machine uses a single link-local address, a single DUID+IAID pair,
2683431Scarlsonj  and supports only Non-temporary Addresses [IA_NA option].  Future
2693431Scarlsonj  enhancements may use multiple leases per DHCPv6 state machine or
2703431Scarlsonj  support other IA types.)
2713431Scarlsonj
2723431Scarlsonj  For all of these new structures, we will use the same insert/hold/
2733431Scarlsonj  release/remove model as with the original ifslist.
2743431Scarlsonj
2753431Scarlsonj  Finally, the remaining items (and the bulk of the original ifslist
2763431Scarlsonj  members) are kept on a per-state-machine basis.  As this is no
2773431Scarlsonj  longer just an "interface," a new dhcp_smach_t structure will hold
2783431Scarlsonj  these, and the ifslist structure is gone.
2793431Scarlsonj
2803431Scarlsonj
2813431ScarlsonjLease Representation
2823431Scarlsonj
2833431Scarlsonj  For DHCPv6, we need to track multiple LIFs per lease (IA), but we
2843431Scarlsonj  also need multiple LIFs per PIF.  Rather than having two sets of
2853431Scarlsonj  list linkage for each LIF, we can observe that a LIF is on exactly
2863431Scarlsonj  one PIF and is a member of at most one lease, and then simplify: the
2873431Scarlsonj  lease structure will use a base pointer for the first LIF in the
2883431Scarlsonj  lease, and a count for the number of consecutive LIFs in the PIF's
2893431Scarlsonj  list of LIFs that belong to the lease.
2903431Scarlsonj
2913431Scarlsonj  When removing a LIF from the system, we need to decrement the count
2923431Scarlsonj  of LIFs in the lease, and advance the base pointer if the LIF being
2933431Scarlsonj  removed is the first one.  Inserting a LIF means just moving it into
2943431Scarlsonj  this list and bumping the counter.
2953431Scarlsonj
2963431Scarlsonj  When removing a lease from a state machine, we need to dispose of
2973431Scarlsonj  the LIFs referenced.  If the LIF being disposed is the main LIF for
2983431Scarlsonj  a state machine, then all that we can do is canonize the LIF
2993431Scarlsonj  (returning it to a default state); this represents the normal IPv4
3003431Scarlsonj  DHCP operation on lease expiry.  Otherwise, the lease is the owner
3013431Scarlsonj  of that LIF (it was created because of a DHCPv6 IA), and disposal
3023431Scarlsonj  means unplumbing the LIF from the actual system and removing the LIF
3033431Scarlsonj  entry from the PIF.
3043431Scarlsonj
3053431Scarlsonj
3063431ScarlsonjMain Structure Linkage
3073431Scarlsonj
3083431Scarlsonj  For IPv4 DHCP, the new linkage is straightforward.  Using the same
3093431Scarlsonj  system configuration example as in the initial design discussion:
3103431Scarlsonj
3113431Scarlsonj          +- lease  +- lease       +- lease
3123431Scarlsonj          |  ^      |  ^           |  ^
3133431Scarlsonj          |  |      |  |           |  |
3143431Scarlsonj          \  smach  \  smach       \  smach
3153431Scarlsonj           \ ^|      \ ^|           \ ^|
3163431Scarlsonj            v|v       v|v            v|v
3173431Scarlsonj            lif ----> lif -> NULL     lif -> NULL
3183431Scarlsonj            net0      net0:1          net1
3193431Scarlsonj            ^                         ^
3203431Scarlsonj            |                         |
3213431Scarlsonj  v4root -> pif --------------------> pif -> NULL
3223431Scarlsonj            net0                      net1
3233431Scarlsonj
3243431Scarlsonj  This diagram shows three separate state machines running (with
3253431Scarlsonj  backpointers omitted for clarity).  Each state machine has a single
3263431Scarlsonj  "main" LIF with which it's associated (and named).  Each also has a
3273431Scarlsonj  single lease structure that points back to the same LIF (count of
3283431Scarlsonj  1), because IPv4 DHCP controls a single address allocation per state
3293431Scarlsonj  machine.
3303431Scarlsonj
3313431Scarlsonj  DHCPv6 is a bit more complex.  This shows DHCPv6 running on two
3323431Scarlsonj  interfaces (more or fewer interfaces are of course possible) and
3333431Scarlsonj  with multiple leases on the first interface, and each lease with
3343431Scarlsonj  multiple addresses (one with two addresses, the second with one).
3353431Scarlsonj
3363431Scarlsonj            lease ----------------> lease -> NULL   lease -> NULL
3373431Scarlsonj            ^   \(2)                |(1)            ^   \ (1)
3383431Scarlsonj            |    \                  |               |    \
3393431Scarlsonj            smach \                 |               smach \
3403431Scarlsonj            ^ |    \                |               ^ |    \
3413431Scarlsonj            | v     v               v               | v     v
3423431Scarlsonj            lif --> lif --> lif --> lif --> NULL    lif --> lif -> NULL
3433431Scarlsonj            net0    net0:1  net0:4  net0:2          net1    net1:5
3443431Scarlsonj            ^                                       ^
3453431Scarlsonj            |                                       |
3463431Scarlsonj  v6root -> pif ----------------------------------> pif -> NULL
3473431Scarlsonj            net0                                    net1
3483431Scarlsonj
3493431Scarlsonj  Note that there's intentionally no ordering based on name in the
3503431Scarlsonj  list of LIFs.  Instead, the contiguous LIF structures in that list
3513431Scarlsonj  represent the addresses in each lease.  The logical interfaces
3523431Scarlsonj  themselves are allocated and numbered by the system kernel, so they
3533431Scarlsonj  may not be sequential, and there may be gaps in the list if other
3543431Scarlsonj  entities (such as in.ndpd) are also configuring interfaces.
3553431Scarlsonj
3563431Scarlsonj  Note also that with IPv4 DHCP, the lease points to the LIF that's
3573431Scarlsonj  also the main LIF for the state machine, because that's the IP
3583431Scarlsonj  interface that dhcpagent controls.  With DHCPv6, the lease (one per
3593431Scarlsonj  IA structure) points to a separate set of LIFs that are created just
3603431Scarlsonj  for the leased addresses (one per IA address in an IAADDR option).
3613431Scarlsonj  The state machine alone points to the main LIF.
3623431Scarlsonj
3633431Scarlsonj
3643431ScarlsonjPacket Structure Extensions
3653431Scarlsonj
3663431Scarlsonj  Obviously, we need some DHCPv6 packet data structures and
3673431Scarlsonj  definitions.  A new <netinet/dhcp6.h> file will be introduced with
3683431Scarlsonj  the necessary #defines and structures.  The key structure there will
3693431Scarlsonj  be:
3703431Scarlsonj
3713431Scarlsonj	struct dhcpv6_message {
3723431Scarlsonj		uint8_t		d6m_msg_type;
3733431Scarlsonj		uint8_t		d6m_transid_ho;
3743431Scarlsonj		uint16_t	d6m_transid_lo;
3753431Scarlsonj	};
3763431Scarlsonj	typedef	struct dhcpv6_message	dhcpv6_message_t;
3773431Scarlsonj
3783431Scarlsonj  This defines the usual (non-relay) DHCPv6 packet header, and is
3793431Scarlsonj  roughly equivalent to PKT for IPv4.
3803431Scarlsonj
3813431Scarlsonj  Extending dhcp_pkt_t for DHCPv6 is straightforward, as it's used
3823431Scarlsonj  only within dhcpagent.  This structure will be amended to use a
3833431Scarlsonj  union for v4/v6 and include a boolean to flag which version is in
3843431Scarlsonj  use.
3853431Scarlsonj
3863431Scarlsonj  For the PKT_LIST structure, things are more complex.  This defines
3873431Scarlsonj  both a queuing mechanism for received packets (typically OFFERs) and
3883431Scarlsonj  a set of packet decoding structures.  The decoding structures are
3893431Scarlsonj  highly specific to IPv4 DHCP -- they have no means to handle nested
3903431Scarlsonj  or repeated options (as used heavily in DHCPv6) and make use of the
3913431Scarlsonj  DHCP_OPT structure which is specific to IPv4 DHCP -- and are
3923431Scarlsonj  somewhat expensive in storage, due to the use of arrays indexed by
3933431Scarlsonj  option code number.
3943431Scarlsonj
3953431Scarlsonj  Worse, this structure is used throughout the system, so changes to
3963431Scarlsonj  it need to be made carefully.  (For example, the existing 'pkt'
3973431Scarlsonj  member can't just be turned into a union.)
3983431Scarlsonj
3993431Scarlsonj  For an initial prototype, since discarded, I created a new
4003431Scarlsonj  dhcp_plist_t structure to represent packet lists as used inside
4013431Scarlsonj  dhcpagent and made dhcp_pkt_t valid for use on input and output.
4023431Scarlsonj  The result is unsatisfying, though, as it results in code that
4033431Scarlsonj  manipulates far too many data structures in common cases; it's a sea
4043431Scarlsonj  of pointers to pointers.
4053431Scarlsonj
4063431Scarlsonj  The better answer is to use PKT_LIST for both IPv4 and IPv6, adding
4073431Scarlsonj  the few new bits of metadata required to the end (receiving ifIndex,
4083431Scarlsonj  packet source/destination addresses), and staying within the overall
4093431Scarlsonj  existing design.
4103431Scarlsonj
4113431Scarlsonj  For option parsing, dhcpv6_find_option() and dhcpv6_pkt_option()
4123431Scarlsonj  functions will be added to libdhcputil.  The former function will
4133431Scarlsonj  walk a DHCPv6 option list, and provide safe (bounds-checked) access
4143431Scarlsonj  to the options inside.  The function can be called recursively, so
4153431Scarlsonj  that option nesting can be handled fairly simply by nested loops,
4163431Scarlsonj  and can be called repeatedly to return each instance of a given
4173431Scarlsonj  option code number.  The latter function is just a convenience
4183431Scarlsonj  wrapper on dhcpv6_find_option() that starts with a PKT_LIST pointer
4193431Scarlsonj  and iterates over the top-level options with a given code number.
4203431Scarlsonj
4213431Scarlsonj  There are two special considerations for the use of these library
4223431Scarlsonj  interfaces: there's no "pad" option for DHCPv6 or alignment
4233431Scarlsonj  requirements on option headers or contents, and nested options
4243431Scarlsonj  always follow a structure that has type-dependent length.  This
4253431Scarlsonj  means that code that handles options must all be written to deal
4263431Scarlsonj  with unaligned data, and suboption code must index the pointer past
4273431Scarlsonj  the type-dependent part.
4283431Scarlsonj
4293431Scarlsonj
4303431ScarlsonjPacket Construction
4313431Scarlsonj
4323431Scarlsonj  Unlike DHCPv4, DHCPv6 places the transaction timer value in an
4333431Scarlsonj  option.  The existing code sets the current time value in
4343431Scarlsonj  send_pkt_internal(), which allows it to be updated in a
4353431Scarlsonj  straightforward way when doing retransmits.
4363431Scarlsonj
4373431Scarlsonj  To make this work in a simple manner for DHCPv6, I added a
4383431Scarlsonj  remove_pkt_opt() function.  The update logic just does a remove and
4393431Scarlsonj  re-adds the option.  We could also just assume the presence of the
4403431Scarlsonj  option, find it, and modify in place, but the remove feature seems
4413431Scarlsonj  more general.
4423431Scarlsonj
4433431Scarlsonj  DHCPv6 uses nesting options.  To make this work, two new utility
4443431Scarlsonj  functions are needed.  First, an add_pkt_subopt() function will take
4453431Scarlsonj  a pointer to an existing option and add an embedded option within
4463431Scarlsonj  it.  The packet length and existing option length are updated.  If
4473431Scarlsonj  that existing option isn't a top-level option, though, this means
4483431Scarlsonj  that the caller must update the lengths of all of the enclosing
4493431Scarlsonj  options up to the top level.  To do this, update_v6opt_len() will be
4503431Scarlsonj  added.  This is used in the special case of adding a Status Code
4513431Scarlsonj  option to an IAADDR option within an IA_NA top-level option.
4523431Scarlsonj
4533431Scarlsonj
4543431ScarlsonjSockets and I/O Handling
4553431Scarlsonj
4563431Scarlsonj  DHCPv6 doesn't need or use either a DLPI or a broadcast IP socket.
4573431Scarlsonj  Instead, a single unicast-bound IP socket on a link-local address
4583431Scarlsonj  would be the most that is needed.  This is roughly equivalent to
4593431Scarlsonj  if_sock_ip_fd in the existing design, but that existing socket is
4603431Scarlsonj  bound only after DHCP reaches BOUND state -- that is, when it
4613431Scarlsonj  switches away from DLPI.  We need something different.
4623431Scarlsonj
4633431Scarlsonj  This, along with the excess of open file descriptors in an otherwise
4643431Scarlsonj  idle daemon and the potentially serious performance problems in
4653431Scarlsonj  leaving DLPI open at all times, argues for a larger redesign of the
4663431Scarlsonj  I/O logic in dhcpagent.
4673431Scarlsonj
4683431Scarlsonj  The first thing that we can do is eliminate the need for the
4693431Scarlsonj  per-ifslist if_sock_fd.  This is used primarily for issuing ioctls
4703431Scarlsonj  to configure interfaces -- a task that would work as well with any
4713431Scarlsonj  open socket -- and is also registered to receive any ACK/NAK packets
4723431Scarlsonj  that may arrive via broadcast.  Both of these can be eliminated by
4733431Scarlsonj  creating a pair of global sockets (IPv4 and IPv6), bound and
4743431Scarlsonj  configured for ACK/NAK reception.  The only functional difference is
4753431Scarlsonj  that the list of running state machines must be scanned on reception
4763431Scarlsonj  to find the correct transaction ID, but the existing design
4773431Scarlsonj  effectively already goes to this effort because the kernel
4783431Scarlsonj  replicates received datagrams among all matching sockets, and each
4793431Scarlsonj  ifslist entry has a socket open.
4803431Scarlsonj
4813431Scarlsonj  (The existing code for if_sock_fd makes oblique reference to unknown
4823431Scarlsonj  problems in the system that may prevent binding from working in some
4833431Scarlsonj  cases.  The reference dates back some seven years to the original
4843431Scarlsonj  DHCP implementation.  I've observed no such problems in extensive
4853431Scarlsonj  testing and if any do show up, they will be dealt with by fixing the
4863431Scarlsonj  underlying bugs.)
4873431Scarlsonj
4883431Scarlsonj  This leads to an important simplification: it's no longer necessary
4893431Scarlsonj  to register, unregister, and re-register for packet reception while
4903431Scarlsonj  changing state -- register_acknak() and unregister_acknak() are
4913431Scarlsonj  gone.  Instead, we always receive, and we dispatch the packets as
4923431Scarlsonj  they arrive.  As a result, when receiving a DHCPv4 ACK or DHCPv6
4933431Scarlsonj  Reply when in BOUND state, we know it's a duplicate, and we can
4943431Scarlsonj  discard.
4953431Scarlsonj
4963431Scarlsonj  The next part is in minimizing DLPI usage.  A DLPI stream is needed
4973431Scarlsonj  at most for each IPv4 PIF, and it's not needed when all of the
4983431Scarlsonj  DHCP instances on that PIF are bound.  In fact, the current
4993431Scarlsonj  implementation deals with this in configure_bound() by setting a
5003431Scarlsonj  "blackhole" packet filter.  The stream is left open.
5013431Scarlsonj
5023431Scarlsonj  To simplify this, we will open at most one DLPI stream on a PIF, and
5033431Scarlsonj  use reference counts from the state machines to determine when the
5043431Scarlsonj  stream must be open and when it can be closed.  This mechanism will
5053431Scarlsonj  be centralized in a set_smach_state() function that changes the
5063431Scarlsonj  state and opens/closes the DLPI stream when needed.
5073431Scarlsonj
5083431Scarlsonj  This leads to another simplification.  The I/O logic in the existing
5093431Scarlsonj  dhcpagent makes use of the protocol state to select between DLPI and
5103431Scarlsonj  sockets.  Now that we keep track of this in a simpler manner, we no
5113431Scarlsonj  longer need to switch out on state in when sending a packet; just
5123431Scarlsonj  test the dsm_using_dlpi flag instead.
5133431Scarlsonj
5143431Scarlsonj  Still another simplification is in the handling of DHCPv4 INFORM.
5153431Scarlsonj  The current code has separate logic in it for getting the interface
5163431Scarlsonj  state and address information.  This is no longer necessary, as the
5173431Scarlsonj  LIF mechanism keeps track of the interface state.  And since we have
5183431Scarlsonj  separate lease structures, and INFORM doesn't acquire a lease, we no
5193431Scarlsonj  longer have to be careful about canonizing the interface on
5203431Scarlsonj  shutdown.
5213431Scarlsonj
5223431Scarlsonj  Although the default is to send all client messages to a well-known
5233431Scarlsonj  multicast address for servers and relays, DHCPv6 also has a
5243431Scarlsonj  mechanism that allows the client to send unicast messages to the
5253431Scarlsonj  server.  The operation of this mechanism is slightly complex.
5263431Scarlsonj  First, the server sends the client a unicast address via an option.
5273431Scarlsonj  We may use this address as the destination (rather than the
5283431Scarlsonj  well-known multicast address for local DHCPv6 servers and relays)
5293431Scarlsonj  only if we have a viable local source address.  This means using
5303431Scarlsonj  SIOCGDSTINFO each time we try to send unicast.  Next, the server may
5313431Scarlsonj  send back a special status code: UseMulticast.  If this is received,
5323431Scarlsonj  and if we were actually using unicast in our messages to the server,
5333431Scarlsonj  then we need to forget the unicast address, switch back to
5343431Scarlsonj  multicast, and resend our last message.
5353431Scarlsonj
5363431Scarlsonj  Note that it's important to avoid the temptation to resend the last
5373431Scarlsonj  message every time UseMulticast is seen, and do it only once on
5383431Scarlsonj  switching back to multicast: otherwise, a potential feedback loop is
5393431Scarlsonj  created.
5403431Scarlsonj
5413431Scarlsonj  Because IP_PKTINFO (PSARC 2006/466) has integrated, we could go a
5423431Scarlsonj  step further by removing the need for any per-LIF sockets and just
5433431Scarlsonj  use the global sockets for all but DLPI.  However, in order to
5443431Scarlsonj  facilitate a Solaris 10 backport, this will be done separately as CR
5453431Scarlsonj  6509317.
5463431Scarlsonj
5473431Scarlsonj  In the case of DHCPv6, we already have IPV6_PKTINFO, so we will pave
5483431Scarlsonj  the way for IPv4 by beginning to using this now, and thus have just
5493431Scarlsonj  a single socket (bound to "::") for all of DHCPv6.  Doing this
5503431Scarlsonj  requires switching from the old BSD4.2 -lsocket -lnsl to the
5513431Scarlsonj  standards-compliant -lxnet in order to use ancillary data.
5523431Scarlsonj
5533431Scarlsonj  It may also be possible to remove the need for DLPI for IPv4, and
5543431Scarlsonj  incidentally simplify the code a fair amount, by adding a kernel
5553431Scarlsonj  option to allow transmission and reception of UDP packets over
5563431Scarlsonj  interfaces that are plumbed but not marked IFF_UP.  This is left for
5573431Scarlsonj  future work.
5583431Scarlsonj
5593431Scarlsonj
5603431ScarlsonjThe State Machine
5613431Scarlsonj
5623431Scarlsonj  Several parts of the existing state machine need additions to handle
5633431Scarlsonj  DHCPv6, which is a superset of DHCPv4.
5643431Scarlsonj
5653431Scarlsonj  First, there are the RENEWING and REBINDING states.  For IPv4 DHCP,
5663431Scarlsonj  these states map one-to-one with a single address and single lease
5673431Scarlsonj  that's undergoing renewal.  It's a simple progression (on timeout)
5683431Scarlsonj  from BOUND, to RENEWING, to REBINDING and finally back to SELECTING
5693431Scarlsonj  to start over.  Each retransmit is done by simply rescheduling the
5703431Scarlsonj  T1 or T2 timer.
5713431Scarlsonj
5723431Scarlsonj  For DHCPv6, things are somewhat more complex.  At any one time,
5733431Scarlsonj  there may be multiple IAs (leases) that are effectively in renewing
5743431Scarlsonj  or rebinding state, based on the T1/T2 timers for each IA, and many
5753431Scarlsonj  addresses that have expired.
5763431Scarlsonj
5773431Scarlsonj  However, because all of the leases are related to a single server,
5783431Scarlsonj  and that server either responds to our requests or doesn't, we can
5793431Scarlsonj  simplify the states to be nearly identical to IPv4 DHCP.
5803431Scarlsonj
5813431Scarlsonj  The revised definition for use with DHCPv6 is:
5823431Scarlsonj
5833431Scarlsonj    - Transition from BOUND to RENEWING state when the first T1 timer
5843431Scarlsonj      (of any lease on the state machine) expires.  At this point, as
5853431Scarlsonj      an optimization, we should begin attempting to renew any IAs
5863431Scarlsonj      that are within REN_TIMEOUT (10 seconds) of reaching T1 as well.
5873431Scarlsonj      We may as well avoid sending an excess of packets.
5883431Scarlsonj
5893431Scarlsonj    - When a T1 lease timer expires and we're in RENEWING or REBINDING
5903431Scarlsonj      state, just ignore it, because the transaction is already in
5913431Scarlsonj      progress.
5923431Scarlsonj
5933431Scarlsonj    - At each retransmit timeout, we should check to see if there are
5943431Scarlsonj      more IAs that need to join in because they've passed point T1 as
5953431Scarlsonj      well, and, if so, add them.  This check isn't necessary at this
5963431Scarlsonj      time, because only a single IA_NA is possible with the initial
5973431Scarlsonj      design.
5983431Scarlsonj
5993431Scarlsonj    - When we reach T2 on any IA and we're in BOUND or RENEWING state,
6003431Scarlsonj      enter REBINDING state.  At this point, we have a choice.  For
6013431Scarlsonj      those other IAs that are past T1 but not yet at T2, we could
6023431Scarlsonj      ignore them (sending only those that have passed point T2),
6033431Scarlsonj      continue to send separate Renew messages for them, or just
6043431Scarlsonj      include them in the Rebind message.  This isn't an issue that
6053431Scarlsonj      must be dealt with for this project, but the plan is to include
6063431Scarlsonj      them in the Rebind message.
6073431Scarlsonj
6083431Scarlsonj    - When a T2 lease timer expires and we're in REBINDING state, just
6093431Scarlsonj      ignore it, as with the corresponding T1 timer.
6103431Scarlsonj
6113431Scarlsonj    - As addresses reach the end of their preferred lifetimes, set the
6123431Scarlsonj      IFF_DEPRECATED flag.  As they reach the end of the valid
6133431Scarlsonj      lifetime, remove them from the system.  When an IA (lease)
6143431Scarlsonj      becomes empty, just remove it.  When there are no more leases
6153431Scarlsonj      left, return to SELECTING state to start over.
6163431Scarlsonj
6173431Scarlsonj  Note that the RFC treats the IAs as separate entities when
6183431Scarlsonj  discussing the renew/rebind T1/T2 timers, but treats them as a unit
6193431Scarlsonj  when doing the initial negotiation.  This is, to say the least,
6203431Scarlsonj  confusing, especially so given that there's no reason to expect that
6213431Scarlsonj  after having failed to elicit any responses at all from the server
6223431Scarlsonj  on one IA, the server will suddenly start responding when we attempt
6233431Scarlsonj  to renew some other IA.  We rationalize this behavior by using a
6243431Scarlsonj  single renew/rebind state for the entire state machine (and thus
6253431Scarlsonj  client/server pair).
6263431Scarlsonj
6273431Scarlsonj  There's a subtle timing difference here between DHCPv4 and DHCPv6.
6283431Scarlsonj  For DHCPv4, the client just sends packets more and more frequently
6293431Scarlsonj  (shorter timeouts) as the next state gets nearer.  DHCPv6 treats
6303431Scarlsonj  each as a transaction, using the same retransmit logic as for other
6313431Scarlsonj  messages.  The DHCPv6 method is a cleaner design, so we will change
6323431Scarlsonj  the DHCPv4 implementation to do the same, and compute the new timer
6333431Scarlsonj  values as part of stop_extending().
6343431Scarlsonj
6353431Scarlsonj  Note that it would be possible to start the SELECTING state earlier
6363431Scarlsonj  than waiting for the last lease to expire, and thus avoid a loss of
6373431Scarlsonj  connectivity.  However, it this point, there are other servers on
6383431Scarlsonj  the network that have seen us attempting to Rebind for quite some
6393431Scarlsonj  time, and they have not responded.  The likelihood that there's a
6403431Scarlsonj  server that will ignore Rebind but then suddenly spring into action
6413431Scarlsonj  on a Solicit message seems low enough that the optimization won't be
6423431Scarlsonj  done now.  (Starting SELECTING state earlier may be done in the
6433431Scarlsonj  future, if it's found to be useful.)
6443431Scarlsonj
6453431Scarlsonj
6463431ScarlsonjPersistent State
6473431Scarlsonj
6483431Scarlsonj  IPv4 DHCP has only minimal need for persistent state, beyond the
6493431Scarlsonj  configuration parameters.  The state is stored when "ifconfig dhcp
6503431Scarlsonj  drop" is run or the daemon receives SIGTERM, which is typically done
6513431Scarlsonj  only well after the system is booted and running.
6523431Scarlsonj
6533431Scarlsonj  The daemon stores this state in /etc/dhcp, because it needs to be
6543431Scarlsonj  available when only the root file system has been mounted.
6553431Scarlsonj
6563431Scarlsonj  Moreover, dhcpagent starts very early in the boot process.  It runs
6573431Scarlsonj  as part of svc:/network/physical:default, which runs well before
6583431Scarlsonj  root is mounted read/write:
6593431Scarlsonj
6603431Scarlsonj     svc:/system/filesystem/root:default ->
6613431Scarlsonj        svc:/system/metainit:default ->
6623431Scarlsonj           svc:/system/identity:node ->
6633431Scarlsonj              svc:/network/physical:default
6643431Scarlsonj           svc:/network/iscsi_initiator:default ->
6653431Scarlsonj              svc:/network/physical:default
6663431Scarlsonj
6673431Scarlsonj  and, of course, well before either /var or /usr is mounted.  This
6683431Scarlsonj  means that any persistent state must be kept in the root file
6693431Scarlsonj  system, and that if we write before shutdown, we have to cope
6703431Scarlsonj  gracefully with the root file system returning EROFS on write
6713431Scarlsonj  attempts.
6723431Scarlsonj
6733431Scarlsonj  For DHCPv6, we need to try to keep our stable DUID and IAID values
6743431Scarlsonj  stable across reboots to fulfill the demands of RFC 3315.
6753431Scarlsonj
6763431Scarlsonj  The DUID is either configured or automatically generated.  When
6773431Scarlsonj  configured, it comes from the /etc/default/dhcpagent file, and thus
6783431Scarlsonj  does not need to be saved by the daemon.  If automatically
6793431Scarlsonj  generated, there's exactly one of these created, and it will
6803431Scarlsonj  eventually be needed before /usr is mounted, if /usr is mounted over
6813431Scarlsonj  IPv6.  This means a new file in the root file system,
6823431Scarlsonj  /etc/dhcp/duid, will be used to hold the automatically generated
6833431Scarlsonj  DUID.
6843431Scarlsonj
6853431Scarlsonj  The determination of whether to use a configured DUID or one saved
6863431Scarlsonj  in a file is made in get_smach_cid().  This function will
6873431Scarlsonj  encapsulate all of the DUID parsing and generation machinery for the
6883431Scarlsonj  rest of dhcpagent.
6893431Scarlsonj
6903431Scarlsonj  If root is not writable at the point when dhcpagent starts, and our
6913431Scarlsonj  attempt fails with EROFS, we will set a timer for 60 second
6923431Scarlsonj  intervals to retry the operation periodically.  In the unlikely case
6933431Scarlsonj  that it just never succeeds or that we're rebooted before root
6943431Scarlsonj  becomes writable, then the impact will be that the daemon will wake
6953431Scarlsonj  up once a minute and, ultimately, we'll choose a different DUID on
6963431Scarlsonj  next start-up, and we'll thus lose our leases across a reboot.
6973431Scarlsonj
6983431Scarlsonj  The IAID similarly must be kept stable if at all possible, but
6993431Scarlsonj  cannot be configured by the user.  To do make these values stable,
7003431Scarlsonj  we will use two strategies.  First the IAID value for a given
7013431Scarlsonj  interface (if not known) will just default to the IP ifIndex value,
7023431Scarlsonj  provided that there's no known saved IAID using that value.  Second,
7033431Scarlsonj  we will save off the IAID we choose in a single /etc/dhcp/iaid file,
7043431Scarlsonj  containing an array of entries indexed by logical interface name.
7053431Scarlsonj  Keeping it in a single file allows us to scan for used and unused
7063431Scarlsonj  IAID values when necessary.
7073431Scarlsonj
7083431Scarlsonj  This mechanism depends on the interface name, and thus will need to
7093431Scarlsonj  be revisited when Clearview vanity naming and NWAM are available.
7103431Scarlsonj
7113431Scarlsonj  Currently, the boot system (GRUB, OBP, the miniroot) does not
7123431Scarlsonj  support installing over IPv6.  This could change in the future, so
7133431Scarlsonj  one of the goals of the above stability plan is to support that
7143431Scarlsonj  event.
7153431Scarlsonj
7163431Scarlsonj  When running in the miniroot on an x86 system, /etc/dhcp (and the
7173431Scarlsonj  rest of the root) is mounted on a read-only ramdisk.  In this case,
7183431Scarlsonj  writing to /etc/dhcp will just never work.  A possible solution
7193431Scarlsonj  would be to add a new privileged command in ifconfig that forces
7203431Scarlsonj  dhcpagent to write to an alternate location.  The initial install
7213431Scarlsonj  process could then do "ifconfig <x> dhcp write /a" to get the needed
7223431Scarlsonj  state written out to the newly-constructed system root.
7233431Scarlsonj
7243431Scarlsonj  This part (the new write option) won't be implemented as part of
7253431Scarlsonj  this project, because it's not needed yet.
7263431Scarlsonj
7273431Scarlsonj
7283431ScarlsonjRouter Advertisements
7293431Scarlsonj
7303431Scarlsonj  IPv6 Router Advertisements perform two functions related to DHCPv6:
7313431Scarlsonj
7323431Scarlsonj    - they specify whether and how to run DHCPv6 on a given interface.
7333431Scarlsonj    - they provide a list of the valid prefixes on an interface.
7343431Scarlsonj
7353431Scarlsonj  For the first function, in.ndpd needs to use the same DHCP control
7363431Scarlsonj  interfaces that ifconfig uses, so that it can launch dhcpagent and
7373431Scarlsonj  trigger DHCPv6 when necessary.  Note that it never needs to shut
7383431Scarlsonj  down DHCPv6, as router advertisements can't do that.
7393431Scarlsonj
7403431Scarlsonj  However, launching dhcpagent presents new problems.  As a part of
7413431Scarlsonj  the "Quagga SMF Modifications" project (PSARC 2006/552), in.ndpd in
7423431Scarlsonj  Nevada is now privilege-aware and runs with limited privileges,
7433431Scarlsonj  courtesy of SMF.  Dhcpagent, on the other hand, must run with all
7443431Scarlsonj  privileges.
7453431Scarlsonj
7463431Scarlsonj  A simple work-around for this issue is to rip out the "privileges="
7473431Scarlsonj  clause from the method_credential for in.ndpd.  I've taken this
7483431Scarlsonj  direction initially, but the right longer-term answer seems to be
7493431Scarlsonj  converting dhcpagent into an SMF service.  This is quite a bit more
7503431Scarlsonj  complex, as it means turning the /sbin/dhcpagent command line
7513431Scarlsonj  interface into a utility that manipulates the service and passes the
7523431Scarlsonj  command line options via IPC extensions.
7533431Scarlsonj
7543431Scarlsonj  Such a design also begs the question of whether dhcpagent itself
7553431Scarlsonj  ought to run with reduced privileges.  It could, but it still needs
7563431Scarlsonj  the ability to grant "all" (traditional UNIX root) privileges to the
7573431Scarlsonj  eventhook script, if present.  There seem to be few ways to do this,
7583431Scarlsonj  though it's a good area for research.
7593431Scarlsonj
7603431Scarlsonj  The second function, prefix handling, is also subtle.  Unlike IPv4
7613431Scarlsonj  DHCP, DHCPv6 does not give the netmask or prefix length along with
7623431Scarlsonj  the leased address.  The client is on its own to determine the right
7633431Scarlsonj  netmask to use.  This is where the advertised prefixes come in:
7643431Scarlsonj  these must be used to finish the interface configuration.
7653431Scarlsonj
7663431Scarlsonj  We will have the DHCPv6 client configure each interface with an
7673431Scarlsonj  all-ones (/128) netmask by default.  In.ndpd will be modified so
7683431Scarlsonj  that when it detects a new IFF_DHCPRUNNING IP logical interface, it
7693431Scarlsonj  checks for a known matching prefix, and sets the netmask as
7703431Scarlsonj  necessary.  If no matching prefix is known, it will send a new
7713431Scarlsonj  Router Solicitation message to try to find one.
7723431Scarlsonj
7733431Scarlsonj  When in.ndpd learns of a new prefix from a Router Advertisement, it
7743431Scarlsonj  will scan all of the IFF_DHCPRUNNING IP logical interfaces on the
7753431Scarlsonj  same physical interface and set the netmasks when necessary.
7763431Scarlsonj  Dhcpagent, for its part, will ignore the netmask on IPv6 interfaces
7773431Scarlsonj  when checking for changes that would require it to "abandon" the
7783431Scarlsonj  interface.
7793431Scarlsonj
7803431Scarlsonj  Given the way that DHCPv6 and in.ndpd control both the horizontal
7813431Scarlsonj  and the vertical in plumbing and removing logical interfaces, and
7823431Scarlsonj  users do not, it might be worthwhile to consider roping off any
7833431Scarlsonj  direct user changes to IPv6 logical interfaces under control of
7843431Scarlsonj  in.ndpd or dhcpagent, and instead force users through a higher-level
7853431Scarlsonj  interface.  This won't be done as part of this project, however.
7863431Scarlsonj
7873431Scarlsonj
7883431ScarlsonjARP Hardware Types
7893431Scarlsonj
7903431Scarlsonj  There are multiple places within the DHCPv6 client where the mapping
7913431Scarlsonj  of DLPI MAC type to ARP Hardware Type is required:
7923431Scarlsonj
7933431Scarlsonj  - When we are constructing an automatic, stable DUID for our own
7943431Scarlsonj    identity, we prefer to use a DUID-LLT if possible.  This is done
7953431Scarlsonj    by finding a link-layer interface, opening it, reading the MAC
7963431Scarlsonj    address and type, and translating in the make_stable_duid()
7973431Scarlsonj    function in libdhcpagent.
7983431Scarlsonj
7993431Scarlsonj  - When we translate a user-configured DUID from
8003431Scarlsonj    /etc/default/dhcpagent into a binary representation, we may have
8013431Scarlsonj    to deal with a physical interface name.  In this case, we must
8023431Scarlsonj    open that interface and read the MAC address and type.
8033431Scarlsonj
8043431Scarlsonj  - As part of the PIF data structure initialization, we need to read
8053431Scarlsonj    out the MAC type so that it can be used in the BOOTP/DHCPv4
8063431Scarlsonj    'htype' field.
8073431Scarlsonj
8083431Scarlsonj  Ideally, these would all be provided by a single libdlpi
8093431Scarlsonj  implementation.  However, that project is on-going at this time and
8103431Scarlsonj  has not yet integrated.  For the time being, a dlpi_to_arp()
8113431Scarlsonj  translation function (taking dl_mac_type and returning an ARP
8123431Scarlsonj  Hardware Type number) will be placed in libdhcputil.
8133431Scarlsonj
8143431Scarlsonj  This temporary function should be removed and this section of the
8153431Scarlsonj  code updated when the new libdlpi from Clearview integrates.
8163431Scarlsonj
8173431Scarlsonj
8183431ScarlsonjField Mappings
8193431Scarlsonj
8203431Scarlsonj  Old (all in ifslist)	New
8213431Scarlsonj  next			dhcp_smach_t.dsm_next
8223431Scarlsonj  prev			dhcp_smach_t.dsm_prev
8233431Scarlsonj  if_hold_count		dhcp_smach_t.dsm_hold_count
8243431Scarlsonj  if_ia			dhcp_smach_t.dsm_ia
8253431Scarlsonj  if_async		dhcp_smach_t.dsm_async
8263431Scarlsonj  if_state		dhcp_smach_t.dsm_state
8273431Scarlsonj  if_dflags		dhcp_smach_t.dsm_dflags
8283431Scarlsonj  if_name		dhcp_smach_t.dsm_name (see text)
8293431Scarlsonj  if_index		dhcp_pif_t.pif_index
8303431Scarlsonj  if_max		dhcp_lif_t.lif_max and dhcp_pif_t.pif_max
8313431Scarlsonj  if_min		(was unused; removed)
8323431Scarlsonj  if_opt		(was unused; removed)
8333431Scarlsonj  if_hwaddr		dhcp_pif_t.pif_hwaddr
8343431Scarlsonj  if_hwlen		dhcp_pif_t.pif_hwlen
8353431Scarlsonj  if_hwtype		dhcp_pif_t.pif_hwtype
8363431Scarlsonj  if_cid		dhcp_smach_t.dsm_cid
8373431Scarlsonj  if_cidlen		dhcp_smach_t.dsm_cidlen
8383431Scarlsonj  if_prl		dhcp_smach_t.dsm_prl
8393431Scarlsonj  if_prllen		dhcp_smach_t.dsm_prllen
8403431Scarlsonj  if_daddr		dhcp_pif_t.pif_daddr
8413431Scarlsonj  if_dlen		dhcp_pif_t.pif_dlen
8423431Scarlsonj  if_saplen		dhcp_pif_t.pif_saplen
8433431Scarlsonj  if_sap_before		dhcp_pif_t.pif_sap_before
8443431Scarlsonj  if_dlpi_fd		dhcp_pif_t.pif_dlpi_fd
8453431Scarlsonj  if_sock_fd		v4_sock_fd and v6_sock_fd (globals)
8463431Scarlsonj  if_sock_ip_fd		dhcp_lif_t.lif_sock_ip_fd
8473431Scarlsonj  if_timer		(see text)
8483431Scarlsonj  if_t1			dhcp_lease_t.dl_t1
8493431Scarlsonj  if_t2			dhcp_lease_t.dl_t2
8503431Scarlsonj  if_lease		dhcp_lif_t.lif_expire
8513431Scarlsonj  if_nrouters		dhcp_smach_t.dsm_nrouters
8523431Scarlsonj  if_routers		dhcp_smach_t.dsm_routers
8533431Scarlsonj  if_server		dhcp_smach_t.dsm_server
8543431Scarlsonj  if_addr		dhcp_lif_t.lif_v6addr
8553431Scarlsonj  if_netmask		dhcp_lif_t.lif_v6mask
8563431Scarlsonj  if_broadcast		dhcp_lif_t.lif_v6peer
8573431Scarlsonj  if_ack		dhcp_smach_t.dsm_ack
8583431Scarlsonj  if_orig_ack		dhcp_smach_t.dsm_orig_ack
8593431Scarlsonj  if_offer_wait		dhcp_smach_t.dsm_offer_wait
8603431Scarlsonj  if_offer_timer	dhcp_smach_t.dsm_offer_timer
8613431Scarlsonj  if_offer_id		dhcp_pif_t.pif_dlpi_id
8623431Scarlsonj  if_acknak_id		dhcp_lif_t.lif_acknak_id
8633431Scarlsonj  if_acknak_bcast_id	v4_acknak_bcast_id (global)
8643431Scarlsonj  if_neg_monosec	dhcp_smach_t.dsm_neg_monosec
8653431Scarlsonj  if_newstart_monosec	dhcp_smach_t.dsm_newstart_monosec
8663431Scarlsonj  if_curstart_monosec	dhcp_smach_t.dsm_curstart_monosec
8673431Scarlsonj  if_disc_secs		dhcp_smach_t.dsm_disc_secs
8683431Scarlsonj  if_reqhost		dhcp_smach_t.dsm_reqhost
8693431Scarlsonj  if_recv_pkt_list	dhcp_smach_t.dsm_recv_pkt_list
8703431Scarlsonj  if_sent		dhcp_smach_t.dsm_sent
8713431Scarlsonj  if_received		dhcp_smach_t.dsm_received
8723431Scarlsonj  if_bad_offers		dhcp_smach_t.dsm_bad_offers
8733431Scarlsonj  if_send_pkt		dhcp_smach_t.dsm_send_pkt
8743431Scarlsonj  if_send_timeout	dhcp_smach_t.dsm_send_timeout
8753431Scarlsonj  if_send_dest		dhcp_smach_t.dsm_send_dest
8763431Scarlsonj  if_send_stop_func	dhcp_smach_t.dsm_send_stop_func
8773431Scarlsonj  if_packet_sent	dhcp_smach_t.dsm_packet_sent
8783431Scarlsonj  if_retrans_timer	dhcp_smach_t.dsm_retrans_timer
8793431Scarlsonj  if_script_fd		dhcp_smach_t.dsm_script_fd
8803431Scarlsonj  if_script_pid		dhcp_smach_t.dsm_script_pid
8813431Scarlsonj  if_script_helper_pid	dhcp_smach_t.dsm_script_helper_pid
8823431Scarlsonj  if_script_event	dhcp_smach_t.dsm_script_event
8833431Scarlsonj  if_script_event_id	dhcp_smach_t.dsm_script_event_id
8843431Scarlsonj  if_callback_msg	dhcp_smach_t.dsm_callback_msg
8853431Scarlsonj  if_script_callback	dhcp_smach_t.dsm_script_callback
8863431Scarlsonj
8873431Scarlsonj  Notes:
8883431Scarlsonj
8893431Scarlsonj    - The dsm_name field currently just points to the lif_name on the
8903431Scarlsonj      controlling LIF.  This may need to be named differently in the
8913431Scarlsonj      future; perhaps when Zones are supported.
8923431Scarlsonj
8933431Scarlsonj    - The timer mechanism will be refactored.  Rather than using the
8943431Scarlsonj      separate if_timer[] array to hold the timer IDs and
8953431Scarlsonj      if_{t1,t2,lease} to hold the relative timer values, we will
8963431Scarlsonj      gather this information into a dhcp_timer_t structure:
8973431Scarlsonj
8983431Scarlsonj	dt_id		timer ID value
8993431Scarlsonj	dt_start	relative start time
9003431Scarlsonj
9013431Scarlsonj  New fields not accounted for above:
9023431Scarlsonj
9033431Scarlsonj  dhcp_pif_t.pif_next		linkage in global list of PIFs
9043431Scarlsonj  dhcp_pif_t.pif_prev		linkage in global list of PIFs
9053431Scarlsonj  dhcp_pif_t.pif_lifs		pointer to list of LIFs on this PIF
9063431Scarlsonj  dhcp_pif_t.pif_isv6		IPv6 flag
9073431Scarlsonj  dhcp_pif_t.pif_dlpi_count	number of state machines using DLPI
9083431Scarlsonj  dhcp_pif_t.pif_hold_count	reference count
9093431Scarlsonj  dhcp_pif_t.pif_name		name of physical interface
9103431Scarlsonj  dhcp_lif_t.lif_next		linkage in per-PIF list of LIFs
9113431Scarlsonj  dhcp_lif_t.lif_prev		linkage in per-PIF list of LIFs
9123431Scarlsonj  dhcp_lif_t.lif_pif		backpointer to parent PIF
9133431Scarlsonj  dhcp_lif_t.lif_smachs		pointer to list of state machines
9143431Scarlsonj  dhcp_lif_t.lif_lease		backpointer to lease holding LIF
9153431Scarlsonj  dhcp_lif_t.lif_flags		interface flags (IFF_*)
9163431Scarlsonj  dhcp_lif_t.lif_hold_count	reference count
9173431Scarlsonj  dhcp_lif_t.lif_dad_wait	waiting for DAD resolution flag
9183431Scarlsonj  dhcp_lif_t.lif_removed	removed from list flag
9193431Scarlsonj  dhcp_lif_t.lif_plumbed	plumbed by dhcpagent flag
9203431Scarlsonj  dhcp_lif_t.lif_expired	lease has expired flag
9213431Scarlsonj  dhcp_lif_t.lif_declined	reason to refuse this address (string)
9223431Scarlsonj  dhcp_lif_t.lif_iaid		unique and stable 32-bit identifier
9233431Scarlsonj  dhcp_lif_t.lif_iaid_id	timer for delayed /etc writes
9243431Scarlsonj  dhcp_lif_t.lif_preferred	preferred timer for v6; deprecate after
9253431Scarlsonj  dhcp_lif_t.lif_name		name of logical interface
9263431Scarlsonj  dhcp_smach_t.dsm_lif		controlling (main) LIF
9273431Scarlsonj  dhcp_smach_t.dsm_leases	pointer to list of leases
9283431Scarlsonj  dhcp_smach_t.dsm_lif_wait	number of LIFs waiting on DAD
9293431Scarlsonj  dhcp_smach_t.dsm_lif_down	number of LIFs that have failed
9303431Scarlsonj  dhcp_smach_t.dsm_using_dlpi	currently using DLPI flag
9313431Scarlsonj  dhcp_smach_t.dsm_send_tcenter	v4 central timer value; v6 MRT
9323431Scarlsonj  dhcp_lease_t.dl_next		linkage in per-state-machine list of leases
9333431Scarlsonj  dhcp_lease_t.dl_prev		linkage in per-state-machine list of leases
9343431Scarlsonj  dhcp_lease_t.dl_smach		back pointer to state machine
9353431Scarlsonj  dhcp_lease_t.dl_lifs		pointer to first LIF configured by lease
9363431Scarlsonj  dhcp_lease_t.dl_nlifs		number of configured consecutive LIFs
9373431Scarlsonj  dhcp_lease_t.dl_hold_count	reference counter
9383431Scarlsonj  dhcp_lease_t.dl_removed	removed from list flag
9393431Scarlsonj  dhcp_lease_t.dl_stale		lease was not updated by Renew/Rebind
9403431Scarlsonj
9413431Scarlsonj
9423431ScarlsonjSnoop
9433431Scarlsonj
9443431Scarlsonj  The snoop changes are fairly straightforward.  As snoop just decodes
9453431Scarlsonj  the messages, and the message format is quite different between
9463431Scarlsonj  DHCPv4 and DHCPv6, a new module will be created to handle DHCPv6
9473431Scarlsonj  decoding, and will export a interpret_dhcpv6() function.
9483431Scarlsonj
9493431Scarlsonj  The one bit of commonality between the two protocols is the use of
9503431Scarlsonj  ARP Hardware Type numbers, which are found in the underlying BOOTP
9513431Scarlsonj  message format for DHCPv4 and in the DUID-LL and DUID-LLT
9523431Scarlsonj  construction for DHCPv6.  To simplify this, the existing static
9533431Scarlsonj  show_htype() function in snoop_dhcp.c will be renamed to arp_htype()
9543431Scarlsonj  (to better reflect its functionality), updated with more modern
9553431Scarlsonj  hardware types, moved to snoop_arp.c (where it belongs), and made a
9563431Scarlsonj  public symbol within snoop.
9573431Scarlsonj
9583431Scarlsonj  While I'm there, I'll update snoop_arp.c so that when it prints an
9593431Scarlsonj  ARP message in verbose mode, it uses arp_htype() to translate the
9603431Scarlsonj  ar_hrd value.
9613431Scarlsonj
9623431Scarlsonj  The snoop updates also involve the addition of a new "dhcp6" keyword
9633431Scarlsonj  for filtering.  As a part of this, CR 6487534 will be fixed.
9643431Scarlsonj
9653431Scarlsonj
9663431ScarlsonjIPv6 Source Address Selection
9673431Scarlsonj
9683431Scarlsonj  One of the customer requests for DHCPv6 is to be able to predict the
9693431Scarlsonj  address selection behavior in the presence of both stateful and
9703431Scarlsonj  stateless addresses on the same network.
9713431Scarlsonj
9723431Scarlsonj  Solaris implements RFC 3484 address selection behavior.  In this
9733431Scarlsonj  scheme, the first seven rules implement some basic preferences for
9743431Scarlsonj  addresses, with Rule 8 being a deterministic tie breaker.
9753431Scarlsonj
9763431Scarlsonj  Rule 8 relies on a special function, CommonPrefixLen, defined in the
9773431Scarlsonj  RFC, that compares leading bits of the address without regard to
9783431Scarlsonj  configured prefix length.  As Rule 1 eliminates equal addresses,
9793431Scarlsonj  this always picks a single address.
9803431Scarlsonj
9813431Scarlsonj  This rule, though, allows for additional checks:
9823431Scarlsonj
9833431Scarlsonj   Rule 8 may be superseded if the implementation has other means of
9843431Scarlsonj   choosing among source addresses.  For example, if the implementation
9853431Scarlsonj   somehow knows which source address will result in the "best"
9863431Scarlsonj   communications performance.
9873431Scarlsonj
9883431Scarlsonj  We will thus split Rule 8 into three separate rules:
9893431Scarlsonj
9903431Scarlsonj  - First, compare on configured prefix.  The interface with the
9913431Scarlsonj    longest configured prefix length that also matches the candidate
9923431Scarlsonj    address will be preferred.
9933431Scarlsonj
9943431Scarlsonj  - Next, check the type of address.  Prefer statically configured
9953431Scarlsonj    addresses above all others.  Next, those from DHCPv6.  Next,
9963431Scarlsonj    stateless autoconfigured addresses.  Finally, temporary addresses.
9973431Scarlsonj    (Note that Rule 7 will take care of temporary address preferences,
9983431Scarlsonj    so that this rule doesn't actually need to look at them.)
9993431Scarlsonj
10003431Scarlsonj  - Finally, run the check-all-bits (CommonPrefixLen) tie breaker.
10013431Scarlsonj
10023431Scarlsonj  The result of this is that if there's a local address in the same
10033431Scarlsonj  configured prefix, then we'll prefer that over other addresses.  If
10043431Scarlsonj  there are multiple to choose from, then will pick static first, then
10053431Scarlsonj  DHCPv6, then dynamic.  Finally, if there are still multiples, we'll
10063431Scarlsonj  use the "closest" address, bitwise.
10073431Scarlsonj
10083431Scarlsonj  Also, this basic implementation scheme also addresses CR 6485164, so
10093431Scarlsonj  a fix for that will be included with this project.
10103431Scarlsonj
10113431Scarlsonj
10123431ScarlsonjMinor Improvements
10133431Scarlsonj
10143431Scarlsonj  Various small problems with the system encountered during
10153431Scarlsonj  development will be fixed along with this project.  Some of these
10163431Scarlsonj  are:
10173431Scarlsonj
10183431Scarlsonj  - List of ARPHRD_* types is a bit short; add some new ones.
10193431Scarlsonj
10203431Scarlsonj  - List of IPPORT_* values is similarly sparse; add others in use by
10213431Scarlsonj    snoop.
10223431Scarlsonj
10233431Scarlsonj  - dhcpmsg.h lacks PRINTFLIKE for dhcpmsg(); add it.
10243431Scarlsonj
10253431Scarlsonj  - CR 6482163 causes excessive lint errors with libxnet; will fix.
10263431Scarlsonj
10273431Scarlsonj  - libdhcpagent uses gettimeofday() for I/O timing, and this can
10283431Scarlsonj    drift on systems with NTP.  It should use a stable time source
10293431Scarlsonj    (gethrtime()) instead, and should return better error values.
10303431Scarlsonj
10313431Scarlsonj  - Controlling debug mode in the daemon shouldn't require changing
10323431Scarlsonj    the command line arguments or jumping through special hoops.  I've
10333431Scarlsonj    added undocumented ".DEBUG_LEVEL=[0-3]" and ".VERBOSE=[01]"
10343431Scarlsonj    features to /etc/default/dhcpagent.
10353431Scarlsonj
10363431Scarlsonj  - The various attributes of the IPC commands (requires privileges,
10373431Scarlsonj    creates a new session, valid with BOOTP, immediate reply) should
10383431Scarlsonj    be gathered together into one look-up table rather than scattered
10393431Scarlsonj    as hard-coded tests.
10403431Scarlsonj
10413431Scarlsonj  - Remove the event unregistration from the command dispatch loop and
10423431Scarlsonj    get rid of the ipc_action_pending() botch.  We'll get a
10433431Scarlsonj    zero-length read any time the client goes away, and that will be
10443431Scarlsonj    enough to trigger termination.  This fix removes async_pending()
10453431Scarlsonj    and async_timeout() as well, and fixes CR 6487958 as a
10463431Scarlsonj    side-effect.
10473431Scarlsonj
10483431Scarlsonj  - Throughout the dhcpagent code, there are private implementations
10493431Scarlsonj    of doubly-linked and singly-linked lists for each data type.
10503431Scarlsonj    These will all be removed and replaced with insque(3C) and
10513431Scarlsonj    remque(3C).
10523431Scarlsonj
10533431Scarlsonj
10543431ScarlsonjTesting
10553431Scarlsonj
10563431Scarlsonj  The implementation was tested using the TAHI test suite for DHCPv6
10573431Scarlsonj  (www.tahi.org).  There are some peculiar aspects to this test suite,
10583431Scarlsonj  and these issues directed some of the design.  In particular:
10593431Scarlsonj
10603431Scarlsonj  - If Renew/Rebind doesn't mention one of our leases, then we need to
10613431Scarlsonj    allow the message to be retransmitted.  Real servers are unlikely
10623431Scarlsonj    to do this.
10633431Scarlsonj
10643431Scarlsonj  - We must look for a status code within IAADDR and within IA_NA, and
10653431Scarlsonj    handle the paradoxical case of "NoAddrAvail."  That doesn't make
10663431Scarlsonj    sense, as a server with no addresses wouldn't use those options.
10673431Scarlsonj    That option makes more sense at the top level of the message.
10683431Scarlsonj
10693431Scarlsonj  - If we get "UseMulticast" when we were already using multicast,
10703431Scarlsonj    then ignore the error code.  Sending another request would cause a
10713431Scarlsonj    loop.
10723431Scarlsonj
10733431Scarlsonj  - TAHI uses "NoBinding" at the top level of the message.  This
10743431Scarlsonj    status code only makes sense within an IA, as it refers to the
10753431Scarlsonj    GUID:IAID binding, which doesn't exist outside an IA.  We must
10763431Scarlsonj    ignore such errors -- treat them as success.
10773431Scarlsonj
10783431Scarlsonj
10793431ScarlsonjInteractions With Other Projects
10803431Scarlsonj
10813431Scarlsonj  Clearview UV (vanity naming) will cause link names, and thus IP
10823431Scarlsonj  interface names, to become changeable over time.  This will break
10833431Scarlsonj  the IAID stability mechanism if UV is used for arbitrary renaming,
10843431Scarlsonj  rather than as just a DR enhancement.
10853431Scarlsonj
10863431Scarlsonj  When this portion of Clearview integrates, this part of the DHCPv6
10873431Scarlsonj  design may need to be revisited.  (The solution will likely be
10883431Scarlsonj  handled at some higher layer, such as within Network Automagic.)
10893431Scarlsonj
10903431Scarlsonj  Clearview is also contributing a new libdlpi that will work for
10913431Scarlsonj  dhcpagent, and is thus removing the private dlpi_io.[ch] functions
10923431Scarlsonj  from this daemon.  When that Clearview project integrates, the
10933431Scarlsonj  DHCPv6 project will need to adjust to the new interfaces, and remove
10943431Scarlsonj  or relocate the dlpi_to_arp() function.
10953431Scarlsonj
10963431Scarlsonj
10973431ScarlsonjFutures
10983431Scarlsonj
10993431Scarlsonj  Zones currently cannot address any IP interfaces by way of DHCP.
11003431Scarlsonj  This project will not fix that problem, but the DUID/IAID could be
11013431Scarlsonj  used to help fix it in the future.
11023431Scarlsonj
11033431Scarlsonj  In particular, the DUID allows the client to obtain separate sets of
11043431Scarlsonj  addresses and configuration parameters on a single interface, just
11053431Scarlsonj  like an IPv4 Client ID, but it includes a clean mechanism for vendor
11063431Scarlsonj  extensions.  If we associate the DUID with the zone identifier or
11073431Scarlsonj  name through an extension, then we have a really simple way of
11083431Scarlsonj  allocating per-zone addresses.
11093431Scarlsonj
11103431Scarlsonj  Moreover, RFC 4361 describes a handy way of using DHCPv6 DUID/IAID
11113431Scarlsonj  values with IPv4 DHCP, which would quickly solve the problem of
11123431Scarlsonj  using DHCP for IPv4 address assignment in non-global zones as well.
11133431Scarlsonj
11143431Scarlsonj  (One potential risk with this plan is that there may be server
11153431Scarlsonj  implementations that either do not implement the RFC correctly or
11163431Scarlsonj  otherwise mishandle the DUID.  This has apparently bitten some early
11173431Scarlsonj  adopters.)
11183431Scarlsonj
11193431Scarlsonj  Implementing the FQDN option for DHCPv6 would, given the current
11203431Scarlsonj  libdhcputil design, require a new 'type' of entry for the inittab6
11213431Scarlsonj  file.  This is because the design does not allow for any simple
11223431Scarlsonj  means to ``compose'' a sequence of basic types together.  Thus,
11233431Scarlsonj  every type of option must either be a basic type, or an array of
11243431Scarlsonj  multiple instances of the same basic type.
11253431Scarlsonj
11263431Scarlsonj  If we implement FQDN in the future, it may be useful to explore some
11273431Scarlsonj  means of allowing a given option instance to be a sequence of basic
11283431Scarlsonj  types.
11293431Scarlsonj
11303431Scarlsonj  This project does not make the DNS resolver or any other subsystem
11313431Scarlsonj  use the data gathered by DHCPv6.  It just makes the data available
11323431Scarlsonj  through dhcpinfo(1).  Future projects should modify those services
11333431Scarlsonj  to use configuration data learned via DHCPv6.  (One of the reasons
11343431Scarlsonj  this is not being done now is that Network Automagic [NWAM] will
11353431Scarlsonj  likely be changing this area substantially in the very near future,
11363431Scarlsonj  and thus the effort would be largely wasted.)
11373431Scarlsonj
11383431Scarlsonj
11393431ScarlsonjAppendix A - Choice of Venue
11403431Scarlsonj
11413431Scarlsonj  There are three logical places to implement DHCPv6:
11423431Scarlsonj
11433431Scarlsonj    - in dhcpagent
11443431Scarlsonj    - in in.ndpd
11453431Scarlsonj    - in a new daemon (say, 'dhcp6agent')
11463431Scarlsonj
11473431Scarlsonj  We need to access parameters via dhcpinfo, and should provide the
11483431Scarlsonj  same set of status and control features via ifconfig as are present
11493431Scarlsonj  for IPv4.  (For the latter, if we fail to do that, it will likely
11503431Scarlsonj  confuse users.  The expense for doing it is comparatively small, and
11513431Scarlsonj  it will be useful for testing, even though it should not be needed
11523431Scarlsonj  in normal operation.)
11533431Scarlsonj
11543431Scarlsonj  If we implement somewhere other than dhcpagent, then we need to give
11553431Scarlsonj  that new daemon (in.ndpd or dhcp6agent) the same basic IPC features
11563431Scarlsonj  as dhcpagent already has.  This means either extracting those bits
11573431Scarlsonj  (async.c and ipc_action.c) into a shared library or just copying
11583431Scarlsonj  them.  Obviously, the former would be preferred, but as those bits
11593431Scarlsonj  depend on the rest of the dhcpagent infrastructure for timers and
11603431Scarlsonj  state handling, this means that the new process would have to look a
11613431Scarlsonj  lot like dhcpagent.
11623431Scarlsonj
11633431Scarlsonj  Implementing DHCPv6 as part of in.ndpd is attractive, as it
11643431Scarlsonj  eliminates the confusion that the router discovery process for
11653431Scarlsonj  determining interface netmasks can cause, along with the need to do
11663431Scarlsonj  any signaling at all to bring DHCPv6 up.  However, the need to make
11673431Scarlsonj  in.ndpd more like dhcpagent is unattractive.
11683431Scarlsonj
11693431Scarlsonj  Having a new dhcp6agent daemon seems to have little to recommend it,
11703431Scarlsonj  other than leaving the existing dhcpagent code untouched.  If we do
11713431Scarlsonj  that, then we end up with two implementations that do many similar
11723431Scarlsonj  things, and must be maintained in parallel.
11733431Scarlsonj
11743431Scarlsonj  Thus, although it leads to some complexity in reworking the data
11753431Scarlsonj  structures to fit both protocols, on balance the simplest solution
11763431Scarlsonj  is to extend dhcpagent.
11773431Scarlsonj
11783431Scarlsonj
11793431ScarlsonjAppendix B - Cross-Reference
11803431Scarlsonj
11813431Scarlsonj  in.ndpd
11823431Scarlsonj
11833431Scarlsonj    - Start dhcpagent and issue "dhcp start" command via libdhcpagent
11843431Scarlsonj    - Parse StatefulAddrConf interface option from ndpd.conf
11853431Scarlsonj    - Watch for M and O bits to trigger DHCPv6
11863431Scarlsonj    - Handle "no routers found" case and start DHCPv6
11873431Scarlsonj    - Track prefixes and set prefix length on IFF_DHCPRUNNING aliases
11883431Scarlsonj    - Send new Router Solicitation when prefix unknown
11893431Scarlsonj    - Change privileges so that dhcpagent can be launched successfully
11903431Scarlsonj
11913431Scarlsonj  libdhcputil
11923431Scarlsonj
11933431Scarlsonj    - Parse new /etc/dhcp/inittab6 file
11943431Scarlsonj    - Handle new UNUMBER24, SNUMBER64, IPV6, DUID and DOMAIN types
11953431Scarlsonj    - Add DHCPv6 option iterators (dhcpv6_find_option and
11963431Scarlsonj      dhcpv6_pkt_option)
11973431Scarlsonj    - Add dlpi_to_arp function (temporary)
11983431Scarlsonj
11993431Scarlsonj  libdhcpagent
12003431Scarlsonj
12013431Scarlsonj    - Add stable DUID and IAID creation and storage support
12023431Scarlsonj      functions and add new dhcp_stable.h include file
12033431Scarlsonj    - Support new DECLINING and RELEASING states introduced by DHCPv6.
12043431Scarlsonj    - Update implementation so that it doesn't rely on gettimeofday()
12053431Scarlsonj      for I/O timeouts
12063431Scarlsonj    - Extend the hostconf functions to support DHCPv6, using a new
12073431Scarlsonj      ".dh6" file
12083431Scarlsonj
12093431Scarlsonj  snoop
12103431Scarlsonj
12113431Scarlsonj    - Add support for DHCPv6 packet decoding (all types)
12123431Scarlsonj    - Add "dhcp6" filter keyword
12133431Scarlsonj    - Fix known bugs in DHCP filtering
12143431Scarlsonj
12153431Scarlsonj  ifconfig
12163431Scarlsonj
12173431Scarlsonj    - Remove inet-only restriction on "dhcp" keyword
12183431Scarlsonj
12193431Scarlsonj  netstat
12203431Scarlsonj
12213431Scarlsonj    - Remove strange "-I list" feature.
12223431Scarlsonj    - Add support for DHCPv6 and iterating over IPv6 interfaces.
12233431Scarlsonj
12243431Scarlsonj  ip
12253431Scarlsonj
12263431Scarlsonj    - Add extensions to IPv6 source address selection to prefer DHCPv6
12273431Scarlsonj      addresses when all else is equal
12283431Scarlsonj    - Fix known bugs in source address selection (remaining from TX
12293431Scarlsonj      integration)
12303431Scarlsonj
12313431Scarlsonj  other
12323431Scarlsonj
12333431Scarlsonj    - Add ifindex and source/destination address into PKT_LIST.
12343431Scarlsonj    - Add more ARPHDR_* and IPPORT_* values.
1235