xref: /illumos-gate/usr/src/cmd/cmd-inet/sbin/dhcpagent/README (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gateCDDL HEADER START
2*7c478bd9Sstevel@tonic-gate
3*7c478bd9Sstevel@tonic-gateThe contents of this file are subject to the terms of the
4*7c478bd9Sstevel@tonic-gateCommon Development and Distribution License, Version 1.0 only
5*7c478bd9Sstevel@tonic-gate(the "License").  You may not use this file except in compliance
6*7c478bd9Sstevel@tonic-gatewith the License.
7*7c478bd9Sstevel@tonic-gate
8*7c478bd9Sstevel@tonic-gateYou can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*7c478bd9Sstevel@tonic-gateor http://www.opensolaris.org/os/licensing.
10*7c478bd9Sstevel@tonic-gateSee the License for the specific language governing permissions
11*7c478bd9Sstevel@tonic-gateand limitations under the License.
12*7c478bd9Sstevel@tonic-gate
13*7c478bd9Sstevel@tonic-gateWhen distributing Covered Code, include this CDDL HEADER in each
14*7c478bd9Sstevel@tonic-gatefile and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*7c478bd9Sstevel@tonic-gateIf applicable, add the following below this CDDL HEADER, with the
16*7c478bd9Sstevel@tonic-gatefields enclosed by brackets "[]" replaced with your own identifying
17*7c478bd9Sstevel@tonic-gateinformation: Portions Copyright [yyyy] [name of copyright owner]
18*7c478bd9Sstevel@tonic-gate
19*7c478bd9Sstevel@tonic-gateCDDL HEADER END
20*7c478bd9Sstevel@tonic-gate
21*7c478bd9Sstevel@tonic-gateCopyright 2004 Sun Microsystems, Inc.  All rights reserved.
22*7c478bd9Sstevel@tonic-gateUse is subject to license terms.
23*7c478bd9Sstevel@tonic-gate
24*7c478bd9Sstevel@tonic-gateArchitectural Overview for the DHCP agent
25*7c478bd9Sstevel@tonic-gatePeter Memishian
26*7c478bd9Sstevel@tonic-gateident	"%Z%%M%	%I%	%E% SMI"
27*7c478bd9Sstevel@tonic-gate
28*7c478bd9Sstevel@tonic-gateINTRODUCTION
29*7c478bd9Sstevel@tonic-gate============
30*7c478bd9Sstevel@tonic-gate
31*7c478bd9Sstevel@tonic-gateThe Solaris DHCP agent (dhcpagent) is an RFC2131-compliant DHCP client
32*7c478bd9Sstevel@tonic-gateimplementation.  The major forces shaping its design were:
33*7c478bd9Sstevel@tonic-gate
34*7c478bd9Sstevel@tonic-gate	* Must be capable of managing multiple network interfaces.
35*7c478bd9Sstevel@tonic-gate	* Must consume little CPU, since it will always be running.
36*7c478bd9Sstevel@tonic-gate	* Must have a small memory footprint, since it will always be
37*7c478bd9Sstevel@tonic-gate	  running.
38*7c478bd9Sstevel@tonic-gate	* Must not rely on any shared libraries, since it must run
39*7c478bd9Sstevel@tonic-gate	  before all filesystems have been mounted.
40*7c478bd9Sstevel@tonic-gate
41*7c478bd9Sstevel@tonic-gateWhen a DHCP agent implementation is only required to control a single
42*7c478bd9Sstevel@tonic-gateinterface on a machine, the problem is expressed well as a simple
43*7c478bd9Sstevel@tonic-gatestate-machine, as shown in RFC2131.  However, when a DHCP agent is
44*7c478bd9Sstevel@tonic-gateresponsible for managing more than one interface at a time, the
45*7c478bd9Sstevel@tonic-gateproblem becomes much more complicated, especially when threads cannot
46*7c478bd9Sstevel@tonic-gatebe used to attack the problem (since the filesystems containing the
47*7c478bd9Sstevel@tonic-gatethread libraries may not be available when the agent starts).
48*7c478bd9Sstevel@tonic-gateInstead, the problem must be solved using an event-driven model, which
49*7c478bd9Sstevel@tonic-gatewhile tried-and-true, is subtle and easy to get wrong.  Indeed, much
50*7c478bd9Sstevel@tonic-gateof the agent's code is there to manage the complexity of programming
51*7c478bd9Sstevel@tonic-gatein an asynchronous event-driven paradigm.
52*7c478bd9Sstevel@tonic-gate
53*7c478bd9Sstevel@tonic-gateTHE BASICS
54*7c478bd9Sstevel@tonic-gate==========
55*7c478bd9Sstevel@tonic-gate
56*7c478bd9Sstevel@tonic-gateThe DHCP agent consists of roughly 20 source files, most with a
57*7c478bd9Sstevel@tonic-gatecompanion header file.  While the largest source file is around 700
58*7c478bd9Sstevel@tonic-gatelines, most are much shorter.  The source files can largely be broken
59*7c478bd9Sstevel@tonic-gateup into three groups:
60*7c478bd9Sstevel@tonic-gate
61*7c478bd9Sstevel@tonic-gate	* Source files, which along with their companion header files,
62*7c478bd9Sstevel@tonic-gate	  define an abstract "object" that is used by other parts of
63*7c478bd9Sstevel@tonic-gate	  the system.  Examples include "timer_queue.c", which along
64*7c478bd9Sstevel@tonic-gate	  with "timer_queue.h" provide a Timer Queue object for use
65*7c478bd9Sstevel@tonic-gate	  by the rest of the agent, and "async.c", which along with
66*7c478bd9Sstevel@tonic-gate	  "async.h" defines an interface for managing asynchronous
67*7c478bd9Sstevel@tonic-gate	  transactions within the agent.
68*7c478bd9Sstevel@tonic-gate
69*7c478bd9Sstevel@tonic-gate	* Source files which implement a given state of the agent; for
70*7c478bd9Sstevel@tonic-gate	  instance, there is a "request.c" which comprises all of
71*7c478bd9Sstevel@tonic-gate	  the procedural "work" which must be done while in the
72*7c478bd9Sstevel@tonic-gate	  REQUESTING state of the agent.  By encapsulating states in
73*7c478bd9Sstevel@tonic-gate	  files, it becomes easier to debug errors in the
74*7c478bd9Sstevel@tonic-gate	  client/server protocol and adapt the agent to new
75*7c478bd9Sstevel@tonic-gate	  constraints, since all the relevant code is in one place.
76*7c478bd9Sstevel@tonic-gate
77*7c478bd9Sstevel@tonic-gate	* Source files, which along with their companion header files,
78*7c478bd9Sstevel@tonic-gate  	  encapsulate a given task or related set of tasks.  The
79*7c478bd9Sstevel@tonic-gate	  difference between this and the first group is that the
80*7c478bd9Sstevel@tonic-gate	  interfaces exported from these files do not operate on
81*7c478bd9Sstevel@tonic-gate	  an "object", but rather perform a specific task.  Examples
82*7c478bd9Sstevel@tonic-gate	  include "dlpi_io.c", which provides a useful interface
83*7c478bd9Sstevel@tonic-gate	  to DLPI-related i/o operations.
84*7c478bd9Sstevel@tonic-gate
85*7c478bd9Sstevel@tonic-gateOVERVIEW
86*7c478bd9Sstevel@tonic-gate========
87*7c478bd9Sstevel@tonic-gate
88*7c478bd9Sstevel@tonic-gateHere we discuss the essential objects and subtle aspects of the
89*7c478bd9Sstevel@tonic-gateDHCP agent implementation.  Note that there is of course much more
90*7c478bd9Sstevel@tonic-gatethat is not discussed here, but after this overview you should be able
91*7c478bd9Sstevel@tonic-gateto fend for yourself in the source code.
92*7c478bd9Sstevel@tonic-gate
93*7c478bd9Sstevel@tonic-gateEvent Handlers and Timer Queues
94*7c478bd9Sstevel@tonic-gate-------------------------------
95*7c478bd9Sstevel@tonic-gate
96*7c478bd9Sstevel@tonic-gateThe most important object in the agent is the event handler, whose
97*7c478bd9Sstevel@tonic-gateinterface is in libinetutil.h and whose implementation is in
98*7c478bd9Sstevel@tonic-gatelibinetutil.  The event handler is essentially an object-oriented
99*7c478bd9Sstevel@tonic-gatewrapper around poll(2): other components of the agent can register to
100*7c478bd9Sstevel@tonic-gatebe called back when specific events on file descriptors happen -- for
101*7c478bd9Sstevel@tonic-gateinstance, to wait for requests to arrive on its IPC socket, the agent
102*7c478bd9Sstevel@tonic-gateregisters a callback function (accept_event()) that will be called
103*7c478bd9Sstevel@tonic-gateback whenever a new connection arrives on the file descriptor
104*7c478bd9Sstevel@tonic-gateassociated with the IPC socket.  When the agent initially begins in
105*7c478bd9Sstevel@tonic-gatemain(), it registers a number of events with the event handler, and
106*7c478bd9Sstevel@tonic-gatethen calls iu_handle_events(), which proceeds to wait for events to
107*7c478bd9Sstevel@tonic-gatehappen -- this function does not return until the agent is shutdown
108*7c478bd9Sstevel@tonic-gatevia signal.
109*7c478bd9Sstevel@tonic-gate
110*7c478bd9Sstevel@tonic-gateWhen the registered events occur, the callback functions are called
111*7c478bd9Sstevel@tonic-gateback, which in turn might lead to additional callbacks being
112*7c478bd9Sstevel@tonic-gateregistered -- this is the classic event-driven model.  (As an aside,
113*7c478bd9Sstevel@tonic-gatenote that programming in an event-driven model means that callbacks
114*7c478bd9Sstevel@tonic-gatecannot block, or else the agent will become unresponsive.)
115*7c478bd9Sstevel@tonic-gate
116*7c478bd9Sstevel@tonic-gateA special kind of "event" is a timeout.  Since there are many timers
117*7c478bd9Sstevel@tonic-gatewhich must be maintained for each DHCP-controlled interface (such as a
118*7c478bd9Sstevel@tonic-gatelease expiration timer, time-to-first-renewal (t1) timer, and so
119*7c478bd9Sstevel@tonic-gateforth), an object-oriented abstraction to timers called a "timer
120*7c478bd9Sstevel@tonic-gatequeue" is provided, whose interface is in libinetutil.h with a
121*7c478bd9Sstevel@tonic-gatecorresponding implementation in libinetutil.  The timer queue allows
122*7c478bd9Sstevel@tonic-gatecallback functions to be "scheduled" for callback after a certain
123*7c478bd9Sstevel@tonic-gateamount of time has passed.
124*7c478bd9Sstevel@tonic-gate
125*7c478bd9Sstevel@tonic-gateThe event handler and timer queue objects work hand-in-hand: the event
126*7c478bd9Sstevel@tonic-gatehandler is passed a pointer to a timer queue in iu_handle_events() --
127*7c478bd9Sstevel@tonic-gatefrom there, it can use the iu_earliest_timer() routine to find the
128*7c478bd9Sstevel@tonic-gatetimer which will next fire, and use this to set its timeout value in
129*7c478bd9Sstevel@tonic-gateits call to poll(2).  If poll(2) returns due to a timeout, the event
130*7c478bd9Sstevel@tonic-gatehandler calls iu_expire_timers() to expire all timers that expired
131*7c478bd9Sstevel@tonic-gate(note that more than one may have expired if, for example, multiple
132*7c478bd9Sstevel@tonic-gatetimers were set to expire at the same time).
133*7c478bd9Sstevel@tonic-gate
134*7c478bd9Sstevel@tonic-gateAlthough it is possible to instantiate more than one timer queue or
135*7c478bd9Sstevel@tonic-gateevent handler object, it doesn't make a lot of sense -- these objects
136*7c478bd9Sstevel@tonic-gateare really "singletons".  Accordingly, the agent has two global
137*7c478bd9Sstevel@tonic-gatevariables, `eh' and `tq', which store pointers to the global event
138*7c478bd9Sstevel@tonic-gatehandler and timer queue.
139*7c478bd9Sstevel@tonic-gate
140*7c478bd9Sstevel@tonic-gateNetwork Interfaces
141*7c478bd9Sstevel@tonic-gate------------------
142*7c478bd9Sstevel@tonic-gate
143*7c478bd9Sstevel@tonic-gateFor each network interface managed by the agent, there is a set of
144*7c478bd9Sstevel@tonic-gateassociated state that describes both its general properties (such as
145*7c478bd9Sstevel@tonic-gatethe maximum MTU) and its DHCP-related state (such as when it acquired
146*7c478bd9Sstevel@tonic-gatea lease).  This state is stored in a a structure called an `ifslist',
147*7c478bd9Sstevel@tonic-gatewhich is a poor name (since it suggests implementation artifacts but
148*7c478bd9Sstevel@tonic-gatenot purpose) but has historical precedent.  Another way to think about
149*7c478bd9Sstevel@tonic-gatean `ifslist' is that it provides all of the context necessary to
150*7c478bd9Sstevel@tonic-gateperform DHCP on a given interface: the state the interface is in, the
151*7c478bd9Sstevel@tonic-gatelast packet DHCP packet received on that interface, and so forth.  As
152*7c478bd9Sstevel@tonic-gateone can imagine, the `ifslist' structure is quite complicated and rules
153*7c478bd9Sstevel@tonic-gategoverning accessing its fields are equally convoluted -- see the
154*7c478bd9Sstevel@tonic-gatecomments in interface.h for more information.
155*7c478bd9Sstevel@tonic-gate
156*7c478bd9Sstevel@tonic-gateOne point that was brushed over in the preceding discussion of event
157*7c478bd9Sstevel@tonic-gatehandlers and timer queues was context.  Recall that the event-driven
158*7c478bd9Sstevel@tonic-gatenature of the agent requires that functions cannot block, lest they
159*7c478bd9Sstevel@tonic-gatestarve out others and impact the observed responsiveness of the agent.
160*7c478bd9Sstevel@tonic-gateAs an example, consider the process of extending a lease: the agent
161*7c478bd9Sstevel@tonic-gatemust send a REQUEST packet and wait for an ACK or NAK packet in
162*7c478bd9Sstevel@tonic-gateresponse.  This is done by sending a REQUEST and then registering a
163*7c478bd9Sstevel@tonic-gatecallback with the event handler that waits for an ACK or NAK packet to
164*7c478bd9Sstevel@tonic-gatearrive on the file descriptor associated with the interface.  Note
165*7c478bd9Sstevel@tonic-gatehowever, that when the ACK or NAK does arrive, and the callback
166*7c478bd9Sstevel@tonic-gatefunction called back, it must know which interface this packet is for
167*7c478bd9Sstevel@tonic-gate(it must get back its context).  This could be handled through an
168*7c478bd9Sstevel@tonic-gatead-hoc mapping of file descriptors to interfaces, but a cleaner
169*7c478bd9Sstevel@tonic-gateapproach is to have the event handler's register function
170*7c478bd9Sstevel@tonic-gate(iu_register_event()) take in an opaque context pointer, which will
171*7c478bd9Sstevel@tonic-gatethen be passed back to the callback.  In the agent, this context
172*7c478bd9Sstevel@tonic-gatepointer is always the `ifslist', but for reasons of decoupling and
173*7c478bd9Sstevel@tonic-gategenerality, the timer queue and event handler objects allow a generic
174*7c478bd9Sstevel@tonic-gate(void *) context argument.
175*7c478bd9Sstevel@tonic-gate
176*7c478bd9Sstevel@tonic-gateNote that there is nothing that guarantees the pointer passed into
177*7c478bd9Sstevel@tonic-gateiu_register_event() or iu_schedule_timer() will still be valid when
178*7c478bd9Sstevel@tonic-gatethe callback is called back (for instance, the memory may have been
179*7c478bd9Sstevel@tonic-gatefreed in the meantime).  To solve this problem, ifslists are reference
180*7c478bd9Sstevel@tonic-gatecounted.  For more details on how the reference count scheme is
181*7c478bd9Sstevel@tonic-gateimplemented, see the closing comments in interface.h regarding memory
182*7c478bd9Sstevel@tonic-gatemanagement.
183*7c478bd9Sstevel@tonic-gate
184*7c478bd9Sstevel@tonic-gateTransactions
185*7c478bd9Sstevel@tonic-gate------------
186*7c478bd9Sstevel@tonic-gate
187*7c478bd9Sstevel@tonic-gateMany operations performed via DHCP must be performed in groups -- for
188*7c478bd9Sstevel@tonic-gateinstance, acquiring a lease requires several steps: sending a
189*7c478bd9Sstevel@tonic-gateDISCOVER, collecting OFFERs, selecting an OFFER, sending a REQUEST,
190*7c478bd9Sstevel@tonic-gateand receiving an ACK, assuming everything goes well.  Note however
191*7c478bd9Sstevel@tonic-gatethat due to the event-driven model the agent operates in, these
192*7c478bd9Sstevel@tonic-gateoperations are not inherently "grouped" -- instead, the agent sends a
193*7c478bd9Sstevel@tonic-gateDISCOVER, goes back into the main event loop, waits for events
194*7c478bd9Sstevel@tonic-gate(perhaps even requests on the IPC channel to begin acquiring a lease
195*7c478bd9Sstevel@tonic-gateon another interface), eventually checks to see if an acceptable OFFER
196*7c478bd9Sstevel@tonic-gatehas come in, and so forth.  To some degree, the notion of the current
197*7c478bd9Sstevel@tonic-gatestate of an interface (SELECTING, REQUESTING, etc) helps control the
198*7c478bd9Sstevel@tonic-gatepotential chaos of the event-driven model (for instance, if while the
199*7c478bd9Sstevel@tonic-gateagent is waiting for an OFFER on a given interface, an IPC event comes
200*7c478bd9Sstevel@tonic-gatein requesting that the interface be RELEASED, the agent knows to send
201*7c478bd9Sstevel@tonic-gateback an error since the interface must be in at least the BOUND state
202*7c478bd9Sstevel@tonic-gatebefore a RELEASE can be performed.)
203*7c478bd9Sstevel@tonic-gate
204*7c478bd9Sstevel@tonic-gateHowever, states are not enough -- for instance, suppose that the agent
205*7c478bd9Sstevel@tonic-gatebegins trying to renew a lease -- this is done by sending a REQUEST
206*7c478bd9Sstevel@tonic-gatepacket and waiting for an ACK or NAK, which might never come.  If,
207*7c478bd9Sstevel@tonic-gatewhile waiting for the ACK or NAK, the user sends a request to renew
208*7c478bd9Sstevel@tonic-gatethe lease as well, then if the agent were to send another REQUEST,
209*7c478bd9Sstevel@tonic-gatethings could get quite complicated (and this is only the beginning of
210*7c478bd9Sstevel@tonic-gatethis rathole).  To protect against this, two objects exist:
211*7c478bd9Sstevel@tonic-gate`async_action' and `ipc_action'.  These objects are related, but
212*7c478bd9Sstevel@tonic-gateindependent of one another; the more essential object is the
213*7c478bd9Sstevel@tonic-gate`async_action', which we will discuss first.
214*7c478bd9Sstevel@tonic-gate
215*7c478bd9Sstevel@tonic-gateIn short, an `async_action' represents a pending transaction (aka
216*7c478bd9Sstevel@tonic-gateasynchronous action), of which each interface can have at most one.
217*7c478bd9Sstevel@tonic-gateThe `async_action' structure is embedded in the `ifslist' structure,
218*7c478bd9Sstevel@tonic-gatewhich is fine since there can be at most one pending transaction per
219*7c478bd9Sstevel@tonic-gateinterface.  Typical "asynchronous transactions" are START, EXTEND, and
220*7c478bd9Sstevel@tonic-gateINFORM, since each consists of a sequence of packets that must be done
221*7c478bd9Sstevel@tonic-gatewithout interruption.  Note that not all DHCP operations are
222*7c478bd9Sstevel@tonic-gate"asynchronous" -- for instance, a RELEASE operation is synchronous
223*7c478bd9Sstevel@tonic-gate(not asynchronous) since after the RELEASE is sent no reply is
224*7c478bd9Sstevel@tonic-gateexpected from the DHCP server.  Also, note that there can be
225*7c478bd9Sstevel@tonic-gatesynchronous operations intermixed with asynchronous operations
226*7c478bd9Sstevel@tonic-gatealthough it's not recommended.
227*7c478bd9Sstevel@tonic-gate
228*7c478bd9Sstevel@tonic-gateWhen the agent realizes it must perform an asynchronous transaction,
229*7c478bd9Sstevel@tonic-gateit first calls async_pending() to see if there is already one pending;
230*7c478bd9Sstevel@tonic-gateif so, the new transaction must fail (the details of failure depend on
231*7c478bd9Sstevel@tonic-gatehow the transaction was initiated, which is described in more detail
232*7c478bd9Sstevel@tonic-gatelater when the `ipc_action' object is discussed).  If there is no
233*7c478bd9Sstevel@tonic-gatepending asynchronous transaction, async_start() is called to begin
234*7c478bd9Sstevel@tonic-gateone.
235*7c478bd9Sstevel@tonic-gate
236*7c478bd9Sstevel@tonic-gateWhen the transaction is complete, async_finish() must be called to
237*7c478bd9Sstevel@tonic-gatecomplete the asynchronous action on that interface.  If the
238*7c478bd9Sstevel@tonic-gatetransaction is unable to complete within a certain amount of time
239*7c478bd9Sstevel@tonic-gate(more on this later), async_timeout() is invoked which attempts to
240*7c478bd9Sstevel@tonic-gatecancel the asynchronous action with async_cancel().  If the event is
241*7c478bd9Sstevel@tonic-gatenot cancellable it is left pending, although this means that no future
242*7c478bd9Sstevel@tonic-gateasynchronous actions can be performed on the interface until the
243*7c478bd9Sstevel@tonic-gatetransaction voluntarily calls async_finish().  While this may seem
244*7c478bd9Sstevel@tonic-gatesuboptimal, cancellation here is quite analogous to thread
245*7c478bd9Sstevel@tonic-gatecancellation, which is generally considered a difficult problem.
246*7c478bd9Sstevel@tonic-gate
247*7c478bd9Sstevel@tonic-gateThe notion of asynchronous transactions is complicated by the fact
248*7c478bd9Sstevel@tonic-gatethat they may originate from both inside and outside of the agent.
249*7c478bd9Sstevel@tonic-gateFor instance, a user initiates an asynchronous START transaction when
250*7c478bd9Sstevel@tonic-gatehe performs an `ifconfig hme0 dhcp start', but the agent will
251*7c478bd9Sstevel@tonic-gateinternally need to perform asynchronous EXTEND transactions to extend
252*7c478bd9Sstevel@tonic-gatethe lease before it expires.  This leads us into the `ipc_action'
253*7c478bd9Sstevel@tonic-gateobject.
254*7c478bd9Sstevel@tonic-gate
255*7c478bd9Sstevel@tonic-gateAn `ipc_action' represents the IPC-related pieces of an asynchronous
256*7c478bd9Sstevel@tonic-gatetransaction that was started as a result of a user request.  Only
257*7c478bd9Sstevel@tonic-gateIPC-generated asynchronous transactions have a valid `ipc_action'
258*7c478bd9Sstevel@tonic-gateobject.  Note that since there can be at most one asynchronous action
259*7c478bd9Sstevel@tonic-gateper interface, there can also be at most one `ipc_action' per
260*7c478bd9Sstevel@tonic-gateinterface (this means it can also conveniently be embedded inside the
261*7c478bd9Sstevel@tonic-gate`ifslist' structure).
262*7c478bd9Sstevel@tonic-gate
263*7c478bd9Sstevel@tonic-gateOne of the main purposes of the `ipc_action' object is to timeout user
264*7c478bd9Sstevel@tonic-gateevents.  This is not the same as timing out the transaction; for
265*7c478bd9Sstevel@tonic-gateinstance, when the user specifies a timeout value as an argument to
266*7c478bd9Sstevel@tonic-gateifconfig, he is specifying an `ipc_action' timeout; in other words,
267*7c478bd9Sstevel@tonic-gatehow long he is willing to wait for the command to complete.  However,
268*7c478bd9Sstevel@tonic-gateeven after the command times out for the user, the asynchronous
269*7c478bd9Sstevel@tonic-gatetransaction continues until async_timeout() occurs.
270*7c478bd9Sstevel@tonic-gate
271*7c478bd9Sstevel@tonic-gateIt is worth understanding these timeouts since the relationship is
272*7c478bd9Sstevel@tonic-gatesubtle but powerful.  The `async_action' timer specifies how long the
273*7c478bd9Sstevel@tonic-gateagent will try to perform the transaction; the `ipc_action' timer
274*7c478bd9Sstevel@tonic-gatespecifies how long the user is willing to wait for the action to
275*7c478bd9Sstevel@tonic-gatecomplete.  If when the `async_action' timer fires and async_timeout()
276*7c478bd9Sstevel@tonic-gateis called, there is no associated `ipc_action' (either because the
277*7c478bd9Sstevel@tonic-gatetransaction was not initiated by a user or because the user already
278*7c478bd9Sstevel@tonic-gatetimed out), then async_cancel() proceeds as described previously.  If,
279*7c478bd9Sstevel@tonic-gateon the other hand, the user is still waiting for the transaction to
280*7c478bd9Sstevel@tonic-gatecomplete, then async_timeout() is rescheduled and the transaction is
281*7c478bd9Sstevel@tonic-gateleft pending.  While this behavior might seem odd, it adheres to the
282*7c478bd9Sstevel@tonic-gateprinciples of least surprise: when a user is willing to wait for a
283*7c478bd9Sstevel@tonic-gatetransaction to complete, the agent should try for as long as they're
284*7c478bd9Sstevel@tonic-gatewilling to wait.  On the other hand, if the agent were to take that
285*7c478bd9Sstevel@tonic-gatestance with its internal transactions, it would block out
286*7c478bd9Sstevel@tonic-gateuser-requested operations if the internal transaction never completed
287*7c478bd9Sstevel@tonic-gate(perhaps because the server never sent an ACK in response to our lease
288*7c478bd9Sstevel@tonic-gateextension REQUEST).
289*7c478bd9Sstevel@tonic-gate
290*7c478bd9Sstevel@tonic-gateThe API provided for the `ipc_action' object is quite similar to the
291*7c478bd9Sstevel@tonic-gateone for the `async_action' object: when an IPC request comes in for an
292*7c478bd9Sstevel@tonic-gateoperation requiring asynchronous operation, ipc_action_start() is
293*7c478bd9Sstevel@tonic-gatecalled.  When the request completes, ipc_action_finish() is called.
294*7c478bd9Sstevel@tonic-gateIf the user times out before the request completes, then
295*7c478bd9Sstevel@tonic-gateipc_action_timeout() is called.
296*7c478bd9Sstevel@tonic-gate
297*7c478bd9Sstevel@tonic-gatePacket Management
298*7c478bd9Sstevel@tonic-gate-----------------
299*7c478bd9Sstevel@tonic-gate
300*7c478bd9Sstevel@tonic-gateAnother complicated area is packet management: building, manipulating,
301*7c478bd9Sstevel@tonic-gatesending and receiving packets.  These operations are all encapsulated
302*7c478bd9Sstevel@tonic-gatebehind a dozen or so interfaces (see packet.h) that abstract the
303*7c478bd9Sstevel@tonic-gateunimportant details away from the rest of the agent code.  In order to
304*7c478bd9Sstevel@tonic-gatesend a DHCP packet, code first calls init_pkt(), which returns a
305*7c478bd9Sstevel@tonic-gatedhcp_pkt_t initialized suitably for transmission.  Note that currently
306*7c478bd9Sstevel@tonic-gateinit_pkt() returns a dhcp_pkt_t that is actually allocated as part of
307*7c478bd9Sstevel@tonic-gatethe `ifslist', but this may change in the future..  After calling
308*7c478bd9Sstevel@tonic-gateinit_pkt(), the add_pkt_opt*() functions are used to add options to
309*7c478bd9Sstevel@tonic-gatethe DHCP packet.  Finally, send_pkt() can be used to transmit the
310*7c478bd9Sstevel@tonic-gatepacket to a given IP address.
311*7c478bd9Sstevel@tonic-gate
312*7c478bd9Sstevel@tonic-gateThe send_pkt() function is actually quite complicated; for one, it
313*7c478bd9Sstevel@tonic-gatemust internally use either DLPI or sockets depending on the state of
314*7c478bd9Sstevel@tonic-gatethe interface; for two, it handles the details of packet timeout and
315*7c478bd9Sstevel@tonic-gateretransmission.  The last argument to send_pkt() is a pointer to a
316*7c478bd9Sstevel@tonic-gate"stop function".  If this argument is passed as NULL, then the packet
317*7c478bd9Sstevel@tonic-gatewill only be sent once (it won't be retransmitted).  Otherwise, before
318*7c478bd9Sstevel@tonic-gateeach retransmission, the stop function will be called back prior to
319*7c478bd9Sstevel@tonic-gateretransmission.  The return value from this function indicates whether
320*7c478bd9Sstevel@tonic-gateto continue retransmission or not, which allows the send_pkt() caller
321*7c478bd9Sstevel@tonic-gateto control the retransmission policy without making it have to deal
322*7c478bd9Sstevel@tonic-gatewith the retransmission mechanism.  See init_reboot.c for an example
323*7c478bd9Sstevel@tonic-gateof this in action.
324*7c478bd9Sstevel@tonic-gate
325*7c478bd9Sstevel@tonic-gateThe recv_pkt() function is simpler but still complicated by the fact
326*7c478bd9Sstevel@tonic-gatethat one may want to receive several different types of packets at
327*7c478bd9Sstevel@tonic-gateonce; for instance, after sending a REQUEST, either an ACK or a NAK is
328*7c478bd9Sstevel@tonic-gateacceptable.  Also, before calling recv_pkt(), the caller must know
329*7c478bd9Sstevel@tonic-gatethat there is data to be read from the socket (this can be
330*7c478bd9Sstevel@tonic-gateaccomplished by using the event handler), otherwise recv_pkt() will
331*7c478bd9Sstevel@tonic-gateblock, which is clearly not acceptable.
332*7c478bd9Sstevel@tonic-gate
333*7c478bd9Sstevel@tonic-gateTime
334*7c478bd9Sstevel@tonic-gate----
335*7c478bd9Sstevel@tonic-gate
336*7c478bd9Sstevel@tonic-gateThe notion of time is an exceptionally subtle area.  You will notice
337*7c478bd9Sstevel@tonic-gatefive ways that time is represented in the source: as lease_t's,
338*7c478bd9Sstevel@tonic-gateuint32_t's, time_t's, hrtime_t's, and monosec_t's.  Each of these
339*7c478bd9Sstevel@tonic-gatetypes serves a slightly different function.
340*7c478bd9Sstevel@tonic-gate
341*7c478bd9Sstevel@tonic-gateThe `lease_t' type is the simplest to understand; it is the unit of
342*7c478bd9Sstevel@tonic-gatetime in the CD_{LEASE,T1,T2}_TIME options in a DHCP packet, as defined
343*7c478bd9Sstevel@tonic-gateby RFC2131. This is defined as a positive number of seconds (relative
344*7c478bd9Sstevel@tonic-gateto some fixed point in time) or the value `-1' (DHCP_PERM) which
345*7c478bd9Sstevel@tonic-gaterepresents infinity (i.e., a permanent lease).  The lease_t should be
346*7c478bd9Sstevel@tonic-gateused either when dealing with actual DHCP packets that are sent on the
347*7c478bd9Sstevel@tonic-gatewire or for variables which follow the exact definition given in the
348*7c478bd9Sstevel@tonic-gateRFC.
349*7c478bd9Sstevel@tonic-gate
350*7c478bd9Sstevel@tonic-gateThe `uint32_t' type is also used to represent a relative time in
351*7c478bd9Sstevel@tonic-gateseconds.  However, here the value `-1' is not special and of course
352*7c478bd9Sstevel@tonic-gatethis type is not tied to any definition given in RFC2131.  Use this
353*7c478bd9Sstevel@tonic-gatefor representing "offsets" from another point in time that are not
354*7c478bd9Sstevel@tonic-gateDHCP lease times.
355*7c478bd9Sstevel@tonic-gate
356*7c478bd9Sstevel@tonic-gateThe `time_t' type is the natural Unix type for representing time since
357*7c478bd9Sstevel@tonic-gatethe epoch.  Unfortunately, it is affected by stime(2) or adjtime(2)
358*7c478bd9Sstevel@tonic-gateand since the DHCP client is used during system installation (and thus
359*7c478bd9Sstevel@tonic-gatewhen time is typically being configured), the time_t cannot be used in
360*7c478bd9Sstevel@tonic-gategeneral to represent an absolute time since the epoch.  For instance,
361*7c478bd9Sstevel@tonic-gateif a time_t were used to keep track of when a lease began, and then a
362*7c478bd9Sstevel@tonic-gateminute later stime(2) was called to adjust the system clock forward a
363*7c478bd9Sstevel@tonic-gateyear, then the lease would appeared to have expired a year ago even
364*7c478bd9Sstevel@tonic-gatethough it has only been a minute.  For this reason, time_t's should
365*7c478bd9Sstevel@tonic-gateonly be used either when wall time must be displayed (such as in
366*7c478bd9Sstevel@tonic-gateDHCP_STATUS ipc transaction) or when a time meaningful across reboots
367*7c478bd9Sstevel@tonic-gatemust be obtained (such as when caching an ACK packet at system
368*7c478bd9Sstevel@tonic-gateshutdown).
369*7c478bd9Sstevel@tonic-gate
370*7c478bd9Sstevel@tonic-gateThe `hrtime_t' type returned from gethrtime() works around the
371*7c478bd9Sstevel@tonic-gatelimitations of the time_t in that it is not affected by stime(2) or
372*7c478bd9Sstevel@tonic-gateadjtime(2), with the disadvantage that it represents time from some
373*7c478bd9Sstevel@tonic-gatearbitrary time in the past and in nanoseconds.  The timer queue code
374*7c478bd9Sstevel@tonic-gatedeals with hrtime_t's directly since that particular piece of code is
375*7c478bd9Sstevel@tonic-gatemeant to be fairly independent of the rest of the DHCP client.
376*7c478bd9Sstevel@tonic-gate
377*7c478bd9Sstevel@tonic-gateHowever, dealing with nanoseconds is error-prone when all the other
378*7c478bd9Sstevel@tonic-gatetime types are in seconds.  As a result, yet another time type, the
379*7c478bd9Sstevel@tonic-gate`monosec_t' was created to represent a monotonically increasing time
380*7c478bd9Sstevel@tonic-gatein seconds, and is really no more than (hrtime_t / NANOSEC).  Note
381*7c478bd9Sstevel@tonic-gatethat this unit is typically used where time_t's would've traditionally
382*7c478bd9Sstevel@tonic-gatebeen used.  The function monosec() in util.c returns the current
383*7c478bd9Sstevel@tonic-gatemonosec, and monosec_to_time() can convert a given monosec to wall
384*7c478bd9Sstevel@tonic-gatetime, using the system's current notion of time.
385*7c478bd9Sstevel@tonic-gate
386*7c478bd9Sstevel@tonic-gateOne additional limitation of the `hrtime_t' and `monosec_t' types is
387*7c478bd9Sstevel@tonic-gatethat they are unaware of the passage of time across checkpoint/resume
388*7c478bd9Sstevel@tonic-gateevents (e.g., those generated by sys-suspend(1M)).  For example, if
389*7c478bd9Sstevel@tonic-gategethrtime() returns time T, and then the machine is suspended for 2
390*7c478bd9Sstevel@tonic-gatehours, and then gethrtime() is called again, the time returned is not
391*7c478bd9Sstevel@tonic-gateT + (2 * 60 * 60 * NANOSEC), but rather approximately still T.
392*7c478bd9Sstevel@tonic-gate
393*7c478bd9Sstevel@tonic-gateTo work around this (and other checkpoint/resume related problems),
394*7c478bd9Sstevel@tonic-gatewhen a system is resumed, the DHCP client makes the pessimistic
395*7c478bd9Sstevel@tonic-gateassumption that all finite leases have expired while the machine was
396*7c478bd9Sstevel@tonic-gatesuspended and must be obtained again.  This is known as "refreshing"
397*7c478bd9Sstevel@tonic-gatethe leases, and is handled by refresh_ifslist().
398*7c478bd9Sstevel@tonic-gate
399*7c478bd9Sstevel@tonic-gateNote that it appears like a more intelligent approach would be to
400*7c478bd9Sstevel@tonic-gaterecord the time(2) when the system is suspended, compare that against
401*7c478bd9Sstevel@tonic-gatethe time(2) when the system is resumed, and use the delta between them
402*7c478bd9Sstevel@tonic-gateto decide which leases have expired.  Sadly, this cannot be done since
403*7c478bd9Sstevel@tonic-gatethrough at least Solaris 8, it is not possible for userland programs
404*7c478bd9Sstevel@tonic-gateto be notified of system suspend events.
405*7c478bd9Sstevel@tonic-gate
406*7c478bd9Sstevel@tonic-gateConfiguration
407*7c478bd9Sstevel@tonic-gate-------------
408*7c478bd9Sstevel@tonic-gate
409*7c478bd9Sstevel@tonic-gateFor the most part, the DHCP client only *retrieves* configuration data
410*7c478bd9Sstevel@tonic-gatefrom the DHCP server, leaving the configuration to scripts (such as
411*7c478bd9Sstevel@tonic-gateboot scripts), which themselves use dhcpinfo(1) to retrieve the data
412*7c478bd9Sstevel@tonic-gatefrom the DHCP client.  This is desirable because it keeps the mechanism
413*7c478bd9Sstevel@tonic-gateof retrieving the configuration data decoupled from the policy of using
414*7c478bd9Sstevel@tonic-gatethe data.
415*7c478bd9Sstevel@tonic-gate
416*7c478bd9Sstevel@tonic-gateHowever, unless used in "inform" mode, the DHCP client *does* configure
417*7c478bd9Sstevel@tonic-gateeach interface enough to allow it to communicate with other hosts.
418*7c478bd9Sstevel@tonic-gateSpecifically, the DHCP client configures the interface's IP address,
419*7c478bd9Sstevel@tonic-gatenetmask, and broadcast address using the information provided by the
420*7c478bd9Sstevel@tonic-gateserver.  Further, for physical interfaces, any provided default routes
421*7c478bd9Sstevel@tonic-gateare also configured.  Since logical interfaces cannot be stored in the
422*7c478bd9Sstevel@tonic-gatekernel routing table, and in most cases, logical interfaces share a
423*7c478bd9Sstevel@tonic-gatedefault route with their associated physical interface, the DHCP client
424*7c478bd9Sstevel@tonic-gatedoes not automatically add or remove default routes when leases are
425*7c478bd9Sstevel@tonic-gateacquired or expired on logical interfaces.
426*7c478bd9Sstevel@tonic-gate
427*7c478bd9Sstevel@tonic-gateEvent Scripting
428*7c478bd9Sstevel@tonic-gate---------------
429*7c478bd9Sstevel@tonic-gate
430*7c478bd9Sstevel@tonic-gateThe DHCP client supports user program invocations on DHCP events.  The
431*7c478bd9Sstevel@tonic-gatesupported events are BOUND, EXTEND, EXPIRE, DROP and RELEASE.  The user
432*7c478bd9Sstevel@tonic-gateprogram runs asynchronous to the DHCP client so that the main event
433*7c478bd9Sstevel@tonic-gateloop stays active to process other events, including events triggered
434*7c478bd9Sstevel@tonic-gateby the user program (for example, when it invokes dhcpinfo).
435*7c478bd9Sstevel@tonic-gate
436*7c478bd9Sstevel@tonic-gateThe user program execution is part of the transaction of a DHCP command.
437*7c478bd9Sstevel@tonic-gateFor example, if the user program is not enabled, the transaction of the
438*7c478bd9Sstevel@tonic-gateDHCP command START is considered over when an ACK is received and the
439*7c478bd9Sstevel@tonic-gateinterface is configured successfully.  If the user program is enabled,
440*7c478bd9Sstevel@tonic-gateit is invoked after the interface is configured successfully, and the
441*7c478bd9Sstevel@tonic-gatetransaction is considered over only when the user program exits.  The
442*7c478bd9Sstevel@tonic-gateevent scripting implementation makes use of the asynchronous operations
443*7c478bd9Sstevel@tonic-gatediscussed in the "Transactions" section.
444*7c478bd9Sstevel@tonic-gate
445*7c478bd9Sstevel@tonic-gateThe upper bound of 58 seconds is imposed on how long the user program
446*7c478bd9Sstevel@tonic-gatecan run. If the user program does not exit after 55 seconds, the signal
447*7c478bd9Sstevel@tonic-gateSIGTERM is sent to it. If it still does not exit after additional 3
448*7c478bd9Sstevel@tonic-gateseconds, the signal SIGKILL is sent to it.  Since the event handler is
449*7c478bd9Sstevel@tonic-gatea wrapper around poll(), the DHCP client cannot directly observe the
450*7c478bd9Sstevel@tonic-gatecompletion of the user program.  Instead, the DHCP client creates a
451*7c478bd9Sstevel@tonic-gatechild "helper" process to synchronously monitor the user program (this
452*7c478bd9Sstevel@tonic-gateprocess is also used to send the aformentioned signals to the process,
453*7c478bd9Sstevel@tonic-gateif necessary). The DHCP client and the helper process share a pipe
454*7c478bd9Sstevel@tonic-gatewhich is included in the set of poll descriptors monitored by the DHCP
455*7c478bd9Sstevel@tonic-gateclient's event handler.  When the user program exits, the helper process
456*7c478bd9Sstevel@tonic-gatepasses the user program exit status to the DHCP client through the pipe,
457*7c478bd9Sstevel@tonic-gateinforming the DHCP client that the user program has finished.  When the
458*7c478bd9Sstevel@tonic-gateDHCP client is asked to shut down, it will wait for any running instances
459*7c478bd9Sstevel@tonic-gateof the user program to complete.
460