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