I have a _very_ rough draft of a detailed design document for DHCPv6. This is based on some prototyping work I've been doing, so I'm pretty sure I've shaken out the major high-level issues, but there are a host of smaller bits yet to be examined in detail.
This is a separate document from the high-level design document I sent a while ago. There is some minor overlap in information, but the high-level document covers the administrative interfaces better, while this covers the implementation issues from a design perspective. If you have the time to review it, I'd appreciate hearing any comments you might have, either on this mailing list or privately. If not, then not to worry; this is just a very early draft. As I get deeper in implementation, I'll be sending out a more solid draft for comment. (And, yes, there's still ARC review to go. And test development is in progress by a separate group.) CDDL HEADER START The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License. You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License. When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner] CDDL HEADER END Copyright 2006 Sun Microsystems, Inc. All rights reserved. Use is subject to license terms. ident "@(#)detailed.txt 1.3 06/10/06 SMI" DHCPv6 Client Low-Level Design Introduction This project adds DHCPv6 client-side (not server) support to Solaris. Future projects may add server-side support as well as enhance the basic capabilities added here. These are not discussed in this document. This document assumes that the reader is familiar with the following other documents: - RFC 3315: the primary description of DHCPv6 - RFCs 2131 and 2132: IPv4 DHCP - RFCs 2461 and 2462: IPv6 NDP and stateless autoconfiguration - ifconfig(1M): Solaris IP interface configuration - dhcpagent(1M): Solaris DHCP client - dhcpinfo(1): Solaris DHCP parameter utility - "DHCPv6 Client High-Level Design" The overall plan is to enhance the existing Solaris dhcpagent so that it is able to process DHCPv6. It would have been possible to create a new, separate process for this, or to integrate the feature into in.ndpd. These alternatives are discussed in Appendix A. This document discusses the internal design issues involved in doing that. It does not discuss the details of the protocol itself (which are more than adequately described in the RFC), nor the individual lines of code (which will be in the code review). Background In order to discuss the design changes for DHCPv6, it's necessary first to talk about the current IPv4-only design, and the assumptions built into that design. The main data structure used in dhcpagent is the 'struct ifslist'. Each instance of this structure represents a Solaris logical IP interface under DHCP's control. It also represents the shared state with the DHCP server that granted the address, the address itself, and contains the negotiated options. There is one list in dhcpagent containing all of the IP interfaces that are under DHCP control. IP interfaces not under DHCP control (for example, those that are statically addressed) are not included in this list, even when plumbed on the system. These ifslist entries are chained like this: ifsheadp -> ifslist -> ifslist -> ifslist -> NULL net0 net0:1 net1 Each ifslist entry contains the address, mask, lease information, interface, hardware information, packets, state, and timers. The name of the logical IP interface under DHCP's control is also the name used in the administrative interfaces (dhcpinfo, ifconfig) and when logging events. Each entry holds open a DLPI stream and two sockets. The DLPI stream is nulled-out with a filter when not in use, but still consumes system resources. (Most significantly, it causes data copies in the driver layer that end up sapping performance.) The entry storage is managed by a insert/hold/release/remove model and reference counts. In this model, insert_ifs() allocates a new ifslist entry and inserts it into the global list, with the global list holding a reference. remove_ifs() removes it from the global list and drops that reference. hold_ifs() and release_ifs() are used by data structures that refer to ifslist entries, such as timer entries, to make sure that the ifslist entry isn't freed until the timer has been dispatched or deleted. The design is single-threaded, so code that walks the global list needn't bother taking holds on the ifslist structure. Only references that may be used at a different time need to be recorded. Packets are handled using PKT (struct dhcp; <netinet/dhcp.h>), PKT_LIST (struct dhcp_list; <dhcp_impl.h>), and dhcp_pkt_t (struct dhcp_pkt; "packet.h"). PKT is just the RFC 2131 DHCP packet structure, and has no additional information, such as packet length. PKT_LIST contains a PKT pointer, length, decoded option arrays, and linkage for putting the packet in a list. Finally, dhcp_pkt_t has a PKT pointer and length values suitable for modifying the packet. Essentially, PKT_LIST is a wrapper for received packets, and dhcp_pkt_t is a wrapper for packets to be sent. The basic PKT structure is used in dhcpagent, inetboot, in.dhcpd, libdhcpagent, libwanboot, libdhcputil, and others. PKT_LIST is used in a similar set of places, including the kernel NFS modules. dhcp_pkt_t is (as the header file implies) limited to dhcpagent. DHCPv6 Differences DHCPv6 has some commonality with IPv4 DHCP, but also has some significant differences. Unlike IPv4 DHCP, DHCPv6 relies on link-local IP addresses to do its work. This means that, on Solaris, the client doesn't need DLPI to perform any of the I/O; regular IP sockets will do the job. It also means that, unlike IPv4 DHCP, DHCPv6 does not lease the address on which it's running. The system provides it automatically. With IPv4 DHCP, a single address plus configuration options is leased with a given client ID and a single state machine instance, and the implementation binds that to a single IP logical interface specified by the user. The lease has a "Lease Time," a required option, as well as two timers, called T1 (renew) and T2 (rebind), which are controlled by regular options. DHCPv6 uses a single client/server session to control the acquisition of configuration options and "identity associations" (IAs). The identity associations, in turn, contain lists of addresses for the client to use and the T1/T2 timer values. Each individual address has its own lifetime. The options used for each are distinct. Notably, two of the mistakes from IPv4 DHCP have been fixed: DHCPv6 doesn't carry a client name, and doesn't attempt to impersonate a routing protocol. Another welcome change is the lack of a netmask/prefix length with DHCPv6. Instead, the client uses the Router Advertisement prefixes to set the correct interface netmask. This reduces the number of databases that need to be kept in sync. Otherwise, DHCPv6 is similar to IPv4 DHCP. The same renew/rebind and lease expiry strategy is used, although the state machine events must now take into account multiple IAs and the fact that each can cause renewing or rebinding state independently. DHCPv6 And Solaris The protocol distinctions above have several important implications. For the logical interfaces: - Because Solaris uses IP logical interfaces to configure addresses, we must have multiple IP logical interfaces per IA with IPv6. - Because we need to support multiple addresses (and thus multiple IP logical interfaces) per IA and multiple IAs per client/server session, the IP logical interface name isn't a unique name for the lease. As a result, IP logical interfaces will come and go with DHCPv6, just as happens with the existing stateless address autoconfiguration support in in.ndpd. The logical interface names have no administrative significance. Fortunately, DHCPv6 does end up with one fixed name that can be used to identify a session. Because DHCPv6 uses link local addresses for communication with the server, the name of the IP logical interface that has this link local address (normally the same as the IP physical interface) can be used as an identifier for dhcpinfo and logging purposes. Dhcpagent Redesign Overview The redesign starts by refactoring the IP interface representation. Because we need to have multiple IP logical interfaces (LIFs) for a single identity association (IA), we should not store all of the DHCP state information along with the LIF information. For DHCPv6, we will need to keep LIFs on a single IP physical interface (PIF) together, so this is probably also a good time to reconsider the way dhcpagent represents physical interfaces. The current design simply replicates the state (notably the DLPI stream, but also the hardware address and other bits) among all of the ifslist entries on the same physical interface. The new design creates two lists of dhcp_pif_t entries, one list for IPv4 and the other for IPv6. Each dhcp_pif_t represents a physical interface, with a list of dhcp_lif_t entries attached, each of which represents a LIF used by dhcpagent. Next, the lease-tracking needs to be refactored. DHCPv6 is the functional superset in this case, as it has one lifetime per address (LIF) and IA groupings with shared T1/T2 timers. To represent these groupings, we will use a new dhcp_lease_t structure. IPv4 DHCP will have one such structure per state machine, while DHCPv6 will have a list. For all of these new structures, we will use the same insert/hold/ release/remove model as with the original ifslist. Finally, the remaining items (and the bulk of the original ifslist members) are kept on a per-state-machine basis. A new dhcp_smach_t structure will hold these. Lease Representation For DHCPv6, we need to track multiple LIFs per lease (IA), but we also need multiple LIFs per PIF. Rather than having two sets of list linkage for each LIF, we can simplify: the lease structure will use a base pointer for the first LIF in the lease, and a count for the number of consecutive LIFs in the existing list that belong to the lease. When removing a LIF from the system, we need to decrement the count of LIFs in the lease, and fix up the base pointer if the LIF being removed is the first one. Inserting a LIF means just moving it into this list and bumping the counter. When removing a lease from a state machine, we need to dispose of the LIFs referenced. If the LIF being disposed is the primary LIF for a state machine, then all that we can do is canonize the LIF (returning it to a default state); this represents the normal IPv4 DHCP operation on lease expiry. Otherwise, the lease is the owner of that LIF (it was created because of a DHCPv6 IA), and disposal means unplumbing the LIF from the actual system and removing the LIF entry from the PIF. Main Structure Linkage For IPv4 DHCP, the new linkage is straightforward. Using the same example as in the initial design discussion: +- lease +- lease +- lease | ^ | ^ | ^ | | | | | | | smach | smach | smach \ ^ \ ^ \ ^ v | v | v | lif ----> lif -> NULL lif -> NULL net0 net0:1 net1 ^ ^ | | v4root -> pif -----------------> pif -> NULL net0 net1 This shows four state machines running. Each state machine has a single primary LIF with which it's associated (and named). Each also has a single lease structure that points back to the same LIF (count of 1), because IPv4 DHCP controls a single address allocation per state machine. DHCPv6 is a bit more complex. This shows DHCPv6 running on one interface (multiple interfaces are of course possible) with multiple leases, and each with multiple addresses (one with 2 addresses, the second with 3). lease ----------------> lease -> NULL ^ \(2) |(3) | \ | smach \ | ^ \ | | v v lif --> lif --> lif --> lif --> lif --> lif --> NULL net0 net0:1 net0:2 net0:3 net0:4 net0:5 ^ | v6root -> pif -> NULL net0 Note that with IPv4 DHCP, the lease points to the LIF that's also the primary LIF for the state machine, because that's the IP interface that dhcpagent controls. With DHCPv6, the lease (one per IA_NA) points to a separate LIF that's created just for the leased address (one per IAADDR). Packet Structure Extensions Obviously, we need some DHCPv6 packet data structures and definitions. A new <netinet/dhcpv6.h> file will be introduced with the necessary #defines and structures. The primary structure there will be: struct dhcpv6_message { uint8_t d6m_msg_type; uint8_t d6m_transid_ho; uint16_t d6m_transid_lo; }; typedef struct dhcpv6_message dhcpv6_message_t; This defines the usual (non-relay) DHCPv6 packet header, and is roughly equivalent to PKT for IPv4. Extending dhcp_pkt_t for DHCPv6 is straightforward, as it's used only within dhcpagent. This structure will be amended to use a union for v4/v6 and include a boolean to flag which version is in use. For the PKT_LIST structure, things are more complex. This defines both a queuing mechanism for received packets (typically OFFERs) and a set of packet decoding structures. The decoding structures are highly specific to IPv4 DHCP -- they have no means to handle nested or repeated options (as used heavily in DHCPv6) and make use of the DHCP_OPT structure which is specific to IPv4 DHCP -- and are somewhat expensive in storage. Worse, this structure is used throughout the system, so changes to it need to be made carefully. (For example, the existing 'pkt' member can't just be turned into a union.) In the prototype, I created a new dhcp_plist_t structure to represent packet lists as used inside dhcpagent and made dhcp_pkt_t valid for use on input and output. The result is unsatisfying, though, as it involves manipulating far too many data structures in common cases. This will need to be revisited. The likely best answer is to use PKT_LIST for both IPv4 and IPv6, adding the few new bits of metadata required to the end (receiving ifIndex, packet source/destination addresses), and staying within the existing design. For option parsing, a dhcpv6_find_option() function will be added to libdhcputil. This function will walk a DHCPv6 option list, and provide safe (bounds-checked) access to the options inside. The function can be called recursively, so that option nesting can be handled fairly simply by nested loops. There is one special consideration here: there's no "pad" option for DHCPv6 or alignment requirements. This means that option handlers must all be written to deal with unaligned data. Sockets and I/O Handling DHCPv6 doesn't need or use either a DLPI or a broadcast IP socket. Instead, a single unicast-bound IP socket on a link-local address is all that is needed. This is roughly equivalent to if_sock_ip_fd in the existing design, but that latter is bound only after DHCP reaches BOUND state -- that is, when it switches away from DLPI. This, along with the excess of open file descriptors in otherwise idle daemons and the potentially serious performance problems in leaving DLPI open at all times, argues for a redesign of the I/O logic in dhcpagent. The first thing that we can do is eliminate the need for the per-ifslist if_sock_fd. This is used primarily for issuing ioctls to configure interfaces -- a task that would work as well with any open socket -- and is also registered to receive any ACK/NAK packets that may arrive via broadcast. Both of these can be eliminated by creating a pair of global sockets (IPv4 and IPv6), bound and configured for ACK/NAK reception. The only difference is that the list of running state machines must be scanned on reception, but the existing design already does this by default as the kernel replicates received datagrams among all matching sockets. The next part is in minimizing DLPI usage. A DLPI stream is needed at most for each IPv4 PIF, and it's not needed when all of the DHCP instances on that PIF are bound. In fact, the current implementation deals with this in configure_bound() by setting a "blackhole" packet filter. The stream is left open. To simplify this, we will open at most one DLPI stream on a PIF, and use reference counts from the state machines to determine when the stream must be open and when it can be closed. This mechanism will be centralized in a set_smach_state() function that changes the state and opens/closes the DLPI stream when needed. When IP_PKTINFO (PSARC 2006/466) integrates, we can go a step further by removing the need for any per-LIF sockets and just use the global sockets for all but DLPI. This could be done now in the case of DHCPv6, as we already have IPV6_PKTINFO, but since it's not available for IPv4, we'll hold off to avoid making things more complicated. It may also be possible to remove the need for DLPI for IPv4, and incidentally simplify the code a fair amount, by adding a kernel option to allow transmission and reception of UDP packets over interfaces that are plumbed but not marked IFF_UP. This is left for future work. The State Machine The only state machine difference between DHCPv6 and IPv4 DHCP is with the RENEWING and REBINDING states. For IPv4 DHCP, these states map one-to-one with a single address and single lease that's undergoing renewal. It's a simple progression (on timeout) from BOUND, to RENEWING, to REBINDING and finally back to SELECTING to start over. For DHCPv6, things are somewhat more complex. At any one time, there may be multiple IAs (leases) that are effectively in renewing or rebinding state, based on the T1/T2 timers for each IA, and many addresses that have expired. However, because all of the leases are related to a single server, and that server either responds to our requests or doesn't, we can simplify the states to be nearly identical to IPv4 DHCP. The revised definition for use with DHCPv6 is: - Transition from BOUND to RENEWING state when the first T1 timer (of any lease on the state machine) expires. At this point, as an optimization, we begin attempting to renew any IAs that are within REN_TIMEOUT (10 seconds) of reaching T1 as well. We may as well avoid sending an excess of packets. - At each retransmit timeout, we check to see if there are more IAs that need to join in because they've passed point T1 as well. If so, then add them. - When we reach T2 on any IA, then enter REBINDING state. At this point, we have a choice. For those other IAs that are past T1 but not yet at T2, we could ignore them (sending only those that have passed point T2), continue to send separate RENEW messages for them, or just include them in the REBIND message. - As addresses reach the end of their lifetimes, remove them from the system. When an IA (lease) becomes empty, just remove it. When there are no more leases left, return to SELECTING state to start over. Note that the RFC treats the IAs as separate entities when doing renew/rebind, but treats them as a unit when doing the initial negotiation. This is, to say the least, confusing, especially so given that there's no reason to expect that after having failed to elicit any responses at all from the server on one lease, the server will suddenly start responding when we attempt to renew some other lease. We rationalize thus this behavior by using a single renew/rebind state for the entire state machine (and thus client/server pair). Note that it would be possible to start the SELECTING state earlier than waiting for the last lease to expire. However, it this point, there are other servers on the network that have seen us attempting to REBIND for quite some time, and they have not responded. The likelihood that there's a server that will ignore REBIND but then suddenly spring into action on SOLICIT message seems low enough that the optimization won't be done now. (Starting SELECTING state earlier may be done in the future, if it's found to be useful.) Router Advertisements IPv6 Router Advertisements perform two functions related to DHCPv6: - they specify whether and how to run DHCPv6 on a given interface. - they provide a list of the valid prefixes on an interface. For the first issue, in.ndpd needs to use the same DHCP control interfaces that ifconfig uses, so that it can launch DHCPv6 when necessary. Note that it never needs to shut down DHCPv6, as router advertisements can't do that. The second issue is more subtle. Unlike IPv4 DHCP, DHCPv6 does not give the netmask along with the leased address. The client is on its own to determine the right netmask to use. This is where the advertised prefixes come in: these must be used to finish the interface configuration. We will have the DHCPv6 client configure each interface with an all-ones (/128) netmask by default. In.ndpd will be modified so that when it detects a new IFF_DHCPRUNNING IP logical interface, it checks for a known matching prefix, and sets the netmask as necessary. When it learns of a new prefix, it will scan all of the IFF_DHCPRUNNING IP logical interfaces on the same physical interface and set the netmasks when necessary. Dhcpagent, for its part, will ignore the netmask on IPv6 interfaces when checking for changes that would require it to "abandon" the interface. Given the way that DHCPv6 controls both the horizontal and the vertical in plumbing and removing logical interfaces, and users do not, it might be worthwhile to consider roping off any direct user changes to IPv6 logical interfaces under control of in.ndpd or dhcpagent, and instead force users through a higher-level interface. This won't be done as part of this project, however. Persistent State IPv4 DHCP had only minimal need for persistent state, beyond the configuration parameters. The state is stored when "ifconfig dhcp drop" is run, which is typically done only from a user command line well after the system is booted and running. The daemon stored this state in /etc/dhcp, because it needs to be available when only the root file system has been mounted. Moreover, dhcpagent starts very early in the boot process. It runs as part of svc:/network/physical:default, which runs well before root is mounted read/write: svc:/system/filesystem/root:default -> svc:/system/metainit:default -> svc:/system/identity:node -> svc:/network/physical:default svc:/network/iscsi_initiator:default -> svc:/network/physical:default and, of course, well before either /var or /usr is mounted. This means that any persistent state must be kept in the root file system, and that if we write, we have to cope gracefully with the root file system returning EROFS on write attempts. For DHCPv6, we need to write out our stable DUID and IAID information to fulfill the demands of RFC 3315. To accomplish this, we will use two strategies. First, our IAID will just default to the IP ifIndex value, and the DUID-LLT form will be used. Dchpagent will then attempt to write out the DUID:IAID information into a file under /etc/dhcp/. If this fails due to EROFS, a timer will be set, and the daemon will try again later. Currently, the boot system (GRUB, OBP, the miniroot) does not support installing over IPv6. This could change in the future, so part of this plan is to support that event. When running in the miniroot on an x86 system, /etc/dhcp (and the rest of the root) is mounted on a read-only ramdisk. In this case, writing to /etc/dhcp will just never work. A possible solution would be to add a new privileged command in ifconfig that forces dhcpagent to write to an alternate location. The initial install process could then do "ifconfig <x> dhcp write /a" to get the needed state written out to the newly-constructed system root. This part (the new write option) won't be implemented as part of this project, because it's not needed yet. Field Mappings Old (all in ifslist) New next dhcp_smach_t.dsm_next prev dhcp_smach_t.dsm_prev if_hold_count dhcp_smach_t.dsm_hold_count if_ia dhcp_smach_t.dsm_ia if_async dhcp_smach_t.dsm_async if_state dhcp_smach_t.dsm_state if_dflags dhcp_smach_t.dsm_dflags if_name dhcp_smach_t.dsm_name (see text) if_index dhcp_pif_t.pif_index if_max dhcp_lif_t.lif_max and dhcp_pif_t.pif_max if_min (was unused; removed) if_opt (was unused; removed) if_hwaddr dhcp_pif_t.pif_hwaddr if_hwlen dhcp_pif_t.pif_hwlen if_hwtype dhcp_pif_t.pif_hwtype if_cid dhcp_smach_t.dsm_cid if_cidlen dhcp_smach_t.dsm_cidlen if_prl dhcp_smach_t.dsm_prl if_prllen dhcp_smach_t.dsm_prllen if_daddr dhcp_pif_t.pif_daddr if_dlen dhcp_pif_t.pif_dlen if_saplen dhcp_pif_t.pif_saplen if_sap_before dhcp_pif_t.pif_sap_before if_dlpi_fd dhcp_pif_t.pif_dlpi_fd if_sock_fd v4_sock_fd and v6_sock_fd (globals) if_sock_ip_fd dhcp_lif_t.lif_sock_ip_fd if_timer (see text) if_t1 dhcp_lease_t.dl_t1 if_t2 dhcp_lease_t.dl_t2 if_lease dhcp_lif_t.lif_expire if_nrouters dhcp_smach_t.dsm_nrouters if_routers dhcp_smach_t.dsm_routers if_server dhcp_smach_t.dsm_server if_addr dhcp_lif_t.lif_v6addr if_netmask dhcp_lif_t.lif_v6mask if_broadcast dhcp_lif_t.lif_v6peer if_ack dhcp_smach_t.dsm_ack if_orig_ack dhcp_smach_t.dsm_orig_ack if_offer_wait dhcp_smach_t.dsm_offer_wait if_offer_timer dhcp_smach_t.dsm_offer_timer if_offer_id dhcp_pif_t.pif_dlpi_id if_acknak_id dhcp_lif_t.lif_acknak_id if_acknak_bcast_id v4_acknak_bcast_id (global) if_neg_monosec dhcp_smach_t.dsm_neg_monosec if_newstart_monosec dhcp_smach_t.dsm_newstart_monosec if_curstart_monosec dhcp_smach_t.dsm_curstart_monosec if_disc_secs dhcp_smach_t.dsm_disc_secs if_reqhost dhcp_smach_t.dsm_reqhost if_recv_pkt_list dhcp_smach_t.dsm_recv_pkt_list if_sent dhcp_smach_t.dsm_sent if_received dhcp_smach_t.dsm_received if_bad_offers dhcp_smach_t.dsm_bad_offers if_send_pkt dhcp_smach_t.dsm_send_pkt if_send_timeout dhcp_smach_t.dsm_send_timeout if_send_dest dhcp_smach_t.dsm_send_dest if_send_stop_func dhcp_smach_t.dsm_send_stop_func if_packet_sent dhcp_smach_t.dsm_packet_sent if_retrans_timer dhcp_smach_t.dsm_retrans_timer if_script_fd dhcp_smach_t.dsm_script_fd if_script_pid dhcp_smach_t.dsm_script_pid if_script_helper_pid dhcp_smach_t.dsm_script_helper_pid if_script_event dhcp_smach_t.dsm_script_event if_script_event_id dhcp_smach_t.dsm_script_event_id if_callback_msg dhcp_smach_t.dsm_callback_msg if_script_callback dhcp_smach_t.dsm_script_callback Notes: - The dsm_name field currently just points to the lif_name on the controlling LIF. This may need to be named differently in the future; perhaps when Zones are supported. - The timer mechanism will be refactored. Rather than using the separate if_timer[] array to hold the timer IDs and if_{t1,t2,lease} to hold the relative timer values, we will gather this information into a dhcp_timer_t structure: dt_id timer ID value dt_start start relative time dt_current current relative time (new) The first two members just gather together the ID and relative time for the timer, and are the same as the existing separate values. The dt_current member simplifies the way callers control the timeout using common utility functions. Instead of passing in a value, the caller sets it up as in: lif->lif_expire.dt_current = next_expire_time; schedule_timer(&lif->lif_expire, callback_func, lif); New fields not accounted for above: dhcp_pif_t.pif_next linkage in global list of PIFs dhcp_pif_t.pif_prev linkage in global list of PIFs dhcp_pif_t.pif_lifs pointer to list of LIFs on this PIF dhcp_pif_t.pif_isv6 IPv6 flag dhcp_pif_t.pif_dlpi_count number of state machines using DLPI dhcp_pif_t.pif_hold_count reference count dhcp_pif_t.pif_name name of physical interface dhcp_lif_t.lif_next linkage in per-PIF list of LIFs dhcp_lif_t.lif_prev linkage in per-PIF list of LIFs dhcp_lif_t.lif_pif backpointer to parent PIF dhcp_lif_t.lif_smachs pointer to list of state machines dhcp_lif_t.lif_lease backpointer to lease holding LIF dhcp_lif_t.lif_flags interface flags (IFF_*) dhcp_lif_t.lif_hold_count reference count dhcp_lif_t.lif_dad_wait waiting for DAD resolution flag dhcp_lif_t.lif_removed removed from list flag dhcp_lif_t.lif_declined reason to refuse this address (string) dhcp_lif_t.lif_name name of logical interface dhcp_smach_t.dsm_lif controlling (main) LIF dhcp_smach_t.dsm_leases pointer to list of leases dhcp_smach_t.dsm_lif_wait number of LIFs waiting on DAD dhcp_smach_t.dsm_lif_down number of LIFs that have failed dhcp_smach_t.dsm_using_dlpi currently using DLPI flag dhcp_lease_t.dl_next linkage in per-state-machine list of leases dhcp_lease_t.dl_prev linkage in per-state-machine list of leases dhcp_lease_t.dl_smach back pointer to state machine dhcp_lease_t.dl_lifs pointer to first LIF configured by lease dhcp_lease_t.dl_nlifs number of configured consecutive LIFs dhcp_lease_t.dl_hold_count reference counter dhcp_lease_t.dl_removed removed from list flag Interactions With Other Projects Clearview UV (vanity naming) will cause IP interface names to become less predictably related to DLPI instance names, and this will directly affect the way LIF and PIF structures will be handled in the new dhcpagent. The same issue appears to be true for the existing dhcpagent logic, which assumes that the DLPI instance can be found by stripping off ':' in the interface name, and that logicals can be discerned when necessary by looking at this name. As the issues are essentially the same in both old and new code, the broader issue of vanity naming for DHCP will be left for the Clearview team to resolve. Futures Zones currently cannot address any IP interfaces by way of DHCP. This project will not fix that problem, but the DUID/IAID could be used to help fix it in the future. In particular, the DUID allows the client to obtain separate sets of addresses and configuration parameters on a single interface, just like an IPv4 Client ID, but it includes a clean mechanism for vendor extensions. If we associate the DUID with the zone identifier or name through an extension, then we have a really simple way of allocating per-zone addresses. Moreover, RFC 4361 describes a handy way of using DHCPv6 DUID/IAID values with IPv4 DHCP, which would quickly solve the problem of using DHCP for IPv4 address assignment in non-global zones as well. In order to plan for that now, the saved DUID/IAID data will be stored in a file named using the state machine name (which could be augmented by zone name), and the data saved will include the interface name for the controlling LIF. (One potential risk with this plan is that there may be server implementations that either do not implement the RFC correctly or otherwise mishandle the DUID. This has apparently bitten some early adopters.) Appendix A - Choice of Venue There are three logical places to implement DHCPv6: - in dhcpagent - in in.ndpd - in a new daemon (say, 'dhcp6agent') We need to access parameters via dhcpinfo, and should provide the same set of status and control features via ifconfig as are present for IPv4. (For the latter, if we fail to do that, it will likely confuse users. The expense for doing it is comparatively small, even though it should not be needed in practice.) If we implement somewhere other than dhcpagent, then we need to give that new daemon (in.ndpd or dhcp6agent) the same basic IPC features as dhcpagent already has. This means either extracting those bits (async.c and ipc_action.c) into a shared library or just copying them. Obviously, the former would be preferred, but as those bits depend on the rest of the dhcpagent infrastructure for timers and state handling, this means that the new process would have to look a lot like dhcpagent. Implementing DHCPv6 as part of in.ndpd is attractive, as it eliminates the confusion that the router discovery process for determining interface netmasks can cause, along with the need to do any signaling at all to bring DHCPv6 up. However, the need to make in.ndpd more like dhcpagent is unattractive. Having a new dhcp6agent daemon seems to have little to recommend it, other than leaving the existing dhcpagent code untouched. If we do that, then we end up with two implementations that do many similar things, and must be maintained in parallel. Thus, although it leads to some complexity in reworking the data structures to fit both protocols, on balance the simplest solution is to extend dhcpagent. -- James Carlson, KISS Network <[EMAIL PROTECTED]> Sun Microsystems / 1 Network Drive 71.232W Vox +1 781 442 2084 MS UBUR02-212 / Burlington MA 01803-2757 42.496N Fax +1 781 442 1677 _______________________________________________ networking-discuss mailing list [email protected]
