xref: /titanic_51/usr/src/cmd/cmd-inet/sbin/dhcpagent/README (revision e704a8f24a369484ba8f4a1cf49d4db00dd91166)
17c478bd9Sstevel@tonic-gateCDDL HEADER START
27c478bd9Sstevel@tonic-gate
37c478bd9Sstevel@tonic-gateThe contents of this file are subject to the terms of the
4d04ccbb3ScarlsonjCommon Development and Distribution License (the "License").
5d04ccbb3ScarlsonjYou may not use this file except in compliance with the License.
67c478bd9Sstevel@tonic-gate
77c478bd9Sstevel@tonic-gateYou can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
87c478bd9Sstevel@tonic-gateor http://www.opensolaris.org/os/licensing.
97c478bd9Sstevel@tonic-gateSee the License for the specific language governing permissions
107c478bd9Sstevel@tonic-gateand limitations under the License.
117c478bd9Sstevel@tonic-gate
127c478bd9Sstevel@tonic-gateWhen distributing Covered Code, include this CDDL HEADER in each
137c478bd9Sstevel@tonic-gatefile and include the License file at usr/src/OPENSOLARIS.LICENSE.
147c478bd9Sstevel@tonic-gateIf applicable, add the following below this CDDL HEADER, with the
157c478bd9Sstevel@tonic-gatefields enclosed by brackets "[]" replaced with your own identifying
167c478bd9Sstevel@tonic-gateinformation: Portions Copyright [yyyy] [name of copyright owner]
177c478bd9Sstevel@tonic-gate
187c478bd9Sstevel@tonic-gateCDDL HEADER END
197c478bd9Sstevel@tonic-gate
20d04ccbb3ScarlsonjCopyright 2007 Sun Microsystems, Inc.  All rights reserved.
217c478bd9Sstevel@tonic-gateUse is subject to license terms.
227c478bd9Sstevel@tonic-gate
237c478bd9Sstevel@tonic-gateArchitectural Overview for the DHCP agent
247c478bd9Sstevel@tonic-gatePeter Memishian
257c478bd9Sstevel@tonic-gateident	"%Z%%M%	%I%	%E% SMI"
267c478bd9Sstevel@tonic-gate
277c478bd9Sstevel@tonic-gateINTRODUCTION
287c478bd9Sstevel@tonic-gate============
297c478bd9Sstevel@tonic-gate
30d04ccbb3ScarlsonjThe Solaris DHCP agent (dhcpagent) is a DHCP client implementation
31d04ccbb3Scarlsonjcompliant with RFCs 2131, 3315, and others.  The major forces shaping
32d04ccbb3Scarlsonjits design were:
337c478bd9Sstevel@tonic-gate
347c478bd9Sstevel@tonic-gate	* Must be capable of managing multiple network interfaces.
357c478bd9Sstevel@tonic-gate	* Must consume little CPU, since it will always be running.
367c478bd9Sstevel@tonic-gate	* Must have a small memory footprint, since it will always be
377c478bd9Sstevel@tonic-gate	  running.
38d04ccbb3Scarlsonj	* Must not rely on any shared libraries outside of /lib, since
39d04ccbb3Scarlsonj	  it must run before all filesystems have been mounted.
407c478bd9Sstevel@tonic-gate
417c478bd9Sstevel@tonic-gateWhen a DHCP agent implementation is only required to control a single
427c478bd9Sstevel@tonic-gateinterface on a machine, the problem is expressed well as a simple
437c478bd9Sstevel@tonic-gatestate-machine, as shown in RFC2131.  However, when a DHCP agent is
447c478bd9Sstevel@tonic-gateresponsible for managing more than one interface at a time, the
45d04ccbb3Scarlsonjproblem becomes much more complicated.
46d04ccbb3Scarlsonj
47d04ccbb3ScarlsonjThis can be resolved using threads or with an event-driven model.
48d04ccbb3ScarlsonjGiven that DHCP's behavior can be expressed concisely as a state
49d04ccbb3Scarlsonjmachine, the event-driven model is the closest match.
50d04ccbb3Scarlsonj
51d04ccbb3ScarlsonjWhile tried-and-true, that model is subtle and easy to get wrong.
52d04ccbb3ScarlsonjIndeed, much of the agent's code is there to manage the complexity of
53d04ccbb3Scarlsonjprogramming in an asynchronous event-driven paradigm.
547c478bd9Sstevel@tonic-gate
557c478bd9Sstevel@tonic-gateTHE BASICS
567c478bd9Sstevel@tonic-gate==========
577c478bd9Sstevel@tonic-gate
58d04ccbb3ScarlsonjThe DHCP agent consists of roughly 30 source files, most with a
59d04ccbb3Scarlsonjcompanion header file.  While the largest source file is around 1700
607c478bd9Sstevel@tonic-gatelines, most are much shorter.  The source files can largely be broken
617c478bd9Sstevel@tonic-gateup into three groups:
627c478bd9Sstevel@tonic-gate
63d04ccbb3Scarlsonj	* Source files that, along with their companion header files,
647c478bd9Sstevel@tonic-gate	  define an abstract "object" that is used by other parts of
65d04ccbb3Scarlsonj	  the system.  Examples include "packet.c", which along with
66d04ccbb3Scarlsonj	  "packet.h" provide a Packet object for use by the rest of
67d04ccbb3Scarlsonj	  the agent; and "async.c", which along with "async.h" defines
68d04ccbb3Scarlsonj	  an interface for managing asynchronous transactions within
69d04ccbb3Scarlsonj	  the agent.
707c478bd9Sstevel@tonic-gate
71d04ccbb3Scarlsonj	* Source files that implement a given state of the agent; for
727c478bd9Sstevel@tonic-gate	  instance, there is a "request.c" which comprises all of
737c478bd9Sstevel@tonic-gate	  the procedural "work" which must be done while in the
747c478bd9Sstevel@tonic-gate	  REQUESTING state of the agent.  By encapsulating states in
757c478bd9Sstevel@tonic-gate	  files, it becomes easier to debug errors in the
767c478bd9Sstevel@tonic-gate	  client/server protocol and adapt the agent to new
777c478bd9Sstevel@tonic-gate	  constraints, since all the relevant code is in one place.
787c478bd9Sstevel@tonic-gate
797c478bd9Sstevel@tonic-gate	* Source files, which along with their companion header files,
807c478bd9Sstevel@tonic-gate  	  encapsulate a given task or related set of tasks.  The
817c478bd9Sstevel@tonic-gate	  difference between this and the first group is that the
827c478bd9Sstevel@tonic-gate	  interfaces exported from these files do not operate on
837c478bd9Sstevel@tonic-gate	  an "object", but rather perform a specific task.  Examples
84*e704a8f2Smeem	  include "defaults.c", which provides a useful interface
85*e704a8f2Smeem	  to /etc/default/dhcpagent file operations.
867c478bd9Sstevel@tonic-gate
877c478bd9Sstevel@tonic-gateOVERVIEW
887c478bd9Sstevel@tonic-gate========
897c478bd9Sstevel@tonic-gate
907c478bd9Sstevel@tonic-gateHere we discuss the essential objects and subtle aspects of the
917c478bd9Sstevel@tonic-gateDHCP agent implementation.  Note that there is of course much more
927c478bd9Sstevel@tonic-gatethat is not discussed here, but after this overview you should be able
937c478bd9Sstevel@tonic-gateto fend for yourself in the source code.
947c478bd9Sstevel@tonic-gate
95d04ccbb3ScarlsonjFor details on the DHCPv6 aspects of the design, and how this relates
96d04ccbb3Scarlsonjto the implementation present in previous releases of Solaris, see the
97d04ccbb3ScarlsonjREADME.v6 file.
98d04ccbb3Scarlsonj
997c478bd9Sstevel@tonic-gateEvent Handlers and Timer Queues
1007c478bd9Sstevel@tonic-gate-------------------------------
1017c478bd9Sstevel@tonic-gate
1027c478bd9Sstevel@tonic-gateThe most important object in the agent is the event handler, whose
1037c478bd9Sstevel@tonic-gateinterface is in libinetutil.h and whose implementation is in
1047c478bd9Sstevel@tonic-gatelibinetutil.  The event handler is essentially an object-oriented
1057c478bd9Sstevel@tonic-gatewrapper around poll(2): other components of the agent can register to
1067c478bd9Sstevel@tonic-gatebe called back when specific events on file descriptors happen -- for
1077c478bd9Sstevel@tonic-gateinstance, to wait for requests to arrive on its IPC socket, the agent
1087c478bd9Sstevel@tonic-gateregisters a callback function (accept_event()) that will be called
1097c478bd9Sstevel@tonic-gateback whenever a new connection arrives on the file descriptor
1107c478bd9Sstevel@tonic-gateassociated with the IPC socket.  When the agent initially begins in
1117c478bd9Sstevel@tonic-gatemain(), it registers a number of events with the event handler, and
1127c478bd9Sstevel@tonic-gatethen calls iu_handle_events(), which proceeds to wait for events to
1137c478bd9Sstevel@tonic-gatehappen -- this function does not return until the agent is shutdown
1147c478bd9Sstevel@tonic-gatevia signal.
1157c478bd9Sstevel@tonic-gate
1167c478bd9Sstevel@tonic-gateWhen the registered events occur, the callback functions are called
1177c478bd9Sstevel@tonic-gateback, which in turn might lead to additional callbacks being
1187c478bd9Sstevel@tonic-gateregistered -- this is the classic event-driven model.  (As an aside,
1197c478bd9Sstevel@tonic-gatenote that programming in an event-driven model means that callbacks
1207c478bd9Sstevel@tonic-gatecannot block, or else the agent will become unresponsive.)
1217c478bd9Sstevel@tonic-gate
1227c478bd9Sstevel@tonic-gateA special kind of "event" is a timeout.  Since there are many timers
1237c478bd9Sstevel@tonic-gatewhich must be maintained for each DHCP-controlled interface (such as a
1247c478bd9Sstevel@tonic-gatelease expiration timer, time-to-first-renewal (t1) timer, and so
1257c478bd9Sstevel@tonic-gateforth), an object-oriented abstraction to timers called a "timer
1267c478bd9Sstevel@tonic-gatequeue" is provided, whose interface is in libinetutil.h with a
1277c478bd9Sstevel@tonic-gatecorresponding implementation in libinetutil.  The timer queue allows
1287c478bd9Sstevel@tonic-gatecallback functions to be "scheduled" for callback after a certain
1297c478bd9Sstevel@tonic-gateamount of time has passed.
1307c478bd9Sstevel@tonic-gate
1317c478bd9Sstevel@tonic-gateThe event handler and timer queue objects work hand-in-hand: the event
1327c478bd9Sstevel@tonic-gatehandler is passed a pointer to a timer queue in iu_handle_events() --
1337c478bd9Sstevel@tonic-gatefrom there, it can use the iu_earliest_timer() routine to find the
1347c478bd9Sstevel@tonic-gatetimer which will next fire, and use this to set its timeout value in
1357c478bd9Sstevel@tonic-gateits call to poll(2).  If poll(2) returns due to a timeout, the event
1367c478bd9Sstevel@tonic-gatehandler calls iu_expire_timers() to expire all timers that expired
1377c478bd9Sstevel@tonic-gate(note that more than one may have expired if, for example, multiple
1387c478bd9Sstevel@tonic-gatetimers were set to expire at the same time).
1397c478bd9Sstevel@tonic-gate
1407c478bd9Sstevel@tonic-gateAlthough it is possible to instantiate more than one timer queue or
1417c478bd9Sstevel@tonic-gateevent handler object, it doesn't make a lot of sense -- these objects
1427c478bd9Sstevel@tonic-gateare really "singletons".  Accordingly, the agent has two global
1437c478bd9Sstevel@tonic-gatevariables, `eh' and `tq', which store pointers to the global event
1447c478bd9Sstevel@tonic-gatehandler and timer queue.
1457c478bd9Sstevel@tonic-gate
1467c478bd9Sstevel@tonic-gateNetwork Interfaces
1477c478bd9Sstevel@tonic-gate------------------
1487c478bd9Sstevel@tonic-gate
1497c478bd9Sstevel@tonic-gateFor each network interface managed by the agent, there is a set of
1507c478bd9Sstevel@tonic-gateassociated state that describes both its general properties (such as
151d04ccbb3Scarlsonjthe maximum MTU) and its connections to DHCP-related state (the
152d04ccbb3Scarlsonjprotocol state machines).  This state is stored in a pair of
153d04ccbb3Scarlsonjstructures called `dhcp_pif_t' (the IP physical interface layer or
154d04ccbb3ScarlsonjPIF) and `dhcp_lif_t' (the IP logical interface layer or LIF).  Each
155d04ccbb3Scarlsonjdhcp_pif_t represents a single physical interface, such as "hme0," for
156d04ccbb3Scarlsonja given IP protocol version (4 or 6), and has a list of dhcp_lif_t
157d04ccbb3Scarlsonjstructures representing the logical interfaces (such as "hme0:1") in
158d04ccbb3Scarlsonjuse by the agent.
159d04ccbb3Scarlsonj
160d04ccbb3ScarlsonjThis split is important because of differences between IPv4 and IPv6.
161d04ccbb3ScarlsonjFor IPv4, each DHCP state machine manages a single IP address and
162d04ccbb3Scarlsonjassociated configuration data.  This corresponds to a single logical
163d04ccbb3Scarlsonjinterface, which must be specified by the user.  For IPv6, however,
164d04ccbb3Scarlsonjeach DHCP state machine manages a group of addresses, and is
165d04ccbb3Scarlsonjassociated with DUID value rather than with just an interface.
166d04ccbb3Scarlsonj
167d04ccbb3ScarlsonjThus, DHCPv6 behaves more like in.ndpd in its creation of "ADDRCONF"
168d04ccbb3Scarlsonjinterfaces.  The agent automatically plumbs logical interfaces when
169d04ccbb3Scarlsonjneeded and removes them when the addresses expire.
170d04ccbb3Scarlsonj
171d04ccbb3ScarlsonjThe state for a given session is stored separately in `dhcp_smach_t'.
172d04ccbb3ScarlsonjThis state machine then points to the main LIF used for I/O, and to a
173d04ccbb3Scarlsonjlist of `dhcp_lease_t' structures representing individual leases, and
174d04ccbb3Scarlsonjeach of those points to a list of LIFs corresponding to the individual
175d04ccbb3Scarlsonjaddresses being managed.
1767c478bd9Sstevel@tonic-gate
1777c478bd9Sstevel@tonic-gateOne point that was brushed over in the preceding discussion of event
1787c478bd9Sstevel@tonic-gatehandlers and timer queues was context.  Recall that the event-driven
1797c478bd9Sstevel@tonic-gatenature of the agent requires that functions cannot block, lest they
1807c478bd9Sstevel@tonic-gatestarve out others and impact the observed responsiveness of the agent.
1817c478bd9Sstevel@tonic-gateAs an example, consider the process of extending a lease: the agent
1827c478bd9Sstevel@tonic-gatemust send a REQUEST packet and wait for an ACK or NAK packet in
183d04ccbb3Scarlsonjresponse.  This is done by sending a REQUEST and then returning to the
184d04ccbb3Scarlsonjevent handler that waits for an ACK or NAK packet to arrive on the
185d04ccbb3Scarlsonjfile descriptor associated with the interface.  Note however, that
186d04ccbb3Scarlsonjwhen the ACK or NAK does arrive, and the callback function called
187d04ccbb3Scarlsonjback, it must know which state machine this packet is for (it must get
188d04ccbb3Scarlsonjback its context).  This could be handled through an ad-hoc mapping of
189d04ccbb3Scarlsonjfile descriptors to state machines, but a cleaner approach is to have
190d04ccbb3Scarlsonjthe event handler's register function (iu_register_event()) take in an
191d04ccbb3Scarlsonjopaque context pointer, which will then be passed back to the
192d04ccbb3Scarlsonjcallback.  In the agent, the context pointer used depends on the
193d04ccbb3Scarlsonjnature of the event: events on LIFs use the dhcp_lif_t pointer, events
194d04ccbb3Scarlsonjon the state machine use dhcp_smach_t, and so on.
1957c478bd9Sstevel@tonic-gate
1967c478bd9Sstevel@tonic-gateNote that there is nothing that guarantees the pointer passed into
1977c478bd9Sstevel@tonic-gateiu_register_event() or iu_schedule_timer() will still be valid when
1987c478bd9Sstevel@tonic-gatethe callback is called back (for instance, the memory may have been
199d04ccbb3Scarlsonjfreed in the meantime).  To solve this problem, all of the data
200d04ccbb3Scarlsonjstructures used in this way are reference counted.  For more details
201d04ccbb3Scarlsonjon how the reference count scheme is implemented, see the closing
202d04ccbb3Scarlsonjcomments in interface.h regarding memory management.
2037c478bd9Sstevel@tonic-gate
2047c478bd9Sstevel@tonic-gateTransactions
2057c478bd9Sstevel@tonic-gate------------
2067c478bd9Sstevel@tonic-gate
2077c478bd9Sstevel@tonic-gateMany operations performed via DHCP must be performed in groups -- for
2087c478bd9Sstevel@tonic-gateinstance, acquiring a lease requires several steps: sending a
2097c478bd9Sstevel@tonic-gateDISCOVER, collecting OFFERs, selecting an OFFER, sending a REQUEST,
2107c478bd9Sstevel@tonic-gateand receiving an ACK, assuming everything goes well.  Note however
2117c478bd9Sstevel@tonic-gatethat due to the event-driven model the agent operates in, these
2127c478bd9Sstevel@tonic-gateoperations are not inherently "grouped" -- instead, the agent sends a
2137c478bd9Sstevel@tonic-gateDISCOVER, goes back into the main event loop, waits for events
2147c478bd9Sstevel@tonic-gate(perhaps even requests on the IPC channel to begin acquiring a lease
215d04ccbb3Scarlsonjon another state machine), eventually checks to see if an acceptable
216d04ccbb3ScarlsonjOFFER has come in, and so forth.  To some degree, the notion of the
217d04ccbb3Scarlsonjstate machine's current state (SELECTING, REQUESTING, etc) helps
218d04ccbb3Scarlsonjcontrol the potential chaos of the event-driven model (for instance,
219d04ccbb3Scarlsonjif while the agent is waiting for an OFFER on a given state machine,
220d04ccbb3Scarlsonjan IPC event comes in requesting that the leases be RELEASED, the
221d04ccbb3Scarlsonjagent knows to send back an error since the state machine must be in
222d04ccbb3Scarlsonjat least the BOUND state before a RELEASE can be performed.)
2237c478bd9Sstevel@tonic-gate
2247c478bd9Sstevel@tonic-gateHowever, states are not enough -- for instance, suppose that the agent
225d04ccbb3Scarlsonjbegins trying to renew a lease.  This is done by sending a REQUEST
2267c478bd9Sstevel@tonic-gatepacket and waiting for an ACK or NAK, which might never come.  If,
2277c478bd9Sstevel@tonic-gatewhile waiting for the ACK or NAK, the user sends a request to renew
2287c478bd9Sstevel@tonic-gatethe lease as well, then if the agent were to send another REQUEST,
2297c478bd9Sstevel@tonic-gatethings could get quite complicated (and this is only the beginning of
2307c478bd9Sstevel@tonic-gatethis rathole).  To protect against this, two objects exist:
2317c478bd9Sstevel@tonic-gate`async_action' and `ipc_action'.  These objects are related, but
2327c478bd9Sstevel@tonic-gateindependent of one another; the more essential object is the
2337c478bd9Sstevel@tonic-gate`async_action', which we will discuss first.
2347c478bd9Sstevel@tonic-gate
2357c478bd9Sstevel@tonic-gateIn short, an `async_action' represents a pending transaction (aka
236d04ccbb3Scarlsonjasynchronous action), of which each state machine can have at most
237d04ccbb3Scarlsonjone.  The `async_action' structure is embedded in the `dhcp_smach_t'
238d04ccbb3Scarlsonjstructure, which is fine since there can be at most one pending
239d04ccbb3Scarlsonjtransaction per state machine.  Typical "asynchronous transactions"
240d04ccbb3Scarlsonjare START, EXTEND, and INFORM, since each consists of a sequence of
241d04ccbb3Scarlsonjpackets that must be done without interruption.  Note that not all
242d04ccbb3ScarlsonjDHCP operations are "asynchronous" -- for instance, a DHCPv4 RELEASE
243d04ccbb3Scarlsonjoperation is synchronous (not asynchronous) since after the RELEASE is
244d04ccbb3Scarlsonjsent no reply is expected from the DHCP server, but DHCPv6 Release is
245d04ccbb3Scarlsonjasynchronous, as all DHCPv6 messages are transactional.  Some
246d04ccbb3Scarlsonjoperations, such as status query, are synchronous and do not affect
247d04ccbb3Scarlsonjthe system state, and thus do not require sequencing.
2487c478bd9Sstevel@tonic-gate
2497c478bd9Sstevel@tonic-gateWhen the agent realizes it must perform an asynchronous transaction,
250d04ccbb3Scarlsonjit calls async_async() to open the transaction.  If one is already
251d04ccbb3Scarlsonjpending, then the new transaction must fail (the details of failure
252d04ccbb3Scarlsonjdepend on how the transaction was initiated, which is described in
253d04ccbb3Scarlsonjmore detail later when the `ipc_action' object is discussed).  If
254d04ccbb3Scarlsonjthere is no pending asynchronous transaction, the operation succeeds.
2557c478bd9Sstevel@tonic-gate
256d04ccbb3ScarlsonjWhen the transaction is complete, either async_finish() or
257d04ccbb3Scarlsonjasync_cancel() must be called to complete or cancel the asynchronous
258d04ccbb3Scarlsonjaction on that state machine.  If the transaction is unable to
259d04ccbb3Scarlsonjcomplete within a certain amount of time (more on this later), a timer
260d04ccbb3Scarlsonjshould be used to cancel the operation.
2617c478bd9Sstevel@tonic-gate
2627c478bd9Sstevel@tonic-gateThe notion of asynchronous transactions is complicated by the fact
2637c478bd9Sstevel@tonic-gatethat they may originate from both inside and outside of the agent.
2647c478bd9Sstevel@tonic-gateFor instance, a user initiates an asynchronous START transaction when
2657c478bd9Sstevel@tonic-gatehe performs an `ifconfig hme0 dhcp start', but the agent will
2667c478bd9Sstevel@tonic-gateinternally need to perform asynchronous EXTEND transactions to extend
267d04ccbb3Scarlsonjthe lease before it expires.  Note that user-initiated actions always
268d04ccbb3Scarlsonjhave priority over internal actions: the former will cancel the
269d04ccbb3Scarlsonjlatter, if necessary.
2707c478bd9Sstevel@tonic-gate
271d04ccbb3ScarlsonjThis leads us into the `ipc_action' object.  An `ipc_action'
272d04ccbb3Scarlsonjrepresents the IPC-related pieces of an asynchronous transaction that
273d04ccbb3Scarlsonjwas started as a result of a user request, as well as the `BUSY' state
274d04ccbb3Scarlsonjof the administrative interface.  Only IPC-generated asynchronous
275d04ccbb3Scarlsonjtransactions have a valid `ipc_action' object.  Note that since there
276d04ccbb3Scarlsonjcan be at most one asynchronous action per state machine, there can
277d04ccbb3Scarlsonjalso be at most one `ipc_action' per state machine (this means it can
278d04ccbb3Scarlsonjalso conveniently be embedded inside the `dhcp_smach_t' structure).
2797c478bd9Sstevel@tonic-gate
2807c478bd9Sstevel@tonic-gateOne of the main purposes of the `ipc_action' object is to timeout user
281d04ccbb3Scarlsonjevents.  When the user specifies a timeout value as an argument to
2827c478bd9Sstevel@tonic-gateifconfig, he is specifying an `ipc_action' timeout; in other words,
283d04ccbb3Scarlsonjhow long he is willing to wait for the command to complete.  When this
284d04ccbb3Scarlsonjtime expires, the ipc_action is terminated, as well as the
285d04ccbb3Scarlsonjasynchronous operation.
2867c478bd9Sstevel@tonic-gate
2877c478bd9Sstevel@tonic-gateThe API provided for the `ipc_action' object is quite similar to the
2887c478bd9Sstevel@tonic-gateone for the `async_action' object: when an IPC request comes in for an
2897c478bd9Sstevel@tonic-gateoperation requiring asynchronous operation, ipc_action_start() is
2907c478bd9Sstevel@tonic-gatecalled.  When the request completes, ipc_action_finish() is called.
2917c478bd9Sstevel@tonic-gateIf the user times out before the request completes, then
2927c478bd9Sstevel@tonic-gateipc_action_timeout() is called.
2937c478bd9Sstevel@tonic-gate
2947c478bd9Sstevel@tonic-gatePacket Management
2957c478bd9Sstevel@tonic-gate-----------------
2967c478bd9Sstevel@tonic-gate
2977c478bd9Sstevel@tonic-gateAnother complicated area is packet management: building, manipulating,
2987c478bd9Sstevel@tonic-gatesending and receiving packets.  These operations are all encapsulated
2997c478bd9Sstevel@tonic-gatebehind a dozen or so interfaces (see packet.h) that abstract the
3007c478bd9Sstevel@tonic-gateunimportant details away from the rest of the agent code.  In order to
3017c478bd9Sstevel@tonic-gatesend a DHCP packet, code first calls init_pkt(), which returns a
3027c478bd9Sstevel@tonic-gatedhcp_pkt_t initialized suitably for transmission.  Note that currently
3037c478bd9Sstevel@tonic-gateinit_pkt() returns a dhcp_pkt_t that is actually allocated as part of
304d04ccbb3Scarlsonjthe `dhcp_smach_t', but this may change in the future..  After calling
3057c478bd9Sstevel@tonic-gateinit_pkt(), the add_pkt_opt*() functions are used to add options to
306d04ccbb3Scarlsonjthe DHCP packet.  Finally, send_pkt() and send_pkt_v6() can be used to
307d04ccbb3Scarlsonjtransmit the packet to a given IP address.
3087c478bd9Sstevel@tonic-gate
309*e704a8f2SmeemThe send_pkt() function handles the details of packet timeout and
3107c478bd9Sstevel@tonic-gateretransmission.  The last argument to send_pkt() is a pointer to a
311d04ccbb3Scarlsonj"stop function."  If this argument is passed as NULL, then the packet
3127c478bd9Sstevel@tonic-gatewill only be sent once (it won't be retransmitted).  Otherwise, before
3137c478bd9Sstevel@tonic-gateeach retransmission, the stop function will be called back prior to
314d04ccbb3Scarlsonjretransmission.  The callback may alter dsm_send_timeout if necessary
315d04ccbb3Scarlsonjto place a cap on the next timeout; this is done for DHCPv6 in
316d04ccbb3Scarlsonjstop_init_reboot() in order to implement the CNF_MAX_RD constraint.
317d04ccbb3Scarlsonj
318d04ccbb3ScarlsonjThe return value from this function indicates whether to continue
319d04ccbb3Scarlsonjretransmission or not, which allows the send_pkt() caller to control
320d04ccbb3Scarlsonjthe retransmission policy without making it have to deal with the
321d04ccbb3Scarlsonjretransmission mechanism.  See request.c for an example of this in
322d04ccbb3Scarlsonjaction.
3237c478bd9Sstevel@tonic-gate
3247c478bd9Sstevel@tonic-gateThe recv_pkt() function is simpler but still complicated by the fact
3257c478bd9Sstevel@tonic-gatethat one may want to receive several different types of packets at
326*e704a8f2Smeemonce.  The caller registers an event handler on the file descriptor,
327*e704a8f2Smeemand then calls recv_pkt() to read in the packet along with meta
328*e704a8f2Smeeminformation about the message (the sender and interface identifier).
329d04ccbb3Scarlsonj
330d04ccbb3ScarlsonjFor IPv6, packet reception is done with a single socket, using
331d04ccbb3ScarlsonjIPV6_PKTINFO to determine the actual destination address and receiving
332d04ccbb3Scarlsonjinterface.  Packets are then matched against the state machines on the
333d04ccbb3Scarlsonjgiven interface through the transaction ID.
334d04ccbb3Scarlsonj
335*e704a8f2SmeemFor IPv4, due to oddities in the DHCP specification (discussed in
336*e704a8f2SmeemPSARC/2007/571), a special IP_DHCPINIT_IF socket option must be used
337*e704a8f2Smeemto allow unicast DHCP traffic to be received on an interface during
338*e704a8f2Smeemlease acquisition.  Since the IP_DHCPINIT_IF socket option can only
339*e704a8f2Smeemenable one interface at a time, one socket must be used per interface.
3407c478bd9Sstevel@tonic-gate
3417c478bd9Sstevel@tonic-gateTime
3427c478bd9Sstevel@tonic-gate----
3437c478bd9Sstevel@tonic-gate
3447c478bd9Sstevel@tonic-gateThe notion of time is an exceptionally subtle area.  You will notice
3457c478bd9Sstevel@tonic-gatefive ways that time is represented in the source: as lease_t's,
3467c478bd9Sstevel@tonic-gateuint32_t's, time_t's, hrtime_t's, and monosec_t's.  Each of these
3477c478bd9Sstevel@tonic-gatetypes serves a slightly different function.
3487c478bd9Sstevel@tonic-gate
3497c478bd9Sstevel@tonic-gateThe `lease_t' type is the simplest to understand; it is the unit of
3507c478bd9Sstevel@tonic-gatetime in the CD_{LEASE,T1,T2}_TIME options in a DHCP packet, as defined
3517c478bd9Sstevel@tonic-gateby RFC2131. This is defined as a positive number of seconds (relative
3527c478bd9Sstevel@tonic-gateto some fixed point in time) or the value `-1' (DHCP_PERM) which
3537c478bd9Sstevel@tonic-gaterepresents infinity (i.e., a permanent lease).  The lease_t should be
3547c478bd9Sstevel@tonic-gateused either when dealing with actual DHCP packets that are sent on the
3557c478bd9Sstevel@tonic-gatewire or for variables which follow the exact definition given in the
3567c478bd9Sstevel@tonic-gateRFC.
3577c478bd9Sstevel@tonic-gate
3587c478bd9Sstevel@tonic-gateThe `uint32_t' type is also used to represent a relative time in
3597c478bd9Sstevel@tonic-gateseconds.  However, here the value `-1' is not special and of course
3607c478bd9Sstevel@tonic-gatethis type is not tied to any definition given in RFC2131.  Use this
3617c478bd9Sstevel@tonic-gatefor representing "offsets" from another point in time that are not
3627c478bd9Sstevel@tonic-gateDHCP lease times.
3637c478bd9Sstevel@tonic-gate
3647c478bd9Sstevel@tonic-gateThe `time_t' type is the natural Unix type for representing time since
3657c478bd9Sstevel@tonic-gatethe epoch.  Unfortunately, it is affected by stime(2) or adjtime(2)
3667c478bd9Sstevel@tonic-gateand since the DHCP client is used during system installation (and thus
3677c478bd9Sstevel@tonic-gatewhen time is typically being configured), the time_t cannot be used in
3687c478bd9Sstevel@tonic-gategeneral to represent an absolute time since the epoch.  For instance,
3697c478bd9Sstevel@tonic-gateif a time_t were used to keep track of when a lease began, and then a
3707c478bd9Sstevel@tonic-gateminute later stime(2) was called to adjust the system clock forward a
3717c478bd9Sstevel@tonic-gateyear, then the lease would appeared to have expired a year ago even
3727c478bd9Sstevel@tonic-gatethough it has only been a minute.  For this reason, time_t's should
3737c478bd9Sstevel@tonic-gateonly be used either when wall time must be displayed (such as in
3747c478bd9Sstevel@tonic-gateDHCP_STATUS ipc transaction) or when a time meaningful across reboots
3757c478bd9Sstevel@tonic-gatemust be obtained (such as when caching an ACK packet at system
3767c478bd9Sstevel@tonic-gateshutdown).
3777c478bd9Sstevel@tonic-gate
3787c478bd9Sstevel@tonic-gateThe `hrtime_t' type returned from gethrtime() works around the
3797c478bd9Sstevel@tonic-gatelimitations of the time_t in that it is not affected by stime(2) or
3807c478bd9Sstevel@tonic-gateadjtime(2), with the disadvantage that it represents time from some
3817c478bd9Sstevel@tonic-gatearbitrary time in the past and in nanoseconds.  The timer queue code
3827c478bd9Sstevel@tonic-gatedeals with hrtime_t's directly since that particular piece of code is
3837c478bd9Sstevel@tonic-gatemeant to be fairly independent of the rest of the DHCP client.
3847c478bd9Sstevel@tonic-gate
3857c478bd9Sstevel@tonic-gateHowever, dealing with nanoseconds is error-prone when all the other
3867c478bd9Sstevel@tonic-gatetime types are in seconds.  As a result, yet another time type, the
3877c478bd9Sstevel@tonic-gate`monosec_t' was created to represent a monotonically increasing time
3887c478bd9Sstevel@tonic-gatein seconds, and is really no more than (hrtime_t / NANOSEC).  Note
3897c478bd9Sstevel@tonic-gatethat this unit is typically used where time_t's would've traditionally
3907c478bd9Sstevel@tonic-gatebeen used.  The function monosec() in util.c returns the current
3917c478bd9Sstevel@tonic-gatemonosec, and monosec_to_time() can convert a given monosec to wall
3927c478bd9Sstevel@tonic-gatetime, using the system's current notion of time.
3937c478bd9Sstevel@tonic-gate
3947c478bd9Sstevel@tonic-gateOne additional limitation of the `hrtime_t' and `monosec_t' types is
3957c478bd9Sstevel@tonic-gatethat they are unaware of the passage of time across checkpoint/resume
3967c478bd9Sstevel@tonic-gateevents (e.g., those generated by sys-suspend(1M)).  For example, if
3977c478bd9Sstevel@tonic-gategethrtime() returns time T, and then the machine is suspended for 2
3987c478bd9Sstevel@tonic-gatehours, and then gethrtime() is called again, the time returned is not
3997c478bd9Sstevel@tonic-gateT + (2 * 60 * 60 * NANOSEC), but rather approximately still T.
4007c478bd9Sstevel@tonic-gate
4017c478bd9Sstevel@tonic-gateTo work around this (and other checkpoint/resume related problems),
4027c478bd9Sstevel@tonic-gatewhen a system is resumed, the DHCP client makes the pessimistic
4037c478bd9Sstevel@tonic-gateassumption that all finite leases have expired while the machine was
4047c478bd9Sstevel@tonic-gatesuspended and must be obtained again.  This is known as "refreshing"
405d04ccbb3Scarlsonjthe leases, and is handled by refresh_smachs().
4067c478bd9Sstevel@tonic-gate
4077c478bd9Sstevel@tonic-gateNote that it appears like a more intelligent approach would be to
4087c478bd9Sstevel@tonic-gaterecord the time(2) when the system is suspended, compare that against
4097c478bd9Sstevel@tonic-gatethe time(2) when the system is resumed, and use the delta between them
4107c478bd9Sstevel@tonic-gateto decide which leases have expired.  Sadly, this cannot be done since
411d04ccbb3Scarlsonjthrough at least Solaris 10, it is not possible for userland programs
4127c478bd9Sstevel@tonic-gateto be notified of system suspend events.
4137c478bd9Sstevel@tonic-gate
4147c478bd9Sstevel@tonic-gateConfiguration
4157c478bd9Sstevel@tonic-gate-------------
4167c478bd9Sstevel@tonic-gate
4177c478bd9Sstevel@tonic-gateFor the most part, the DHCP client only *retrieves* configuration data
4187c478bd9Sstevel@tonic-gatefrom the DHCP server, leaving the configuration to scripts (such as
4197c478bd9Sstevel@tonic-gateboot scripts), which themselves use dhcpinfo(1) to retrieve the data
4207c478bd9Sstevel@tonic-gatefrom the DHCP client.  This is desirable because it keeps the mechanism
4217c478bd9Sstevel@tonic-gateof retrieving the configuration data decoupled from the policy of using
4227c478bd9Sstevel@tonic-gatethe data.
4237c478bd9Sstevel@tonic-gate
424d04ccbb3ScarlsonjHowever, unless used in "inform" mode, the DHCP client *does*
425d04ccbb3Scarlsonjconfigure each IP interface enough to allow it to communicate with
426d04ccbb3Scarlsonjother hosts.  Specifically, the DHCP client configures the interface's
427d04ccbb3ScarlsonjIP address, netmask, and broadcast address using the information
428d04ccbb3Scarlsonjprovided by the server.  Further, for IPv4 logical interface 0
429d04ccbb3Scarlsonj("hme0"), any provided default routes are also configured.
430d04ccbb3Scarlsonj
431d04ccbb3ScarlsonjFor IPv6, only the IP addresses are set.  The netmask (prefix) is then
432d04ccbb3Scarlsonjset automatically by in.ndpd, and routes are discovered in the usual
433d04ccbb3Scarlsonjway by router discovery or routing protocols.  DHCPv6 doesn't set
434d04ccbb3Scarlsonjroutes.
435d04ccbb3Scarlsonj
436d04ccbb3ScarlsonjSince logical interfaces cannot be specified as output interfaces in
437d04ccbb3Scarlsonjthe kernel forwarding table, and in most cases, logical interfaces
438d04ccbb3Scarlsonjshare a default route with their associated physical interface, the
439d04ccbb3ScarlsonjDHCP client does not automatically add or remove default routes when
440d04ccbb3ScarlsonjIPv4 leases are acquired or expired on logical interfaces.
4417c478bd9Sstevel@tonic-gate
4427c478bd9Sstevel@tonic-gateEvent Scripting
4437c478bd9Sstevel@tonic-gate---------------
4447c478bd9Sstevel@tonic-gate
4457c478bd9Sstevel@tonic-gateThe DHCP client supports user program invocations on DHCP events.  The
446d04ccbb3Scarlsonjsupported events are BOUND, EXTEND, EXPIRE, DROP, RELEASE, and INFORM
447d04ccbb3Scarlsonjfor DHCPv4, and BUILD6, EXTEND6, EXPIRE6, DROP6, LOSS6, RELEASE6, and
448d04ccbb3ScarlsonjINFORM6 for DHCPv6.  The user program runs asynchronous to the DHCP
449d04ccbb3Scarlsonjclient so that the main event loop stays active to process other
450d04ccbb3Scarlsonjevents, including events triggered by the user program (for example,
451d04ccbb3Scarlsonjwhen it invokes dhcpinfo).
4527c478bd9Sstevel@tonic-gate
4537c478bd9Sstevel@tonic-gateThe user program execution is part of the transaction of a DHCP command.
4547c478bd9Sstevel@tonic-gateFor example, if the user program is not enabled, the transaction of the
4557c478bd9Sstevel@tonic-gateDHCP command START is considered over when an ACK is received and the
4567c478bd9Sstevel@tonic-gateinterface is configured successfully.  If the user program is enabled,
4577c478bd9Sstevel@tonic-gateit is invoked after the interface is configured successfully, and the
4587c478bd9Sstevel@tonic-gatetransaction is considered over only when the user program exits.  The
4597c478bd9Sstevel@tonic-gateevent scripting implementation makes use of the asynchronous operations
4607c478bd9Sstevel@tonic-gatediscussed in the "Transactions" section.
4617c478bd9Sstevel@tonic-gate
462d04ccbb3ScarlsonjAn upper bound of 58 seconds is imposed on how long the user program
4637c478bd9Sstevel@tonic-gatecan run. If the user program does not exit after 55 seconds, the signal
4647c478bd9Sstevel@tonic-gateSIGTERM is sent to it. If it still does not exit after additional 3
4657c478bd9Sstevel@tonic-gateseconds, the signal SIGKILL is sent to it.  Since the event handler is
4667c478bd9Sstevel@tonic-gatea wrapper around poll(), the DHCP client cannot directly observe the
4677c478bd9Sstevel@tonic-gatecompletion of the user program.  Instead, the DHCP client creates a
4687c478bd9Sstevel@tonic-gatechild "helper" process to synchronously monitor the user program (this
4697c478bd9Sstevel@tonic-gateprocess is also used to send the aformentioned signals to the process,
4707c478bd9Sstevel@tonic-gateif necessary).  The DHCP client and the helper process share a pipe
4717c478bd9Sstevel@tonic-gatewhich is included in the set of poll descriptors monitored by the DHCP
4727c478bd9Sstevel@tonic-gateclient's event handler.  When the user program exits, the helper process
4737c478bd9Sstevel@tonic-gatepasses the user program exit status to the DHCP client through the pipe,
4747c478bd9Sstevel@tonic-gateinforming the DHCP client that the user program has finished.  When the
4757c478bd9Sstevel@tonic-gateDHCP client is asked to shut down, it will wait for any running instances
4767c478bd9Sstevel@tonic-gateof the user program to complete.
477